1 /* 2 Copyright 2020 The Flux authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package v1beta1 18 19 import ( 20 apimeta "k8s.io/apimachinery/pkg/api/meta" 21 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 22 23 "github.com/fluxcd/pkg/apis/acl" 24 "github.com/fluxcd/pkg/apis/meta" 25 ) 26 27 const ( 28 // BucketKind is the string representation of a Bucket. 29 BucketKind = "Bucket" 30 ) 31 32 // BucketSpec defines the desired state of an S3 compatible bucket 33 type BucketSpec struct { 34 // The S3 compatible storage provider name, default ('generic'). 35 // +kubebuilder:validation:Enum=generic;aws;gcp 36 // +kubebuilder:default:=generic 37 // +optional 38 Provider string `json:"provider,omitempty"` 39 40 // The bucket name. 41 // +required 42 BucketName string `json:"bucketName"` 43 44 // The bucket endpoint address. 45 // +required 46 Endpoint string `json:"endpoint"` 47 48 // Insecure allows connecting to a non-TLS S3 HTTP endpoint. 49 // +optional 50 Insecure bool `json:"insecure,omitempty"` 51 52 // The bucket region. 53 // +optional 54 Region string `json:"region,omitempty"` 55 56 // The name of the secret containing authentication credentials 57 // for the Bucket. 58 // +optional 59 SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"` 60 61 // The interval at which to check for bucket updates. 62 // +required 63 Interval metav1.Duration `json:"interval"` 64 65 // The timeout for download operations, defaults to 60s. 66 // +kubebuilder:default="60s" 67 // +optional 68 Timeout *metav1.Duration `json:"timeout,omitempty"` 69 70 // Ignore overrides the set of excluded patterns in the .sourceignore format 71 // (which is the same as .gitignore). If not provided, a default will be used, 72 // consult the documentation for your version to find out what those are. 73 // +optional 74 Ignore *string `json:"ignore,omitempty"` 75 76 // This flag tells the controller to suspend the reconciliation of this source. 77 // +optional 78 Suspend bool `json:"suspend,omitempty"` 79 80 // AccessFrom defines an Access Control List for allowing cross-namespace references to this object. 81 // +optional 82 AccessFrom *acl.AccessFrom `json:"accessFrom,omitempty"` 83 } 84 85 const ( 86 GenericBucketProvider string = "generic" 87 AmazonBucketProvider string = "aws" 88 GoogleBucketProvider string = "gcp" 89 ) 90 91 // BucketStatus defines the observed state of a bucket 92 type BucketStatus struct { 93 // ObservedGeneration is the last observed generation. 94 // +optional 95 ObservedGeneration int64 `json:"observedGeneration,omitempty"` 96 97 // Conditions holds the conditions for the Bucket. 98 // +optional 99 Conditions []metav1.Condition `json:"conditions,omitempty"` 100 101 // URL is the download link for the artifact output of the last Bucket sync. 102 // +optional 103 URL string `json:"url,omitempty"` 104 105 // Artifact represents the output of the last successful Bucket sync. 106 // +optional 107 Artifact *Artifact `json:"artifact,omitempty"` 108 109 meta.ReconcileRequestStatus `json:",inline"` 110 } 111 112 const ( 113 // BucketOperationSucceedReason represents the fact that the bucket listing and 114 // download operations succeeded. 115 BucketOperationSucceedReason string = "BucketOperationSucceed" 116 117 // BucketOperationFailedReason represents the fact that the bucket listing or 118 // download operations failed. 119 BucketOperationFailedReason string = "BucketOperationFailed" 120 ) 121 122 // BucketProgressing resets the conditions of the Bucket to metav1.Condition of 123 // type meta.ReadyCondition with status 'Unknown' and meta.ProgressingReason 124 // reason and message. It returns the modified Bucket. 125 func BucketProgressing(bucket Bucket) Bucket { 126 bucket.Status.ObservedGeneration = bucket.Generation 127 bucket.Status.URL = "" 128 bucket.Status.Conditions = []metav1.Condition{} 129 newCondition := metav1.Condition{ 130 Type: meta.ReadyCondition, 131 Status: metav1.ConditionUnknown, 132 Reason: meta.ProgressingReason, 133 Message: "reconciliation in progress", 134 } 135 apimeta.SetStatusCondition(bucket.GetStatusConditions(), newCondition) 136 return bucket 137 } 138 139 // BucketReady sets the given Artifact and URL on the Bucket and sets the 140 // meta.ReadyCondition to 'True', with the given reason and message. It returns 141 // the modified Bucket. 142 func BucketReady(bucket Bucket, artifact Artifact, url, reason, message string) Bucket { 143 bucket.Status.Artifact = &artifact 144 bucket.Status.URL = url 145 newCondition := metav1.Condition{ 146 Type: meta.ReadyCondition, 147 Status: metav1.ConditionTrue, 148 Reason: reason, 149 Message: message, 150 } 151 apimeta.SetStatusCondition(bucket.GetStatusConditions(), newCondition) 152 return bucket 153 } 154 155 // BucketNotReady sets the meta.ReadyCondition on the Bucket to 'False', with 156 // the given reason and message. It returns the modified Bucket. 157 func BucketNotReady(bucket Bucket, reason, message string) Bucket { 158 newCondition := metav1.Condition{ 159 Type: meta.ReadyCondition, 160 Status: metav1.ConditionFalse, 161 Reason: reason, 162 Message: message, 163 } 164 apimeta.SetStatusCondition(bucket.GetStatusConditions(), newCondition) 165 return bucket 166 } 167 168 // BucketReadyMessage returns the message of the metav1.Condition of type 169 // meta.ReadyCondition with status 'True' if present, or an empty string. 170 func BucketReadyMessage(bucket Bucket) string { 171 if c := apimeta.FindStatusCondition(bucket.Status.Conditions, meta.ReadyCondition); c != nil { 172 if c.Status == metav1.ConditionTrue { 173 return c.Message 174 } 175 } 176 return "" 177 } 178 179 // GetArtifact returns the latest artifact from the source if present in the 180 // status sub-resource. 181 func (in *Bucket) GetArtifact() *Artifact { 182 return in.Status.Artifact 183 } 184 185 // GetStatusConditions returns a pointer to the Status.Conditions slice 186 func (in *Bucket) GetStatusConditions() *[]metav1.Condition { 187 return &in.Status.Conditions 188 } 189 190 // GetInterval returns the interval at which the source is updated. 191 func (in *Bucket) GetInterval() metav1.Duration { 192 return in.Spec.Interval 193 } 194 195 // +genclient 196 // +kubebuilder:object:root=true 197 // +kubebuilder:subresource:status 198 // +kubebuilder:deprecatedversion:warning="v1beta1 Bucket is deprecated, upgrade to v1" 199 // +kubebuilder:printcolumn:name="Endpoint",type=string,JSONPath=`.spec.endpoint` 200 // +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description="" 201 // +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description="" 202 // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="" 203 204 // Bucket is the Schema for the buckets API 205 type Bucket struct { 206 metav1.TypeMeta `json:",inline"` 207 metav1.ObjectMeta `json:"metadata,omitempty"` 208 209 Spec BucketSpec `json:"spec,omitempty"` 210 // +kubebuilder:default={"observedGeneration":-1} 211 Status BucketStatus `json:"status,omitempty"` 212 } 213 214 // +kubebuilder:object:root=true 215 216 // BucketList contains a list of Bucket 217 type BucketList struct { 218 metav1.TypeMeta `json:",inline"` 219 metav1.ListMeta `json:"metadata,omitempty"` 220 Items []Bucket `json:"items"` 221 } 222 223 func init() { 224 SchemeBuilder.Register(&Bucket{}, &BucketList{}) 225 } 226