Repository: rancher/fleet
Pull request #2034 (2023-12-21 17:03:43)

Add initial gitrepo controller integration test
Refers to #2018 

* fix package name for bundle test



Patch:
diff --git a/.github/workflows/aks.yml b/.github/workflows/aks.yml
index 871eb332..34a70ac2 100644
--- a/.github/workflows/aks.yml
+++ b/.github/workflows/aks.yml
@@ -36,7 +36,7 @@ jobs:
           fetch-depth: 0
       -
         name: Setup Go
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
           go-version-file: 'go.mod'
           check-latest: true
diff --git a/.github/workflows/check-changes.yml b/.github/workflows/check-changes.yml
index 1ac1eddb..01b7ccfe 100644
--- a/.github/workflows/check-changes.yml
+++ b/.github/workflows/check-changes.yml
@@ -18,7 +18,7 @@ jobs:
         with:
           fetch-depth: 0
       -
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
           go-version-file: 'go.mod'
           check-latest: true
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 2d33aa27..0e8d835d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -22,7 +22,7 @@ jobs:
         with:
           fetch-depth: 0
       -
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
           go-version-file: 'go.mod'
           check-latest: true
diff --git a/.github/workflows/eks.yml b/.github/workflows/eks.yml
index 88634372..0ffbc965 100644
--- a/.github/workflows/eks.yml
+++ b/.github/workflows/eks.yml
@@ -45,7 +45,7 @@ jobs:
           fetch-depth: 0
       -
         name: Setup Go
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
           go-version-file: 'go.mod'
           check-latest: true
diff --git a/.github/workflows/fleet-upgrade.yml b/.github/workflows/fleet-upgrade.yml
index 06437c8b..14793301 100644
--- a/.github/workflows/fleet-upgrade.yml
+++ b/.github/workflows/fleet-upgrade.yml
@@ -30,7 +30,7 @@ jobs:
         with:
           fetch-depth: 0
       -
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
           go-version-file: 'go.mod'
           check-latest: true
@@ -101,7 +101,7 @@ jobs:
           ./.github/scripts/dump-failed-k3ds.sh
       -
         name: Upload Logs
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         if: failure()
         with:
           name: gha-fleet-e2e-logs-${{ github.sha }}-${{ matrix.k3s_version }}-${{ github.run_id }}
diff --git a/.github/workflows/gke.yml b/.github/workflows/gke.yml
index 7a47a843..9995833a 100644
--- a/.github/workflows/gke.yml
+++ b/.github/workflows/gke.yml
@@ -36,7 +36,7 @@ jobs:
           fetch-depth: 0
       -
         name: Setup Go
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
           go-version-file: 'go.mod'
           check-latest: true
@@ -50,7 +50,7 @@ jobs:
           credentials_json: '${{ secrets.CI_GOOGLE_CREDENTIALS }}'
       -
         name: Setup gcloud
-        uses: google-github-actions/setup-gcloud@v1
+        uses: google-github-actions/setup-gcloud@v2
         with:
           install_components: 'gke-gcloud-auth-plugin'
       -
diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml
index ca9f034c..5bb6b4fa 100644
--- a/.github/workflows/golangci-lint.yml
+++ b/.github/workflows/golangci-lint.yml
@@ -16,7 +16,7 @@ jobs:
           submodules: recursive
 
       - name: Setup Go
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
           go-version-file: 'go.mod'
           check-latest: true
diff --git a/.github/workflows/nightly-ci.yml b/.github/workflows/nightly-ci.yml
index 274c1518..56dc0ae9 100644
--- a/.github/workflows/nightly-ci.yml
+++ b/.github/workflows/nightly-ci.yml
@@ -38,7 +38,7 @@ jobs:
         with:
           fetch-depth: 0
       -
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
           go-version-file: 'go.mod'
           check-latest: true
@@ -160,7 +160,7 @@ jobs:
           ./.github/scripts/dump-failed-k3ds.sh
       -
         name: Upload Logs
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         if: failure()
         with:
           name: gha-nightly-e2e-logs-${{ github.sha }}-${{ matrix.k3s_version }}-${{ github.run_id }}
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index c7620a17..6627c8b7 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -22,7 +22,7 @@ jobs:
           fetch-depth: 0
       -
         name: Set up Go
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
           go-version-file: 'go.mod'
           check-latest: true
@@ -117,21 +117,21 @@ jobs:
           helm package --version="$VERSION" --app-version="$VERSION" -d ./dist ./charts/fleet-agent
       -
         name: Upload Controller Chart
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         with:
           name: fleet-${{ env.VERSION }}.tgz
           path: ./dist/fleet-${{ env.VERSION }}.tgz
           retention-days: 3
       -
         name: Upload CRD Chart
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         with:
           name: fleet-crd-${{ env.VERSION }}.tgz
           path: ./dist/fleet-crd-${{ env.VERSION }}.tgz
           retention-days: 3
       -
         name: Upload Agent Chart
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         with:
           name: fleet-agent-${{ env.VERSION }}.tgz
           path: ./dist/fleet-agent-${{ env.VERSION }}.tgz
