diff --git a/apis/goharbor.io/v1alpha3/harborcluster_conversion.go b/apis/goharbor.io/v1alpha3/harborcluster_conversion.go index 2ca7a261e..a8a03c43a 100644 --- a/apis/goharbor.io/v1alpha3/harborcluster_conversion.go +++ b/apis/goharbor.io/v1alpha3/harborcluster_conversion.go @@ -253,16 +253,14 @@ func Convert_v1alpha3_RedisSpec_To_v1beta1_CacheSpec(src *RedisSpec, dst *v1beta func Convert_v1alpha3_HarborStorageImageChartStorageSpec_To_v1beta1_Storage(src *HarborStorageImageChartStorageSpec, dst *v1beta1.Storage) { // nolint if src.FileSystem != nil { dst.Kind = "FileSystem" - dst.Spec = v1beta1.StorageSpec{ - FileSystem: &v1beta1.FileSystemSpec{ - HarborStorageImageChartStorageFileSystemSpec: v1beta1.HarborStorageImageChartStorageFileSystemSpec{ - RegistryPersistentVolume: v1beta1.HarborStorageRegistryPersistentVolumeSpec{ - HarborStoragePersistentVolumeSpec: v1beta1.HarborStoragePersistentVolumeSpec{ - PersistentVolumeClaimVolumeSource: src.FileSystem.RegistryPersistentVolume.PersistentVolumeClaimVolumeSource, - Prefix: src.FileSystem.RegistryPersistentVolume.Prefix, - }, - MaxThreads: src.FileSystem.RegistryPersistentVolume.MaxThreads, + dst.Spec.FileSystem = &v1beta1.FileSystemSpec{ + HarborStorageImageChartStorageFileSystemSpec: v1beta1.HarborStorageImageChartStorageFileSystemSpec{ + RegistryPersistentVolume: v1beta1.HarborStorageRegistryPersistentVolumeSpec{ + HarborStoragePersistentVolumeSpec: v1beta1.HarborStoragePersistentVolumeSpec{ + PersistentVolumeClaimVolumeSource: src.FileSystem.RegistryPersistentVolume.PersistentVolumeClaimVolumeSource, + Prefix: src.FileSystem.RegistryPersistentVolume.Prefix, }, + MaxThreads: src.FileSystem.RegistryPersistentVolume.MaxThreads, }, }, } @@ -274,6 +272,57 @@ func Convert_v1alpha3_HarborStorageImageChartStorageSpec_To_v1beta1_Storage(src } } } + + if src.S3 != nil { + dst.Kind = "S3" + dst.Spec.S3 = &v1beta1.S3Spec{ + HarborStorageImageChartStorageS3Spec: v1beta1.HarborStorageImageChartStorageS3Spec{ + RegistryStorageDriverS3Spec: v1beta1.RegistryStorageDriverS3Spec{ + AccessKey: src.S3.AccessKey, + SecretKeyRef: src.S3.SecretKeyRef, + Region: src.S3.Region, + RegionEndpoint: src.S3.RegionEndpoint, + Bucket: src.S3.Bucket, + RootDirectory: src.S3.RootDirectory, + StorageClass: src.S3.StorageClass, + KeyID: src.S3.KeyID, + Encrypt: src.S3.Encrypt, + SkipVerify: src.S3.SkipVerify, + CertificateRef: src.S3.CertificateRef, + Secure: src.S3.Secure, + V4Auth: src.S3.V4Auth, + ChunkSize: src.S3.ChunkSize, + }, + }, + } + } + + if src.Swift != nil { + dst.Kind = "Swift" + dst.Spec.Swift = &v1beta1.SwiftSpec{ + HarborStorageImageChartStorageSwiftSpec: v1beta1.HarborStorageImageChartStorageSwiftSpec{ + RegistryStorageDriverSwiftSpec: v1beta1.RegistryStorageDriverSwiftSpec{ + AuthURL: src.Swift.AuthURL, + Username: src.Swift.Username, + PasswordRef: src.Swift.PasswordRef, + Region: src.Swift.Region, + Container: src.Swift.Container, + Tenant: src.Swift.Tenant, + TenantID: src.Swift.TenantID, + Domain: src.Swift.Domain, + DomainID: src.Swift.DomainID, + TrustID: src.Swift.TrustID, + InsecureSkipVerify: src.Swift.InsecureSkipVerify, + ChunkSize: src.Swift.ChunkSize, + Prefix: src.Swift.Prefix, + SecretKeyRef: src.Swift.SecretKeyRef, + AccessKey: src.Swift.AccessKey, + AuthVersion: src.Swift.AuthVersion, + EndpointType: src.Swift.EndpointType, + }, + }, + } + } } func Convert_v1alpha3_Storage_To_v1beta1_Storage(src *Storage, dst *v1beta1.Storage) { // nolint @@ -400,13 +449,24 @@ func Convert_v1beta1_HarborClusterSpec_To_v1alpha3_HarborClusterSpec(src *v1beta Convert_v1beta1_Cache_To_v1alpha3_Cache(&src.Cache, dst.InClusterCache) } - if src.Storage.Kind == "FileSystem" && src.Storage.Spec.FileSystem != nil { - if dst.ImageChartStorage == nil { - dst.ImageChartStorage = &HarborStorageImageChartStorageSpec{} - } + if dst.ImageChartStorage == nil { + dst.ImageChartStorage = &HarborStorageImageChartStorageSpec{} + } - Convert_v1beta1_FileSystemSpec_To_v1alpha3_HarborStorageImageChartStorage(src.Storage.Spec.FileSystem, dst.ImageChartStorage) - } else if src.Storage.Kind == "MinIO" { + switch src.Storage.Kind { + case "FileSystem": + if src.Storage.Spec.FileSystem != nil { + Convert_v1beta1_FileSystemSpec_To_v1alpha3_HarborStorageImageChartStorage(src.Storage.Spec.FileSystem, dst.ImageChartStorage) + } + case "S3": + if src.Storage.Spec.S3 != nil { + Convert_v1beta1_S3Spec_To_v1alpha3_HarborStorageImageChartStorage(src.Storage.Spec.S3, dst.ImageChartStorage) + } + case "Swift": + if src.Storage.Spec.Swift != nil { + Convert_v1beta1_SwiftSpec_To_v1alpha3_HarborStorageImageChartStorage(src.Storage.Spec.Swift, dst.ImageChartStorage) + } + case "MinIO": if dst.InClusterStorage == nil { dst.InClusterStorage = &Storage{} } @@ -431,6 +491,51 @@ func Convert_v1beta1_HarborClusterSpec_To_v1alpha3_HarborClusterSpec(src *v1beta Convert_v1beta1_EmbeddedHarborSpec_To_v1alpha3_HarborSpec(&src.EmbeddedHarborSpec, &dst.HarborSpec) } +func Convert_v1beta1_S3Spec_To_v1alpha3_HarborStorageImageChartStorage(src *v1beta1.S3Spec, dst *HarborStorageImageChartStorageSpec) { // nolint + dst.S3 = &HarborStorageImageChartStorageS3Spec{ + RegistryStorageDriverS3Spec: RegistryStorageDriverS3Spec{ + AccessKey: src.AccessKey, + SecretKeyRef: src.SecretKeyRef, + Region: src.Region, + RegionEndpoint: src.RegionEndpoint, + Bucket: src.Bucket, + RootDirectory: src.RootDirectory, + StorageClass: src.StorageClass, + KeyID: src.KeyID, + Encrypt: src.Encrypt, + SkipVerify: src.SkipVerify, + CertificateRef: src.CertificateRef, + Secure: src.Secure, + V4Auth: src.V4Auth, + ChunkSize: src.ChunkSize, + }, + } +} + +func Convert_v1beta1_SwiftSpec_To_v1alpha3_HarborStorageImageChartStorage(src *v1beta1.SwiftSpec, dst *HarborStorageImageChartStorageSpec) { // nolint + dst.Swift = &HarborStorageImageChartStorageSwiftSpec{ + RegistryStorageDriverSwiftSpec: RegistryStorageDriverSwiftSpec{ + AuthURL: src.AuthURL, + Username: src.Username, + PasswordRef: src.PasswordRef, + Region: src.Region, + Container: src.Container, + Tenant: src.Tenant, + TenantID: src.TenantID, + Domain: src.Domain, + DomainID: src.DomainID, + TrustID: src.TrustID, + InsecureSkipVerify: src.InsecureSkipVerify, + ChunkSize: src.ChunkSize, + Prefix: src.Prefix, + SecretKeyRef: src.SecretKeyRef, + AccessKey: src.AccessKey, + AuthVersion: src.AuthVersion, + EndpointType: src.EndpointType, + }, + } +} + func Convert_v1beta1_EmbeddedHarborSpec_To_v1alpha3_HarborSpec(src *v1beta1.EmbeddedHarborSpec, dst *HarborSpec) { // nolint dst.ExternalURL = src.ExternalURL dst.InternalTLS = HarborInternalTLSSpec{ diff --git a/apis/goharbor.io/v1beta1/harborcluster_types.go b/apis/goharbor.io/v1beta1/harborcluster_types.go index 5342554b5..d5d01bcff 100644 --- a/apis/goharbor.io/v1beta1/harborcluster_types.go +++ b/apis/goharbor.io/v1beta1/harborcluster_types.go @@ -196,7 +196,7 @@ type ZlandoPostgreSQLSpec struct { type Storage struct { // Kind of which storage service to be used. Only support MinIO now. - // +kubebuilder:validation:Enum={MinIO,FileSystem} + // +kubebuilder:validation:Enum={MinIO,S3,Swift,FileSystem} Kind string `json:"kind"` Spec StorageSpec `json:"spec"` @@ -207,8 +207,12 @@ type StorageSpec struct { // inCluster options. // +kubebuilder:validation:Optional MinIO *MinIOSpec `json:"minIO,omitempty"` - + // +kubebuilder:validation:Optional FileSystem *FileSystemSpec `json:"fileSystem,omitempty"` + // +kubebuilder:validation:Optional + S3 *S3Spec `json:"s3,omitempty"` + // +kubebuilder:validation:Optional + Swift *SwiftSpec `json:"swift,omitempty"` } // StorageRedirectSpec defines if the redirection is disabled. @@ -224,6 +228,14 @@ type FileSystemSpec struct { HarborStorageImageChartStorageFileSystemSpec `json:",inline"` } +type S3Spec struct { + HarborStorageImageChartStorageS3Spec `json:",inline"` +} + +type SwiftSpec struct { + HarborStorageImageChartStorageSwiftSpec `json:",inline"` +} + type MinIOSpec struct { harbormetav1.ImageSpec `json:",inline"` diff --git a/apis/goharbor.io/v1beta1/zz_generated.deepcopy.go b/apis/goharbor.io/v1beta1/zz_generated.deepcopy.go index 1e6dc2a9c..c83b993c4 100644 --- a/apis/goharbor.io/v1beta1/zz_generated.deepcopy.go +++ b/apis/goharbor.io/v1beta1/zz_generated.deepcopy.go @@ -3808,6 +3808,22 @@ func (in *RegistryValidationSpec) DeepCopy() *RegistryValidationSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *S3Spec) DeepCopyInto(out *S3Spec) { + *out = *in + in.HarborStorageImageChartStorageS3Spec.DeepCopyInto(&out.HarborStorageImageChartStorageS3Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S3Spec. +func (in *S3Spec) DeepCopy() *S3Spec { + if in == nil { + return nil + } + out := new(S3Spec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Storage) DeepCopyInto(out *Storage) { *out = *in @@ -3857,6 +3873,16 @@ func (in *StorageSpec) DeepCopyInto(out *StorageSpec) { *out = new(FileSystemSpec) (*in).DeepCopyInto(*out) } + if in.S3 != nil { + in, out := &in.S3, &out.S3 + *out = new(S3Spec) + (*in).DeepCopyInto(*out) + } + if in.Swift != nil { + in, out := &in.Swift, &out.Swift + *out = new(SwiftSpec) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageSpec. @@ -3869,6 +3895,22 @@ func (in *StorageSpec) DeepCopy() *StorageSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SwiftSpec) DeepCopyInto(out *SwiftSpec) { + *out = *in + out.HarborStorageImageChartStorageSwiftSpec = in.HarborStorageImageChartStorageSwiftSpec +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SwiftSpec. +func (in *SwiftSpec) DeepCopy() *SwiftSpec { + if in == nil { + return nil + } + out := new(SwiftSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Trivy) DeepCopyInto(out *Trivy) { *out = *in diff --git a/manifests/cluster/deployment.yaml b/manifests/cluster/deployment.yaml index 59f28582d..4e8a03284 100644 --- a/manifests/cluster/deployment.yaml +++ b/manifests/cluster/deployment.yaml @@ -7634,6 +7634,8 @@ spec: description: Kind of which storage service to be used. Only support MinIO now. enum: - MinIO + - S3 + - Swift - FileSystem type: string spec: @@ -7931,6 +7933,128 @@ spec: - replicas - volumesPerServer type: object + s3: + properties: + accesskey: + description: The AWS Access Key. If you use IAM roles, omit to fetch temporary credentials from IAM. + type: string + bucket: + description: The bucket name in which you want to store the registry’s data. + type: string + certificateRef: + pattern: '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*' + type: string + chunksize: + description: The S3 API requires multipart upload chunks to be at least 5MB. + format: int64 + minimum: 5242880 + type: integer + encrypt: + default: false + description: Specifies whether the registry stores the image in encrypted format or not. A boolean value. + type: boolean + keyid: + description: KMS key ID to use for encryption (encrypt must be true, or this parameter is ignored). + type: string + region: + description: The AWS region in which your bucket exists. For the moment, the Go AWS library in use does not use the newer DNS based bucket routing. For a list of regions, see http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html + type: string + regionendpoint: + description: Endpoint for S3 compatible storage services (Minio, etc). + type: string + rootdirectory: + description: This is a prefix that is applied to all S3 keys to allow you to segment data in your bucket if necessary. + type: string + secretkeyRef: + description: Reference to the secret containing the AWS Secret Key. If you use IAM roles, omit to fetch temporary credentials from IAM. + pattern: '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*' + type: string + secure: + default: true + type: boolean + skipverify: + default: false + description: Skips TLS verification when the value is set to true. + type: boolean + storageclass: + default: STANDARD + description: The S3 storage class applied to each registry file. + type: string + v4auth: + default: true + description: Indicates whether the registry uses Version 4 of AWS’s authentication. + type: boolean + required: + - bucket + - region + type: object + swift: + properties: + accesskey: + description: The access key to generate temporary URLs. It is used by HP Cloud Object Storage in addition to the secretkey parameter. + type: string + authurl: + description: URL for obtaining an auth token. https://storage.myprovider.com/v2.0 or https://storage.myprovider.com/v3/auth + type: string + authversion: + description: Specify the OpenStack Auth’s version, for example 3. By default the driver autodetects the auth’s version from the authurl. + type: string + chunksize: + description: Size of the data segments for the Swift Dynamic Large Objects. This value should be a number. + format: int64 + minimum: 5242880 + type: integer + container: + description: The name of your Swift container where you wish to store the registry’s data. The driver creates the named container during its initialization. + type: string + domain: + description: Your Openstack domain name for Identity v3 API. You can either use domain or domainid. + type: string + domainID: + description: Your Openstack domain ID for Identity v3 API. You can either use domain or domainid. + type: string + endpointtype: + default: public + description: The endpoint type used when connecting to swift. + enum: + - public + - internal + - admin + type: string + insecureskipverify: + default: false + description: Skips TLS verification if the value is set to true. + type: boolean + passwordRef: + description: Secret name containing the Openstack password. + pattern: '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*' + type: string + prefix: + description: This is a prefix that is applied to all Swift keys to allow you to segment data in your container if necessary. Defaults to the container’s root. + type: string + region: + description: The Openstack region in which your container exists. + type: string + secretkeyRef: + description: The secret key used to generate temporary URLs. + pattern: '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*' + type: string + tenant: + description: Your Openstack tenant name. You can either use tenant or tenantid. + type: string + tenantID: + description: Your Openstack tenant ID. You can either use tenant or tenantid. + type: string + trustid: + description: Your Openstack trust ID for Identity v3 API. + type: string + username: + description: The Openstack user name. + type: string + required: + - authurl + - container + type: object type: object required: - kind diff --git a/manifests/harbor/deployment.yaml b/manifests/harbor/deployment.yaml index 41679ec11..bf188f7ec 100644 --- a/manifests/harbor/deployment.yaml +++ b/manifests/harbor/deployment.yaml @@ -7634,6 +7634,8 @@ spec: description: Kind of which storage service to be used. Only support MinIO now. enum: - MinIO + - S3 + - Swift - FileSystem type: string spec: @@ -7931,6 +7933,128 @@ spec: - replicas - volumesPerServer type: object + s3: + properties: + accesskey: + description: The AWS Access Key. If you use IAM roles, omit to fetch temporary credentials from IAM. + type: string + bucket: + description: The bucket name in which you want to store the registry’s data. + type: string + certificateRef: + pattern: '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*' + type: string + chunksize: + description: The S3 API requires multipart upload chunks to be at least 5MB. + format: int64 + minimum: 5242880 + type: integer + encrypt: + default: false + description: Specifies whether the registry stores the image in encrypted format or not. A boolean value. + type: boolean + keyid: + description: KMS key ID to use for encryption (encrypt must be true, or this parameter is ignored). + type: string + region: + description: The AWS region in which your bucket exists. For the moment, the Go AWS library in use does not use the newer DNS based bucket routing. For a list of regions, see http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html + type: string + regionendpoint: + description: Endpoint for S3 compatible storage services (Minio, etc). + type: string + rootdirectory: + description: This is a prefix that is applied to all S3 keys to allow you to segment data in your bucket if necessary. + type: string + secretkeyRef: + description: Reference to the secret containing the AWS Secret Key. If you use IAM roles, omit to fetch temporary credentials from IAM. + pattern: '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*' + type: string + secure: + default: true + type: boolean + skipverify: + default: false + description: Skips TLS verification when the value is set to true. + type: boolean + storageclass: + default: STANDARD + description: The S3 storage class applied to each registry file. + type: string + v4auth: + default: true + description: Indicates whether the registry uses Version 4 of AWS’s authentication. + type: boolean + required: + - bucket + - region + type: object + swift: + properties: + accesskey: + description: The access key to generate temporary URLs. It is used by HP Cloud Object Storage in addition to the secretkey parameter. + type: string + authurl: + description: URL for obtaining an auth token. https://storage.myprovider.com/v2.0 or https://storage.myprovider.com/v3/auth + type: string + authversion: + description: Specify the OpenStack Auth’s version, for example 3. By default the driver autodetects the auth’s version from the authurl. + type: string + chunksize: + description: Size of the data segments for the Swift Dynamic Large Objects. This value should be a number. + format: int64 + minimum: 5242880 + type: integer + container: + description: The name of your Swift container where you wish to store the registry’s data. The driver creates the named container during its initialization. + type: string + domain: + description: Your Openstack domain name for Identity v3 API. You can either use domain or domainid. + type: string + domainID: + description: Your Openstack domain ID for Identity v3 API. You can either use domain or domainid. + type: string + endpointtype: + default: public + description: The endpoint type used when connecting to swift. + enum: + - public + - internal + - admin + type: string + insecureskipverify: + default: false + description: Skips TLS verification if the value is set to true. + type: boolean + passwordRef: + description: Secret name containing the Openstack password. + pattern: '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*' + type: string + prefix: + description: This is a prefix that is applied to all Swift keys to allow you to segment data in your container if necessary. Defaults to the container’s root. + type: string + region: + description: The Openstack region in which your container exists. + type: string + secretkeyRef: + description: The secret key used to generate temporary URLs. + pattern: '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*' + type: string + tenant: + description: Your Openstack tenant name. You can either use tenant or tenantid. + type: string + tenantID: + description: Your Openstack tenant ID. You can either use tenant or tenantid. + type: string + trustid: + description: Your Openstack trust ID for Identity v3 API. + type: string + username: + description: The Openstack user name. + type: string + required: + - authurl + - container + type: object type: object required: - kind diff --git a/pkg/cluster/controllers/harbor/harbor.go b/pkg/cluster/controllers/harbor/harbor.go index 07b5f278d..4c2028e85 100644 --- a/pkg/cluster/controllers/harbor/harbor.go +++ b/pkg/cluster/controllers/harbor/harbor.go @@ -119,6 +119,7 @@ func (harbor *Controller) getHarborCR(ctx context.Context, harborcluster *goharb InternalTLS: goharborv1.HarborInternalTLSSpec{ Enabled: spec.InternalTLS.Enabled, }, + ImageChartStorage: &goharborv1.HarborStorageImageChartStorageSpec{}, LogLevel: spec.LogLevel, HarborAdminPasswordRef: spec.HarborAdminPasswordRef, UpdateStrategyType: spec.UpdateStrategyType, @@ -131,10 +132,20 @@ func (harbor *Controller) getHarborCR(ctx context.Context, harborcluster *goharb } if harborcluster.Spec.Storage.Spec.FileSystem != nil { - harborCR.Spec.ImageChartStorage = &goharborv1.HarborStorageImageChartStorageSpec{ - FileSystem: harborCR.Spec.ImageChartStorage.FileSystem, - } + harborCR.Spec.ImageChartStorage.FileSystem = + harborcluster.Spec.Storage.Spec.FileSystem.HarborStorageImageChartStorageFileSystemSpec.DeepCopy() + } + + if harborcluster.Spec.Storage.Spec.S3 != nil { + harborCR.Spec.ImageChartStorage.S3 = + harborcluster.Spec.Storage.Spec.S3.HarborStorageImageChartStorageS3Spec.DeepCopy() } + + if harborcluster.Spec.Storage.Spec.Swift != nil { + harborCR.Spec.ImageChartStorage.Swift = + harborcluster.Spec.Storage.Spec.Swift.HarborStorageImageChartStorageSwiftSpec.DeepCopy() + } + // Use incluster spec in first priority. // Check based on the case that if the related dependent services are created if db := harbor.getDatabaseSpec(dependencies); db != nil {