diff --git a/rollouts/api/v1alpha1/remotesync_types.go b/rollouts/api/v1alpha1/remotesync_types.go index afa0d21acc..d01d30099a 100644 --- a/rollouts/api/v1alpha1/remotesync_types.go +++ b/rollouts/api/v1alpha1/remotesync_types.go @@ -37,8 +37,10 @@ type Template struct { } type SyncSpec struct { + SourceType string `json:"sourceType,omitempty"` SourceFormat string `json:"sourceFormat,omitempty"` Git *GitInfo `json:"git,omitempty"` + Oci *OciInfo `json:"oci,omitempty"` } type GitInfo struct { @@ -54,6 +56,43 @@ type GitInfo struct { NoSSLVerify bool `json:"noSSLVerify,omitempty"` } +// Oci contains configuration specific to importing resources from an OCI package. +type OciInfo struct { + // image is the OCI image repository URL for the package to sync from. + // e.g. `LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY_NAME/PACKAGE_NAME`. + // The image can be pulled by TAG or by DIGEST if it is specified in PACKAGE_NAME. + // - Pull by tag: `LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY_NAME/PACKAGE_NAME:TAG`. + // - Pull by digest: `LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY_NAME/PACKAGE_NAME@sha256:DIGEST`. + // If neither TAG nor DIGEST is specified, it pulls with the `latest` tag by default. + Image string `json:"image,omitempty"` + + // dir is the absolute path of the directory that contains + // the local resources. Default: the root directory of the image. + // +optional + Dir string `json:"dir,omitempty"` + + // period is the time duration between consecutive syncs. Default: 15s. + // Note to developers that customers specify this value using + // string (https://golang.org/pkg/time/#Duration.String) like "3s" + // in their Custom Resource YAML. However, time.Duration is at a nanosecond + // granularity, and it is easy to introduce a bug where it looks like the + // code is dealing with seconds but its actually nanoseconds (or vice versa). + // +optional + Period metav1.Duration `json:"period,omitempty"` + + // auth is the type of secret configured for access to the OCI package. + // Must be one of gcenode, gcpserviceaccount, or none. + // The validation of this is case-sensitive. Required. + // + // +kubebuilder:validation:Enum=gcenode;gcpserviceaccount;none + Auth string `json:"auth"` + + // gcpServiceAccountEmail specifies the GCP service account used to annotate + // the RootSync/RepoSync controller Kubernetes Service Account. + // Note: The field is used when secretType: gcpServiceAccount. + GCPServiceAccountEmail string `json:"gcpServiceAccountEmail,omitempty"` +} + // Metadata specifies labels and annotations to add to the RSync object. type Metadata struct { Labels map[string]string `json:"labels,omitempty"` diff --git a/rollouts/api/v1alpha1/rollout_types.go b/rollouts/api/v1alpha1/rollout_types.go index 358fa38e5d..7ea0fef2f1 100644 --- a/rollouts/api/v1alpha1/rollout_types.go +++ b/rollouts/api/v1alpha1/rollout_types.go @@ -107,17 +107,19 @@ type ClusterSourceKind struct { const ( GitHub PackageSourceType = "GitHub" GitLab PackageSourceType = "GitLab" + OCI PackageSourceType = "OCI" ) -// +kubebuilder:validation:Enum=GitHub;GitLab +// +kubebuilder:validation:Enum=GitHub;GitLab;OCI type PackageSourceType string // PackagesConfig defines the packages the Rollout should deploy. type PackagesConfig struct { SourceType PackageSourceType `json:"sourceType"` - GitHub GitHubSource `json:"github,omitempty"` - GitLab GitLabSource `json:"gitlab,omitempty"` + GitHub GitHubSource `json:"github,omitempty"` + GitLab GitLabSource `json:"gitlab,omitempty"` + OciSource *OCISource `json:"oci,omitempty"` } // GitHubSource defines the packages source in GitHub. @@ -157,6 +159,22 @@ type GitLabSelector struct { Branch string `json:"branch,omitempty"` } +type OCISource struct { + // image is the OCI image repository URL for the package to sync from. + // e.g. `LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY_NAME/PACKAGE_NAME`. + // The image can be pulled by TAG or by DIGEST if it is specified in PACKAGE_NAME. + // - Pull by tag: `LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY_NAME/PACKAGE_NAME:TAG`. + // - Pull by digest: `LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY_NAME/PACKAGE_NAME@sha256:DIGEST`. + // If neither TAG nor DIGEST is specified, it pulls with the `latest` tag by default. + // Required + Image string `json:"image"` + + // dir is the absolute path of the directory that contains + // the local resources. Default: the root directory of the image. + // +optional + Dir string `json:"dir,omitempty"` +} + // SecretReference contains the reference to the secret type SecretReference struct { // Name represents the secret name @@ -183,6 +201,7 @@ type SyncTemplate struct { type RootSyncTemplate struct { SourceFormat string `json:"sourceFormat,omitempty"` Git *GitInfo `json:"git,omitempty"` + Oci *OciInfo `json:"oci,omitempty"` Metadata *Metadata `json:"metadata,omitempty"` } @@ -190,6 +209,7 @@ type RootSyncTemplate struct { type RepoSyncTemplate struct { SourceFormat string `json:"sourceFormat,omitempty"` Git *GitInfo `json:"git,omitempty"` + Oci *OciInfo `json:"oci,omitempty"` Metadata *Metadata `json:"metadata,omitempty"` } diff --git a/rollouts/api/v1alpha1/zz_generated.deepcopy.go b/rollouts/api/v1alpha1/zz_generated.deepcopy.go index 43fb23c0c8..f84c508d35 100644 --- a/rollouts/api/v1alpha1/zz_generated.deepcopy.go +++ b/rollouts/api/v1alpha1/zz_generated.deepcopy.go @@ -247,6 +247,37 @@ func (in *Metadata) DeepCopy() *Metadata { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OCISource) DeepCopyInto(out *OCISource) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCISource. +func (in *OCISource) DeepCopy() *OCISource { + if in == nil { + return nil + } + out := new(OCISource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OciInfo) DeepCopyInto(out *OciInfo) { + *out = *in + out.Period = in.Period +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OciInfo. +func (in *OciInfo) DeepCopy() *OciInfo { + if in == nil { + return nil + } + out := new(OciInfo) + 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 @@ -282,6 +313,11 @@ func (in *PackagesConfig) DeepCopyInto(out *PackagesConfig) { *out = *in out.GitHub = in.GitHub out.GitLab = in.GitLab + if in.OciSource != nil { + in, out := &in.OciSource, &out.OciSource + *out = new(OCISource) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackagesConfig. @@ -515,6 +551,11 @@ func (in *RepoSyncTemplate) DeepCopyInto(out *RepoSyncTemplate) { *out = new(GitInfo) **out = **in } + if in.Oci != nil { + in, out := &in.Oci, &out.Oci + *out = new(OciInfo) + **out = **in + } if in.Metadata != nil { in, out := &in.Metadata, &out.Metadata *out = new(Metadata) @@ -595,7 +636,7 @@ func (in *RolloutList) DeepCopyObject() runtime.Object { func (in *RolloutSpec) DeepCopyInto(out *RolloutSpec) { *out = *in in.Clusters.DeepCopyInto(&out.Clusters) - out.Packages = in.Packages + in.Packages.DeepCopyInto(&out.Packages) in.Targets.DeepCopyInto(&out.Targets) out.PackageToTargetMatcher = in.PackageToTargetMatcher if in.SyncTemplate != nil { @@ -688,6 +729,11 @@ func (in *RootSyncTemplate) DeepCopyInto(out *RootSyncTemplate) { *out = new(GitInfo) **out = **in } + if in.Oci != nil { + in, out := &in.Oci, &out.Oci + *out = new(OciInfo) + **out = **in + } if in.Metadata != nil { in, out := &in.Metadata, &out.Metadata *out = new(Metadata) @@ -774,6 +820,11 @@ func (in *SyncSpec) DeepCopyInto(out *SyncSpec) { *out = new(GitInfo) **out = **in } + if in.Oci != nil { + in, out := &in.Oci, &out.Oci + *out = new(OciInfo) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SyncSpec. diff --git a/rollouts/config/crd/bases/gitops.kpt.dev_remotesyncs.yaml b/rollouts/config/crd/bases/gitops.kpt.dev_remotesyncs.yaml index b0621dbc75..3f1686aa79 100644 --- a/rollouts/config/crd/bases/gitops.kpt.dev_remotesyncs.yaml +++ b/rollouts/config/crd/bases/gitops.kpt.dev_remotesyncs.yaml @@ -116,8 +116,55 @@ spec: - auth - repo type: object + oci: + description: Oci contains configuration specific to importing + resources from an OCI package. + properties: + auth: + description: auth is the type of secret configured for + access to the OCI package. Must be one of gcenode, gcpserviceaccount, + or none. The validation of this is case-sensitive. Required. + enum: + - gcenode + - gcpserviceaccount + - none + type: string + dir: + description: 'dir is the absolute path of the directory + that contains the local resources. Default: the root + directory of the image.' + type: string + gcpServiceAccountEmail: + description: 'gcpServiceAccountEmail specifies the GCP + service account used to annotate the RootSync/RepoSync + controller Kubernetes Service Account. Note: The field + is used when secretType: gcpServiceAccount.' + type: string + image: + description: 'image is the OCI image repository URL for + the package to sync from. e.g. `LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY_NAME/PACKAGE_NAME`. + The image can be pulled by TAG or by DIGEST if it is + specified in PACKAGE_NAME. - Pull by tag: `LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY_NAME/PACKAGE_NAME:TAG`. + - Pull by digest: `LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY_NAME/PACKAGE_NAME@sha256:DIGEST`. + If neither TAG nor DIGEST is specified, it pulls with + the `latest` tag by default.' + type: string + period: + description: 'period is the time duration between consecutive + syncs. Default: 15s. Note to developers that customers + specify this value using string (https://golang.org/pkg/time/#Duration.String) + like "3s" in their Custom Resource YAML. However, time.Duration + is at a nanosecond granularity, and it is easy to introduce + a bug where it looks like the code is dealing with seconds + but its actually nanoseconds (or vice versa).' + type: string + required: + - auth + type: object sourceFormat: type: string + sourceType: + type: string type: object type: object type: diff --git a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml index 98543db88f..a947dce47b 100644 --- a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml +++ b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml @@ -167,10 +167,30 @@ spec: required: - selector type: object + oci: + properties: + dir: + description: 'dir is the absolute path of the directory that + contains the local resources. Default: the root directory + of the image.' + type: string + image: + description: 'image is the OCI image repository URL for the + package to sync from. e.g. `LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY_NAME/PACKAGE_NAME`. + The image can be pulled by TAG or by DIGEST if it is specified + in PACKAGE_NAME. - Pull by tag: `LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY_NAME/PACKAGE_NAME:TAG`. + - Pull by digest: `LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY_NAME/PACKAGE_NAME@sha256:DIGEST`. + If neither TAG nor DIGEST is specified, it pulls with the + `latest` tag by default. Required' + type: string + required: + - image + type: object sourceType: enum: - GitHub - GitLab + - OCI type: string required: - sourceType @@ -277,6 +297,51 @@ spec: type: string type: object type: object + oci: + description: Oci contains configuration specific to importing + resources from an OCI package. + properties: + auth: + description: auth is the type of secret configured for + access to the OCI package. Must be one of gcenode, gcpserviceaccount, + or none. The validation of this is case-sensitive. Required. + enum: + - gcenode + - gcpserviceaccount + - none + type: string + dir: + description: 'dir is the absolute path of the directory + that contains the local resources. Default: the root + directory of the image.' + type: string + gcpServiceAccountEmail: + description: 'gcpServiceAccountEmail specifies the GCP + service account used to annotate the RootSync/RepoSync + controller Kubernetes Service Account. Note: The field + is used when secretType: gcpServiceAccount.' + type: string + image: + description: 'image is the OCI image repository URL for + the package to sync from. e.g. `LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY_NAME/PACKAGE_NAME`. + The image can be pulled by TAG or by DIGEST if it is + specified in PACKAGE_NAME. - Pull by tag: `LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY_NAME/PACKAGE_NAME:TAG`. + - Pull by digest: `LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY_NAME/PACKAGE_NAME@sha256:DIGEST`. + If neither TAG nor DIGEST is specified, it pulls with + the `latest` tag by default.' + type: string + period: + description: 'period is the time duration between consecutive + syncs. Default: 15s. Note to developers that customers + specify this value using string (https://golang.org/pkg/time/#Duration.String) + like "3s" in their Custom Resource YAML. However, time.Duration + is at a nanosecond granularity, and it is easy to introduce + a bug where it looks like the code is dealing with seconds + but its actually nanoseconds (or vice versa).' + type: string + required: + - auth + type: object sourceFormat: type: string type: object @@ -329,6 +394,51 @@ spec: type: string type: object type: object + oci: + description: Oci contains configuration specific to importing + resources from an OCI package. + properties: + auth: + description: auth is the type of secret configured for + access to the OCI package. Must be one of gcenode, gcpserviceaccount, + or none. The validation of this is case-sensitive. Required. + enum: + - gcenode + - gcpserviceaccount + - none + type: string + dir: + description: 'dir is the absolute path of the directory + that contains the local resources. Default: the root + directory of the image.' + type: string + gcpServiceAccountEmail: + description: 'gcpServiceAccountEmail specifies the GCP + service account used to annotate the RootSync/RepoSync + controller Kubernetes Service Account. Note: The field + is used when secretType: gcpServiceAccount.' + type: string + image: + description: 'image is the OCI image repository URL for + the package to sync from. e.g. `LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY_NAME/PACKAGE_NAME`. + The image can be pulled by TAG or by DIGEST if it is + specified in PACKAGE_NAME. - Pull by tag: `LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY_NAME/PACKAGE_NAME:TAG`. + - Pull by digest: `LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY_NAME/PACKAGE_NAME@sha256:DIGEST`. + If neither TAG nor DIGEST is specified, it pulls with + the `latest` tag by default.' + type: string + period: + description: 'period is the time duration between consecutive + syncs. Default: 15s. Note to developers that customers + specify this value using string (https://golang.org/pkg/time/#Duration.String) + like "3s" in their Custom Resource YAML. However, time.Duration + is at a nanosecond granularity, and it is easy to introduce + a bug where it looks like the code is dealing with seconds + but its actually nanoseconds (or vice versa).' + type: string + required: + - auth + type: object sourceFormat: type: string type: object diff --git a/rollouts/config/samples/test-ns-rollout.yaml b/rollouts/config/samples/test-ns-rollout.yaml new file mode 100644 index 0000000000..d678ebf81b --- /dev/null +++ b/rollouts/config/samples/test-ns-rollout.yaml @@ -0,0 +1,34 @@ +apiVersion: gitops.kpt.dev/v1alpha1 +kind: Rollout +metadata: + name: test-ns-rollout +spec: + description: namespace rollout + clusters: + sourceType: GCPFleet + gcpFleet: + projectIds: + - sunilarora-sandbox + packages: + sourceType: OCI + oci: + image: us-west1-docker.pkg.dev/sunilarora-sandbox/rollouts-samples/test-namespace:v1 + dir: . + packageToTargetMatcher: + type: AllClusters + targets: + selector: + matchExpressions: + - {key: state, operator: In, values: [ga, ny]} + syncTemplate: + type: RootSync + rootSync: + sourceFormat: unstructured + oci: + image: something + auth: gcpserviceaccount + gcpServiceAccountEmail: rollouts-samples-reader@sunilarora-sandbox.iam.gserviceaccount.com + strategy: + type: RollingUpdate + rollingUpdate: + maxConcurrent: 2 diff --git a/rollouts/controllers/remotesync_controller.go b/rollouts/controllers/remotesync_controller.go index b2d16a4bb9..2334c2c04e 100644 --- a/rollouts/controllers/remotesync_controller.go +++ b/rollouts/controllers/remotesync_controller.go @@ -194,7 +194,7 @@ func (r *RemoteSyncReconciler) syncExternalSync(ctx context.Context, rs *gitopsv return "", fmt.Errorf("failed to create/update sync: %w", err) } - r.setupWatches(ctx, rs.Name, rs.Namespace, rs.Spec.ClusterRef) + r.setupWatches(ctx, externalSyncName(rs), rs.Namespace, rs.Spec.ClusterRef) syncStatus, err := checkSyncStatus(ctx, dynCl, rs) if err != nil { @@ -261,7 +261,7 @@ func (r *RemoteSyncReconciler) patchExternalSync(ctx context.Context, client dyn return fmt.Errorf("failed to encode %s to JSON: %w", gvk.Kind, err) } - _, err = client.Resource(gvr).Namespace(namespace).Patch(ctx, rs.Name, types.ApplyPatchType, data, metav1.PatchOptions{FieldManager: rs.Name}) + _, err = client.Resource(gvr).Namespace(namespace).Patch(ctx, newRootSync.GetName(), types.ApplyPatchType, data, metav1.PatchOptions{FieldManager: rs.Name}) if err != nil { return fmt.Errorf("failed to patch %s: %w", gvk.Kind, err) } @@ -348,7 +348,7 @@ func BuildObjectsToApply(remotesync *gitopsv1alpha1.RemoteSync, u := unstructured.Unstructured{Object: newRootSync} u.SetGroupVersionKind(gvk) - u.SetName(remotesync.Name) + u.SetName(externalSyncName(remotesync)) u.SetNamespace(namespace) labels := u.GetLabels() diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go index b063f3f6d5..b189166665 100644 --- a/rollouts/controllers/rollout_controller.go +++ b/rollouts/controllers/rollout_controller.go @@ -418,7 +418,8 @@ func (r *RolloutReconciler) computeTargets(ctx context.Context, rs := gitopsv1alpha1.RemoteSync{} key := client.ObjectKey{ Namespace: rollout.Namespace, - Name: fmt.Sprintf("%s-%s", pkgID(pkg), clusterName), + // Name: fmt.Sprintf("%s-%s", pkgID(pkg), clusterName), + Name: remoteSyncName(clusterName, rollout.GetName()), } // since this RS need to exist, remove it from the deletion list delete(RSkeysToBeDeleted, key) @@ -455,6 +456,16 @@ func (r *RolloutReconciler) computeTargets(ctx context.Context, return targets, nil } +func remoteSyncName(clusterName, rolloutName string) string { + return fmt.Sprintf("%s-%s", clusterName, rolloutName) +} + +func externalSyncName(rrs *gitopsv1alpha1.RemoteSync) string { + clusterRef := rrs.Spec.ClusterRef + clusterName := clusterRef.Name[strings.LastIndex(clusterRef.Name, "/")+1:] + return strings.TrimPrefix(rrs.GetName(), fmt.Sprintf("%s-", clusterName)) +} + // rsNeedsUpdate checks if the underlying remotesync needs to be updated by creating a new RemoteSync object and comparing it to the existing one func rsNeedsUpdate(ctx context.Context, rollout *gitopsv1alpha1.Rollout, currentRS *gitopsv1alpha1.RemoteSync, target *clusterPackagePair) (*gitopsv1alpha1.RemoteSync, bool) { desiredRS := newRemoteSync(rollout, target) @@ -732,7 +743,8 @@ func newRemoteSync(rollout *gitopsv1alpha1.Rollout, target *clusterPackagePair) // or a RootSync in the config-management-system namespace. return &gitopsv1alpha1.RemoteSync{ ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s-%s", pkgID(target.packageRef), clusterName), + // Name: fmt.Sprintf("%s-%s", pkgID(target.packageRef), clusterName), + Name: remoteSyncName(clusterName, rollout.GetName()), Namespace: rollout.Namespace, Labels: map[string]string{ rolloutLabel: rollout.Name, @@ -752,17 +764,34 @@ func newRemoteSync(rollout *gitopsv1alpha1.Rollout, target *clusterPackagePair) Type: templateType, ClusterRef: clusterRef, Template: &gitopsv1alpha1.Template{ - Spec: toSyncSpec(target.packageRef), + Spec: toSyncSpec(target.packageRef, rollout), Metadata: getSpecMetadata(rollout), }, }, } } -func toSyncSpec(dpkg *packagediscovery.DiscoveredPackage) *gitopsv1alpha1.SyncSpec { - return &gitopsv1alpha1.SyncSpec{ +func toSyncSpec(dpkg *packagediscovery.DiscoveredPackage, rollout *gitopsv1alpha1.Rollout) *gitopsv1alpha1.SyncSpec { + syncSpec := &gitopsv1alpha1.SyncSpec{ SourceFormat: "unstructured", - Git: &gitopsv1alpha1.GitInfo{ + } + switch { + case dpkg.OciRepo != nil: + syncSpec.SourceType = "oci" + syncSpec.Oci = &gitopsv1alpha1.OciInfo{ + // TODO(droot): Repo URL can be an HTTP, GIT or SSH based URL + // Need to make it configurable + Image: dpkg.OciRepo.Image, + Dir: dpkg.Directory, + } + if rollout.Spec.SyncTemplate.RootSync != nil { + syncSpec.Oci.Auth = rollout.Spec.SyncTemplate.RootSync.Oci.Auth + syncSpec.Oci.GCPServiceAccountEmail = rollout.Spec.SyncTemplate.RootSync.Oci.GCPServiceAccountEmail + } + // TODO(droot): support reposync as well + default: + syncSpec.SourceType = "git" + syncSpec.Git = &gitopsv1alpha1.GitInfo{ // TODO(droot): Repo URL can be an HTTP, GIT or SSH based URL // Need to make it configurable Repo: dpkg.HTTPURL(), @@ -770,8 +799,9 @@ func toSyncSpec(dpkg *packagediscovery.DiscoveredPackage) *gitopsv1alpha1.SyncSp Dir: dpkg.Directory, Branch: dpkg.Branch, Auth: "none", - }, + } } + return syncSpec } func pkgID(dpkg *packagediscovery.DiscoveredPackage) string { diff --git a/rollouts/controllers/status.go b/rollouts/controllers/status.go index 68919d9b83..21c3eb11c0 100644 --- a/rollouts/controllers/status.go +++ b/rollouts/controllers/status.go @@ -32,7 +32,7 @@ func checkSyncStatus(ctx context.Context, client dynamic.Interface, remotesync * return "", err } - rs, err := client.Resource(gvr).Namespace(getExternalSyncNamespace(remotesync)).Get(ctx, remotesync.Name, metav1.GetOptions{}) + rs, err := client.Resource(gvr).Namespace(getExternalSyncNamespace(remotesync)).Get(ctx, externalSyncName(remotesync), metav1.GetOptions{}) if err != nil { return "", fmt.Errorf("failed to get %s: %w", gvk.Kind, err) } diff --git a/rollouts/pkg/packagediscovery/oci.go b/rollouts/pkg/packagediscovery/oci.go new file mode 100644 index 0000000000..7738e54c30 --- /dev/null +++ b/rollouts/pkg/packagediscovery/oci.go @@ -0,0 +1,36 @@ +// Copyright 2023 The kpt Authors +// +// 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. + +package packagediscovery + +import ( + "context" + + gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" +) + +// getOCIPackages discovers OCI packages for OCI config. +func (d *PackageDiscovery) getOCIPackages(ctx context.Context, config gitopsv1alpha1.PackagesConfig) ([]DiscoveredPackage, error) { + var discoveredPackages []DiscoveredPackage + + oci := config.OciSource + + discoveredPackages = append(discoveredPackages, DiscoveredPackage{ + Directory: oci.Dir, + OciRepo: &OCIArtifact{ + Image: oci.Image, + }, + }) + return discoveredPackages, nil +} diff --git a/rollouts/pkg/packagediscovery/packagediscovery.go b/rollouts/pkg/packagediscovery/packagediscovery.go index 48249145e0..53ae87f604 100644 --- a/rollouts/pkg/packagediscovery/packagediscovery.go +++ b/rollouts/pkg/packagediscovery/packagediscovery.go @@ -64,6 +64,12 @@ type DiscoveredPackage struct { GitLabProject *gitlab.Project // GithubRepo contains the info retrieved from GitHub GitHubRepo *github.Repository + + OciRepo *OCIArtifact +} + +type OCIArtifact struct { + Image string } // HTTPURL refers to the HTTP URL for the repository. @@ -113,6 +119,8 @@ func (dp *DiscoveredPackage) String() string { return dp.GitLabProject.Name case dp.GitHubRepo != nil: return *dp.GitHubRepo.Name + case dp.OciRepo != nil: + return dp.OciRepo.Image } return "" } @@ -149,6 +157,11 @@ func (d *PackageDiscovery) GetPackages(ctx context.Context, config gitopsv1alpha if err != nil { return nil, fmt.Errorf("unable to fetch gitlab packages: %w", err) } + case gitopsv1alpha1.OCI: + discoveredPackages, err = d.getOCIPackages(ctx, config) + if err != nil { + return nil, fmt.Errorf("unable to fetch OCI packages: %w", err) + } default: return nil, fmt.Errorf("%v source type not supported yet", config.SourceType) }