From 0e6ae3b769b2b657999c581dac96c027c4dd64a8 Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Fri, 18 Sep 2020 11:19:13 +0300 Subject: [PATCH] Add Bucket type to API --- PROJECT | 3 + api/v1alpha1/bucket_types.go | 200 +++++++++ api/v1alpha1/zz_generated.deepcopy.go | 117 +++++ .../source.toolkit.fluxcd.io_buckets.yaml | 174 ++++++++ config/crd/kustomization.yaml | 1 + config/rbac/bucket_editor_role.yaml | 24 ++ config/rbac/bucket_viewer_role.yaml | 20 + config/rbac/role.yaml | 20 + config/samples/source_v1alpha1_bucket.yaml | 12 + docs/api/source.md | 398 ++++++++++++++++++ 10 files changed, 969 insertions(+) create mode 100644 api/v1alpha1/bucket_types.go create mode 100644 config/crd/bases/source.toolkit.fluxcd.io_buckets.yaml create mode 100644 config/rbac/bucket_editor_role.yaml create mode 100644 config/rbac/bucket_viewer_role.yaml create mode 100644 config/samples/source_v1alpha1_bucket.yaml diff --git a/PROJECT b/PROJECT index 0b8f5bd1..e1408f7f 100644 --- a/PROJECT +++ b/PROJECT @@ -10,4 +10,7 @@ resources: - group: source kind: HelmChart version: v1alpha1 +- group: source + kind: Bucket + version: v1alpha1 version: "2" diff --git a/api/v1alpha1/bucket_types.go b/api/v1alpha1/bucket_types.go new file mode 100644 index 00000000..896a0a48 --- /dev/null +++ b/api/v1alpha1/bucket_types.go @@ -0,0 +1,200 @@ +/* +Copyright 2020 The Flux CD contributors. + +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 v1alpha1 + +import ( + "time" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + BucketKind = "Bucket" + BucketTimeout = time.Second * 20 +) + +// BucketSpec defines the desired state of an S3 compatible bucket +type BucketSpec struct { + // The S3 compatible storage provider name, default ('generic'). + // +kubebuilder:validation:Enum=generic;aws + // +optional + Provider string `json:"provider,omitempty"` + + // The bucket name. + // +required + BucketName string `json:"bucketName"` + + // The bucket endpoint address. + // +required + Endpoint string `json:"endpoint"` + + // Insecure allows connecting to a non-TLS S3 HTTP endpoint. + // +optional + Insecure bool `json:"insecure,omitempty"` + + // The bucket region. + // +optional + Region string `json:"region,omitempty"` + + // The secret name containing the bucket accesskey and secretkey. + // +optional + SecretRef *corev1.LocalObjectReference `json:"secretRef,omitempty"` + + // The interval at which to check for bucket updates. + // +required + Interval metav1.Duration `json:"interval"` + + // The timeout for download operations, default ('20s'). + // +optional + Timeout *metav1.Duration `json:"timeout,omitempty"` + + // Ignore overrides the set of excluded patterns in the .sourceignore + // format (which is the same as .gitignore). + // +optional + Ignore *string `json:"ignore,omitempty"` +} + +// BucketStatus defines the observed state of a bucket +type BucketStatus struct { + // ObservedGeneration is the last observed generation. + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // Conditions holds the conditions for the Bucket. + // +optional + Conditions []SourceCondition `json:"conditions,omitempty"` + + // URL is the download link for the artifact output of the last Bucket sync. + // +optional + URL string `json:"url,omitempty"` + + // Artifact represents the output of the last successful Bucket sync. + // +optional + Artifact *Artifact `json:"artifact,omitempty"` +} + +const ( + // BucketOperationSucceedReason represents the fact that the bucket listing + // and download operations succeeded. + BucketOperationSucceedReason string = "BucketOperationSucceed" + + // BucketOperationFailedReason represents the fact that the bucket listing + // or download operations failed. + BucketOperationFailedReason string = "BucketOperationFailed" +) + +// BucketProgressing resets the conditions of the Bucket +// to SourceCondition of type Ready with status unknown and +// progressing reason and message. It returns the modified Bucket. +func BucketProgressing(bucket Bucket) Bucket { + bucket.Status.ObservedGeneration = bucket.Generation + bucket.Status.URL = "" + bucket.Status.Conditions = []SourceCondition{} + SetBucketCondition(&bucket, ReadyCondition, corev1.ConditionUnknown, ProgressingReason, "reconciliation in progress") + return bucket +} + +// SetBucketCondition sets the given condition with the given status, reason and message on the Bucket. +func SetBucketCondition(bucket *Bucket, condition string, status corev1.ConditionStatus, reason, message string) { + bucket.Status.Conditions = filterOutSourceCondition(bucket.Status.Conditions, condition) + bucket.Status.Conditions = append(bucket.Status.Conditions, SourceCondition{ + Type: condition, + Status: status, + LastTransitionTime: metav1.Now(), + Reason: reason, + Message: message, + }) +} + +// BucketReady sets the given artifact and url on the Bucket +// and sets the ReadyCondition to True, with the given reason and +// message. It returns the modified Bucket. +func BucketReady(repository Bucket, artifact Artifact, url, reason, message string) Bucket { + repository.Status.Artifact = &artifact + repository.Status.URL = url + SetBucketCondition(&repository, ReadyCondition, corev1.ConditionTrue, reason, message) + return repository +} + +// BucketNotReady sets the ReadyCondition on the given Bucket +// to False, with the given reason and message. It returns the modified Bucket. +func BucketNotReady(repository Bucket, reason, message string) Bucket { + SetBucketCondition(&repository, ReadyCondition, corev1.ConditionFalse, reason, message) + return repository +} + +// BucketReadyMessage returns the message of the SourceCondition +// of type Ready with status true if present, or an empty string. +func BucketReadyMessage(repository Bucket) string { + for _, condition := range repository.Status.Conditions { + if condition.Type == ReadyCondition && condition.Status == corev1.ConditionTrue { + return condition.Message + } + } + return "" +} + +// GetTimeout returns the configured timeout or the default. +func (in *Bucket) GetTimeout() time.Duration { + if in.Spec.Timeout != nil { + return in.Spec.Timeout.Duration + } + return BucketTimeout +} + +// GetArtifact returns the latest artifact from the source +// if present in the status sub-resource. +func (in *Bucket) GetArtifact() *Artifact { + return in.Status.Artifact +} + +// GetInterval returns the interval at which the source is updated. +func (in *Bucket) GetInterval() metav1.Duration { + return in.Spec.Interval +} + +// +genclient +// +genclient:Namespaced +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="URL",type=string,JSONPath=`.spec.url` +// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description="" +// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description="" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="" + +// Bucket is the Schema for the buckets API +type Bucket struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec BucketSpec `json:"spec,omitempty"` + Status BucketStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// BucketList contains a list of Bucket +type BucketList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Bucket `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Bucket{}, &BucketList{}) +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 48003a1f..249db1a0 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -42,6 +42,123 @@ func (in *Artifact) DeepCopy() *Artifact { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Bucket) DeepCopyInto(out *Bucket) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Bucket. +func (in *Bucket) DeepCopy() *Bucket { + if in == nil { + return nil + } + out := new(Bucket) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Bucket) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BucketList) DeepCopyInto(out *BucketList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Bucket, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BucketList. +func (in *BucketList) DeepCopy() *BucketList { + if in == nil { + return nil + } + out := new(BucketList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BucketList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BucketSpec) DeepCopyInto(out *BucketSpec) { + *out = *in + if in.SecretRef != nil { + in, out := &in.SecretRef, &out.SecretRef + *out = new(v1.LocalObjectReference) + **out = **in + } + out.Interval = in.Interval + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(metav1.Duration) + **out = **in + } + if in.Ignore != nil { + in, out := &in.Ignore, &out.Ignore + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BucketSpec. +func (in *BucketSpec) DeepCopy() *BucketSpec { + if in == nil { + return nil + } + out := new(BucketSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BucketStatus) DeepCopyInto(out *BucketStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]SourceCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Artifact != nil { + in, out := &in.Artifact, &out.Artifact + *out = new(Artifact) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BucketStatus. +func (in *BucketStatus) DeepCopy() *BucketStatus { + if in == nil { + return nil + } + out := new(BucketStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GitRepository) DeepCopyInto(out *GitRepository) { *out = *in diff --git a/config/crd/bases/source.toolkit.fluxcd.io_buckets.yaml b/config/crd/bases/source.toolkit.fluxcd.io_buckets.yaml new file mode 100644 index 00000000..667e7127 --- /dev/null +++ b/config/crd/bases/source.toolkit.fluxcd.io_buckets.yaml @@ -0,0 +1,174 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.3.0 + creationTimestamp: null + name: buckets.source.toolkit.fluxcd.io +spec: + group: source.toolkit.fluxcd.io + names: + kind: Bucket + listKind: BucketList + plural: buckets + singular: bucket + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.url + name: URL + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: Bucket is the Schema for the buckets API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: BucketSpec defines the desired state of an S3 compatible + bucket + properties: + bucketName: + description: The bucket name. + type: string + endpoint: + description: The bucket endpoint address. + type: string + ignore: + description: Ignore overrides the set of excluded patterns in the + .sourceignore format (which is the same as .gitignore). + type: string + insecure: + description: Insecure allows connecting to a non-TLS S3 HTTP endpoint. + type: boolean + interval: + description: The interval at which to check for bucket updates. + type: string + provider: + description: The S3 compatible storage provider name, default ('generic'). + enum: + - generic + - aws + type: string + region: + description: The bucket region. + type: string + secretRef: + description: The secret name containing the bucket accesskey and secretkey. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + timeout: + description: The timeout for download operations, default ('20s'). + type: string + required: + - bucketName + - endpoint + - interval + type: object + status: + description: BucketStatus defines the observed state of a bucket + properties: + artifact: + description: Artifact represents the output of the last successful + Bucket sync. + properties: + checksum: + description: Checksum is the SHA1 checksum of the artifact. + type: string + lastUpdateTime: + description: LastUpdateTime is the timestamp corresponding to + the last update of this artifact. + format: date-time + type: string + path: + description: Path is the relative file path of this artifact. + type: string + revision: + description: Revision is a human readable identifier traceable + in the origin source system. It can be a Git commit sha, Git + tag, a Helm index timestamp, a Helm chart version, etc. + type: string + url: + description: URL is the HTTP address of this artifact. + type: string + required: + - path + - url + type: object + conditions: + description: Conditions holds the conditions for the Bucket. + items: + description: SourceCondition contains condition information for + a source. + properties: + lastTransitionTime: + description: LastTransitionTime is the timestamp corresponding + to the last status change of this condition. + format: date-time + type: string + message: + description: Message is a human readable description of the + details of the last transition, complementing reason. + type: string + reason: + description: Reason is a brief machine readable explanation + for the condition's last transition. + type: string + status: + description: Status of the condition, one of ('True', 'False', + 'Unknown'). + type: string + type: + description: Type of the condition, currently ('Ready'). + type: string + required: + - status + - type + type: object + type: array + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + url: + description: URL is the download link for the artifact output of the + last Bucket sync. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index b108deaf..a666a925 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -4,4 +4,5 @@ resources: - bases/source.toolkit.fluxcd.io_gitrepositories.yaml - bases/source.toolkit.fluxcd.io_helmrepositories.yaml - bases/source.toolkit.fluxcd.io_helmcharts.yaml +- bases/source.toolkit.fluxcd.io_buckets.yaml # +kubebuilder:scaffold:crdkustomizeresource diff --git a/config/rbac/bucket_editor_role.yaml b/config/rbac/bucket_editor_role.yaml new file mode 100644 index 00000000..63d2229a --- /dev/null +++ b/config/rbac/bucket_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit buckets. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: bucket-editor-role +rules: +- apiGroups: + - source.toolkit.fluxcd.io + resources: + - buckets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - source.toolkit.fluxcd.io + resources: + - buckets/status + verbs: + - get diff --git a/config/rbac/bucket_viewer_role.yaml b/config/rbac/bucket_viewer_role.yaml new file mode 100644 index 00000000..f8033a28 --- /dev/null +++ b/config/rbac/bucket_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view buckets. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: bucket-viewer-role +rules: +- apiGroups: + - source.toolkit.fluxcd.io + resources: + - buckets + verbs: + - get + - list + - watch +- apiGroups: + - source.toolkit.fluxcd.io + resources: + - buckets/status + verbs: + - get diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 95e3d4db..a6020f2b 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -13,6 +13,26 @@ rules: verbs: - create - patch +- apiGroups: + - source.toolkit.fluxcd.io + resources: + - buckets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - source.toolkit.fluxcd.io + resources: + - buckets/status + verbs: + - get + - patch + - update - apiGroups: - source.toolkit.fluxcd.io resources: diff --git a/config/samples/source_v1alpha1_bucket.yaml b/config/samples/source_v1alpha1_bucket.yaml new file mode 100644 index 00000000..d9dad440 --- /dev/null +++ b/config/samples/source_v1alpha1_bucket.yaml @@ -0,0 +1,12 @@ +apiVersion: source.toolkit.fluxcd.io/v1alpha1 +kind: Bucket +metadata: + name: bucket-sample + namespace: gitops-system +spec: + interval: 1m + provider: generic + bucketName: podinfo + endpoint: minio.minio.svc.cluster.local:9000 + region: us-east-1 + insecure: true diff --git a/docs/api/source.md b/docs/api/source.md index 46508777..a264f4bb 100644 --- a/docs/api/source.md +++ b/docs/api/source.md @@ -9,12 +9,202 @@

Package v1alpha1 contains API Schema definitions for the source v1alpha1 API group

Resource Types: +

Bucket +

+

Bucket is the Schema for the buckets API

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+apiVersion
+string
+source.toolkit.fluxcd.io/v1alpha1 +
+kind
+string +
+Bucket +
+metadata
+ + +Kubernetes meta/v1.ObjectMeta + + +
+Refer to the Kubernetes API documentation for the fields of the +metadata field. +
+spec
+ + +BucketSpec + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+provider
+ +string + +
+(Optional) +

The S3 compatible storage provider name, default (‘generic’).

+
+bucketName
+ +string + +
+

The bucket name.

+
+endpoint
+ +string + +
+

The bucket endpoint address.

+
+insecure
+ +bool + +
+(Optional) +

Insecure allows connecting to a non-TLS S3 HTTP endpoint.

+
+region
+ +string + +
+(Optional) +

The bucket region.

+
+secretRef
+ + +Kubernetes core/v1.LocalObjectReference + + +
+(Optional) +

The secret name containing the bucket accesskey and secretkey.

+
+interval
+ + +Kubernetes meta/v1.Duration + + +
+

The interval at which to check for bucket updates.

+
+timeout
+ + +Kubernetes meta/v1.Duration + + +
+(Optional) +

The timeout for download operations, default (‘20s’).

+
+ignore
+ +string + +
+(Optional) +

Ignore overrides the set of excluded patterns in the .sourceignore +format (which is the same as .gitignore).

+
+
+status
+ + +BucketStatus + + +
+
+
+

GitRepository

GitRepository is the Schema for the gitrepositories API

@@ -467,6 +657,7 @@ HelmRepositoryStatus

(Appears on: +BucketStatus, GitRepositoryStatus, HelmChartStatus, HelmRepositoryStatus) @@ -548,6 +739,212 @@ update of this artifact.

+

BucketSpec +

+

+(Appears on: +Bucket) +

+

BucketSpec defines the desired state of an S3 compatible bucket

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+provider
+ +string + +
+(Optional) +

The S3 compatible storage provider name, default (‘generic’).

+
+bucketName
+ +string + +
+

The bucket name.

+
+endpoint
+ +string + +
+

The bucket endpoint address.

+
+insecure
+ +bool + +
+(Optional) +

Insecure allows connecting to a non-TLS S3 HTTP endpoint.

+
+region
+ +string + +
+(Optional) +

The bucket region.

+
+secretRef
+ + +Kubernetes core/v1.LocalObjectReference + + +
+(Optional) +

The secret name containing the bucket accesskey and secretkey.

+
+interval
+ + +Kubernetes meta/v1.Duration + + +
+

The interval at which to check for bucket updates.

+
+timeout
+ + +Kubernetes meta/v1.Duration + + +
+(Optional) +

The timeout for download operations, default (‘20s’).

+
+ignore
+ +string + +
+(Optional) +

Ignore overrides the set of excluded patterns in the .sourceignore +format (which is the same as .gitignore).

+
+
+
+

BucketStatus +

+

+(Appears on: +Bucket) +

+

BucketStatus defines the observed state of a bucket

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+observedGeneration
+ +int64 + +
+(Optional) +

ObservedGeneration is the last observed generation.

+
+conditions
+ + +[]SourceCondition + + +
+(Optional) +

Conditions holds the conditions for the Bucket.

+
+url
+ +string + +
+(Optional) +

URL is the download link for the artifact output of the last Bucket sync.

+
+artifact
+ + +Artifact + + +
+(Optional) +

Artifact represents the output of the last successful Bucket sync.

+
+
+

GitRepositoryRef

@@ -1228,6 +1625,7 @@ string

(Appears on: +BucketStatus, GitRepositoryStatus, HelmChartStatus, HelmRepositoryStatus)