diff --git a/controllers/application/api/v1/application_types.go b/controllers/application/api/v1/application_types.go index a7398692a3..495f4d949b 100644 --- a/controllers/application/api/v1/application_types.go +++ b/controllers/application/api/v1/application_types.go @@ -66,11 +66,11 @@ type ApplicationStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster // Important: Run "make" to regenerate code after modifying this file - // Appid + // AppId //+kubebuilder:validation:MinLength=3 //+kubebuilder:validation:MaxLength=16 //+kubebuilder:validation:Required - Appid string `json:"appid"` + AppId string `json:"appid"` // Specification for the application Specification SpecificationSpec `json:"specification"` diff --git a/controllers/database/api/v1/database_types.go b/controllers/database/api/v1/database_types.go index 03b7a4b006..deceaa4419 100644 --- a/controllers/database/api/v1/database_types.go +++ b/controllers/database/api/v1/database_types.go @@ -59,7 +59,7 @@ type DatabaseStatus struct { // Store name of this database. // The controller has created the corresponding storage resources based on this store. //+kubebuilder:validation:Required - StoreName string `json:"store,omitempty"` + StoreName string `json:"storeName,omitempty"` //+kubebuilder:validation:Required StoreNamespace string `json:"storeNamespace,omitempty"` diff --git a/controllers/database/config/crd/bases/database.laf.dev_databases.yaml b/controllers/database/config/crd/bases/database.laf.dev_databases.yaml index eea0584f75..1bb5d78e4e 100644 --- a/controllers/database/config/crd/bases/database.laf.dev_databases.yaml +++ b/controllers/database/config/crd/bases/database.laf.dev_databases.yaml @@ -83,7 +83,7 @@ spec: connectionURI: description: ConnectionURI of the database. type: string - store: + storeName: description: Store name of this database. The controller has created the corresponding storage resources based on this store. type: string diff --git a/controllers/database/controllers/database_controller.go b/controllers/database/controllers/database_controller.go index 1e32eec6a4..44fe87717a 100644 --- a/controllers/database/controllers/database_controller.go +++ b/controllers/database/controllers/database_controller.go @@ -196,7 +196,6 @@ func (r *DatabaseReconciler) selectStore(ctx context.Context, database *database // select the store: // - match the provider - // - match the region // - have enough capacity // - have the higher priority var store databasev1.Store diff --git a/controllers/oss/api/v1/bucket_types.go b/controllers/oss/api/v1/bucket_types.go index c4a1d1b8b3..f8a75ca986 100644 --- a/controllers/oss/api/v1/bucket_types.go +++ b/controllers/oss/api/v1/bucket_types.go @@ -17,6 +17,7 @@ limitations under the License. package v1 import ( + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -27,9 +28,9 @@ import ( type BucketPolicy string const ( - BucketPolicyReadOnly BucketPolicy = "readonly" - BucketPolicyReadWrite BucketPolicy = "public" - BucketPolicyPrivate BucketPolicy = "private" + BucketPolicyPrivate BucketPolicy = "private" + BucketPolicyReadOnly BucketPolicy = "readonly" + BucketPolicyPublic BucketPolicy = "readwrite" ) // BucketSpec defines the desired state of Bucket @@ -37,28 +38,15 @@ type BucketSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file - // Name of bucket in oss. It's read-only after creation. - // This will be used as the bucket name in storage store. - // The length is between 3-63 and can only include letters, numbers and short horizontal lines (-). - //+kubebuilder:validation:Required - //+kubebuilder:validation:MinLength=3 - //+kubebuilder:validation:MaxLength=64 - //+kubebuilder:validation:Pattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - Name string `json:"name"` - - // Policy of bucket in oss. required. - //+kubebuilder:validation:Required + // Policy of bucket in oss, defaults to 'private'. + //+kubebuilder:validation:Enum=private;readonly;readwrite + //+kubebuilder:validation:Default=private + //+optional Policy BucketPolicy `json:"policy"` - // Storage space of this bucket, in MB. It defaults to 0, which means no limit. + // Storage space of this bucket. //+kubebuilder:validation:Required - //+kubebuilder:validation:Minimum=0 - //+kubebuilder:default=0 - Storage int64 `json:"storage"` - - // The name of oss user. - //+kubebuilder:validation:Required - User string `json:"user"` + Storage resource.Quantity `json:"storage"` } // BucketStatus defines the observed state of Bucket @@ -66,17 +54,25 @@ type BucketStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster // Important: Run "make" to regenerate code after modifying this file + // Username of bucket in oss. + User string `json:"user"` + + // Policy of bucket in oss. + Policy BucketPolicy `json:"policy"` + + // Versioning of bucket in oss. + Versioning bool `json:"versioning"` + // Capacity of this bucket. - Capacity BucketCapacity `json:"capacity,omitempty"` + Capacity BucketCapacity `json:"capacity"` } type BucketCapacity struct { - // The user's storage space. The unit is MB. - // The default value is 0 which means unlimited. - //+kubebuilder:validation:Minimum=0 - //+kubebuilder:default=0 - //+optional - Storage int64 `json:"storage,omitempty"` + // Storage space of this bucket. + MaxStorage resource.Quantity `json:"maxStorage,omitempty"` + + // The used storage space. + UsedStorage resource.Quantity `json:"storage,omitempty"` // The user's number of objects. //+optional diff --git a/controllers/oss/api/v1/store_types.go b/controllers/oss/api/v1/store_types.go index 9218185f9d..113447b062 100644 --- a/controllers/oss/api/v1/store_types.go +++ b/controllers/oss/api/v1/store_types.go @@ -17,6 +17,7 @@ limitations under the License. package v1 import ( + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -32,10 +33,8 @@ type StoreCapacity struct { // The storage space. The unit is MB. // The default value is 0 which means unlimited. - //+kubebuilder:validation:Minimum=0 - //+kubebuilder:default=0 //+optional - Storage int64 `json:"storage,omitempty"` + Storage resource.Quantity `json:"storage,omitempty"` // The number of objects. The default value is 0 which means unlimited. //+optional @@ -50,6 +49,16 @@ type StoreCapacity struct { BucketCount int64 `json:"bucketCount,omitempty"` } +type StoreState string + +const ( + // StoreStateEnabled means the store is enabled. + StoreStateEnabled StoreState = "Enabled" + + // StoreStateDisabled means the store is disabled. + StoreStateDisabled StoreState = "Disabled" +) + // StoreSpec defines the desired state of Store type StoreSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster @@ -85,6 +94,11 @@ type StoreSpec struct { //+kubebuilder:validation:Required Endpoint string `json:"endpoint,omitempty"` + // UseSSL indicates whether to use ssl to connect to the store service. + //+kubebuilder:validation:Required + //+kubebuilder:default=false + UseSSL bool `json:"useSSL,omitempty"` + // AccessKey is the access key which have admin rights of the store service. //+kubebuilder:validation:Required AccessKey string `json:"accessKey,omitempty"` @@ -99,7 +113,7 @@ type StoreSpec struct { // Priority is used to guide the allocation of resources. // The higher the priority, the first to allocate resources in. - // If this value is 0, this store will not be selected for allocating new user. + // If this value is 0, this store will not be selected for allocating new user. //+kubebuilder:validation:Minimum=0 //+kubebuilder:validation:Maximum=100 //+kubebuilder:default=10 @@ -115,6 +129,11 @@ type StoreStatus struct { // The observed capacity of Store. //+optional Capacity StoreCapacity `json:"capacity,omitempty"` + + // The state of the store, defaults to "Pending". + //+kubebuilder:default=Pending + //+kubebuilder:validation:Enum=Pending;Enabled;Disabled + State StoreState `json:"state,omitempty"` } //+kubebuilder:object:root=true diff --git a/controllers/oss/api/v1/user_types.go b/controllers/oss/api/v1/user_types.go index b239bfe81f..9d2cfa8878 100644 --- a/controllers/oss/api/v1/user_types.go +++ b/controllers/oss/api/v1/user_types.go @@ -17,6 +17,7 @@ limitations under the License. package v1 import ( + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -28,16 +29,23 @@ type UserSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file + // AppId is the unique identifier for the app, usually used as the username of this User. + //+kubebuilder:validation:MinLength=3 + //+kubebuilder:validation:MaxLength=32 + //+kubebuilder:validation:Required + AppId string `json:"appid"` + + // Password is the secret name of the user, which is used to authenticate the user. + //+kubebuilder:validation:MinLength=3 + //+kubebuilder:validation:MaxLength=32 + //+kubebuilder:validation:Required + Password string `json:"password"` + // Provider name of a oss store. It's read-only after creation. // The controller will create the corresponding storage resources based on this provider. //+kubebuilder:validation:Required Provider string `json:"provider"` - // Region of oss store. It's read-only after creation. - // The controller will create the corresponding storage resources based on this region. - //+kubebuilder:validation:Required - Region string `json:"region"` - // Capacity that user desired. Capacity UserCapacity `json:"capacity,omitempty"` } @@ -47,41 +55,38 @@ type UserStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster // Important: Run "make" to regenerate code after modifying this file - // AccessKey for this user. This field might be generated by controller if accessKey not given in spec. - AccessKey string `json:"accessKey,omitempty"` - - // SecretKey for this user. This field might be generated by controller if accessKey not given in spec. - SecretKey string `json:"secretKey,omitempty"` - - // Store name of a oss store. It's read-only after creation. + // StoreName of the oss store. It's read-only after creation. // The controller has created the corresponding storage resources based on this store. //+kubebuilder:validation:Required - Store string `json:"store,omitempty"` + StoreName string `json:"storeName,omitempty"` - // The region name identifies the location of the store. //+kubebuilder:validation:Required - //+kubebuilder:validation:MinLength=2 - //+kubebuilder:validation:MaxLength=64 - //+kubebuilder:default=default - //+kubebuilder:validation:Pattern=[a-zA-Z0-9-]+ - Region string `json:"region,omitempty"` + StoreNamespace string `json:"storeNamespace,omitempty"` + + // AccessKey is the access key of the user + AccessKey string `json:"accessKey,omitempty"` + + // SecretKey is the secret key of the user + SecretKey string `json:"secretKey,omitempty"` // Endpoint is the store service endpoint. //+kubebuilder:validation:Required Endpoint string `json:"endpoint,omitempty"` + // Region of oss store. + //+kubebuilder:validation:Required + Region string `json:"region"` + // The user's capacity observed by the controller. Capacity UserCapacity `json:"capacity,omitempty"` } // UserCapacity is used to obtain the user's used capacity. type UserCapacity struct { - // The user's storage space. The unit is MB. + // The user's storage space. // The default value is 0 which means unlimited. - //+kubebuilder:validation:Minimum=0 - //+kubebuilder:default=0 //+optional - Storage int64 `json:"storage,omitempty"` + Storage resource.Quantity `json:"storage,omitempty"` // The user's number of objects. //+optional diff --git a/controllers/oss/api/v1/zz_generated.deepcopy.go b/controllers/oss/api/v1/zz_generated.deepcopy.go index 2f0e77468c..008dfa45aa 100644 --- a/controllers/oss/api/v1/zz_generated.deepcopy.go +++ b/controllers/oss/api/v1/zz_generated.deepcopy.go @@ -30,8 +30,8 @@ func (in *Bucket) DeepCopyInto(out *Bucket) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - out.Status = in.Status + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Bucket. @@ -55,6 +55,8 @@ func (in *Bucket) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BucketCapacity) DeepCopyInto(out *BucketCapacity) { *out = *in + out.MaxStorage = in.MaxStorage.DeepCopy() + out.UsedStorage = in.UsedStorage.DeepCopy() } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BucketCapacity. @@ -102,6 +104,7 @@ func (in *BucketList) DeepCopyObject() runtime.Object { // 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 + out.Storage = in.Storage.DeepCopy() } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BucketSpec. @@ -117,7 +120,7 @@ func (in *BucketSpec) DeepCopy() *BucketSpec { // 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 - out.Capacity = in.Capacity + in.Capacity.DeepCopyInto(&out.Capacity) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BucketStatus. @@ -135,8 +138,8 @@ func (in *Store) DeepCopyInto(out *Store) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - out.Status = in.Status + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Store. @@ -160,6 +163,7 @@ func (in *Store) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *StoreCapacity) DeepCopyInto(out *StoreCapacity) { *out = *in + out.Storage = in.Storage.DeepCopy() } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StoreCapacity. @@ -207,7 +211,7 @@ func (in *StoreList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *StoreSpec) DeepCopyInto(out *StoreSpec) { *out = *in - out.Capacity = in.Capacity + in.Capacity.DeepCopyInto(&out.Capacity) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StoreSpec. @@ -223,7 +227,7 @@ func (in *StoreSpec) DeepCopy() *StoreSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *StoreStatus) DeepCopyInto(out *StoreStatus) { *out = *in - out.Capacity = in.Capacity + in.Capacity.DeepCopyInto(&out.Capacity) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StoreStatus. @@ -241,8 +245,8 @@ func (in *User) DeepCopyInto(out *User) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - out.Status = in.Status + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new User. @@ -266,6 +270,7 @@ func (in *User) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *UserCapacity) DeepCopyInto(out *UserCapacity) { *out = *in + out.Storage = in.Storage.DeepCopy() } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserCapacity. @@ -313,7 +318,7 @@ func (in *UserList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *UserSpec) DeepCopyInto(out *UserSpec) { *out = *in - out.Capacity = in.Capacity + in.Capacity.DeepCopyInto(&out.Capacity) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserSpec. @@ -329,7 +334,7 @@ func (in *UserSpec) DeepCopy() *UserSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *UserStatus) DeepCopyInto(out *UserStatus) { *out = *in - out.Capacity = in.Capacity + in.Capacity.DeepCopyInto(&out.Capacity) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserStatus. diff --git a/controllers/oss/config/crd/bases/oss.laf.dev_buckets.yaml b/controllers/oss/config/crd/bases/oss.laf.dev_buckets.yaml index f16d3b98b1..446ff2e06c 100644 --- a/controllers/oss/config/crd/bases/oss.laf.dev_buckets.yaml +++ b/controllers/oss/config/crd/bases/oss.laf.dev_buckets.yaml @@ -35,33 +35,22 @@ spec: spec: description: BucketSpec defines the desired state of Bucket properties: - name: - description: Name of bucket in oss. It's read-only after creation. - This will be used as the bucket name in storage store. The length - is between 3-63 and can only include letters, numbers and short - horizontal lines (-). - maxLength: 64 - minLength: 3 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string policy: - description: Policy of bucket in oss. required. + description: Policy of bucket in oss, defaults to 'private'. + enum: + - private + - readonly + - readwrite type: string storage: - default: 0 - description: Storage space of this bucket, in MB. It defaults to 0, - which means no limit. - format: int64 - minimum: 0 - type: integer - user: - description: The name of oss user. - type: string + anyOf: + - type: integer + - type: string + description: Storage space of this bucket. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true required: - - name - - policy - storage - - user type: object status: description: BucketStatus defines the observed state of Bucket @@ -69,6 +58,13 @@ spec: capacity: description: Capacity of this bucket. properties: + maxStorage: + anyOf: + - type: integer + - type: string + description: Storage space of this bucket. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true objectCount: default: 0 description: The user's number of objects. @@ -76,13 +72,27 @@ spec: minimum: 0 type: integer storage: - default: 0 - description: The user's storage space. The unit is MB. The default - value is 0 which means unlimited. - format: int64 - minimum: 0 - type: integer + anyOf: + - type: integer + - type: string + description: The used storage space. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true type: object + policy: + description: Policy of bucket in oss. + type: string + user: + description: Username of bucket in oss. + type: string + versioning: + description: Versioning of bucket in oss. + type: boolean + required: + - capacity + - policy + - user + - versioning type: object type: object served: true diff --git a/controllers/oss/config/crd/bases/oss.laf.dev_stores.yaml b/controllers/oss/config/crd/bases/oss.laf.dev_stores.yaml index 2b981031b6..54134dadfb 100644 --- a/controllers/oss/config/crd/bases/oss.laf.dev_stores.yaml +++ b/controllers/oss/config/crd/bases/oss.laf.dev_stores.yaml @@ -57,12 +57,13 @@ spec: minimum: 0 type: integer storage: - default: 0 + anyOf: + - type: integer + - type: string description: The storage space. The unit is MB. The default value is 0 which means unlimited. - format: int64 - minimum: 0 - type: integer + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true userCount: description: The user count of a store. format: int64 @@ -75,9 +76,9 @@ spec: priority: default: 10 description: Priority is used to guide the allocation of resources. - The higher the priority, the first to allocate resources in. It - is worth noting that if the priority value is 0, it means that the - resource is suspended on the store. + The higher the priority, the first to allocate resources in. If + this value is 0, this store will not be selected for allocating + new user. maximum: 100 minimum: 0 type: integer @@ -99,20 +100,17 @@ spec: This is a required field and default value is "default" maxLength: 64 minLength: 2 - pattern: '[a-zA-Z0-9-]+' + pattern: '[a-z0-9-]+' type: string secretKey: description: SecretKey is the secret key which have admin rights of the store service. type: string - zone: - default: default - description: Zone identifies the availability zone of the store. This - is a required field and default value is "default" - maxLength: 64 - minLength: 2 - pattern: '[a-zA-Z0-9-]+' - type: string + useSSL: + default: false + description: UseSSL indicates whether to use ssl to connect to the + store service. + type: boolean type: object status: description: StoreStatus defines the observed state of Store @@ -135,17 +133,26 @@ spec: minimum: 0 type: integer storage: - default: 0 + anyOf: + - type: integer + - type: string description: The storage space. The unit is MB. The default value is 0 which means unlimited. - format: int64 - minimum: 0 - type: integer + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true userCount: description: The user count of a store. format: int64 type: integer type: object + state: + default: Pending + description: The state of the store, defaults to "Pending". + enum: + - Pending + - Enabled + - Disabled + type: string type: object type: object served: true diff --git a/controllers/oss/config/crd/bases/oss.laf.dev_users.yaml b/controllers/oss/config/crd/bases/oss.laf.dev_users.yaml index 907fbd4e02..3f0e451f9d 100644 --- a/controllers/oss/config/crd/bases/oss.laf.dev_users.yaml +++ b/controllers/oss/config/crd/bases/oss.laf.dev_users.yaml @@ -35,6 +35,12 @@ spec: spec: description: UserSpec defines the desired state of User properties: + appid: + description: AppId is the unique identifier for the app, usually used + as the username of this User. + maxLength: 32 + minLength: 3 + type: string capacity: description: Capacity that user desired. properties: @@ -51,33 +57,35 @@ spec: minimum: 0 type: integer storage: - default: 0 - description: The user's storage space. The unit is MB. The default - value is 0 which means unlimited. - format: int64 - minimum: 0 - type: integer + anyOf: + - type: integer + - type: string + description: The user's storage space. The default value is 0 + which means unlimited. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true type: object + password: + description: Password is the secret name of the user, which is used + to authenticate the user. + maxLength: 32 + minLength: 3 + type: string provider: description: Provider name of a oss store. It's read-only after creation. The controller will create the corresponding storage resources based on this provider. type: string - region: - description: Region of oss store. It's read-only after creation. The - controller will create the corresponding storage resources based - on this region. - type: string required: + - appid + - password - provider - - region type: object status: description: UserStatus defines the observed state of User properties: accessKey: - description: AccessKey for this user. This field might be generated - by controller if accessKey not given in spec. + description: AccessKey is the access key of the user type: string capacity: description: The user's capacity observed by the controller. @@ -95,32 +103,32 @@ spec: minimum: 0 type: integer storage: - default: 0 - description: The user's storage space. The unit is MB. The default - value is 0 which means unlimited. - format: int64 - minimum: 0 - type: integer + anyOf: + - type: integer + - type: string + description: The user's storage space. The default value is 0 + which means unlimited. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true type: object endpoint: description: Endpoint is the store service endpoint. type: string region: - default: default - description: The region name identifies the location of the store. - maxLength: 64 - minLength: 2 - pattern: '[a-zA-Z0-9-]+' + description: Region of oss store. type: string secretKey: - description: SecretKey for this user. This field might be generated - by controller if accessKey not given in spec. + description: SecretKey is the secret key of the user type: string - store: - description: Store name of a oss store. It's read-only after creation. + storeName: + description: StoreName of the oss store. It's read-only after creation. The controller has created the corresponding storage resources based on this store. type: string + storeNamespace: + type: string + required: + - region type: object type: object served: true diff --git a/controllers/oss/config/samples/minio.yaml b/controllers/oss/config/samples/minio.yaml new file mode 100644 index 0000000000..d2a3dbcf82 --- /dev/null +++ b/controllers/oss/config/samples/minio.yaml @@ -0,0 +1,61 @@ +--- +kind: Service +apiVersion: v1 +metadata: + name: oss +# namespace: laf +spec: + selector: + app: oss + clusterIP: None + ports: + - port: 9000 + targetPort: 9000 + name: http + - port: 9001 + targetPort: 9001 + name: console + + +### This oss ONLY work for demo purpose, you should config your own volume for production use! +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: oss +# namespace: laf + labels: + app: oss +spec: + replicas: 1 + selector: + matchLabels: + app: oss + serviceName: "oss" + template: + metadata: + labels: + app: oss + spec: + terminationGracePeriodSeconds: 30 + containers: + - image: minio/minio:RELEASE.2022-08-13T21-54-44Z + name: oss + env: + - name: MINIO_ROOT_USER + value: minio-root-user + - name: MINIO_ROOT_PASSWORD + value: minio-root-password + - name: MINIO_REGION_NAME + value: default + command: ["minio", "server", "/data/{0...3}", "--console-address", ":9001"] + ports: + - containerPort: 9000 + - containerPort: 9001 + volumeMounts: + - mountPath: /data + name: data + volumes: + - name: data + emptyDir: {} + restartPolicy: Always \ No newline at end of file diff --git a/controllers/oss/config/samples/oss_v1_bucket.yaml b/controllers/oss/config/samples/oss_v1_bucket.yaml index 16d9161171..0df3e22a20 100644 --- a/controllers/oss/config/samples/oss_v1_bucket.yaml +++ b/controllers/oss/config/samples/oss_v1_bucket.yaml @@ -1,6 +1,9 @@ apiVersion: oss.laf.dev/v1 kind: Bucket metadata: - name: bucket-sample + name: app1-sample + labels: + appid: app1 spec: - # TODO(user): Add fields here + policy: readonly + storage: 100Mi \ No newline at end of file diff --git a/controllers/oss/config/samples/oss_v1_stores.yaml b/controllers/oss/config/samples/oss_v1_stores.yaml index 7a7846fc54..0be7f4fc92 100644 --- a/controllers/oss/config/samples/oss_v1_stores.yaml +++ b/controllers/oss/config/samples/oss_v1_stores.yaml @@ -3,4 +3,16 @@ kind: Store metadata: name: store-sample spec: - # TODO(user): Add fields here + provider: minio + endpoint: localhost:9000 + accessKey: minio-root-user + secretKey: minio-root-password + region: default + priority: 10 + useSSL: false + capacity: + storage: 10Gi + objectCount: 10000 + userCount: 1000 + bucketCount: 2000 + diff --git a/controllers/oss/config/samples/oss_v1_users.yaml b/controllers/oss/config/samples/oss_v1_users.yaml index 06e6395512..e3191cb05b 100644 --- a/controllers/oss/config/samples/oss_v1_users.yaml +++ b/controllers/oss/config/samples/oss_v1_users.yaml @@ -1,6 +1,12 @@ apiVersion: oss.laf.dev/v1 kind: User metadata: - name: user-sample + name: app1 + labels: + appid: app1 spec: - # TODO(user): Add fields here + provider: minio + appid: app1 + password: app1-minio-password + capacity: + storage: 2Gi diff --git a/controllers/oss/controllers/bucket_controller.go b/controllers/oss/controllers/bucket_controller.go index f74cc22022..44c28d0582 100644 --- a/controllers/oss/controllers/bucket_controller.go +++ b/controllers/oss/controllers/bucket_controller.go @@ -18,6 +18,10 @@ package controllers import ( "context" + "errors" + "github.com/labring/laf/controllers/oss/driver" + "laf/pkg/util" + "time" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" @@ -27,6 +31,8 @@ import ( ossv1 "github.com/labring/laf/controllers/oss/api/v1" ) +const bucketFinalizer = "bucket.oss.laf.dev" + // BucketReconciler reconciles a Bucket object type BucketReconciler struct { client.Client @@ -49,11 +55,215 @@ type BucketReconciler struct { func (r *BucketReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { _ = log.FromContext(ctx) - // TODO(user): your logic here + // get the bucket + var bucket ossv1.Bucket + if err := r.Get(ctx, req.NamespacedName, &bucket); err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + // reconcile deletions + if !bucket.ObjectMeta.DeletionTimestamp.IsZero() { + return r.delete(ctx, &bucket) + } + + return r.apply(ctx, &bucket) +} + +// apply the bucket +func (r *BucketReconciler) apply(ctx context.Context, bucket *ossv1.Bucket) (ctrl.Result, error) { + _log := log.FromContext(ctx) + + // add finalizer if not present + if !util.ContainsString(bucket.ObjectMeta.Finalizers, bucketFinalizer) { + bucket.ObjectMeta.Finalizers = append(bucket.ObjectMeta.Finalizers, bucketFinalizer) + if err := r.Update(ctx, bucket); err != nil { + return ctrl.Result{}, err + } + _log.Info("Added finalizer to bucket", "finalizer", bucketFinalizer) + } + + // reconcile the bucket user + if bucket.Status.User == "" { + if err := r.createBucket(ctx, bucket); err != nil { + return ctrl.Result{}, err + } + _log.Info("Created bucket", "bucket", bucket.Name) + } + + // reconcile the versioning + if !bucket.Status.Versioning { + if err := r.enableVersioning(ctx, bucket); err != nil { + return ctrl.Result{}, err + } + _log.Info("Enabled versioning", "bucket", bucket.Name) + } + + // reconcile the policy + if bucket.Status.Policy != bucket.Spec.Policy { + if err := r.setBucketPolicy(ctx, bucket); err != nil { + return ctrl.Result{}, err + } + _log.Info("Set bucket policy", "bucket", bucket.Name, "policy", bucket.Spec.Policy) + } + + // reconcile the quota + if bucket.Status.Capacity.MaxStorage.Cmp(bucket.Spec.Storage) != 0 { + if err := r.setBucketQuota(ctx, bucket); err != nil { + return ctrl.Result{}, err + } + _log.Info("Set bucket quota", "bucket", bucket.Name, "quota", bucket.Spec.Storage.String()) + } + + // TODO: reconcile the bucket capacity + return ctrl.Result{RequeueAfter: time.Minute * 15}, nil +} + +// delete the bucket +func (r *BucketReconciler) delete(ctx context.Context, bucket *ossv1.Bucket) (ctrl.Result, error) { + // TODO: delete the bucket return ctrl.Result{}, nil } +// setBucketQuota - set bucket quota +func (r *BucketReconciler) setBucketQuota(ctx context.Context, bucket *ossv1.Bucket) error { + // create the minio client + mca, err := r.createMinioClient(ctx, bucket) + if err != nil { + return err + } + + // set bucket quota + if err := mca.SetBucketQuota(bucket.Name, bucket.Spec.Storage); err != nil { + return err + } + + // update the status + bucket.Status.Capacity.MaxStorage = bucket.Spec.Storage + if err := r.Status().Update(ctx, bucket); err != nil { + return err + } + + return nil +} + +// setBucketPolicy - set bucket policy +func (r *BucketReconciler) setBucketPolicy(ctx context.Context, bucket *ossv1.Bucket) error { + // create the minio client + mca, err := r.createMinioClient(ctx, bucket) + if err != nil { + return err + } + + // set bucket policy + if err := mca.SetBucketPolicy(bucket.Name, bucket.Spec.Policy); err != nil { + return err + } + + // update the status + bucket.Status.Policy = bucket.Spec.Policy + if err := r.Status().Update(ctx, bucket); err != nil { + return err + } + + return nil +} + +// enableVersioning enables versioning +func (r *BucketReconciler) enableVersioning(ctx context.Context, bucket *ossv1.Bucket) error { + // create the minio client + mca, err := r.createMinioClient(ctx, bucket) + if err != nil { + return err + } + + // enable versioning + if err := mca.EnableVersioning(bucket.Name); err != nil { + return err + } + + // update the status + bucket.Status.Versioning = true + if err := r.Status().Update(ctx, bucket); err != nil { + return err + } + + return nil +} + +// createBucket creates a bucket +func (r *BucketReconciler) createBucket(ctx context.Context, bucket *ossv1.Bucket) error { + // get the user + user, err := r.getUser(ctx, bucket) + if err != nil { + return err + } + + // get the store of user + var store ossv1.Store + if err := r.Get(ctx, client.ObjectKey{Namespace: user.Status.StoreNamespace, Name: user.Status.StoreName}, &store); err != nil { + return err + } + + // create minio client + mca, err := driver.NewMinioClientAdmin(ctx, store.Spec.Endpoint, store.Spec.AccessKey, store.Spec.SecretKey, store.Spec.UseSSL) + if err != nil { + return err + } + + // create the bucket + if err := mca.CreateBucket(bucket.Name, store.Spec.Region, true); err != nil { + return err + } + + // update the status + bucket.Status.User = user.Name + if err := r.Status().Update(ctx, bucket); err != nil { + return err + } + return nil +} + +// createMinioClient creates a minio client +func (r *BucketReconciler) createMinioClient(ctx context.Context, bucket *ossv1.Bucket) (*driver.MinioClientAdmin, error) { + // get the user + user, err := r.getUser(ctx, bucket) + if err != nil { + return nil, err + } + + // get the store of user + var store ossv1.Store + if err := r.Get(ctx, client.ObjectKey{Namespace: user.Status.StoreNamespace, Name: user.Status.StoreName}, &store); err != nil { + return nil, err + } + + // create minio client + mca, err := driver.NewMinioClientAdmin(ctx, store.Spec.Endpoint, store.Spec.AccessKey, store.Spec.SecretKey, store.Spec.UseSSL) + if err != nil { + return nil, err + } + + return mca, nil +} + +// getUser gets the user +func (r *BucketReconciler) getUser(ctx context.Context, bucket *ossv1.Bucket) (*ossv1.User, error) { + // get user list in the namespace of bucket + var userList ossv1.UserList + if err := r.List(ctx, &userList, client.InNamespace(bucket.Namespace)); err != nil { + return nil, err + } + + if userList.Items == nil || len(userList.Items) == 0 { + return nil, errors.New("no user found") + } + + // get the first user + user := userList.Items[0] + return &user, nil +} + // SetupWithManager sets up the controller with the Manager. func (r *BucketReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). diff --git a/controllers/oss/controllers/store_controller.go b/controllers/oss/controllers/store_controller.go index 89cd1003a1..75172db08c 100644 --- a/controllers/oss/controllers/store_controller.go +++ b/controllers/oss/controllers/store_controller.go @@ -18,6 +18,9 @@ package controllers import ( "context" + "github.com/labring/laf/controllers/oss/driver" + "laf/pkg/util" + "time" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" @@ -27,6 +30,8 @@ import ( ossv1 "github.com/labring/laf/controllers/oss/api/v1" ) +const storeFinalizer = "store.oss.laf.dev" + // StoreReconciler reconciles a Store object type StoreReconciler struct { client.Client @@ -39,7 +44,6 @@ type StoreReconciler struct { // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. -// TODO(user): Modify the Reconcile function to compare the state specified by // the Store object against the actual cluster state, and then // perform operations to make the cluster state reflect the state specified by // the user. @@ -47,14 +51,116 @@ type StoreReconciler struct { // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.12.2/pkg/reconcile func (r *StoreReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := log.FromContext(ctx) + _ = log.FromContext(ctx) + + // get the store + var store ossv1.Store + if err := r.Get(ctx, req.NamespacedName, &store); err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + // reconcile the deletion + if !store.DeletionTimestamp.IsZero() { + return r.delete(ctx, &store) + } - // TODO(user): your logic here - log.Info("Reconciling Store") + return r.apply(ctx, &store) +} +// delete deletes the store. +// TODO: implement the deletion of the store. +func (r *StoreReconciler) delete(ctx context.Context, store *ossv1.Store) (ctrl.Result, error) { + // TODO: reject deletion return ctrl.Result{}, nil } +// apply the store. +func (r *StoreReconciler) apply(ctx context.Context, store *ossv1.Store) (ctrl.Result, error) { + _log := log.FromContext(ctx) + + // add finalizer + if !util.ContainsString(store.GetFinalizers(), storeFinalizer) { + store.SetFinalizers(append(store.GetFinalizers(), storeFinalizer)) + if err := r.Update(ctx, store); err != nil { + return ctrl.Result{}, err + } + _log.Info("added finalizer", "finalizer", storeFinalizer) + } + + // reconcile the store state + if store.Status.State == "" { + err := r.initStore(ctx, store) + if err != nil { + return ctrl.Result{Requeue: true, RequeueAfter: time.Minute}, err + } + _log.Info("initialized store", "store", store.Name) + } + + // sync capacity status + if store.Status.State == ossv1.StoreStateEnabled { + err := r.syncCapacityStatus(ctx, store) + if err != nil { + return ctrl.Result{Requeue: true, RequeueAfter: time.Minute}, err + } + _log.Info("synced capacity status", "store", store.Name) + } + + return ctrl.Result{Requeue: true, RequeueAfter: time.Minute * 5}, nil +} + +// initStore initializes the store. +func (r *StoreReconciler) initStore(ctx context.Context, store *ossv1.Store) error { + // create minio admin client + mca, err := driver.NewMinioClientAdmin(ctx, store.Spec.Endpoint, store.Spec.AccessKey, store.Spec.SecretKey, false) + if err != nil { + return err + } + + // create initial policy + if err := mca.CreateInitialPolicy(); err != nil { + return err + } + + // create initial group + if err := mca.CreateInitialGroup(); err != nil { + return err + } + + // update store state to enabled + store.Status.State = ossv1.StoreStateEnabled + if err := r.Status().Update(ctx, store); err != nil { + return err + } + + return nil +} + +// syncCapacityStatus syncs the capacity status of the store. +func (r *StoreReconciler) syncCapacityStatus(ctx context.Context, store *ossv1.Store) error { + // create minio admin client + mca, err := driver.NewMinioClientAdmin(ctx, store.Spec.Endpoint, store.Spec.AccessKey, store.Spec.SecretKey, false) + if err != nil { + return err + } + + // get capacity + info, err := mca.GetServerInfo() + if err != nil { + return err + } + + // TODO: update the user count + // update capacity status + store.Status.Capacity.Storage.Set(int64(info.Usage.Size)) + store.Status.Capacity.BucketCount = int64(info.Buckets.Count) + store.Status.Capacity.ObjectCount = int64(info.Objects.Count) + if err := r.Status().Update(ctx, store); err != nil { + return err + } + + return nil +} + // SetupWithManager sets up the controller with the Manager. func (r *StoreReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). diff --git a/controllers/oss/controllers/user_controller.go b/controllers/oss/controllers/user_controller.go index c98f09f06c..358d23e3b3 100644 --- a/controllers/oss/controllers/user_controller.go +++ b/controllers/oss/controllers/user_controller.go @@ -18,15 +18,20 @@ package controllers import ( "context" + "errors" + "github.com/labring/laf/controllers/oss/driver" + "laf/pkg/util" + "sigs.k8s.io/controller-runtime/pkg/log" + "time" + ossv1 "github.com/labring/laf/controllers/oss/api/v1" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/log" - - ossv1 "github.com/labring/laf/controllers/oss/api/v1" ) +const finalizerName = "user.laf.dev" + // UserReconciler reconciles a User object type UserReconciler struct { client.Client @@ -47,14 +52,163 @@ type UserReconciler struct { // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.12.2/pkg/reconcile func (r *UserReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := log.FromContext(ctx) - // TODO(user): your logic here - log.Info("Reconciling User") + // get the user object + var user ossv1.User + if err := r.Get(ctx, req.NamespacedName, &user); err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + // reconcile deletions + if !user.DeletionTimestamp.IsZero() { + return r.delete(ctx, &user) + } + + return r.apply(ctx, &user) +} + +// apply the user object. +func (r *UserReconciler) apply(ctx context.Context, user *ossv1.User) (ctrl.Result, error) { + _log := log.FromContext(ctx) + + // add finalizer if not present + if !util.ContainsString(user.GetFinalizers(), finalizerName) { + user.SetFinalizers(append(user.GetFinalizers(), finalizerName)) + if err := r.Update(ctx, user); err != nil { + return ctrl.Result{}, err + } + _log.Info("added finalizer", "finalizer", finalizerName) + } + + // reconcile the store + if user.Status.StoreName == "" { + if err := r.selectStore(ctx, user); err != nil { + return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 10}, err + } + _log.Info("selected store", "storeName", user.Status.StoreName) + } + + // create the AccessKey & SecretKey + if user.Status.AccessKey == "" || user.Status.SecretKey == "" { + if err := r.createUser(ctx, user); err != nil { + return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 10}, err + } + _log.Info("reconciled access key", "accessKey", user.Status.AccessKey) + } + + // TODO: reconcile the capacity + // query the buckets capacity to update user capacity + + return ctrl.Result{}, nil +} + +// delete deletes the user object. +func (r *UserReconciler) delete(ctx context.Context, user *ossv1.User) (ctrl.Result, error) { + _log := log.FromContext(ctx) + // get the store of user + var store ossv1.Store + if err := r.Get(ctx, client.ObjectKey{Namespace: user.Namespace, Name: user.Status.StoreName}, &store); err != nil { + return ctrl.Result{}, err + } + + // delete the user + if user.Status.AccessKey != "" && user.Status.SecretKey != "" { + mca, err := driver.NewMinioClientAdmin(ctx, store.Spec.Endpoint, store.Spec.AccessKey, store.Spec.SecretKey, store.Spec.UseSSL) + if err != nil { + return ctrl.Result{}, err + } + if err := mca.DeleteUser(user.Status.AccessKey); err != nil { + return ctrl.Result{}, err + } + _log.Info("deleted user", "user", user.Status.AccessKey) + } + + // remove finalizer + user.SetFinalizers(util.RemoveString(user.GetFinalizers(), finalizerName)) + if err := r.Update(ctx, user); err != nil { + return ctrl.Result{}, err + } + + _log.Info("removed finalizer", "finalizer", finalizerName) return ctrl.Result{}, nil } +// createUser creates the user object. +func (r *UserReconciler) createUser(ctx context.Context, user *ossv1.User) error { + // get the store of user + var store ossv1.Store + if err := r.Get(ctx, client.ObjectKey{Namespace: user.Namespace, Name: user.Status.StoreName}, &store); err != nil { + return err + } + + // create the user + mca, err := driver.NewMinioClientAdmin(ctx, store.Spec.Endpoint, store.Spec.AccessKey, store.Spec.SecretKey, store.Spec.UseSSL) + if err != nil { + return err + } + + userName := user.Spec.AppId + if err := mca.CreateUser(userName, user.Spec.Password); err != nil { + return err + } + + // add user to initial group + if err := mca.AddUserToInitialGroup(userName); err != nil { + return err + } + + // update the user status + user.Status.AccessKey = user.Spec.AppId + user.Status.SecretKey = user.Spec.Password + return r.Status().Update(ctx, user) +} + +// selectStore selects the store for the user. +func (r *UserReconciler) selectStore(ctx context.Context, user *ossv1.User) error { + // get store list + var stores ossv1.StoreList + if err := r.List(ctx, &stores); err != nil { + return err + } + + // select the store + // - match the provider + // - match the state + var store ossv1.Store + for _, s := range stores.Items { + if s.Spec.Provider != user.Spec.Provider { + continue + } + + if s.Status.State != ossv1.StoreStateEnabled { + continue + } + + // skip if the priority is lower + if s.Spec.Priority < store.Spec.Priority { + continue + } + + store = s + } + + if store.Name == "" { + return errors.New("no available store found") + } + + // update the user status + user.Status.StoreName = store.Name + user.Status.StoreNamespace = store.Namespace + user.Status.Region = store.Spec.Region + user.Status.Endpoint = store.Spec.Endpoint + + if err := r.Status().Update(ctx, user); err != nil { + return err + } + return nil +} + // SetupWithManager sets up the controller with the Manager. func (r *UserReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). diff --git a/controllers/oss/driver/minio.go b/controllers/oss/driver/minio.go new file mode 100644 index 0000000000..6e8c15c274 --- /dev/null +++ b/controllers/oss/driver/minio.go @@ -0,0 +1,263 @@ +package driver + +import ( + "errors" + v1 "github.com/labring/laf/controllers/oss/api/v1" + gonanoid "github.com/matoous/go-nanoid/v2" + "github.com/minio/madmin-go" + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" + "golang.org/x/net/context" + "k8s.io/apimachinery/pkg/api/resource" + "strings" +) + +// MinioClientAdmin is a wrapper for madmin.AdminClient +type MinioClientAdmin struct { + adminClient *madmin.AdminClient + s3Client *minio.Client + context context.Context +} + +const initialUserGroup = "owner_by_prefix_group" +const initialPolicyName = "owner_by_prefix" +const initialPolicy = `{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "s3:GetBucketPolicy", + "s3:GetObject", + "s3:ListBucket", + "s3:ListBucketMultipartUploads", + "s3:ListMultipartUploadParts", + "s3:PutObject", + "s3:DeleteObject", + "s3:GetBucketLocation" + ], + "Resource": [ + "arn:aws:s3:::${aws:username}-*" + ] + } + ] +}` + +func NewMinioClientAdmin(context context.Context, endpoint string, accessKeyID string, secretAccessKey string, secure bool) (*MinioClientAdmin, error) { + mca := MinioClientAdmin{context: context} + + // create minio admin client + client, err := madmin.New(endpoint, accessKeyID, secretAccessKey, secure) + if err != nil { + return nil, err + } + mca.adminClient = client + + // create s3 client + s3Client, err := minio.New(endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""), + Secure: secure, + }) + if err != nil { + return nil, err + } + mca.s3Client = s3Client + + return &mca, nil +} + +// GetServerInfo - returns the server info. +func (mca *MinioClientAdmin) GetServerInfo() (*madmin.InfoMessage, error) { + stats, err := mca.adminClient.ServerInfo(mca.context) + if err != nil { + return nil, err + } + + return &stats, nil +} + +// CreateInitialPolicy - creates a canned policy. +func (mca *MinioClientAdmin) CreateInitialPolicy() error { + err := mca.adminClient.AddCannedPolicy(mca.context, initialPolicyName, []byte(initialPolicy)) + if err != nil { + return err + } + + return nil +} + +// AddUserToInitialGroup - creates a user group in minio. +func (mca *MinioClientAdmin) AddUserToInitialGroup(user string) error { + param := madmin.GroupAddRemove{ + Group: initialUserGroup, + Members: []string{user}, + IsRemove: false, + } + + err := mca.adminClient.UpdateGroupMembers(mca.context, param) + if err != nil { + return err + } + + return nil +} + +// CreateInitialGroup - creates a user group in minio. +func (mca *MinioClientAdmin) CreateInitialGroup() error { + // create a temporary user + user := "tempUser" + err := mca.adminClient.AddUser(mca.context, user, gonanoid.Must()) + if err != nil { + return err + } + + // create initial group + err = mca.AddUserToInitialGroup(user) + if err != nil { + return err + } + + // set policy for user group + err = mca.adminClient.SetPolicy(mca.context, initialPolicyName, initialUserGroup, true) + return nil +} + +// CreateUser - creates a user in minio. +func (mca *MinioClientAdmin) CreateUser(accessKey string, secretKey string) error { + err := mca.adminClient.AddUser(mca.context, accessKey, secretKey) + return err +} + +// DeleteUser - deletes a user in minio. +func (mca *MinioClientAdmin) DeleteUser(accessKey string) error { + err := mca.adminClient.RemoveUser(mca.context, accessKey) + return err +} + +// CreateBucket - creates a bucket in minio. +func (mca *MinioClientAdmin) CreateBucket(bucketName string, region string, ignoreExisting bool) error { + if ignoreExisting { + found, err := mca.s3Client.BucketExists(mca.context, bucketName) + if err != nil { + return err + } + + if found { + return nil + } + } + + err := mca.s3Client.MakeBucket(mca.context, bucketName, minio.MakeBucketOptions{ + Region: region, + }) + + return err +} + +// DeleteBucket - deletes a bucket in minio. +func (mca *MinioClientAdmin) DeleteBucket(bucketName string) error { + err := mca.s3Client.RemoveBucket(mca.context, bucketName) + return err +} + +// SetBucketPolicy - sets a bucket policy in minio. +func (mca *MinioClientAdmin) SetBucketPolicy(bucketName string, policy v1.BucketPolicy) error { + var policyString string + if policy == v1.BucketPolicyReadOnly { + policyString = getReadonlyPolicy(bucketName) + } + + if policy == v1.BucketPolicyPublic { + policyString = getPublicPolicy(bucketName) + } + + if policy == v1.BucketPolicyPrivate { + return nil + } + + if policyString == "" { + return errors.New("invalid policy") + } + err := mca.s3Client.SetBucketPolicy(mca.context, bucketName, policyString) + return err +} + +// SetBucketQuota - sets a bucket quota in minio. +func (mca *MinioClientAdmin) SetBucketQuota(bucketName string, quota resource.Quantity) error { + value, _ := quota.AsInt64() + var _quota = &madmin.BucketQuota{Quota: uint64(value), Type: madmin.HardQuota} + err := mca.adminClient.SetBucketQuota(mca.context, bucketName, _quota) + return err +} + +// EnableVersioning - enable a bucket versioning in minio. +func (mca *MinioClientAdmin) EnableVersioning(bucketName string) error { + err := mca.s3Client.EnableVersioning(mca.context, bucketName) + return err +} + +func getReadonlyPolicy(bucket string) string { + const tpl = `{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "s3:GetObject" + ], + "Principal": { + "AWS": ["*"] + }, + "Resource": [ + "arn:aws:s3:::${bucket}/*" + ] + } + ] +}` + + return strings.ReplaceAll(tpl, "${bucket}", bucket) +} + +func getPublicPolicy(bucket string) string { + const tpl = `{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "s3:GetBucketLocation", + "s3:ListBucket", + "s3:ListBucketMultipartUploads" + ], + "Effect": "Allow", + "Principal": { + "AWS": [ + "*" + ] + }, + "Resource": [ + "arn:aws:s3:::${bucket}" + ] + }, + { + "Action": [ + "s3:AbortMultipartUpload", + "s3:DeleteObject", + "s3:GetObject", + "s3:ListMultipartUploadParts", + "s3:PutObject" + ], + "Effect": "Allow", + "Principal": { + "AWS": [ + "*" + ] + }, + "Resource": [ + "arn:aws:s3:::${bucket}/*" + ] + } + ] +}` + + return strings.ReplaceAll(tpl, "${bucket}", bucket) +} diff --git a/controllers/oss/go.mod b/controllers/oss/go.mod index cc33da1da5..79507aa21e 100644 --- a/controllers/oss/go.mod +++ b/controllers/oss/go.mod @@ -20,6 +20,7 @@ require ( github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -29,6 +30,7 @@ require ( github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/go-logr/logr v1.2.0 // indirect github.com/go-logr/zapr v1.2.0 // indirect + github.com/go-ole/go-ole v1.2.4 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.5 // indirect github.com/go-openapi/swag v0.19.14 // indirect @@ -36,30 +38,43 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.5 // indirect + github.com/google/go-cmp v0.5.6 // indirect github.com/google/gofuzz v1.1.0 // indirect github.com/google/uuid v1.1.2 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.0.4 // indirect github.com/mailru/easyjson v0.7.6 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/minio/madmin-go v1.4.25 // indirect + github.com/minio/minio-go/v7 v7.0.23 // indirect + github.com/minio/sha256-simd v1.0.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nxadm/tail v1.4.8 // indirect + github.com/philhofer/fwd v1.1.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.12.1 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect + github.com/rs/xid v1.2.1 // indirect + github.com/secure-io/sio-go v0.3.1 // indirect + github.com/shirou/gopsutil/v3 v3.21.6 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/tinylib/msgp v1.1.3 // indirect + github.com/tklauser/go-sysconf v0.3.6 // indirect + github.com/tklauser/numcpus v0.2.2 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.19.1 // indirect golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 // indirect @@ -68,6 +83,7 @@ require ( google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.27.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/ini.v1 v1.57.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect diff --git a/controllers/oss/go.sum b/controllers/oss/go.sum index 7087c092f6..75caca2eaf 100644 --- a/controllers/oss/go.sum +++ b/controllers/oss/go.sum @@ -62,6 +62,8 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= +github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -162,6 +164,8 @@ github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/zapr v1.2.0 h1:n4JnPI1T3Qq1SFEi/F8rwLrZERp2bso19PJZDB9dayk= github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= +github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= +github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -232,6 +236,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -311,6 +317,8 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/cpuid/v2 v2.0.4 h1:g0I61F2K2DjRHz1cnxlkNSBIaePVoJIjjnHui8QHbiw= +github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= @@ -332,8 +340,15 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/minio/madmin-go v1.4.25 h1:IEJpsTXlHz18/yRI5HxY84KaJdDjUUYdMiC5f8fId4g= +github.com/minio/madmin-go v1.4.25/go.mod h1:ez87VmMtsxP7DRxjKJKD4RDNW+nhO2QF9KSzwxBDQ98= +github.com/minio/minio-go/v7 v7.0.23 h1:NleyGQvAn9VQMU+YHVrgV4CX+EPtxPt/78lHOOTncy4= +github.com/minio/minio-go/v7 v7.0.23/go.mod h1:ei5JjmxwHaMrgsMrn4U/+Nmg+d8MKS1U2DAn1ou4+Do= +github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= +github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= @@ -381,6 +396,8 @@ github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -420,10 +437,16 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/secure-io/sio-go v0.3.1 h1:dNvY9awjabXTYGsTF1PiCySl9Ltofk9GA3VdWlo7rRc= +github.com/secure-io/sio-go v0.3.1/go.mod h1:+xbkjDzPjwh4Axd07pRKSNriS9SCiYksWnZqdnfpQxs= +github.com/shirou/gopsutil/v3 v3.21.6 h1:vU7jrp1Ic/2sHB7w6UNs7MIkn7ebVtTb5D9j45o9VYE= +github.com/shirou/gopsutil/v3 v3.21.6/go.mod h1:JfVbDpIBLVzT8oKbvMg9P3wEIMDDpVn+LwHTKj0ST88= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -457,6 +480,12 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tinylib/msgp v1.1.3 h1:3giwAkmtaEDLSV0MdO1lDLuPgklgPzmk8H9+So2BVfA= +github.com/tinylib/msgp v1.1.3/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tklauser/go-sysconf v0.3.6 h1:oc1sJWvKkmvIxhDHeKWvZS4f6AW+YcoguSfRF2/Hmo4= +github.com/tklauser/go-sysconf v0.3.6/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= +github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= +github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= @@ -518,6 +547,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -633,6 +663,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -686,6 +717,7 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -908,6 +940,8 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww= +gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= diff --git a/go.work.sum b/go.work.sum index 901cdf599d..bca666ce1d 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1 +1,3 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= +github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4=