diff --git a/.github/workflows/rancher-integration.yml b/.github/workflows/rancher-integration.yml
index 344639a9..a7c813a3 100644
--- a/.github/workflows/rancher-integration.yml
+++ b/.github/workflows/rancher-integration.yml
@@ -38,7 +38,7 @@ jobs:
           fetch-depth: 0
           ref: ${{ github.event.inputs.ref }}
       -
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
           go-version-file: 'go.mod'
           check-latest: true
@@ -186,7 +186,7 @@ jobs:
           ./.github/scripts/dump-failed-k3ds.sh
       -
         name: Upload logs
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         if: failure()
         with:
           name: gha-fleet-rancher-logs-${{ github.sha }}-${{ github.run_id }}
diff --git a/.github/workflows/rancher-upgrade-fleet.yml b/.github/workflows/rancher-upgrade-fleet.yml
index 254d691c..b35be4aa 100644
--- a/.github/workflows/rancher-upgrade-fleet.yml
+++ b/.github/workflows/rancher-upgrade-fleet.yml
@@ -51,7 +51,7 @@ jobs:
           fetch-depth: 0
           ref: ${{ github.event.inputs.ref }}
       -
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
           go-version-file: 'go.mod'
           check-latest: true
@@ -173,7 +173,7 @@ jobs:
           ./.github/scripts/dump-failed-k3ds.sh
       -
         name: Upload logs
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         if: failure()
         with:
           name: gha-fleet-upgrade-rancher-logs-${{ matrix.rancher_version }}-${{ matrix.k3s_version }}-${{ github.sha }}-${{ github.run_id }}
diff --git a/.github/workflows/release-against-rancher.yml b/.github/workflows/release-against-rancher.yml
index c97c5335..b604cd41 100644
--- a/.github/workflows/release-against-rancher.yml
+++ b/.github/workflows/release-against-rancher.yml
@@ -44,7 +44,7 @@ jobs:
           repository: rancher/rancher
           ref: ${{github.event.inputs.rancher_ref}}
           path: rancher
-      - uses: actions/setup-go@v4
+      - uses: actions/setup-go@v5
         with:
           go-version: ${{ env.SETUP_GO_VERSION }}
       - name: Run release script
diff --git a/charts/fleet-crd/templates/crds.yaml b/charts/fleet-crd/templates/crds.yaml
index a6ad100d..98cfbaae 100644
--- a/charts/fleet-crd/templates/crds.yaml
+++ b/charts/fleet-crd/templates/crds.yaml
@@ -156,6 +156,10 @@ spec:
                         enforce or lock down the deployment to a specific namespace.
                       nullable: true
                       type: string
+                    deleteCRDResources:
+                      description: DeleteCRDResources deletes CRDs. Warning! this
+                        will also delete all your Custom Resources.
+                      type: boolean
                     diff:
                       description: Diff can be used to ignore the modified state of
                         objects which are amended at runtime.
@@ -454,6 +458,10 @@ spec:
                         enforce or lock down the deployment to a specific namespace.
                       nullable: true
                       type: string
+                    deleteCRDResources:
+                      description: DeleteCRDResources deletes CRDs. Warning! this
+                        will also delete all your Custom Resources.
+                      type: boolean
                     diff:
                       description: Diff can be used to ignore the modified state of
                         objects which are amended at runtime.
@@ -1085,6 +1093,10 @@ spec:
                     or lock down the deployment to a specific namespace.
                   nullable: true
                   type: string
+                deleteCRDResources:
+                  description: DeleteCRDResources deletes CRDs. Warning! this will
+                    also delete all your Custom Resources.
+                  type: boolean
                 dependsOn:
                   description: DependsOn refers to the bundles which must be ready
                     before this bundle can be deployed.
@@ -1827,6 +1839,10 @@ spec:
                           namespace.
                         nullable: true
                         type: string
+                      deleteCRDResources:
+                        description: DeleteCRDResources deletes CRDs. Warning! this
+                          will also delete all your Custom Resources.
+                        type: boolean
                       diff:
                         description: Diff can be used to ignore the modified state
                           of objects which are amended at runtime.
