diff --git a/rollouts/api/v1alpha1/remoterootsync_types.go b/rollouts/api/v1alpha1/remoterootsync_types.go index ea65dd9c91..a4cee11e6c 100644 --- a/rollouts/api/v1alpha1/remoterootsync_types.go +++ b/rollouts/api/v1alpha1/remoterootsync_types.go @@ -33,7 +33,8 @@ type RemoteRootSyncSpec struct { } type RootSyncInfo struct { - Spec *RootSyncSpec `json:"spec,omitempty"` + Spec *RootSyncSpec `json:"spec,omitempty"` + Metadata *Metadata `json:"metadata,omitempty"` } type RootSyncSpec struct { @@ -54,6 +55,12 @@ type GitInfo struct { NoSSLVerify bool `json:"noSSLVerify,omitempty"` } +// Metadata specifies labels and annotations to add to the RSync object. +type Metadata struct { + Labels map[string]string `json:"labels,omitempty"` + Annotations map[string]string `json:"annotations,omitempty"` +} + // RemoteRootSyncStatus defines the observed state of RemoteRootSync type RemoteRootSyncStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster diff --git a/rollouts/api/v1alpha1/rollout_types.go b/rollouts/api/v1alpha1/rollout_types.go index 736328f2f3..b6a408cd72 100644 --- a/rollouts/api/v1alpha1/rollout_types.go +++ b/rollouts/api/v1alpha1/rollout_types.go @@ -128,13 +128,16 @@ type SyncTemplate struct { // RootSyncTemplate represent the sync template for RootSync. type RootSyncTemplate struct { - SourceFormat string `json:"sourceFormat,omitempty"` - Git *GitInfo `json:"git,omitempty"` + SourceFormat string `json:"sourceFormat,omitempty"` + Git *GitInfo `json:"git,omitempty"` + Metadata *Metadata `json:"metadata,omitempty"` } +// RepoSyncTemplate represent the sync template for RepoSync. type RepoSyncTemplate struct { - SourceFormat string `json:"sourceFormat,omitempty"` - Git *GitInfo `json:"git,omitempty"` + SourceFormat string `json:"sourceFormat,omitempty"` + Git *GitInfo `json:"git,omitempty"` + Metadata *Metadata `json:"metadata,omitempty"` } // +kubebuilder:validation:Enum=AllClusters;Custom diff --git a/rollouts/api/v1alpha1/zz_generated.deepcopy.go b/rollouts/api/v1alpha1/zz_generated.deepcopy.go index bac94d0c9b..c0b14cd3c9 100644 --- a/rollouts/api/v1alpha1/zz_generated.deepcopy.go +++ b/rollouts/api/v1alpha1/zz_generated.deepcopy.go @@ -166,6 +166,35 @@ func (in *GitInfo) DeepCopy() *GitInfo { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Metadata) DeepCopyInto(out *Metadata) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Metadata. +func (in *Metadata) DeepCopy() *Metadata { + if in == nil { + return nil + } + out := new(Metadata) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PackageStatus) DeepCopyInto(out *PackageStatus) { *out = *in @@ -433,6 +462,11 @@ func (in *RepoSyncTemplate) DeepCopyInto(out *RepoSyncTemplate) { *out = new(GitInfo) **out = **in } + if in.Metadata != nil { + in, out := &in.Metadata, &out.Metadata + *out = new(Metadata) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RepoSyncTemplate. @@ -601,6 +635,11 @@ func (in *RootSyncInfo) DeepCopyInto(out *RootSyncInfo) { *out = new(RootSyncSpec) (*in).DeepCopyInto(*out) } + if in.Metadata != nil { + in, out := &in.Metadata, &out.Metadata + *out = new(Metadata) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RootSyncInfo. @@ -641,6 +680,11 @@ func (in *RootSyncTemplate) DeepCopyInto(out *RootSyncTemplate) { *out = new(GitInfo) **out = **in } + if in.Metadata != nil { + in, out := &in.Metadata, &out.Metadata + *out = new(Metadata) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RootSyncTemplate. diff --git a/rollouts/config/crd/bases/gitops.kpt.dev_remoterootsyncs.yaml b/rollouts/config/crd/bases/gitops.kpt.dev_remoterootsyncs.yaml index 00a424b235..6a6e096794 100644 --- a/rollouts/config/crd/bases/gitops.kpt.dev_remoterootsyncs.yaml +++ b/rollouts/config/crd/bases/gitops.kpt.dev_remoterootsyncs.yaml @@ -60,6 +60,19 @@ spec: type: object template: properties: + metadata: + description: Metadata specifies labels and annotations to add + to the RSync object. + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object spec: properties: git: diff --git a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml index 7e77a08e42..022ef2d93e 100644 --- a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml +++ b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml @@ -184,6 +184,8 @@ spec: RSync object used to syncing the packages. properties: repoSync: + description: RepoSyncTemplate represent the sync template for + RepoSync. properties: git: properties: @@ -217,6 +219,19 @@ spec: - auth - repo type: object + metadata: + description: Metadata specifies labels and annotations to + add to the RSync object. + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object sourceFormat: type: string type: object @@ -256,6 +271,19 @@ spec: - auth - repo type: object + metadata: + description: Metadata specifies labels and annotations to + add to the RSync object. + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object sourceFormat: type: string type: object diff --git a/rollouts/config/samples/gitops_rollout_deletionpolicy.yaml b/rollouts/config/samples/gitops_rollout_deletionpolicy.yaml new file mode 100644 index 0000000000..b36bd266a5 --- /dev/null +++ b/rollouts/config/samples/gitops_rollout_deletionpolicy.yaml @@ -0,0 +1,52 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Adding 'configsync.gke.io/deletion-propagation-policy: Foreground' will result in +# managed objects being cleaned up by Config Sync when the RootSync object created +# by the rollout is deleted. As a note, this is not included in any release tag (as +# of the release date of v1.14.2), and to use this, you must build Config Sync from +# HEAD. + +apiVersion: gitops.kpt.dev/v1alpha1 +kind: Rollout +metadata: + name: sample +spec: + description: rollout kpt samples + clusters: + sourceType: KCC + packages: + sourceType: GitHub + github: + selector: + org: GoogleContainerTools + repo: kpt-samples + directory: "*" + revision: main + targets: + selector: + matchLabels: + location/city: example + packageToTargetMatcher: + type: AllClusters + strategy: + type: RollingUpdate + rollingUpdate: + maxConcurrent: 2 + syncTemplate: + rootSync: + metadata: + annotations: + configsync.gke.io/deletion-propagation-policy: Foreground + type: RootSync \ No newline at end of file diff --git a/rollouts/controllers/remoterootsync_controller.go b/rollouts/controllers/remoterootsync_controller.go index 3545e5f27a..b1d8c984af 100644 --- a/rollouts/controllers/remoterootsync_controller.go +++ b/rollouts/controllers/remoterootsync_controller.go @@ -263,19 +263,22 @@ func (r *RemoteRootSyncReconciler) pruneWatches(rrsnn types.NamespacedName, clus func BuildObjectsToApply(remoterootsync *gitopsv1alpha1.RemoteRootSync) (*unstructured.Unstructured, error) { newRootSync, err := runtime.DefaultUnstructuredConverter.ToUnstructured(remoterootsync.Spec.Template) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to convert to unstructured type: %w", err) } + u := unstructured.Unstructured{Object: newRootSync} u.SetGroupVersionKind(rootSyncGVK) u.SetName(remoterootsync.Name) u.SetNamespace(rootSyncNamespace) - u.SetLabels(map[string]string{ - remoteRootSyncNameLabel: remoterootsync.Name, - remoteRootSyncNamespaceLabel: remoterootsync.Namespace, - }) - if err != nil { - return nil, fmt.Errorf("failed to convert to unstructured type: %w", err) + + labels := u.GetLabels() + if labels == nil { + labels = make(map[string]string) } + labels[remoteRootSyncNameLabel] = remoterootsync.Name + labels[remoteRootSyncNamespaceLabel] = remoterootsync.Namespace + u.SetLabels(labels) + return &u, nil } diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go index 2513bc9449..603075caf0 100644 --- a/rollouts/controllers/rollout_controller.go +++ b/rollouts/controllers/rollout_controller.go @@ -21,6 +21,7 @@ import ( "flag" "fmt" "math" + "reflect" "sort" "strings" "sync" @@ -276,6 +277,14 @@ func (r *RolloutReconciler) getPackageDiscoveryClient(rolloutNamespacedName type func (r *RolloutReconciler) reconcileRollout(ctx context.Context, rollout *gitopsv1alpha1.Rollout, strategy *gitopsv1alpha1.ProgressiveRolloutStrategy, packageDiscoveryClient *packagediscovery.PackageDiscovery) error { logger := klog.FromContext(ctx) + if rollout != nil && rollout.Spec.SyncTemplate != nil { + if rollout.Spec.SyncTemplate.Type == gitopsv1alpha1.TemplateTypeRepoSync { + err := fmt.Errorf("reposync is not yet supported") + logger.Error(err, "") + return err + } + } + targetClusters, err := r.store.ListClusters(ctx, &rollout.Spec.Clusters, rollout.Spec.Targets.Selector) discoveredPackages, err := packageDiscoveryClient.GetPackages(ctx, rollout.Spec.Packages) if err != nil { @@ -413,10 +422,9 @@ func (r *RolloutReconciler) computeTargets(ctx context.Context, } } else { // remoterootsync already exists - if pkg.Revision != rrs.Spec.Template.Spec.Git.Revision { - rrs.Spec.Template.Spec.Git.Revision = pkg.Revision - // revision has been updated - targets.ToBeUpdated = append(targets.ToBeUpdated, &rrs) + updated, needsUpdate := pkgNeedsUpdate(rollout, rrs, pkg) + if needsUpdate { + targets.ToBeUpdated = append(targets.ToBeUpdated, updated) } else { targets.Unchanged = append(targets.Unchanged, &rrs) } @@ -430,6 +438,18 @@ func (r *RolloutReconciler) computeTargets(ctx context.Context, return targets, nil } +func pkgNeedsUpdate(rollout *gitopsv1alpha1.Rollout, rrs gitopsv1alpha1.RemoteRootSync, pkg *packagediscovery.DiscoveredPackage) (*gitopsv1alpha1.RemoteRootSync, bool) { + // TODO: We need to check other things here besides git.Revision and metadata + if pkg.Revision != rrs.Spec.Template.Spec.Git.Revision || + !reflect.DeepEqual(rollout.Spec.SyncTemplate.RootSync.Metadata, + rrs.Spec.Template.Metadata) { + rrs.Spec.Template.Spec.Git.Revision = pkg.Revision + rrs.Spec.Template.Metadata = rollout.Spec.SyncTemplate.RootSync.Metadata + return &rrs, true + } + return nil, false +} + func (r *RolloutReconciler) getWaveTargets(ctx context.Context, rollout *gitopsv1alpha1.Rollout, allTargets *Targets, allClusters []clusterstore.Cluster, allWaves []gitopsv1alpha1.Wave) ([]WaveTarget, error) { allWaveTargets := []WaveTarget{} @@ -508,6 +528,12 @@ func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv } } + var metadata *gitopsv1alpha1.Metadata + if rollout != nil && rollout.Spec.SyncTemplate != nil && + rollout.Spec.SyncTemplate.RootSync != nil { + metadata = rollout.Spec.SyncTemplate.RootSync.Metadata + } + for _, target := range targets.ToBeCreated { rootSyncSpec := toRootSyncSpec(target.packageRef) rrs := newRemoteRootSync(rollout, @@ -515,6 +541,7 @@ func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv rootSyncSpec, pkgID(target.packageRef), wave.Name, + metadata, ) if maxConcurrent > concurrentUpdates { @@ -685,7 +712,12 @@ func isRRSErrored(rss *gitopsv1alpha1.RemoteRootSync) bool { } // Given a package identifier and cluster, create a RemoteRootSync object. -func newRemoteRootSync(rollout *gitopsv1alpha1.Rollout, clusterRef gitopsv1alpha1.ClusterRef, rssSpec *gitopsv1alpha1.RootSyncSpec, pkgID string, waveName string) *gitopsv1alpha1.RemoteRootSync { +func newRemoteRootSync(rollout *gitopsv1alpha1.Rollout, + clusterRef gitopsv1alpha1.ClusterRef, + rssSpec *gitopsv1alpha1.RootSyncSpec, + pkgID string, + waveName string, + metadata *gitopsv1alpha1.Metadata) *gitopsv1alpha1.RemoteRootSync { t := true clusterName := clusterRef.Name[strings.LastIndex(clusterRef.Name, "/")+1:] @@ -709,7 +741,8 @@ func newRemoteRootSync(rollout *gitopsv1alpha1.Rollout, clusterRef gitopsv1alpha Spec: gitopsv1alpha1.RemoteRootSyncSpec{ ClusterRef: clusterRef, Template: &gitopsv1alpha1.RootSyncInfo{ - Spec: rssSpec, + Spec: rssSpec, + Metadata: metadata, }, }, }