diff --git a/go.mod b/go.mod
index 53a2c96d..96f01b30 100644
--- a/go.mod
+++ b/go.mod
@@ -66,6 +66,7 @@ require (
 	gopkg.in/yaml.v2 v2.4.0
 	helm.sh/helm/v3 v3.12.3
 	k8s.io/api v0.28.4
+	k8s.io/apiextensions-apiserver v0.28.3
 	k8s.io/apimachinery v0.28.4
 	k8s.io/cli-runtime v0.28.3
 	k8s.io/client-go v12.0.0+incompatible
@@ -242,7 +243,6 @@ require (
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/warnings.v0 v0.1.2 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
-	k8s.io/apiextensions-apiserver v0.28.3 // indirect
 	k8s.io/apiserver v0.28.3 // indirect
 	k8s.io/code-generator v0.28.3 // indirect
 	k8s.io/component-base v0.28.3 // indirect
diff --git a/integrationtests/utils/utils.go b/integrationtests/utils/namespace.go
similarity index 100%
rename from integrationtests/utils/utils.go
rename to integrationtests/utils/namespace.go
diff --git a/internal/cmd/cli/cleanup/cleanup.go b/internal/cmd/cli/cleanup/cleanup.go
index 14378a39..24835152 100644
--- a/internal/cmd/cli/cleanup/cleanup.go
+++ b/internal/cmd/cli/cleanup/cleanup.go
@@ -60,7 +60,7 @@ func ClusterRegistrations(ctx context.Context, client Getter, opts Options) erro
 
 		clusterKey := types.NamespacedName{Namespace: cr.Namespace, Name: cr.Status.ClusterName}
 		ts := latestGranted[clusterKey]
-		if cr.Status.Granted && ts.Before(&cr.CreationTimestamp) {
+		if cr.Status.Granted && ts.Before(&cr.CreationTimestamp) { // nolint:gosec // does not store pointer
 			latestGranted[clusterKey] = cr.CreationTimestamp
 		}
 	}
diff --git a/internal/helmdeployer/postrender.go b/internal/helmdeployer/postrender.go
index a1596bad..1c4bd30b 100644
--- a/internal/helmdeployer/postrender.go
+++ b/internal/helmdeployer/postrender.go
@@ -5,6 +5,8 @@ import (
 	"fmt"
 	"strings"
 
+	"helm.sh/helm/v3/pkg/kube"
+
 	"helm.sh/helm/v3/pkg/chart"
 
 	"github.com/rancher/fleet/internal/helmdeployer/kustomize"
@@ -18,6 +20,8 @@ import (
 	"k8s.io/apimachinery/pkg/api/meta"
 )
 
+const CRDKind = "CustomResourceDefinition"
+
 type postRender struct {
 	labelPrefix string
 	labelSuffix string
@@ -79,6 +83,10 @@ func (p *postRender) Run(renderedManifests *bytes.Buffer) (modifiedManifests *by
 		if err != nil {
 			return nil, err
 		}
+		if !p.opts.DeleteCRDResources &&
+			obj.GetObjectKind().GroupVersionKind().Kind == CRDKind {
+			annotations[kube.ResourcePolicyAnno] = kube.KeepPolicy
+		}
 		m.SetLabels(mergeMaps(m.GetLabels(), labels))
 		m.SetAnnotations(mergeMaps(m.GetAnnotations(), annotations))
 
diff --git a/package/Dockerfile b/package/Dockerfile
index 6f4bee07..ae854134 100644
--- a/package/Dockerfile
+++ b/package/Dockerfile
@@ -1,7 +1,6 @@
 ARG BUILD_ENV=dapper
 
 FROM registry.suse.com/bci/bci-busybox:15.5 AS base
-USER 1000
 
 FROM base AS copy_dapper
 ONBUILD ARG ARCH
@@ -12,4 +11,6 @@ ONBUILD ARG TARGETARCH
 ONBUILD COPY bin/fleetcontroller-linux-${TARGETARCH} /usr/bin/fleetcontroller
 
 FROM copy_${BUILD_ENV}
+RUN addgroup -g 1000 fleet-apply && adduser -u 1000 -G fleet-apply -D fleet-apply
+USER 1000
 CMD ["fleetcontroller"]
diff --git a/package/Dockerfile-windows.agent b/package/Dockerfile-windows.agent
index b38213d3..4a33d7d1 100644
--- a/package/Dockerfile-windows.agent
+++ b/package/Dockerfile-windows.agent
@@ -2,11 +2,12 @@ ARG SERVERCORE_VERSION
 FROM mcr.microsoft.com/windows/servercore:${SERVERCORE_VERSION}
 ARG RELEASES
 ARG VERSION
+USER ContainerUser
 ENV RELEASES=$RELEASES
 ENV VERSION=$VERSION
 SHELL ["powershell", "-NoLogo", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
 RUN pushd c:\; \
-    $URL = 'https://github.com/git-for-windows/git/releases/download/v2.33.1.windows.1/MinGit-2.33.1-64-bit.zip'; \
+    $URL = 'https://github.com/git-for-windows/git/releases/download/v2.42.0.windows.2/MinGit-2.42.0.2-64-bit.zip'; \
     \
     Write-Host ('Downloading git from {0} ...' -f $URL); \
     [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; \
diff --git a/package/Dockerfile.agent b/package/Dockerfile.agent
index ab60d676..32a19353 100644
--- a/package/Dockerfile.agent
+++ b/package/Dockerfile.agent
@@ -1,8 +1,12 @@
 ARG BUILD_ENV=dapper
 
 FROM registry.suse.com/suse/git:2.35 AS base
-USER 1000
 COPY package/log.sh /usr/bin/
+# Create non-root user and group
+RUN echo "fleet-apply:x:1000:1000::/home/fleet-apply:/bin/bash" >> /etc/passwd && \
+    echo "fleet-apply:x:1000:" >> /etc/group && \
+    mkdir /home/fleet-apply && \
+    chown -R fleet-apply:fleet-apply /home/fleet-apply
 
 FROM base AS copy_dapper
 ONBUILD ARG ARCH
@@ -15,4 +19,5 @@ ONBUILD COPY bin/fleetagent-linux-$TARGETARCH /usr/bin/fleetagent
 ONBUILD COPY bin/fleet-linux-$TARGETARCH /usr/bin/fleet
 
 FROM copy_${BUILD_ENV}
+USER 1000
 CMD ["fleetagent"]
diff --git a/pkg/apis/fleet.cattle.io/v1alpha1/bundledeployment_types.go b/pkg/apis/fleet.cattle.io/v1alpha1/bundledeployment_types.go
index 5edea999..91d4b00e 100644
--- a/pkg/apis/fleet.cattle.io/v1alpha1/bundledeployment_types.go
+++ b/pkg/apis/fleet.cattle.io/v1alpha1/bundledeployment_types.go
@@ -105,6 +105,9 @@ type BundleDeploymentOptions struct {
 	// NamespaceAnnotations are annotations that will be appended to the namespace created by Fleet.
 	// +nullable
 	NamespaceAnnotations *map[string]string `json:"namespaceAnnotations,omitempty"`
+
+	// DeleteCRDResources deletes CRDs. Warning! this will also delete all your Custom Resources.
+	DeleteCRDResources bool `json:"deleteCRDResources,omitempty"`
 }
 
 type DiffOptions struct {
diff --git a/pkg/apis/fleet.cattle.io/v1alpha1/doc.go b/pkg/apis/fleet.cattle.io/v1alpha1/doc.go
index 99f552e3..4a251048 100644
--- a/pkg/apis/fleet.cattle.io/v1alpha1/doc.go
+++ b/pkg/apis/fleet.cattle.io/v1alpha1/doc.go
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2020 - 2023 SUSE LLC
+Copyright (c) 2020 - 2024 SUSE LLC
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/pkg/apis/fleet.cattle.io/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/fleet.cattle.io/v1alpha1/zz_generated.deepcopy.go
index 28773a84..bc4a9c87 100644
--- a/pkg/apis/fleet.cattle.io/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/fleet.cattle.io/v1alpha1/zz_generated.deepcopy.go
@@ -2,7 +2,7 @@
 // +build !ignore_autogenerated
 
 /*
-Copyright (c) 2020 - 2023 SUSE LLC
+Copyright (c) 2020 - 2024 SUSE LLC
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/pkg/generated/controllers/fleet.cattle.io/factory.go b/pkg/generated/controllers/fleet.cattle.io/factory.go
index e1fd8f77..95df9be6 100644
--- a/pkg/generated/controllers/fleet.cattle.io/factory.go
+++ b/pkg/generated/controllers/fleet.cattle.io/factory.go
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2020 - 2023 SUSE LLC
+Copyright (c) 2020 - 2024 SUSE LLC
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/pkg/generated/controllers/fleet.cattle.io/interface.go b/pkg/generated/controllers/fleet.cattle.io/interface.go
index 49de0125..42fe1afe 100644
--- a/pkg/generated/controllers/fleet.cattle.io/interface.go
+++ b/pkg/generated/controllers/fleet.cattle.io/interface.go
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2020 - 2023 SUSE LLC
+Copyright (c) 2020 - 2024 SUSE LLC
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/bundle.go b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/bundle.go
index 4518089e..81d37e9c 100644
--- a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/bundle.go
+++ b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/bundle.go
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2020 - 2023 SUSE LLC
+Copyright (c) 2020 - 2024 SUSE LLC
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/bundledeployment.go b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/bundledeployment.go
index db17e03b..893afc01 100644
--- a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/bundledeployment.go
+++ b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/bundledeployment.go
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2020 - 2023 SUSE LLC
+Copyright (c) 2020 - 2024 SUSE LLC
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/bundlenamespacemapping.go b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/bundlenamespacemapping.go
index 1bf29eef..ee704337 100644
--- a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/bundlenamespacemapping.go
+++ b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/bundlenamespacemapping.go
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2020 - 2023 SUSE LLC
+Copyright (c) 2020 - 2024 SUSE LLC
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/cluster.go b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/cluster.go
index 684d8305..a50bf715 100644
--- a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/cluster.go
+++ b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/cluster.go
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2020 - 2023 SUSE LLC
+Copyright (c) 2020 - 2024 SUSE LLC
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/clustergroup.go b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/clustergroup.go
index de063082..f935e684 100644
--- a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/clustergroup.go
+++ b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/clustergroup.go
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2020 - 2023 SUSE LLC
+Copyright (c) 2020 - 2024 SUSE LLC
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/clusterregistration.go b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/clusterregistration.go
index 653e303a..3f35e2ca 100644
--- a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/clusterregistration.go
+++ b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/clusterregistration.go
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2020 - 2023 SUSE LLC
+Copyright (c) 2020 - 2024 SUSE LLC
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/clusterregistrationtoken.go b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/clusterregistrationtoken.go
index cda3a46d..cad611c4 100644
--- a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/clusterregistrationtoken.go
+++ b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/clusterregistrationtoken.go
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2020 - 2023 SUSE LLC
+Copyright (c) 2020 - 2024 SUSE LLC
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/content.go b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/content.go
index 282c57fc..17b79a09 100644
--- a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/content.go
+++ b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/content.go
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2020 - 2023 SUSE LLC
+Copyright (c) 2020 - 2024 SUSE LLC
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/gitrepo.go b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/gitrepo.go
index a4f75593..5627fbee 100644
--- a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/gitrepo.go
+++ b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/gitrepo.go
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2020 - 2023 SUSE LLC
+Copyright (c) 2020 - 2024 SUSE LLC
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/gitreporestriction.go b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/gitreporestriction.go
index 4023294b..c3b671ce 100644
--- a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/gitreporestriction.go
+++ b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/gitreporestriction.go
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2020 - 2023 SUSE LLC
+Copyright (c) 2020 - 2024 SUSE LLC
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/imagescan.go b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/imagescan.go
index 2595eda1..dcd60478 100644
--- a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/imagescan.go
+++ b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/imagescan.go
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2020 - 2023 SUSE LLC
+Copyright (c) 2020 - 2024 SUSE LLC
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/interface.go b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/interface.go
index f4e7870a..053ad1de 100644
--- a/pkg/generated/controllers/fleet.cattle.io/v1alpha1/interface.go
+++ b/pkg/generated/controllers/fleet.cattle.io/v1alpha1/interface.go
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2020 - 2023 SUSE LLC
+Copyright (c) 2020 - 2024 SUSE LLC
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.


Test patch:
diff --git a/.github/workflows/e2e-ci.yml b/.github/workflows/e2e-ci.yml
index eee0ec93..8ce08ef1 100644
--- a/.github/workflows/e2e-ci.yml
+++ b/.github/workflows/e2e-ci.yml
@@ -35,7 +35,7 @@ jobs:
         with:
           fetch-depth: 0
       -
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
           go-version-file: 'go.mod'
           check-latest: true
@@ -163,7 +163,7 @@ jobs:
           ./.github/scripts/dump-failed-k3ds.sh
       -
         name: Upload Logs
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         if: failure()
         with:
           name: gha-fleet-e2e-logs-${{ github.sha }}-${{ matrix.k3s_version }}-${{ github.run_id }}
diff --git a/.github/workflows/e2e-multicluster-ci.yml b/.github/workflows/e2e-multicluster-ci.yml
index ef579bd0..a4b6e102 100644
--- a/.github/workflows/e2e-multicluster-ci.yml
+++ b/.github/workflows/e2e-multicluster-ci.yml
@@ -30,7 +30,7 @@ jobs:
         with:
           fetch-depth: 0
       -
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
           go-version-file: 'go.mod'
           check-latest: true
@@ -155,7 +155,7 @@ jobs:
           ./.github/scripts/dump-failed-k3ds.sh
       -
         name: Upload Logs
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         if: failure()
         with:
           name: gha-fleet-mc-e2e-logs-${{ github.sha }}-${{ github.run_id }}
diff --git a/integrationtests/controller/bundle/bundle_labels_test.go b/integrationtests/controller/bundle/bundle_labels_test.go
index 0d37b9ea..c86a85dd 100644
--- a/integrationtests/controller/bundle/bundle_labels_test.go
+++ b/integrationtests/controller/bundle/bundle_labels_test.go
@@ -1,4 +1,4 @@
-package agent
+package bundle
 
 import (
 	. "github.com/onsi/ginkgo/v2"
diff --git a/integrationtests/controller/bundle/bundle_targets_test.go b/integrationtests/controller/bundle/bundle_targets_test.go
index 5af0750d..d162f46a 100644
--- a/integrationtests/controller/bundle/bundle_targets_test.go
+++ b/integrationtests/controller/bundle/bundle_targets_test.go
@@ -1,4 +1,4 @@
-package agent
+package bundle
 
 import (
 	"strings"
@@ -21,14 +21,14 @@ var _ = Describe("Bundle targets", Ordered, func() {
 		var err error
 		namespace, err = utils.NewNamespaceName()
 		Expect(err).ToNot(HaveOccurred())
-		Expect(k8sClient.Create(ctx, &corev1.Namespace{
-			ObjectMeta: metav1.ObjectMeta{Name: namespace},
-		})).ToNot(HaveOccurred())
+
+		ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}
+		Expect(k8sClient.Create(ctx, ns)).ToNot(HaveOccurred())
 
 		createClustersAndClusterGroups()
 
 		DeferCleanup(func() {
-			Expect(k8sClient.Delete(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}})).ToNot(HaveOccurred())
+			Expect(k8sClient.Delete(ctx, ns)).ToNot(HaveOccurred())
 		})
 	})
 
diff --git a/integrationtests/controller/bundle/suite_test.go b/integrationtests/controller/bundle/suite_test.go
index 307b4376..c59cbf11 100644
--- a/integrationtests/controller/bundle/suite_test.go
+++ b/integrationtests/controller/bundle/suite_test.go
@@ -1,4 +1,4 @@
-package agent
+package bundle
 
 import (
 	"context"
@@ -28,11 +28,12 @@ import (
 )
 
 var (
+	cancel    context.CancelFunc
 	cfg       *rest.Config
-	testEnv   *envtest.Environment
 	ctx       context.Context
-	cancel    context.CancelFunc
 	k8sClient client.Client
+	testEnv   *envtest.Environment
+
 	namespace string
 )
 
@@ -57,19 +58,14 @@ var _ = BeforeSuite(func() {
 	var err error
 	cfg, err = testEnv.Start()
 	Expect(err).NotTo(HaveOccurred())
-	Expect(cfg).NotTo(BeNil())
 
 	utilruntime.Must(v1alpha1.AddToScheme(scheme.Scheme))
 	//+kubebuilder:scaffold:scheme
 
 	k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
 	Expect(err).NotTo(HaveOccurred())
-	Expect(k8sClient).NotTo(BeNil())
 
-	zopts := zap.Options{
-		Development: true,
-	}
-	ctrl.SetLogger(zap.New(zap.UseFlagOptions(&zopts)))
+	ctrl.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true})))
 
 	mgr, err := ctrl.NewManager(cfg, ctrl.Options{
 		Scheme:         scheme.Scheme,
diff --git a/integrationtests/controller/gitrepo/gitrepo_test.go b/integrationtests/controller/gitrepo/gitrepo_test.go
new file mode 100644
index 00000000..11267346
--- /dev/null
+++ b/integrationtests/controller/gitrepo/gitrepo_test.go
@@ -0,0 +1,109 @@
+package gitrepo
+
+import (
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+
+	"github.com/rancher/fleet/integrationtests/utils"
+	"github.com/rancher/fleet/pkg/apis/fleet.cattle.io/v1alpha1"
+	gitjob "github.com/rancher/gitjob/pkg/apis/gitjob.cattle.io/v1"
+
+	corev1 "k8s.io/api/core/v1"
+	rbacv1 "k8s.io/api/rbac/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/types"
+)
+
+var _ = Describe("GitRepo", Ordered, func() {
+	BeforeAll(func() {
+		var err error
+		namespace, err = utils.NewNamespaceName()
+		Expect(err).ToNot(HaveOccurred())
+		ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}
+		Expect(k8sClient.Create(ctx, ns)).ToNot(HaveOccurred())
+
+		DeferCleanup(func() {
+			Expect(k8sClient.Delete(ctx, ns)).ToNot(HaveOccurred())
+		})
+	})
+
+	var gitrepo *v1alpha1.GitRepo
+
+	BeforeEach(func() {
+		gitrepo = &v1alpha1.GitRepo{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "test-gitrepo",
+				Namespace: namespace,
+			},
+			Spec: v1alpha1.GitRepoSpec{
+				Repo: "https://github.com/rancher/fleet-test-data/not-found",
+			},
+		}
+
+		DeferCleanup(func() {
+			Expect(k8sClient.Delete(ctx, gitrepo)).ToNot(HaveOccurred())
+		})
+	})
+
+	When("creating a gitrepo", func() {
+		JustBeforeEach(func() {
+			err := k8sClient.Create(ctx, gitrepo)
+			Expect(err).NotTo(HaveOccurred())
+		})
+
+		It("creates a gitjob and RBAC resources", func() {
+			gitjob := &gitjob.GitJob{}
+			Eventually(func() error {
+				return k8sClient.Get(ctx, types.NamespacedName{Name: "test-gitrepo", Namespace: namespace}, gitjob)
+			}).ShouldNot(HaveOccurred())
+
+			Expect(gitjob).ToNot(BeNil())
+			Expect(gitjob.Spec.Git.Repo).To(Equal(gitrepo.Spec.Repo))
+			Expect(gitjob.Spec.SyncInterval).To(Equal(0))
+			Expect(gitjob.Spec.JobSpec.Template.Spec.Containers).To(HaveLen(1))
+			Expect(gitjob.Spec.JobSpec.Template.Spec.Containers[0].Args).To(ContainElements("fleet", "apply"))
+
+			err := k8sClient.Get(ctx, types.NamespacedName{Name: "git-test-gitrepo", Namespace: namespace}, &corev1.ServiceAccount{})
+			Expect(err).NotTo(HaveOccurred())
+			err = k8sClient.Get(ctx, types.NamespacedName{Name: "git-test-gitrepo", Namespace: namespace}, &rbacv1.Role{})
+			Expect(err).NotTo(HaveOccurred())
+			err = k8sClient.Get(ctx, types.NamespacedName{Name: "git-test-gitrepo", Namespace: namespace}, &rbacv1.RoleBinding{})
+			Expect(err).NotTo(HaveOccurred())
+		})
+
+		It("updates the gitrepo status", func() {
+			org := gitrepo.ResourceVersion
+			Eventually(func() bool {
+				_ = k8sClient.Get(ctx, types.NamespacedName{Name: "test-gitrepo", Namespace: namespace}, gitrepo)
+				return gitrepo.ResourceVersion > org
+			}).Should(BeTrue())
+
+			Expect(gitrepo.Status.Display.ReadyBundleDeployments).To(Equal("0/0"))
+			Expect(gitrepo.Status.Display.State).To(Equal("GitUpdating"))
+			Expect(gitrepo.Status.Display.Error).To(BeFalse())
+			Expect(gitrepo.Status.Conditions).To(HaveLen(1))
+			Expect(gitrepo.Status.Conditions[0].Type).To(Equal("Accepted"))
+			Expect(string(gitrepo.Status.Conditions[0].Status)).To(Equal("True"))
+			Expect(gitrepo.Status.DeepCopy().ObservedGeneration).To(Equal(int64(1)))
+		})
+	})
+
+	When("updating a gitrepo", func() {
+		JustBeforeEach(func() {
+			err := k8sClient.Create(ctx, gitrepo)
+			Expect(err).NotTo(HaveOccurred())
+
+			gitrepo.Spec.Repo = "newURL"
+			err = k8sClient.Update(ctx, gitrepo)
+			Expect(err).NotTo(HaveOccurred())
+		})
+
+		It("updates the gitjob", func() {
+			gitjob := &gitjob.GitJob{}
+			Eventually(func() bool {
+				_ = k8sClient.Get(ctx, types.NamespacedName{Name: "test-gitrepo", Namespace: namespace}, gitjob)
+				return gitjob != nil && gitjob.Spec.Git.Repo == "newURL"
+			}).Should(BeTrue())
+		})
+	})
+})
diff --git a/integrationtests/controller/gitrepo/suite_test.go b/integrationtests/controller/gitrepo/suite_test.go
new file mode 100644
index 00000000..1bd90a9f
--- /dev/null
+++ b/integrationtests/controller/gitrepo/suite_test.go
@@ -0,0 +1,108 @@
+package gitrepo
+
+import (
+	"context"
+	"path/filepath"
+	"testing"
+	"time"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	"github.com/reugn/go-quartz/quartz"
+
+	"github.com/rancher/fleet/internal/cmd/controller/reconciler"
+	"github.com/rancher/fleet/internal/config"
+	"github.com/rancher/fleet/pkg/apis/fleet.cattle.io/v1alpha1"
+	gitjob "github.com/rancher/gitjob/pkg/apis/gitjob.cattle.io/v1"
+
+	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+	"k8s.io/client-go/rest"
+	"k8s.io/kubectl/pkg/scheme"
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/envtest"
+	"sigs.k8s.io/controller-runtime/pkg/log/zap"
+	metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
+)
+
+var (
+	cancel    context.CancelFunc
+	cfg       *rest.Config
+	ctx       context.Context
+	testEnv   *envtest.Environment
+	k8sClient client.Client
+
+	namespace string
+)
+
+const (
+	timeout = 30 * time.Second
+)
+
+func TestFleet(t *testing.T) {
+	RegisterFailHandler(Fail)
+	RunSpecs(t, "Fleet GitRepo Suite")
+}
+
+var _ = BeforeSuite(func() {
+	SetDefaultEventuallyTimeout(timeout)
+
+	ctx, cancel = context.WithCancel(context.TODO())
+	testEnv = &envtest.Environment{
+		CRDDirectoryPaths: []string{
+			filepath.Join("..", "..", "..", "charts", "fleet-crd", "templates", "crds.yaml"),
+			filepath.Join("..", "..", "..", "charts", "fleet-crd", "templates", "gitjobs-crds.yaml"),
+		},
+		ErrorIfCRDPathMissing: true,
+	}
+
+	var err error
+	cfg, err = testEnv.Start()
+	Expect(err).NotTo(HaveOccurred())
+
+	utilruntime.Must(v1alpha1.AddToScheme(scheme.Scheme))
+	utilruntime.Must(gitjob.AddToScheme(scheme.Scheme))
+	//+kubebuilder:scaffold:scheme
+
+	k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
+	Expect(err).NotTo(HaveOccurred())
+
+	ctrl.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true})))
+
+	config.Set(config.DefaultConfig())
+
+	mgr, err := ctrl.NewManager(cfg, ctrl.Options{
+		Scheme:         scheme.Scheme,
+		LeaderElection: false,
+		Metrics:        metricsserver.Options{BindAddress: "0"},
+	})
+	Expect(err).ToNot(HaveOccurred())
+
+	sched := quartz.NewStdScheduler()
+	Expect(sched).ToNot(BeNil())
+
+	// Set up the gitrepo reconciler
+	err = (&reconciler.GitRepoReconciler{
+		Client: mgr.GetClient(),
+		Scheme: mgr.GetScheme(),
+
+		Scheduler: sched,
+	}).SetupWithManager(mgr)
+	Expect(err).ToNot(HaveOccurred(), "failed to set up manager")
+
+	sched.Start(ctx)
+	DeferCleanup(func() {
+		sched.Stop()
+	})
+
+	go func() {
+		defer GinkgoRecover()
+		err = mgr.Start(ctx)
+		Expect(err).ToNot(HaveOccurred(), "failed to run manager")
+	}()
+})
+
+var _ = AfterSuite(func() {
+	cancel()
+	Expect(testEnv.Stop()).ToNot(HaveOccurred())
+})
diff --git a/internal/helmdeployer/postrender_test.go b/internal/helmdeployer/postrender_test.go
new file mode 100644
index 00000000..0fb23d1d
--- /dev/null
+++ b/internal/helmdeployer/postrender_test.go
@@ -0,0 +1,119 @@
+package helmdeployer
+
+import (
+	"bytes"
+	"testing"
+
+	"github.com/rancher/fleet/internal/manifest"
+	"github.com/rancher/fleet/pkg/apis/fleet.cattle.io/v1alpha1"
+	"github.com/rancher/wrangler/v2/pkg/yaml"
+
+	"github.com/google/go-cmp/cmp"
+	"helm.sh/helm/v3/pkg/chart"
+	"helm.sh/helm/v3/pkg/kube"
+	corev1 "k8s.io/api/core/v1"
+	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+	"k8s.io/apimachinery/pkg/api/meta"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	kruntime "k8s.io/apimachinery/pkg/runtime"
+)
+
+func TestPostRenderer_Run_DeleteCRDs(t *testing.T) {
+	tests := map[string]struct {
+		obj                 kruntime.Object
+		opts                v1alpha1.BundleDeploymentOptions
+		expectedAnnotations map[string]string
+	}{
+		"default (no DeleteCRDResources specified)": {
+			obj: &apiextensionsv1.CustomResourceDefinition{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       CRDKind,
+					APIVersion: "apiextensions.k8s.io/v1",
+				},
+			},
+			opts: v1alpha1.BundleDeploymentOptions{},
+			expectedAnnotations: map[string]string{
+				kube.ResourcePolicyAnno:      kube.KeepPolicy,
+				"objectset.rio.cattle.io/id": "-",
+			},
+		},
+		"DeleteCRDResources set to true": {
+			obj: &apiextensionsv1.CustomResourceDefinition{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       CRDKind,
+					APIVersion: "apiextensions.k8s.io/v1",
+				},
+			},
+			opts: v1alpha1.BundleDeploymentOptions{
+				DeleteCRDResources: true,
+			},
+			expectedAnnotations: map[string]string{
+				"objectset.rio.cattle.io/id": "-",
+			},
+		},
+		"DeleteCRDResources set to false": {
+			obj: &apiextensionsv1.CustomResourceDefinition{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       CRDKind,
+					APIVersion: "apiextensions.k8s.io/v1",
+				},
+			},
+			opts: v1alpha1.BundleDeploymentOptions{
+				DeleteCRDResources: false,
+			},
+			expectedAnnotations: map[string]string{
+				kube.ResourcePolicyAnno:      kube.KeepPolicy,
+				"objectset.rio.cattle.io/id": "-",
+			},
+		},
+		"Annotation not added for non CRDs resources": {
+			obj: &corev1.Pod{
+				TypeMeta: metav1.TypeMeta{
+					Kind: "Pod",
+				},
+			},
+			opts: v1alpha1.BundleDeploymentOptions{
+				DeleteCRDResources: false,
+			},
+			expectedAnnotations: map[string]string{
+				"objectset.rio.cattle.io/id": "-",
+			},
+		},
+	}
+
+	for name, test := range tests {
+		t.Run(name, func(t *testing.T) {
+			data, err := yaml.ToBytes([]kruntime.Object{test.obj})
+			if err != nil {
+				t.Errorf("unexpected error %v", err)
+			}
+			renderedManifests := bytes.NewBuffer(data)
+
+			pr := postRender{
+				manifest: &manifest.Manifest{
+					Resources: []v1alpha1.BundleResource{},
+				},
+				chart: &chart.Chart{},
+				opts:  test.opts,
+			}
+			postRenderedManifests, err := pr.Run(renderedManifests)
+			if err != nil {
+				t.Errorf("unexpected error %v", err)
+			}
+
+			data = postRenderedManifests.Bytes()
+			objs, err := yaml.ToObjects(bytes.NewBuffer(data))
+			if err != nil {
+				t.Errorf("unexpected error %v", err)
+			}
+
+			m, err := meta.Accessor(objs[0])
+			if err != nil {
+				t.Errorf("unexpected error %v", err)
+			}
+			if !cmp.Equal(m.GetAnnotations(), test.expectedAnnotations) {
+				t.Errorf("expected %s, got %s", test.expectedAnnotations, m.GetAnnotations())
+			}
+		})
+	}
+}