diff --git a/.claude/settings.local.json b/.claude/settings.local.json index e27921b0b..9fa092137 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -6,7 +6,8 @@ "Bash(go build:*)", "Bash(go fmt:*)", "Bash(go doc:*)", - "Bash(make test:*)" + "Bash(make test:*)", + "Bash(openstack *)" ], "deny": [], "ask": [] diff --git a/PROJECT b/PROJECT index e5a188a8b..a487f79a3 100644 --- a/PROJECT +++ b/PROJECT @@ -160,6 +160,14 @@ resources: kind: Service path: github.com/k-orc/openstack-resource-controller/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + domain: k-orc.cloud + group: openstack + kind: Share + path: github.com/k-orc/openstack-resource-controller/api/v1alpha1 + version: v1alpha1 - api: crdVersion: v1 namespaced: true diff --git a/api/v1alpha1/share_types.go b/api/v1alpha1/share_types.go new file mode 100644 index 000000000..f5227c7d7 --- /dev/null +++ b/api/v1alpha1/share_types.go @@ -0,0 +1,154 @@ +/* +Copyright The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +// ShareResourceSpec contains the desired state of the resource. +type ShareResourceSpec struct { + // name will be the name of the created resource. If not specified, the + // name of the ORC object will be used. + // +optional + Name *OpenStackName `json:"name,omitempty"` + + // description is a human-readable description for the resource. + // +kubebuilder:validation:MinLength:=1 + // +kubebuilder:validation:MaxLength:=255 + // +optional + Description *string `json:"description,omitempty"` + + // shareProto is the shared file system protocol (e.g., NFS, CIFS). + // +required + // +kubebuilder:validation:MaxLength=255 + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="shareProto is immutable" + ShareProto string `json:"shareProto"` + + // size is the size of the share in GB. + // +required + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="size is immutable" + Size int32 `json:"size,omitempty"` + + // shareNetworkRef is a reference to the ORC ShareNetwork which this resource is associated with. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="shareNetworkRef is immutable" + ShareNetworkRef *KubernetesNameRef `json:"shareNetworkRef,omitempty"` + + // availabilityZone is the availability zone in which to create the share. + // +optional + // +kubebuilder:validation:MaxLength=255 + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="availabilityZone is immutable" + AvailabilityZone *string `json:"availabilityZone,omitempty"` + + // shareType is the share type to use. If not specified, the default share type is used. + // +optional + // +kubebuilder:validation:MaxLength=255 + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="shareType is immutable" + ShareType *string `json:"shareType,omitempty"` + + // metadata contains key-value pairs of metadata for the share. + // +optional + Metadata map[string]string `json:"metadata,omitempty"` + + // isPublic determines whether the share is public. + // +optional + IsPublic *bool `json:"isPublic,omitempty"` +} + +// ShareFilter defines an existing resource by its properties +// +kubebuilder:validation:MinProperties:=1 +type ShareFilter struct { + // name of the existing resource + // +optional + Name *OpenStackName `json:"name,omitempty"` + + // description of the existing resource + // +kubebuilder:validation:MinLength:=1 + // +kubebuilder:validation:MaxLength:=255 + // +optional + Description *string `json:"description,omitempty"` + + // status filters by share status + // +optional + // +kubebuilder:validation:MaxLength=255 + Status *string `json:"status,omitempty"` + + // shareProto filters by share protocol + // +optional + // +kubebuilder:validation:MaxLength=255 + ShareProto *string `json:"shareProto,omitempty"` +} + +// ShareResourceStatus represents the observed state of the resource. +type ShareResourceStatus struct { + // name is a Human-readable name for the resource. Might not be unique. + // +kubebuilder:validation:MaxLength=1024 + // +optional + Name string `json:"name,omitempty"` + + // description is a human-readable description for the resource. + // +kubebuilder:validation:MaxLength=1024 + // +optional + Description string `json:"description,omitempty"` + + // status is the current status of the share. + // +kubebuilder:validation:MaxLength=1024 + // +optional + Status string `json:"status,omitempty"` + + // shareProto is the shared file system protocol. + // +kubebuilder:validation:MaxLength=1024 + // +optional + ShareProto string `json:"shareProto,omitempty"` + + // size is the size of the share in GB. + // +optional + Size *int32 `json:"size,omitempty"` + + // availabilityZone is the availability zone of the share. + // +kubebuilder:validation:MaxLength=1024 + // +optional + AvailabilityZone string `json:"availabilityZone,omitempty"` + + // shareType is the UUID of the share type. + // +kubebuilder:validation:MaxLength=1024 + // +optional + ShareType string `json:"shareType,omitempty"` + + // shareTypeName is the name of the share type. + // +kubebuilder:validation:MaxLength=1024 + // +optional + ShareTypeName string `json:"shareTypeName,omitempty"` + + // shareNetworkID is the ID of the ShareNetwork to which the resource is associated. + // +kubebuilder:validation:MaxLength=1024 + // +optional + ShareNetworkID string `json:"shareNetworkID,omitempty"` + + // isPublic indicates the visibility of the share. + // +optional + IsPublic *bool `json:"isPublic,omitempty"` + + // metadata contains key-value pairs of custom metadata. + // +optional + Metadata map[string]string `json:"metadata,omitempty"` + + // exportLocations contains the export locations for mounting the share. + // +listType=atomic + // +kubebuilder:validation:MaxItems=100 + // +kubebuilder:validation:items:MaxLength=1024 + // +optional + ExportLocations []string `json:"exportLocations,omitempty"` +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index ead3ef708..3fa06b76c 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -5599,6 +5599,125 @@ func (in *ServiceStatus) DeepCopy() *ServiceStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Share) DeepCopyInto(out *Share) { + *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 Share. +func (in *Share) DeepCopy() *Share { + if in == nil { + return nil + } + out := new(Share) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Share) 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 *ShareFilter) DeepCopyInto(out *ShareFilter) { + *out = *in + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(OpenStackName) + **out = **in + } + if in.Description != nil { + in, out := &in.Description, &out.Description + *out = new(string) + **out = **in + } + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(string) + **out = **in + } + if in.ShareProto != nil { + in, out := &in.ShareProto, &out.ShareProto + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShareFilter. +func (in *ShareFilter) DeepCopy() *ShareFilter { + if in == nil { + return nil + } + out := new(ShareFilter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ShareImport) DeepCopyInto(out *ShareImport) { + *out = *in + if in.ID != nil { + in, out := &in.ID, &out.ID + *out = new(string) + **out = **in + } + if in.Filter != nil { + in, out := &in.Filter, &out.Filter + *out = new(ShareFilter) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShareImport. +func (in *ShareImport) DeepCopy() *ShareImport { + if in == nil { + return nil + } + out := new(ShareImport) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ShareList) DeepCopyInto(out *ShareList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Share, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShareList. +func (in *ShareList) DeepCopy() *ShareList { + if in == nil { + return nil + } + out := new(ShareList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ShareList) 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 *ShareNetwork) DeepCopyInto(out *ShareNetwork) { *out = *in @@ -5839,6 +5958,158 @@ func (in *ShareNetworkStatus) DeepCopy() *ShareNetworkStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ShareResourceSpec) DeepCopyInto(out *ShareResourceSpec) { + *out = *in + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(OpenStackName) + **out = **in + } + if in.Description != nil { + in, out := &in.Description, &out.Description + *out = new(string) + **out = **in + } + if in.ShareNetworkRef != nil { + in, out := &in.ShareNetworkRef, &out.ShareNetworkRef + *out = new(KubernetesNameRef) + **out = **in + } + if in.AvailabilityZone != nil { + in, out := &in.AvailabilityZone, &out.AvailabilityZone + *out = new(string) + **out = **in + } + if in.ShareType != nil { + in, out := &in.ShareType, &out.ShareType + *out = new(string) + **out = **in + } + if in.Metadata != nil { + in, out := &in.Metadata, &out.Metadata + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.IsPublic != nil { + in, out := &in.IsPublic, &out.IsPublic + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShareResourceSpec. +func (in *ShareResourceSpec) DeepCopy() *ShareResourceSpec { + if in == nil { + return nil + } + out := new(ShareResourceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ShareResourceStatus) DeepCopyInto(out *ShareResourceStatus) { + *out = *in + if in.Size != nil { + in, out := &in.Size, &out.Size + *out = new(int32) + **out = **in + } + if in.IsPublic != nil { + in, out := &in.IsPublic, &out.IsPublic + *out = new(bool) + **out = **in + } + if in.Metadata != nil { + in, out := &in.Metadata, &out.Metadata + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.ExportLocations != nil { + in, out := &in.ExportLocations, &out.ExportLocations + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShareResourceStatus. +func (in *ShareResourceStatus) DeepCopy() *ShareResourceStatus { + if in == nil { + return nil + } + out := new(ShareResourceStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ShareSpec) DeepCopyInto(out *ShareSpec) { + *out = *in + if in.Import != nil { + in, out := &in.Import, &out.Import + *out = new(ShareImport) + (*in).DeepCopyInto(*out) + } + if in.Resource != nil { + in, out := &in.Resource, &out.Resource + *out = new(ShareResourceSpec) + (*in).DeepCopyInto(*out) + } + if in.ManagedOptions != nil { + in, out := &in.ManagedOptions, &out.ManagedOptions + *out = new(ManagedOptions) + **out = **in + } + out.CloudCredentialsRef = in.CloudCredentialsRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShareSpec. +func (in *ShareSpec) DeepCopy() *ShareSpec { + if in == nil { + return nil + } + out := new(ShareSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ShareStatus) DeepCopyInto(out *ShareStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ID != nil { + in, out := &in.ID, &out.ID + *out = new(string) + **out = **in + } + if in.Resource != nil { + in, out := &in.Resource, &out.Resource + *out = new(ShareResourceStatus) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShareStatus. +func (in *ShareStatus) DeepCopy() *ShareStatus { + if in == nil { + return nil + } + out := new(ShareStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Subnet) DeepCopyInto(out *Subnet) { *out = *in diff --git a/api/v1alpha1/zz_generated.share-resource.go b/api/v1alpha1/zz_generated.share-resource.go new file mode 100644 index 000000000..b7d508472 --- /dev/null +++ b/api/v1alpha1/zz_generated.share-resource.go @@ -0,0 +1,179 @@ +// Code generated by resource-generator. DO NOT EDIT. +/* +Copyright The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ShareImport specifies an existing resource which will be imported instead of +// creating a new one +// +kubebuilder:validation:MinProperties:=1 +// +kubebuilder:validation:MaxProperties:=1 +type ShareImport struct { + // id contains the unique identifier of an existing OpenStack resource. Note + // that when specifying an import by ID, the resource MUST already exist. + // The ORC object will enter an error state if the resource does not exist. + // +kubebuilder:validation:Format:=uuid + // +kubebuilder:validation:MaxLength:=36 + // +optional + ID *string `json:"id,omitempty"` //nolint:kubeapilinter + + // filter contains a resource query which is expected to return a single + // result. The controller will continue to retry if filter returns no + // results. If filter returns multiple results the controller will set an + // error state and will not continue to retry. + // +optional + Filter *ShareFilter `json:"filter,omitempty"` +} + +// ShareSpec defines the desired state of an ORC object. +// +kubebuilder:validation:XValidation:rule="self.managementPolicy == 'managed' ? has(self.resource) : true",message="resource must be specified when policy is managed" +// +kubebuilder:validation:XValidation:rule="self.managementPolicy == 'managed' ? !has(self.__import__) : true",message="import may not be specified when policy is managed" +// +kubebuilder:validation:XValidation:rule="self.managementPolicy == 'unmanaged' ? !has(self.resource) : true",message="resource may not be specified when policy is unmanaged" +// +kubebuilder:validation:XValidation:rule="self.managementPolicy == 'unmanaged' ? has(self.__import__) : true",message="import must be specified when policy is unmanaged" +// +kubebuilder:validation:XValidation:rule="has(self.managedOptions) ? self.managementPolicy == 'managed' : true",message="managedOptions may only be provided when policy is managed" +type ShareSpec struct { + // import refers to an existing OpenStack resource which will be imported instead of + // creating a new one. + // +optional + Import *ShareImport `json:"import,omitempty"` + + // resource specifies the desired state of the resource. + // + // resource may not be specified if the management policy is `unmanaged`. + // + // resource must be specified if the management policy is `managed`. + // +optional + Resource *ShareResourceSpec `json:"resource,omitempty"` + + // managementPolicy defines how ORC will treat the object. Valid values are + // `managed`: ORC will create, update, and delete the resource; `unmanaged`: + // ORC will import an existing resource, and will not apply updates to it or + // delete it. + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="managementPolicy is immutable" + // +kubebuilder:default:=managed + // +optional + ManagementPolicy ManagementPolicy `json:"managementPolicy,omitempty"` + + // managedOptions specifies options which may be applied to managed objects. + // +optional + ManagedOptions *ManagedOptions `json:"managedOptions,omitempty"` + + // cloudCredentialsRef points to a secret containing OpenStack credentials + // +required + CloudCredentialsRef CloudCredentialsReference `json:"cloudCredentialsRef,omitzero"` +} + +// ShareStatus defines the observed state of an ORC resource. +type ShareStatus struct { + // conditions represents the observed status of the object. + // Known .status.conditions.type are: "Available", "Progressing" + // + // Available represents the availability of the OpenStack resource. If it is + // true then the resource is ready for use. + // + // Progressing indicates whether the controller is still attempting to + // reconcile the current state of the OpenStack resource to the desired + // state. Progressing will be False either because the desired state has + // been achieved, or because some terminal error prevents it from ever being + // achieved and the controller is no longer attempting to reconcile. If + // Progressing is True, an observer waiting on the resource should continue + // to wait. + // + // +kubebuilder:validation:MaxItems:=32 + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + + // id is the unique identifier of the OpenStack resource. + // +kubebuilder:validation:MaxLength:=1024 + // +optional + ID *string `json:"id,omitempty"` + + // resource contains the observed state of the OpenStack resource. + // +optional + Resource *ShareResourceStatus `json:"resource,omitempty"` +} + +var _ ObjectWithConditions = &Share{} + +func (i *Share) GetConditions() []metav1.Condition { + return i.Status.Conditions +} + +// +genclient +// +kubebuilder:object:root=true +// +kubebuilder:resource:categories=openstack +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="ID",type="string",JSONPath=".status.id",description="Resource ID" +// +kubebuilder:printcolumn:name="Available",type="string",JSONPath=".status.conditions[?(@.type=='Available')].status",description="Availability status of resource" +// +kubebuilder:printcolumn:name="Message",type="string",JSONPath=".status.conditions[?(@.type=='Progressing')].message",description="Message describing current progress status" + +// Share is the Schema for an ORC resource. +type Share struct { + metav1.TypeMeta `json:",inline"` + + // metadata contains the object metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // spec specifies the desired state of the resource. + // +required + Spec ShareSpec `json:"spec,omitzero"` + + // status defines the observed state of the resource. + // +optional + Status ShareStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// ShareList contains a list of Share. +type ShareList struct { + metav1.TypeMeta `json:",inline"` + + // metadata contains the list metadata + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + + // items contains a list of Share. + // +required + Items []Share `json:"items"` +} + +func (l *ShareList) GetItems() []Share { + return l.Items +} + +func init() { + SchemeBuilder.Register(&Share{}, &ShareList{}) +} + +func (i *Share) GetCloudCredentialsRef() (*string, *CloudCredentialsReference) { + if i == nil { + return nil, nil + } + + return &i.Namespace, &i.Spec.CloudCredentialsRef +} + +var _ CloudCredentialsRefProvider = &Share{} diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 9addc552d..9767f0354 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -47,6 +47,7 @@ import ( "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/server" "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/servergroup" "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/service" + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/share" "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/sharenetwork" "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/subnet" "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/trunk" @@ -134,6 +135,7 @@ func main() { volumetype.New(scopeFactory), domain.New(scopeFactory), service.New(scopeFactory), + share.New(scopeFactory), sharenetwork.New(scopeFactory), keypair.New(scopeFactory), group.New(scopeFactory), diff --git a/cmd/models-schema/zz_generated.openapi.go b/cmd/models-schema/zz_generated.openapi.go index 5601a14eb..b851b9b8c 100644 --- a/cmd/models-schema/zz_generated.openapi.go +++ b/cmd/models-schema/zz_generated.openapi.go @@ -223,6 +223,10 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ServiceResourceStatus": schema_openstack_resource_controller_v2_api_v1alpha1_ServiceResourceStatus(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ServiceSpec": schema_openstack_resource_controller_v2_api_v1alpha1_ServiceSpec(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ServiceStatus": schema_openstack_resource_controller_v2_api_v1alpha1_ServiceStatus(ref), + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.Share": schema_openstack_resource_controller_v2_api_v1alpha1_Share(ref), + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareFilter": schema_openstack_resource_controller_v2_api_v1alpha1_ShareFilter(ref), + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareImport": schema_openstack_resource_controller_v2_api_v1alpha1_ShareImport(ref), + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareList": schema_openstack_resource_controller_v2_api_v1alpha1_ShareList(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareNetwork": schema_openstack_resource_controller_v2_api_v1alpha1_ShareNetwork(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareNetworkFilter": schema_openstack_resource_controller_v2_api_v1alpha1_ShareNetworkFilter(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareNetworkImport": schema_openstack_resource_controller_v2_api_v1alpha1_ShareNetworkImport(ref), @@ -231,6 +235,10 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareNetworkResourceStatus": schema_openstack_resource_controller_v2_api_v1alpha1_ShareNetworkResourceStatus(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareNetworkSpec": schema_openstack_resource_controller_v2_api_v1alpha1_ShareNetworkSpec(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareNetworkStatus": schema_openstack_resource_controller_v2_api_v1alpha1_ShareNetworkStatus(ref), + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareResourceSpec": schema_openstack_resource_controller_v2_api_v1alpha1_ShareResourceSpec(ref), + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareResourceStatus": schema_openstack_resource_controller_v2_api_v1alpha1_ShareResourceStatus(ref), + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareSpec": schema_openstack_resource_controller_v2_api_v1alpha1_ShareSpec(ref), + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareStatus": schema_openstack_resource_controller_v2_api_v1alpha1_ShareStatus(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.Subnet": schema_openstack_resource_controller_v2_api_v1alpha1_Subnet(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.SubnetFilter": schema_openstack_resource_controller_v2_api_v1alpha1_SubnetFilter(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.SubnetGateway": schema_openstack_resource_controller_v2_api_v1alpha1_SubnetGateway(ref), @@ -10582,6 +10590,177 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_ServiceStatus(ref comm } } +func schema_openstack_resource_controller_v2_api_v1alpha1_Share(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Share is the Schema for an ORC resource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "metadata contains the object metadata", + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "spec specifies the desired state of the resource.", + Default: map[string]interface{}{}, + Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "status defines the observed state of the resource.", + Default: map[string]interface{}{}, + Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareStatus"), + }, + }, + }, + Required: []string{"spec"}, + }, + }, + Dependencies: []string{ + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareSpec", "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_openstack_resource_controller_v2_api_v1alpha1_ShareFilter(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ShareFilter defines an existing resource by its properties", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "name of the existing resource", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "description of the existing resource", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "status filters by share status", + Type: []string{"string"}, + Format: "", + }, + }, + "shareProto": { + SchemaProps: spec.SchemaProps{ + Description: "shareProto filters by share protocol", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_openstack_resource_controller_v2_api_v1alpha1_ShareImport(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ShareImport specifies an existing resource which will be imported instead of creating a new one", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "id": { + SchemaProps: spec.SchemaProps{ + Description: "id contains the unique identifier of an existing OpenStack resource. Note that when specifying an import by ID, the resource MUST already exist. The ORC object will enter an error state if the resource does not exist.", + Type: []string{"string"}, + Format: "", + }, + }, + "filter": { + SchemaProps: spec.SchemaProps{ + Description: "filter contains a resource query which is expected to return a single result. The controller will continue to retry if filter returns no results. If filter returns multiple results the controller will set an error state and will not continue to retry.", + Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareFilter"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareFilter"}, + } +} + +func schema_openstack_resource_controller_v2_api_v1alpha1_ShareList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ShareList contains a list of Share.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "metadata contains the list metadata", + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "items contains a list of Share.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.Share"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.Share", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + func schema_openstack_resource_controller_v2_api_v1alpha1_ShareNetwork(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -10971,6 +11150,312 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_ShareNetworkStatus(ref } } +func schema_openstack_resource_controller_v2_api_v1alpha1_ShareResourceSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ShareResourceSpec contains the desired state of the resource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "name will be the name of the created resource. If not specified, the name of the ORC object will be used.", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "description is a human-readable description for the resource.", + Type: []string{"string"}, + Format: "", + }, + }, + "shareProto": { + SchemaProps: spec.SchemaProps{ + Description: "shareProto is the shared file system protocol (e.g., NFS, CIFS).", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "size": { + SchemaProps: spec.SchemaProps{ + Description: "size is the size of the share in GB.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "shareNetworkRef": { + SchemaProps: spec.SchemaProps{ + Description: "shareNetworkRef is a reference to the ORC ShareNetwork which this resource is associated with.", + Type: []string{"string"}, + Format: "", + }, + }, + "availabilityZone": { + SchemaProps: spec.SchemaProps{ + Description: "availabilityZone is the availability zone in which to create the share.", + Type: []string{"string"}, + Format: "", + }, + }, + "shareType": { + SchemaProps: spec.SchemaProps{ + Description: "shareType is the share type to use. If not specified, the default share type is used.", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "metadata contains key-value pairs of metadata for the share.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "isPublic": { + SchemaProps: spec.SchemaProps{ + Description: "isPublic determines whether the share is public.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"shareProto", "size"}, + }, + }, + } +} + +func schema_openstack_resource_controller_v2_api_v1alpha1_ShareResourceStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ShareResourceStatus represents the observed state of the resource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "name is a Human-readable name for the resource. Might not be unique.", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "description is a human-readable description for the resource.", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "status is the current status of the share.", + Type: []string{"string"}, + Format: "", + }, + }, + "shareProto": { + SchemaProps: spec.SchemaProps{ + Description: "shareProto is the shared file system protocol.", + Type: []string{"string"}, + Format: "", + }, + }, + "size": { + SchemaProps: spec.SchemaProps{ + Description: "size is the size of the share in GB.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "availabilityZone": { + SchemaProps: spec.SchemaProps{ + Description: "availabilityZone is the availability zone of the share.", + Type: []string{"string"}, + Format: "", + }, + }, + "shareType": { + SchemaProps: spec.SchemaProps{ + Description: "shareType is the UUID of the share type.", + Type: []string{"string"}, + Format: "", + }, + }, + "shareTypeName": { + SchemaProps: spec.SchemaProps{ + Description: "shareTypeName is the name of the share type.", + Type: []string{"string"}, + Format: "", + }, + }, + "shareNetworkID": { + SchemaProps: spec.SchemaProps{ + Description: "shareNetworkID is the ID of the ShareNetwork to which the resource is associated.", + Type: []string{"string"}, + Format: "", + }, + }, + "isPublic": { + SchemaProps: spec.SchemaProps{ + Description: "isPublic indicates the visibility of the share.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "metadata contains key-value pairs of custom metadata.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "exportLocations": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "exportLocations contains the export locations for mounting the share.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_openstack_resource_controller_v2_api_v1alpha1_ShareSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ShareSpec defines the desired state of an ORC object.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "import": { + SchemaProps: spec.SchemaProps{ + Description: "import refers to an existing OpenStack resource which will be imported instead of creating a new one.", + Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareImport"), + }, + }, + "resource": { + SchemaProps: spec.SchemaProps{ + Description: "resource specifies the desired state of the resource.\n\nresource may not be specified if the management policy is `unmanaged`.\n\nresource must be specified if the management policy is `managed`.", + Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareResourceSpec"), + }, + }, + "managementPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "managementPolicy defines how ORC will treat the object. Valid values are `managed`: ORC will create, update, and delete the resource; `unmanaged`: ORC will import an existing resource, and will not apply updates to it or delete it.", + Type: []string{"string"}, + Format: "", + }, + }, + "managedOptions": { + SchemaProps: spec.SchemaProps{ + Description: "managedOptions specifies options which may be applied to managed objects.", + Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ManagedOptions"), + }, + }, + "cloudCredentialsRef": { + SchemaProps: spec.SchemaProps{ + Description: "cloudCredentialsRef points to a secret containing OpenStack credentials", + Default: map[string]interface{}{}, + Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.CloudCredentialsReference"), + }, + }, + }, + Required: []string{"cloudCredentialsRef"}, + }, + }, + Dependencies: []string{ + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.CloudCredentialsReference", "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ManagedOptions", "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareImport", "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareResourceSpec"}, + } +} + +func schema_openstack_resource_controller_v2_api_v1alpha1_ShareStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ShareStatus defines the observed state of an ORC resource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "type", + }, + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "conditions represents the observed status of the object. Known .status.conditions.type are: \"Available\", \"Progressing\"\n\nAvailable represents the availability of the OpenStack resource. If it is true then the resource is ready for use.\n\nProgressing indicates whether the controller is still attempting to reconcile the current state of the OpenStack resource to the desired state. Progressing will be False either because the desired state has been achieved, or because some terminal error prevents it from ever being achieved and the controller is no longer attempting to reconcile. If Progressing is True, an observer waiting on the resource should continue to wait.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Condition"), + }, + }, + }, + }, + }, + "id": { + SchemaProps: spec.SchemaProps{ + Description: "id is the unique identifier of the OpenStack resource.", + Type: []string{"string"}, + Format: "", + }, + }, + "resource": { + SchemaProps: spec.SchemaProps{ + Description: "resource contains the observed state of the OpenStack resource.", + Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareResourceStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ShareResourceStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.Condition"}, + } +} + func schema_openstack_resource_controller_v2_api_v1alpha1_Subnet(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/cmd/resource-generator/main.go b/cmd/resource-generator/main.go index 609bf9084..af6d1cacd 100644 --- a/cmd/resource-generator/main.go +++ b/cmd/resource-generator/main.go @@ -160,6 +160,9 @@ var resources []templateFields = []templateFields{ { Name: "Service", }, + { + Name: "Share", + }, { Name: "ShareNetwork", }, diff --git a/config/crd/bases/openstack.k-orc.cloud_shares.yaml b/config/crd/bases/openstack.k-orc.cloud_shares.yaml new file mode 100644 index 000000000..c2cfc0a37 --- /dev/null +++ b/config/crd/bases/openstack.k-orc.cloud_shares.yaml @@ -0,0 +1,397 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.20.1 + name: shares.openstack.k-orc.cloud +spec: + group: openstack.k-orc.cloud + names: + categories: + - openstack + kind: Share + listKind: ShareList + plural: shares + singular: share + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Resource ID + jsonPath: .status.id + name: ID + type: string + - description: Availability status of resource + jsonPath: .status.conditions[?(@.type=='Available')].status + name: Available + type: string + - description: Message describing current progress status + jsonPath: .status.conditions[?(@.type=='Progressing')].message + name: Message + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: Share is the Schema for an ORC resource. + 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: spec specifies the desired state of the resource. + properties: + cloudCredentialsRef: + description: cloudCredentialsRef points to a secret containing OpenStack + credentials + properties: + cloudName: + description: cloudName specifies the name of the entry in the + clouds.yaml file to use. + maxLength: 256 + minLength: 1 + type: string + secretName: + description: |- + secretName is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain a key named `clouds.yaml` which contains an OpenStack clouds.yaml file. + The secret may optionally contain a key named `cacert` containing a PEM-encoded CA certificate. + maxLength: 253 + minLength: 1 + type: string + required: + - cloudName + - secretName + type: object + import: + description: |- + import refers to an existing OpenStack resource which will be imported instead of + creating a new one. + maxProperties: 1 + minProperties: 1 + properties: + filter: + description: |- + filter contains a resource query which is expected to return a single + result. The controller will continue to retry if filter returns no + results. If filter returns multiple results the controller will set an + error state and will not continue to retry. + minProperties: 1 + properties: + description: + description: description of the existing resource + maxLength: 255 + minLength: 1 + type: string + name: + description: name of the existing resource + maxLength: 255 + minLength: 1 + pattern: ^[^,]+$ + type: string + shareProto: + description: shareProto filters by share protocol + maxLength: 255 + type: string + status: + description: status filters by share status + maxLength: 255 + type: string + type: object + id: + description: |- + id contains the unique identifier of an existing OpenStack resource. Note + that when specifying an import by ID, the resource MUST already exist. + The ORC object will enter an error state if the resource does not exist. + format: uuid + maxLength: 36 + type: string + type: object + managedOptions: + description: managedOptions specifies options which may be applied + to managed objects. + properties: + onDelete: + default: delete + description: |- + onDelete specifies the behaviour of the controller when the ORC + object is deleted. Options are `delete` - delete the OpenStack resource; + `detach` - do not delete the OpenStack resource. If not specified, the + default is `delete`. + enum: + - delete + - detach + type: string + type: object + managementPolicy: + default: managed + description: |- + managementPolicy defines how ORC will treat the object. Valid values are + `managed`: ORC will create, update, and delete the resource; `unmanaged`: + ORC will import an existing resource, and will not apply updates to it or + delete it. + enum: + - managed + - unmanaged + type: string + x-kubernetes-validations: + - message: managementPolicy is immutable + rule: self == oldSelf + resource: + description: |- + resource specifies the desired state of the resource. + + resource may not be specified if the management policy is `unmanaged`. + + resource must be specified if the management policy is `managed`. + properties: + availabilityZone: + description: availabilityZone is the availability zone in which + to create the share. + maxLength: 255 + type: string + x-kubernetes-validations: + - message: availabilityZone is immutable + rule: self == oldSelf + description: + description: description is a human-readable description for the + resource. + maxLength: 255 + minLength: 1 + type: string + isPublic: + description: isPublic determines whether the share is public. + type: boolean + metadata: + additionalProperties: + type: string + description: metadata contains key-value pairs of metadata for + the share. + type: object + name: + description: |- + name will be the name of the created resource. If not specified, the + name of the ORC object will be used. + maxLength: 255 + minLength: 1 + pattern: ^[^,]+$ + type: string + shareNetworkRef: + description: shareNetworkRef is a reference to the ORC ShareNetwork + which this resource is associated with. + maxLength: 253 + minLength: 1 + type: string + x-kubernetes-validations: + - message: shareNetworkRef is immutable + rule: self == oldSelf + shareProto: + description: shareProto is the shared file system protocol (e.g., + NFS, CIFS). + maxLength: 255 + type: string + x-kubernetes-validations: + - message: shareProto is immutable + rule: self == oldSelf + shareType: + description: shareType is the share type to use. If not specified, + the default share type is used. + maxLength: 255 + type: string + x-kubernetes-validations: + - message: shareType is immutable + rule: self == oldSelf + size: + description: size is the size of the share in GB. + format: int32 + minimum: 1 + type: integer + x-kubernetes-validations: + - message: size is immutable + rule: self == oldSelf + required: + - shareProto + - size + type: object + required: + - cloudCredentialsRef + type: object + x-kubernetes-validations: + - message: resource must be specified when policy is managed + rule: 'self.managementPolicy == ''managed'' ? has(self.resource) : true' + - message: import may not be specified when policy is managed + rule: 'self.managementPolicy == ''managed'' ? !has(self.__import__) + : true' + - message: resource may not be specified when policy is unmanaged + rule: 'self.managementPolicy == ''unmanaged'' ? !has(self.resource) + : true' + - message: import must be specified when policy is unmanaged + rule: 'self.managementPolicy == ''unmanaged'' ? has(self.__import__) + : true' + - message: managedOptions may only be provided when policy is managed + rule: 'has(self.managedOptions) ? self.managementPolicy == ''managed'' + : true' + status: + description: status defines the observed state of the resource. + properties: + conditions: + description: |- + conditions represents the observed status of the object. + Known .status.conditions.type are: "Available", "Progressing" + + Available represents the availability of the OpenStack resource. If it is + true then the resource is ready for use. + + Progressing indicates whether the controller is still attempting to + reconcile the current state of the OpenStack resource to the desired + state. Progressing will be False either because the desired state has + been achieved, or because some terminal error prevents it from ever being + achieved and the controller is no longer attempting to reconcile. If + Progressing is True, an observer waiting on the resource should continue + to wait. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 32 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + id: + description: id is the unique identifier of the OpenStack resource. + maxLength: 1024 + type: string + resource: + description: resource contains the observed state of the OpenStack + resource. + properties: + availabilityZone: + description: availabilityZone is the availability zone of the + share. + maxLength: 1024 + type: string + description: + description: description is a human-readable description for the + resource. + maxLength: 1024 + type: string + exportLocations: + description: exportLocations contains the export locations for + mounting the share. + items: + maxLength: 1024 + type: string + maxItems: 100 + type: array + x-kubernetes-list-type: atomic + isPublic: + description: isPublic indicates the visibility of the share. + type: boolean + metadata: + additionalProperties: + type: string + description: metadata contains key-value pairs of custom metadata. + type: object + name: + description: name is a Human-readable name for the resource. Might + not be unique. + maxLength: 1024 + type: string + shareNetworkID: + description: shareNetworkID is the ID of the ShareNetwork to which + the resource is associated. + maxLength: 1024 + type: string + shareProto: + description: shareProto is the shared file system protocol. + maxLength: 1024 + type: string + shareType: + description: shareType is the UUID of the share type. + maxLength: 1024 + type: string + shareTypeName: + description: shareTypeName is the name of the share type. + maxLength: 1024 + type: string + size: + description: size is the size of the share in GB. + format: int32 + type: integer + status: + description: status is the current status of the share. + maxLength: 1024 + type: string + type: object + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 26b47f63f..aa444f75d 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -22,6 +22,7 @@ resources: - bases/openstack.k-orc.cloud_servers.yaml - bases/openstack.k-orc.cloud_servergroups.yaml - bases/openstack.k-orc.cloud_services.yaml +- bases/openstack.k-orc.cloud_shares.yaml - bases/openstack.k-orc.cloud_sharenetworks.yaml - bases/openstack.k-orc.cloud_subnets.yaml - bases/openstack.k-orc.cloud_trunks.yaml diff --git a/config/manifests/bases/orc.clusterserviceversion.yaml b/config/manifests/bases/orc.clusterserviceversion.yaml index 89421fc32..aba6f71f1 100644 --- a/config/manifests/bases/orc.clusterserviceversion.yaml +++ b/config/manifests/bases/orc.clusterserviceversion.yaml @@ -119,6 +119,11 @@ spec: kind: ShareNetwork name: sharenetworks.openstack.k-orc.cloud version: v1alpha1 + - description: Share is the Schema for an ORC resource. + displayName: Share + kind: Share + name: shares.openstack.k-orc.cloud + version: v1alpha1 - description: Subnet is the Schema for an ORC resource. displayName: Subnet kind: Subnet diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 4991cff67..2e6286b48 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -37,6 +37,7 @@ rules: - servers - services - sharenetworks + - shares - subnets - trunks - users @@ -73,6 +74,7 @@ rules: - servers/status - services/status - sharenetworks/status + - shares/status - subnets/status - trunks/status - users/status diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 8a50ba039..0338bcdbb 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -20,6 +20,7 @@ resources: - openstack_v1alpha1_server.yaml - openstack_v1alpha1_servergroup.yaml - openstack_v1alpha1_service.yaml +- openstack_v1alpha1_share.yaml - openstack_v1alpha1_sharenetwork.yaml - openstack_v1alpha1_subnet.yaml - openstack_v1alpha1_trunk.yaml diff --git a/config/samples/openstack_v1alpha1_share.yaml b/config/samples/openstack_v1alpha1_share.yaml new file mode 100644 index 000000000..25988c481 --- /dev/null +++ b/config/samples/openstack_v1alpha1_share.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-sample +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + name: my-nfs-share + description: Sample NFS share for development + shareProto: NFS + size: 10 + metadata: + environment: dev + team: platform diff --git a/internal/controllers/share/actuator.go b/internal/controllers/share/actuator.go new file mode 100644 index 000000000..f415dbeeb --- /dev/null +++ b/internal/controllers/share/actuator.go @@ -0,0 +1,280 @@ +/* +Copyright The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package share + +import ( + "context" + "iter" + "time" + + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/shares" + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/ptr" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/interfaces" + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/progress" + "github.com/k-orc/openstack-resource-controller/v2/internal/logging" + "github.com/k-orc/openstack-resource-controller/v2/internal/osclients" + orcerrors "github.com/k-orc/openstack-resource-controller/v2/internal/util/errors" +) + +// OpenStack resource types +type ( + osResourceT = shares.Share + + createResourceActuator = interfaces.CreateResourceActuator[orcObjectPT, orcObjectT, filterT, osResourceT] + deleteResourceActuator = interfaces.DeleteResourceActuator[orcObjectPT, orcObjectT, osResourceT] + resourceReconciler = interfaces.ResourceReconciler[orcObjectPT, osResourceT] + helperFactory = interfaces.ResourceHelperFactory[orcObjectPT, orcObjectT, resourceSpecT, filterT, osResourceT] +) + +// The frequency to poll when waiting for the resource to become available +const shareAvailablePollingPeriod = 15 * time.Second + +// The frequency to poll when waiting for the resource to be deleted +const shareDeletingPollingPeriod = 15 * time.Second + +type shareActuator struct { + osClient osclients.ShareClient + k8sClient client.Client +} + +var _ createResourceActuator = shareActuator{} +var _ deleteResourceActuator = shareActuator{} + +func (shareActuator) GetResourceID(osResource *osResourceT) string { + return osResource.ID +} + +func (actuator shareActuator) GetOSResourceByID(ctx context.Context, id string) (*osResourceT, progress.ReconcileStatus) { + resource, err := actuator.osClient.GetShare(ctx, id) + if err != nil { + return nil, progress.WrapError(err) + } + return resource, nil +} + +func (actuator shareActuator) ListOSResourcesForAdoption(ctx context.Context, orcObject orcObjectPT) (iter.Seq2[*osResourceT, error], bool) { + resourceSpec := orcObject.Spec.Resource + if resourceSpec == nil { + return nil, false + } + + listOpts := shares.ListOpts{ + Name: getResourceName(orcObject), + } + + // Filter by ShareProto and Size client-side since they're not in ListOpts + iter := actuator.osClient.ListShares(ctx, listOpts) + iter = osclients.Filter(iter, func(share *shares.Share) bool { + return share.ShareProto == resourceSpec.ShareProto && + share.Size == int(resourceSpec.Size) + }) + + return iter, true +} + +func (actuator shareActuator) ListOSResourcesForImport(ctx context.Context, obj orcObjectPT, filter filterT) (iter.Seq2[*osResourceT, error], progress.ReconcileStatus) { + listOpts := shares.ListOpts{ + Name: string(ptr.Deref(filter.Name, "")), + DescriptionPattern: ptr.Deref(filter.Description, ""), + Status: ptr.Deref(filter.Status, ""), + } + + // ShareProto is not directly supported by ListOpts, so we'll need client-side filtering + iter := actuator.osClient.ListShares(ctx, listOpts) + if filter.ShareProto != nil { + iter = osclients.Filter(iter, func(share *shares.Share) bool { + return share.ShareProto == *filter.ShareProto + }) + } + + return iter, nil +} + +func (actuator shareActuator) CreateResource(ctx context.Context, obj orcObjectPT) (*osResourceT, progress.ReconcileStatus) { + resource := obj.Spec.Resource + + if resource == nil { + // Should have been caught by API validation + return nil, progress.WrapError( + orcerrors.Terminal(orcv1alpha1.ConditionReasonInvalidConfiguration, "Creation requested, but spec.resource is not set")) + } + var reconcileStatus progress.ReconcileStatus + + var shareNetworkID string + if resource.ShareNetworkRef != nil { + shareNetwork, shareNetworkDepRS := shareNetworkDependency.GetDependency( + ctx, actuator.k8sClient, obj, orcv1alpha1.IsAvailable, + ) + reconcileStatus = reconcileStatus.WithReconcileStatus(shareNetworkDepRS) + if shareNetwork != nil { + shareNetworkID = ptr.Deref(shareNetwork.Status.ID, "") + } + } + if needsReschedule, _ := reconcileStatus.NeedsReschedule(); needsReschedule { + return nil, reconcileStatus + } + createOpts := shares.CreateOpts{ + ShareProto: resource.ShareProto, + Size: int(resource.Size), + Name: getResourceName(obj), + Description: ptr.Deref(resource.Description, ""), + ShareNetworkID: shareNetworkID, + AvailabilityZone: ptr.Deref(resource.AvailabilityZone, ""), + ShareType: ptr.Deref(resource.ShareType, ""), + Metadata: resource.Metadata, + IsPublic: resource.IsPublic, + } + + osResource, err := actuator.osClient.CreateShare(ctx, createOpts) + if err != nil { + if !orcerrors.IsRetryable(err) { + err = orcerrors.Terminal(orcv1alpha1.ConditionReasonInvalidConfiguration, "invalid configuration creating resource: "+err.Error(), err) + } + return nil, progress.WrapError(err) + } + + return osResource, nil +} + +func (actuator shareActuator) DeleteResource(ctx context.Context, _ orcObjectPT, resource *osResourceT) progress.ReconcileStatus { + if resource.Status == ShareStatusDeleting { + return progress.WaitingOnOpenStack(progress.WaitingOnReady, shareDeletingPollingPeriod) + } + return progress.WrapError(actuator.osClient.DeleteShare(ctx, resource.ID)) +} + +func (actuator shareActuator) updateResource(ctx context.Context, obj orcObjectPT, osResource *osResourceT) progress.ReconcileStatus { + log := ctrl.LoggerFrom(ctx) + resource := obj.Spec.Resource + if resource == nil { + // Should have been caught by API validation + return progress.WrapError( + orcerrors.Terminal(orcv1alpha1.ConditionReasonInvalidConfiguration, "Update requested, but spec.resource is not set")) + } + + updateOpts := shares.UpdateOpts{} + + handleNameUpdate(&updateOpts, obj, osResource) + handleDescriptionUpdate(&updateOpts, resource, osResource) + handleIsPublicUpdate(&updateOpts, resource, osResource) + + needsUpdate, err := needsUpdate(updateOpts) + if err != nil { + return progress.WrapError( + orcerrors.Terminal(orcv1alpha1.ConditionReasonInvalidConfiguration, "invalid configuration updating resource: "+err.Error(), err)) + } + if !needsUpdate { + log.V(logging.Debug).Info("No changes") + return nil + } + + _, err = actuator.osClient.UpdateShare(ctx, osResource.ID, updateOpts) + + if err != nil { + if !orcerrors.IsRetryable(err) { + err = orcerrors.Terminal(orcv1alpha1.ConditionReasonInvalidConfiguration, "invalid configuration updating resource: "+err.Error(), err) + } + return progress.WrapError(err) + } + + return progress.NeedsRefresh() +} + +func needsUpdate(updateOpts shares.UpdateOpts) (bool, error) { + updateOptsMap, err := updateOpts.ToShareUpdateMap() + if err != nil { + return false, err + } + + updateMap, ok := updateOptsMap["share"].(map[string]any) + if !ok { + updateMap = make(map[string]any) + } + + return len(updateMap) > 0, nil +} + +func handleNameUpdate(updateOpts *shares.UpdateOpts, obj orcObjectPT, osResource *osResourceT) { + name := getResourceName(obj) + if osResource.Name != name { + updateOpts.DisplayName = &name + } +} + +func handleDescriptionUpdate(updateOpts *shares.UpdateOpts, resource *resourceSpecT, osResource *osResourceT) { + description := ptr.Deref(resource.Description, "") + if osResource.Description != description { + updateOpts.DisplayDescription = &description + } +} + +func handleIsPublicUpdate(updateOpts *shares.UpdateOpts, resource *resourceSpecT, osResource *osResourceT) { + if resource.IsPublic != nil && *resource.IsPublic != osResource.IsPublic { + updateOpts.IsPublic = resource.IsPublic + } +} + +func (actuator shareActuator) GetResourceReconcilers(ctx context.Context, orcObject orcObjectPT, osResource *osResourceT, controller interfaces.ResourceController) ([]resourceReconciler, progress.ReconcileStatus) { + return []resourceReconciler{ + actuator.updateResource, + }, nil +} + +type shareHelperFactory struct{} + +var _ helperFactory = shareHelperFactory{} + +func newActuator(ctx context.Context, orcObject *orcv1alpha1.Share, controller interfaces.ResourceController) (shareActuator, progress.ReconcileStatus) { + log := ctrl.LoggerFrom(ctx) + + // Ensure credential secrets exist and have our finalizer + _, reconcileStatus := credentialsDependency.GetDependencies(ctx, controller.GetK8sClient(), orcObject, func(*corev1.Secret) bool { return true }) + if needsReschedule, _ := reconcileStatus.NeedsReschedule(); needsReschedule { + return shareActuator{}, reconcileStatus + } + + clientScope, err := controller.GetScopeFactory().NewClientScopeFromObject(ctx, controller.GetK8sClient(), log, orcObject) + if err != nil { + return shareActuator{}, progress.WrapError(err) + } + osClient, err := clientScope.NewShareClient() + if err != nil { + return shareActuator{}, progress.WrapError(err) + } + + return shareActuator{ + osClient: osClient, + k8sClient: controller.GetK8sClient(), + }, nil +} + +func (shareHelperFactory) NewAPIObjectAdapter(obj orcObjectPT) adapterI { + return shareAdapter{obj} +} + +func (shareHelperFactory) NewCreateActuator(ctx context.Context, orcObject orcObjectPT, controller interfaces.ResourceController) (createResourceActuator, progress.ReconcileStatus) { + return newActuator(ctx, orcObject, controller) +} + +func (shareHelperFactory) NewDeleteActuator(ctx context.Context, orcObject orcObjectPT, controller interfaces.ResourceController) (deleteResourceActuator, progress.ReconcileStatus) { + return newActuator(ctx, orcObject, controller) +} diff --git a/internal/controllers/share/actuator_test.go b/internal/controllers/share/actuator_test.go new file mode 100644 index 000000000..797f90fdc --- /dev/null +++ b/internal/controllers/share/actuator_test.go @@ -0,0 +1,119 @@ +/* +Copyright The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package share + +import ( + "testing" + + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/shares" + orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + "k8s.io/utils/ptr" +) + +func TestNeedsUpdate(t *testing.T) { + testCases := []struct { + name string + updateOpts shares.UpdateOpts + expectChange bool + }{ + { + name: "Empty base opts", + updateOpts: shares.UpdateOpts{}, + expectChange: false, + }, + { + name: "Updated opts", + updateOpts: shares.UpdateOpts{DisplayName: ptr.To("updated")}, + expectChange: true, + }, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + got, _ := needsUpdate(tt.updateOpts) + if got != tt.expectChange { + t.Errorf("Expected change: %v, got: %v", tt.expectChange, got) + } + }) + } +} + +func TestHandleNameUpdate(t *testing.T) { + ptrToName := ptr.To[orcv1alpha1.OpenStackName] + testCases := []struct { + name string + newValue *orcv1alpha1.OpenStackName + existingValue string + expectChange bool + }{ + {name: "Identical", newValue: ptrToName("name"), existingValue: "name", expectChange: false}, + {name: "Different", newValue: ptrToName("new-name"), existingValue: "name", expectChange: true}, + {name: "No value provided, existing is identical to object name", newValue: nil, existingValue: "object-name", expectChange: false}, + {name: "No value provided, existing is different from object name", newValue: nil, existingValue: "different-from-object-name", expectChange: true}, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + resource := &orcv1alpha1.Share{} + resource.Name = "object-name" + resource.Spec = orcv1alpha1.ShareSpec{ + Resource: &orcv1alpha1.ShareResourceSpec{Name: tt.newValue}, + } + osResource := &osResourceT{Name: tt.existingValue} + + updateOpts := shares.UpdateOpts{} + handleNameUpdate(&updateOpts, resource, osResource) + + got, _ := needsUpdate(updateOpts) + if got != tt.expectChange { + t.Errorf("Expected change: %v, got: %v", tt.expectChange, got) + } + }) + + } +} + +func TestHandleDescriptionUpdate(t *testing.T) { + ptrToDescription := ptr.To[string] + testCases := []struct { + name string + newValue *string + existingValue string + expectChange bool + }{ + {name: "Identical", newValue: ptrToDescription("desc"), existingValue: "desc", expectChange: false}, + {name: "Different", newValue: ptrToDescription("new-desc"), existingValue: "desc", expectChange: true}, + {name: "No value provided, existing is set", newValue: nil, existingValue: "desc", expectChange: true}, + {name: "No value provided, existing is empty", newValue: nil, existingValue: "", expectChange: false}, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + resource := &orcv1alpha1.ShareResourceSpec{Description: tt.newValue} + osResource := &osResourceT{Description: tt.existingValue} + + updateOpts := shares.UpdateOpts{} + handleDescriptionUpdate(&updateOpts, resource, osResource) + + got, _ := needsUpdate(updateOpts) + if got != tt.expectChange { + t.Errorf("Expected change: %v, got: %v", tt.expectChange, got) + } + }) + + } +} diff --git a/internal/controllers/share/controller.go b/internal/controllers/share/controller.go new file mode 100644 index 000000000..142f36ade --- /dev/null +++ b/internal/controllers/share/controller.go @@ -0,0 +1,93 @@ +/* +Copyright The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package share + +import ( + "context" + "errors" + + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/controller" + + orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/interfaces" + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/reconciler" + "github.com/k-orc/openstack-resource-controller/v2/internal/scope" + "github.com/k-orc/openstack-resource-controller/v2/internal/util/credentials" + "github.com/k-orc/openstack-resource-controller/v2/internal/util/dependency" + "github.com/k-orc/openstack-resource-controller/v2/pkg/predicates" +) + +const controllerName = "share" + +// +kubebuilder:rbac:groups=openstack.k-orc.cloud,resources=shares,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=openstack.k-orc.cloud,resources=shares/status,verbs=get;update;patch + +type shareReconcilerConstructor struct { + scopeFactory scope.Factory +} + +func New(scopeFactory scope.Factory) interfaces.Controller { + return shareReconcilerConstructor{scopeFactory: scopeFactory} +} + +func (shareReconcilerConstructor) GetName() string { + return controllerName +} + +var shareNetworkDependency = dependency.NewDeletionGuardDependency[*orcv1alpha1.ShareList, *orcv1alpha1.ShareNetwork]( + "spec.resource.shareNetworkRef", + func(share *orcv1alpha1.Share) []string { + resource := share.Spec.Resource + if resource == nil || resource.ShareNetworkRef == nil { + return nil + } + return []string{string(*resource.ShareNetworkRef)} + }, + finalizer, externalObjectFieldOwner, +) + +// SetupWithManager sets up the controller with the Manager. +func (c shareReconcilerConstructor) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error { + log := ctrl.LoggerFrom(ctx) + k8sClient := mgr.GetClient() + + shareNetworkWatchEventHandler, err := shareNetworkDependency.WatchEventHandler(log, k8sClient) + if err != nil { + return err + } + + builder := ctrl.NewControllerManagedBy(mgr). + WithOptions(options). + Watches(&orcv1alpha1.ShareNetwork{}, shareNetworkWatchEventHandler, + builder.WithPredicates(predicates.NewBecameAvailable(log, &orcv1alpha1.ShareNetwork{})), + ). + For(&orcv1alpha1.Share{}) + + if err := errors.Join( + shareNetworkDependency.AddToManager(ctx, mgr), + credentialsDependency.AddToManager(ctx, mgr), + credentials.AddCredentialsWatch(log, mgr.GetClient(), builder, credentialsDependency), + ); err != nil { + return err + } + + r := reconciler.NewController(controllerName, mgr.GetClient(), c.scopeFactory, shareHelperFactory{}, shareStatusWriter{}) + return builder.Complete(&r) +} diff --git a/internal/controllers/share/status.go b/internal/controllers/share/status.go new file mode 100644 index 000000000..78e01e6d6 --- /dev/null +++ b/internal/controllers/share/status.go @@ -0,0 +1,126 @@ +/* +Copyright The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package share + +import ( + "github.com/go-logr/logr" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/interfaces" + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/progress" + orcapplyconfigv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/pkg/clients/applyconfiguration/api/v1alpha1" +) + +// Manila share status constants +// Based on https://docs.openstack.org/manila/latest/contributor/share_status.html +const ( + ShareStatusCreating = "creating" + ShareStatusAvailable = "available" + ShareStatusDeleting = "deleting" + ShareStatusError = "error" + ShareStatusErrorDeleting = "error_deleting" + ShareStatusManageStarting = "manage_starting" + ShareStatusManageError = "manage_error" + ShareStatusUnmanageStarting = "unmanage_starting" + ShareStatusUnmanageError = "unmanage_error" + ShareStatusExtending = "extending" + ShareStatusExtendingError = "extending_error" + ShareStatusShrinking = "shrinking" + ShareStatusShrinkingError = "shrinking_error" + ShareStatusMigrating = "migrating" + ShareStatusMigratingTo = "migrating_to" + ShareStatusRevertingToSnapshot = "reverting_to_snapshot" + ShareStatusRevertingError = "reverting_error" +) + +type shareStatusWriter struct{} + +type objectApplyT = orcapplyconfigv1alpha1.ShareApplyConfiguration +type statusApplyT = orcapplyconfigv1alpha1.ShareStatusApplyConfiguration + +var _ interfaces.ResourceStatusWriter[*orcv1alpha1.Share, *osResourceT, *objectApplyT, *statusApplyT] = shareStatusWriter{} + +func (shareStatusWriter) GetApplyConfig(name, namespace string) *objectApplyT { + return orcapplyconfigv1alpha1.Share(name, namespace) +} + +func (shareStatusWriter) ResourceAvailableStatus(orcObject *orcv1alpha1.Share, osResource *osResourceT) (metav1.ConditionStatus, progress.ReconcileStatus) { + if osResource == nil { + if orcObject.Status.ID == nil { + return metav1.ConditionFalse, nil + } else { + return metav1.ConditionUnknown, nil + } + } + + // Share is available when it's in available status + if osResource.Status == ShareStatusAvailable { + return metav1.ConditionTrue, nil + } + + // Error states are terminal - won't become available without intervention + switch osResource.Status { + case ShareStatusError, ShareStatusErrorDeleting, ShareStatusManageError, + ShareStatusUnmanageError, ShareStatusExtendingError, ShareStatusShrinkingError, + ShareStatusRevertingError: + return metav1.ConditionFalse, nil + } + + // Otherwise we're in a transitional state, continue polling + return metav1.ConditionFalse, progress.WaitingOnOpenStack(progress.WaitingOnReady, shareAvailablePollingPeriod) +} + +func (shareStatusWriter) ApplyResourceStatus(log logr.Logger, osResource *osResourceT, statusApply *statusApplyT) { + resourceStatus := orcapplyconfigv1alpha1.ShareResourceStatus(). + WithName(osResource.Name). + WithStatus(osResource.Status). + WithShareProto(osResource.ShareProto). + WithSize(int32(osResource.Size)) + + if osResource.Description != "" { + resourceStatus.WithDescription(osResource.Description) + } + + if osResource.AvailabilityZone != "" { + resourceStatus.WithAvailabilityZone(osResource.AvailabilityZone) + } + + if osResource.ShareType != "" { + resourceStatus.WithShareType(osResource.ShareType) + } + + if osResource.ShareTypeName != "" { + resourceStatus.WithShareTypeName(osResource.ShareTypeName) + } + + if osResource.ShareNetworkID != "" { + resourceStatus.WithShareNetworkID(osResource.ShareNetworkID) + } + + resourceStatus.WithIsPublic(osResource.IsPublic) + + if len(osResource.Metadata) > 0 { + resourceStatus.WithMetadata(osResource.Metadata) + } + + // Note: Export locations need to be fetched separately via ListExportLocations API + // They are not included in the basic Share struct from Get/List operations + // We'll skip them for now and can add them later as a separate reconciler if needed + + statusApply.WithResource(resourceStatus) +} diff --git a/internal/controllers/share/tests/share-create-full/00-assert.yaml b/internal/controllers/share/tests/share-create-full/00-assert.yaml new file mode 100644 index 000000000..a1e4d52ba --- /dev/null +++ b/internal/controllers/share/tests/share-create-full/00-assert.yaml @@ -0,0 +1,42 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-create-full +status: + resource: + name: share-create-full-override + description: Share from "create full" test + shareProto: NFS + size: 10 + status: available + isPublic: false + metadata: + test: full + component: e2e + conditions: + - type: Available + status: "True" + reason: Success + - type: Progressing + status: "False" + reason: Success +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Share + name: share-create-full + ref: share + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: ShareNetwork + name: share-create-full + ref: shareNetwork +assertAll: + - celExpr: "share.status.id != ''" + - celExpr: "share.status.resource.shareNetworkID == shareNetwork.status.id" + - celExpr: "share.status.resource.shareProto == 'NFS'" + - celExpr: "share.status.resource.size == 10" + - celExpr: "share.status.resource.isPublic == false" + - celExpr: "share.status.resource.metadata.test == 'full'" diff --git a/internal/controllers/share/tests/share-create-full/00-create-resource.yaml b/internal/controllers/share/tests/share-create-full/00-create-resource.yaml new file mode 100644 index 000000000..d34db6911 --- /dev/null +++ b/internal/controllers/share/tests/share-create-full/00-create-resource.yaml @@ -0,0 +1,32 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: ShareNetwork +metadata: + name: share-create-full +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: {} +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-create-full +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + name: share-create-full-override + description: Share from "create full" test + shareProto: NFS + size: 10 + shareType: default + shareNetworkRef: share-create-full + isPublic: false + metadata: + test: full + component: e2e diff --git a/internal/controllers/share/tests/share-create-full/00-secret.yaml b/internal/controllers/share/tests/share-create-full/00-secret.yaml new file mode 100644 index 000000000..045711ee7 --- /dev/null +++ b/internal/controllers/share/tests/share-create-full/00-secret.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl create secret generic openstack-clouds --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT} + namespaced: true diff --git a/internal/controllers/share/tests/share-create-full/README.md b/internal/controllers/share/tests/share-create-full/README.md new file mode 100644 index 000000000..0a4de00eb --- /dev/null +++ b/internal/controllers/share/tests/share-create-full/README.md @@ -0,0 +1,11 @@ +# Create a Share with all the options + +## Step 00 + +Create a Share using all available fields, and verify that the observed state corresponds to the spec. + +Also validate that the OpenStack resource uses the name from the spec when it is specified. + +## Reference + +https://k-orc.cloud/development/writing-tests/#create-full diff --git a/internal/controllers/share/tests/share-create-minimal/00-assert.yaml b/internal/controllers/share/tests/share-create-minimal/00-assert.yaml new file mode 100644 index 000000000..b08789cdf --- /dev/null +++ b/internal/controllers/share/tests/share-create-minimal/00-assert.yaml @@ -0,0 +1,31 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-create-minimal +status: + resource: + name: share-create-minimal + shareProto: NFS + size: 1 + status: available + conditions: + - type: Available + status: "True" + reason: Success + - type: Progressing + status: "False" + reason: Success +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Share + name: share-create-minimal + ref: share +assertAll: + - celExpr: "share.status.id != ''" + - celExpr: "share.status.resource.shareProto == 'NFS'" + - celExpr: "share.status.resource.size == 1" + - celExpr: "share.status.resource.status == 'available'" diff --git a/internal/controllers/share/tests/share-create-minimal/00-create-resource.yaml b/internal/controllers/share/tests/share-create-minimal/00-create-resource.yaml new file mode 100644 index 000000000..d8742d7a7 --- /dev/null +++ b/internal/controllers/share/tests/share-create-minimal/00-create-resource.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: ShareNetwork +metadata: + name: share-create-minimal +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: {} +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-create-minimal +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + shareProto: NFS + size: 1 + shareType: default + shareNetworkRef: share-create-minimal diff --git a/internal/controllers/share/tests/share-create-minimal/00-secret.yaml b/internal/controllers/share/tests/share-create-minimal/00-secret.yaml new file mode 100644 index 000000000..045711ee7 --- /dev/null +++ b/internal/controllers/share/tests/share-create-minimal/00-secret.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl create secret generic openstack-clouds --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT} + namespaced: true diff --git a/internal/controllers/share/tests/share-create-minimal/01-assert.yaml b/internal/controllers/share/tests/share-create-minimal/01-assert.yaml new file mode 100644 index 000000000..25276c933 --- /dev/null +++ b/internal/controllers/share/tests/share-create-minimal/01-assert.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: v1 + kind: Secret + name: openstack-clouds + ref: secret +assertAll: + - celExpr: "secret.metadata.deletionTimestamp != 0" + - celExpr: "'openstack.k-orc.cloud/share' in secret.metadata.finalizers" diff --git a/internal/controllers/share/tests/share-create-minimal/01-delete-secret.yaml b/internal/controllers/share/tests/share-create-minimal/01-delete-secret.yaml new file mode 100644 index 000000000..1620791b9 --- /dev/null +++ b/internal/controllers/share/tests/share-create-minimal/01-delete-secret.yaml @@ -0,0 +1,7 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + # We expect the deletion to hang due to the finalizer, so use --wait=false + - command: kubectl delete secret openstack-clouds --wait=false + namespaced: true diff --git a/internal/controllers/share/tests/share-create-minimal/README.md b/internal/controllers/share/tests/share-create-minimal/README.md new file mode 100644 index 000000000..30c5ed6b7 --- /dev/null +++ b/internal/controllers/share/tests/share-create-minimal/README.md @@ -0,0 +1,15 @@ +# Create a Share with the minimum options + +## Step 00 + +Create a minimal Share, that sets only the required fields, and verify that the observed state corresponds to the spec. + +Also validate that the OpenStack resource uses the name of the ORC object when no name is explicitly specified. + +## Step 01 + +Try deleting the secret and ensure that it is not deleted thanks to the finalizer. + +## Reference + +https://k-orc.cloud/development/writing-tests/#create-minimal diff --git a/internal/controllers/share/tests/share-dependency/00-assert.yaml b/internal/controllers/share/tests/share-dependency/00-assert.yaml new file mode 100644 index 000000000..167ad8dd5 --- /dev/null +++ b/internal/controllers/share/tests/share-dependency/00-assert.yaml @@ -0,0 +1,30 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-dependency-no-secret +status: + conditions: + - type: Available + message: Waiting for Secret/share-dependency to be created + status: "False" + reason: Progressing + - type: Progressing + message: Waiting for Secret/share-dependency to be created + status: "True" + reason: Progressing +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-dependency-no-sharenetwork +status: + conditions: + - type: Available + message: Waiting for ShareNetwork/share-dependency to be created + status: "False" + reason: Progressing + - type: Progressing + message: Waiting for ShareNetwork/share-dependency to be created + status: "True" + reason: Progressing diff --git a/internal/controllers/share/tests/share-dependency/00-create-resources-missing-deps.yaml b/internal/controllers/share/tests/share-dependency/00-create-resources-missing-deps.yaml new file mode 100644 index 000000000..956537820 --- /dev/null +++ b/internal/controllers/share/tests/share-dependency/00-create-resources-missing-deps.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-dependency-no-sharenetwork +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resource needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + shareNetworkRef: share-dependency + # TODO(scaffolding): Add the necessary fields to create the resource +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-dependency-no-secret +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resource needs admin credentials to be created + cloudName: openstack + secretName: share-dependency + managementPolicy: managed + # TODO(scaffolding): Add the necessary fields to create the resource + resource: {} diff --git a/internal/controllers/share/tests/share-dependency/00-secret.yaml b/internal/controllers/share/tests/share-dependency/00-secret.yaml new file mode 100644 index 000000000..045711ee7 --- /dev/null +++ b/internal/controllers/share/tests/share-dependency/00-secret.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl create secret generic openstack-clouds --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT} + namespaced: true diff --git a/internal/controllers/share/tests/share-dependency/01-assert.yaml b/internal/controllers/share/tests/share-dependency/01-assert.yaml new file mode 100644 index 000000000..682a29972 --- /dev/null +++ b/internal/controllers/share/tests/share-dependency/01-assert.yaml @@ -0,0 +1,30 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-dependency-no-secret +status: + conditions: + - type: Available + message: OpenStack resource is available + status: "True" + reason: Success + - type: Progressing + message: OpenStack resource is up to date + status: "False" + reason: Success +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-dependency-no-sharenetwork +status: + conditions: + - type: Available + message: OpenStack resource is available + status: "True" + reason: Success + - type: Progressing + message: OpenStack resource is up to date + status: "False" + reason: Success diff --git a/internal/controllers/share/tests/share-dependency/01-create-dependencies.yaml b/internal/controllers/share/tests/share-dependency/01-create-dependencies.yaml new file mode 100644 index 000000000..b93f4ddbd --- /dev/null +++ b/internal/controllers/share/tests/share-dependency/01-create-dependencies.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl create secret generic share-dependency --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT} + namespaced: true +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: ShareNetwork +metadata: + name: share-dependency +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resource needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + # TODO(scaffolding): Add the necessary fields to create the resource + resource: {} diff --git a/internal/controllers/share/tests/share-dependency/02-assert.yaml b/internal/controllers/share/tests/share-dependency/02-assert.yaml new file mode 100644 index 000000000..3e85e7f04 --- /dev/null +++ b/internal/controllers/share/tests/share-dependency/02-assert.yaml @@ -0,0 +1,17 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: ShareNetwork + name: share-dependency + ref: shareNetwork + - apiVersion: v1 + kind: Secret + name: share-dependency + ref: secret +assertAll: + - celExpr: "shareNetwork.metadata.deletionTimestamp != 0" + - celExpr: "'openstack.k-orc.cloud/share' in shareNetwork.metadata.finalizers" + - celExpr: "secret.metadata.deletionTimestamp != 0" + - celExpr: "'openstack.k-orc.cloud/share' in secret.metadata.finalizers" diff --git a/internal/controllers/share/tests/share-dependency/02-delete-dependencies.yaml b/internal/controllers/share/tests/share-dependency/02-delete-dependencies.yaml new file mode 100644 index 000000000..bd778c687 --- /dev/null +++ b/internal/controllers/share/tests/share-dependency/02-delete-dependencies.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + # We expect the deletion to hang due to the finalizer, so use --wait=false + - command: kubectl delete sharenetwork.openstack.k-orc.cloud share-dependency --wait=false + namespaced: true + - command: kubectl delete secret share-dependency --wait=false + namespaced: true diff --git a/internal/controllers/share/tests/share-dependency/03-assert.yaml b/internal/controllers/share/tests/share-dependency/03-assert.yaml new file mode 100644 index 000000000..1799c8117 --- /dev/null +++ b/internal/controllers/share/tests/share-dependency/03-assert.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +commands: +# Dependencies that were prevented deletion before should now be gone +- script: "! kubectl get sharenetwork.openstack.k-orc.cloud share-dependency --namespace $NAMESPACE" + skipLogOutput: true +- script: "! kubectl get secret share-dependency --namespace $NAMESPACE" + skipLogOutput: true diff --git a/internal/controllers/share/tests/share-dependency/03-delete-resources.yaml b/internal/controllers/share/tests/share-dependency/03-delete-resources.yaml new file mode 100644 index 000000000..46e4c3cc5 --- /dev/null +++ b/internal/controllers/share/tests/share-dependency/03-delete-resources.yaml @@ -0,0 +1,10 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +delete: +- apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Share + name: share-dependency-no-secret +- apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Share + name: share-dependency-no-sharenetwork diff --git a/internal/controllers/share/tests/share-dependency/README.md b/internal/controllers/share/tests/share-dependency/README.md new file mode 100644 index 000000000..80e85ec5d --- /dev/null +++ b/internal/controllers/share/tests/share-dependency/README.md @@ -0,0 +1,21 @@ +# Creation and deletion dependencies + +## Step 00 + +Create Shares referencing non-existing resources. Each Share is dependent on other non-existing resource. Verify that the Shares are waiting for the needed resources to be created externally. + +## Step 01 + +Create the missing dependencies and verify all the Shares are available. + +## Step 02 + +Delete all the dependencies and check that ORC prevents deletion since there is still a resource that depends on them. + +## Step 03 + +Delete the Shares and validate that all resources are gone. + +## Reference + +https://k-orc.cloud/development/writing-tests/#dependency diff --git a/internal/controllers/share/tests/share-import-error/00-assert.yaml b/internal/controllers/share/tests/share-import-error/00-assert.yaml new file mode 100644 index 000000000..2b1f34e3a --- /dev/null +++ b/internal/controllers/share/tests/share-import-error/00-assert.yaml @@ -0,0 +1,30 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-import-error-external-1 +status: + conditions: + - type: Available + message: OpenStack resource is available + status: "True" + reason: Success + - type: Progressing + message: OpenStack resource is up to date + status: "False" + reason: Success +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-import-error-external-2 +status: + conditions: + - type: Available + message: OpenStack resource is available + status: "True" + reason: Success + - type: Progressing + message: OpenStack resource is up to date + status: "False" + reason: Success diff --git a/internal/controllers/share/tests/share-import-error/00-create-resources.yaml b/internal/controllers/share/tests/share-import-error/00-create-resources.yaml new file mode 100644 index 000000000..c99484ebd --- /dev/null +++ b/internal/controllers/share/tests/share-import-error/00-create-resources.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-import-error-external-1 +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resource needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + description: Share from "import error" test + # TODO(scaffolding): add any required field +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-import-error-external-2 +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resource needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + description: Share from "import error" test + # TODO(scaffolding): add any required field diff --git a/internal/controllers/share/tests/share-import-error/00-secret.yaml b/internal/controllers/share/tests/share-import-error/00-secret.yaml new file mode 100644 index 000000000..045711ee7 --- /dev/null +++ b/internal/controllers/share/tests/share-import-error/00-secret.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl create secret generic openstack-clouds --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT} + namespaced: true diff --git a/internal/controllers/share/tests/share-import-error/01-assert.yaml b/internal/controllers/share/tests/share-import-error/01-assert.yaml new file mode 100644 index 000000000..eb12e844a --- /dev/null +++ b/internal/controllers/share/tests/share-import-error/01-assert.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-import-error +status: + conditions: + - type: Available + message: found more than one matching OpenStack resource during import + status: "False" + reason: InvalidConfiguration + - type: Progressing + message: found more than one matching OpenStack resource during import + status: "False" + reason: InvalidConfiguration diff --git a/internal/controllers/share/tests/share-import-error/01-import-resource.yaml b/internal/controllers/share/tests/share-import-error/01-import-resource.yaml new file mode 100644 index 000000000..b018dc7ab --- /dev/null +++ b/internal/controllers/share/tests/share-import-error/01-import-resource.yaml @@ -0,0 +1,13 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-import-error +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: unmanaged + import: + filter: + description: Share from "import error" test diff --git a/internal/controllers/share/tests/share-import-error/README.md b/internal/controllers/share/tests/share-import-error/README.md new file mode 100644 index 000000000..648698d54 --- /dev/null +++ b/internal/controllers/share/tests/share-import-error/README.md @@ -0,0 +1,13 @@ +# Import Share with more than one matching resources + +## Step 00 + +Create two Shares with identical specs. + +## Step 01 + +Ensure that an imported Share with a filter matching the resources returns an error. + +## Reference + +https://k-orc.cloud/development/writing-tests/#import-error diff --git a/internal/controllers/share/tests/share-import/00-assert.yaml b/internal/controllers/share/tests/share-import/00-assert.yaml new file mode 100644 index 000000000..a6c6dabc7 --- /dev/null +++ b/internal/controllers/share/tests/share-import/00-assert.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-import +status: + conditions: + - type: Available + message: Waiting for OpenStack resource to be created externally + status: "False" + reason: Progressing + - type: Progressing + message: Waiting for OpenStack resource to be created externally + status: "True" + reason: Progressing diff --git a/internal/controllers/share/tests/share-import/00-import-resource.yaml b/internal/controllers/share/tests/share-import/00-import-resource.yaml new file mode 100644 index 000000000..e1cfad498 --- /dev/null +++ b/internal/controllers/share/tests/share-import/00-import-resource.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: ShareNetwork +metadata: + name: share-import +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: {} +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-import +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + name: share-for-import + shareProto: NFS + size: 3 + shareType: default + shareNetworkRef: share-import diff --git a/internal/controllers/share/tests/share-import/00-secret.yaml b/internal/controllers/share/tests/share-import/00-secret.yaml new file mode 100644 index 000000000..045711ee7 --- /dev/null +++ b/internal/controllers/share/tests/share-import/00-secret.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl create secret generic openstack-clouds --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT} + namespaced: true diff --git a/internal/controllers/share/tests/share-import/01-assert.yaml b/internal/controllers/share/tests/share-import/01-assert.yaml new file mode 100644 index 000000000..916080e32 --- /dev/null +++ b/internal/controllers/share/tests/share-import/01-assert.yaml @@ -0,0 +1,34 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-import-external-not-this-one +status: + conditions: + - type: Available + message: OpenStack resource is available + status: "True" + reason: Success + - type: Progressing + message: OpenStack resource is up to date + status: "False" + reason: Success + resource: + name: share-import-external-not-this-one + description: Share share-import-external from "share-import" test + # TODO(scaffolding): Add fields necessary to match filter +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-import +status: + conditions: + - type: Available + message: Waiting for OpenStack resource to be created externally + status: "False" + reason: Progressing + - type: Progressing + message: Waiting for OpenStack resource to be created externally + status: "True" + reason: Progressing diff --git a/internal/controllers/share/tests/share-import/01-create-trap-resource.yaml b/internal/controllers/share/tests/share-import/01-create-trap-resource.yaml new file mode 100644 index 000000000..dcd2fe9af --- /dev/null +++ b/internal/controllers/share/tests/share-import/01-create-trap-resource.yaml @@ -0,0 +1,20 @@ +--- +# This `share-import-external-not-this-one` resource serves two purposes: +# - ensure that we can successfully create another resource which name is a substring of it (i.e. it's not being adopted) +# - ensure that importing a resource which name is a substring of it will not pick this one. +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-import-external-not-this-one +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + name: share-import-external-not-this-one + description: Share share-import-external from "share-import" test + shareProto: NFS + size: 7 + shareType: default + shareNetworkRef: share-import diff --git a/internal/controllers/share/tests/share-import/02-assert.yaml b/internal/controllers/share/tests/share-import/02-assert.yaml new file mode 100644 index 000000000..972640bda --- /dev/null +++ b/internal/controllers/share/tests/share-import/02-assert.yaml @@ -0,0 +1,33 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Share + name: share-import-external + ref: share1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Share + name: share-import-external-not-this-one + ref: share2 +assertAll: + - celExpr: "share1.status.id != share2.status.id" +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-import +status: + conditions: + - type: Available + message: OpenStack resource is available + status: "True" + reason: Success + - type: Progressing + message: OpenStack resource is up to date + status: "False" + reason: Success + resource: + name: share-import-external + description: Share share-import-external from "share-import" test + # TODO(scaffolding): Add all fields the resource supports diff --git a/internal/controllers/share/tests/share-import/02-create-resource.yaml b/internal/controllers/share/tests/share-import/02-create-resource.yaml new file mode 100644 index 000000000..189fe18b6 --- /dev/null +++ b/internal/controllers/share/tests/share-import/02-create-resource.yaml @@ -0,0 +1,17 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-import-external +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + name: share-import-external + description: Share share-import-external from "share-import" test + shareProto: NFS + size: 5 + shareType: default + shareNetworkRef: share-import diff --git a/internal/controllers/share/tests/share-import/README.md b/internal/controllers/share/tests/share-import/README.md new file mode 100644 index 000000000..9a95b7094 --- /dev/null +++ b/internal/controllers/share/tests/share-import/README.md @@ -0,0 +1,18 @@ +# Import Share + +## Step 00 + +Import a share that matches all fields in the filter, and verify it is waiting for the external resource to be created. + +## Step 01 + +Create a share whose name is a superstring of the one specified in the import filter, otherwise matching the filter, and verify that it's not being imported. + +## Step 02 + +Create a share matching the filter and verify that the observed status on the imported share corresponds to the spec of the created share. +Also, confirm that it does not adopt any share whose name is a superstring of its own. + +## Reference + +https://k-orc.cloud/development/writing-tests/#import diff --git a/internal/controllers/share/tests/share-update/00-assert.yaml b/internal/controllers/share/tests/share-update/00-assert.yaml new file mode 100644 index 000000000..d41d0642c --- /dev/null +++ b/internal/controllers/share/tests/share-update/00-assert.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Share + name: share-update + ref: share +assertAll: + - celExpr: "!has(share.status.resource.description)" +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-update +status: + resource: + name: share-update + # TODO(scaffolding): Add matches for more fields + conditions: + - type: Available + status: "True" + reason: Success + - type: Progressing + status: "False" + reason: Success diff --git a/internal/controllers/share/tests/share-update/00-minimal-resource.yaml b/internal/controllers/share/tests/share-update/00-minimal-resource.yaml new file mode 100644 index 000000000..8ebc3311a --- /dev/null +++ b/internal/controllers/share/tests/share-update/00-minimal-resource.yaml @@ -0,0 +1,29 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: ShareNetwork +metadata: + name: share-update +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: {} +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-update +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + name: share-update-original + description: Share from "share-update" test + shareProto: NFS + size: 4 + shareType: default + shareNetworkRef: share-update + isPublic: false diff --git a/internal/controllers/share/tests/share-update/00-secret.yaml b/internal/controllers/share/tests/share-update/00-secret.yaml new file mode 100644 index 000000000..045711ee7 --- /dev/null +++ b/internal/controllers/share/tests/share-update/00-secret.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl create secret generic openstack-clouds --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT} + namespaced: true diff --git a/internal/controllers/share/tests/share-update/01-assert.yaml b/internal/controllers/share/tests/share-update/01-assert.yaml new file mode 100644 index 000000000..5782261f7 --- /dev/null +++ b/internal/controllers/share/tests/share-update/01-assert.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-update +status: + resource: + name: share-update-updated + description: share-update-updated + shareProto: NFS + size: 4 + isPublic: true + conditions: + - type: Progressing + status: "False" + reason: Success diff --git a/internal/controllers/share/tests/share-update/01-updated-resource.yaml b/internal/controllers/share/tests/share-update/01-updated-resource.yaml new file mode 100644 index 000000000..1d727c1de --- /dev/null +++ b/internal/controllers/share/tests/share-update/01-updated-resource.yaml @@ -0,0 +1,12 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-update +spec: + resource: + name: share-update-updated + description: share-update-updated + shareProto: NFS + size: 4 + isPublic: true diff --git a/internal/controllers/share/tests/share-update/02-assert.yaml b/internal/controllers/share/tests/share-update/02-assert.yaml new file mode 100644 index 000000000..a8f01433e --- /dev/null +++ b/internal/controllers/share/tests/share-update/02-assert.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Share + name: share-update + ref: share +assertAll: + - celExpr: "!has(share.status.resource.description)" +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Share +metadata: + name: share-update +status: + resource: + name: share-update-original + shareProto: NFS + size: 4 + isPublic: false + conditions: + - type: Available + status: "True" + reason: Success + - type: Progressing + status: "False" + reason: Success diff --git a/internal/controllers/share/tests/share-update/02-reverted-resource.yaml b/internal/controllers/share/tests/share-update/02-reverted-resource.yaml new file mode 100644 index 000000000..2c6c253ff --- /dev/null +++ b/internal/controllers/share/tests/share-update/02-reverted-resource.yaml @@ -0,0 +1,7 @@ +# NOTE: kuttl only does patch updates, which means we can't delete a field. +# We have to use a kubectl apply command instead. +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl replace -f 00-minimal-resource.yaml + namespaced: true diff --git a/internal/controllers/share/tests/share-update/README.md b/internal/controllers/share/tests/share-update/README.md new file mode 100644 index 000000000..eed647c82 --- /dev/null +++ b/internal/controllers/share/tests/share-update/README.md @@ -0,0 +1,17 @@ +# Update Share + +## Step 00 + +Create a Share using only mandatory fields. + +## Step 01 + +Update all mutable fields. + +## Step 02 + +Revert the resource to its original value and verify that the resulting object matches its state when first created. + +## Reference + +https://k-orc.cloud/development/writing-tests/#update diff --git a/internal/controllers/share/zz_generated.adapter.go b/internal/controllers/share/zz_generated.adapter.go new file mode 100644 index 000000000..7632d2212 --- /dev/null +++ b/internal/controllers/share/zz_generated.adapter.go @@ -0,0 +1,88 @@ +// Code generated by resource-generator. DO NOT EDIT. +/* +Copyright The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package share + +import ( + orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/interfaces" +) + +// Fundamental types +type ( + orcObjectT = orcv1alpha1.Share + orcObjectListT = orcv1alpha1.ShareList + resourceSpecT = orcv1alpha1.ShareResourceSpec + filterT = orcv1alpha1.ShareFilter +) + +// Derived types +type ( + orcObjectPT = *orcObjectT + adapterI = interfaces.APIObjectAdapter[orcObjectPT, resourceSpecT, filterT] + adapterT = shareAdapter +) + +type shareAdapter struct { + *orcv1alpha1.Share +} + +var _ adapterI = &adapterT{} + +func (f adapterT) GetObject() orcObjectPT { + return f.Share +} + +func (f adapterT) GetManagementPolicy() orcv1alpha1.ManagementPolicy { + return f.Spec.ManagementPolicy +} + +func (f adapterT) GetManagedOptions() *orcv1alpha1.ManagedOptions { + return f.Spec.ManagedOptions +} + +func (f adapterT) GetStatusID() *string { + return f.Status.ID +} + +func (f adapterT) GetResourceSpec() *resourceSpecT { + return f.Spec.Resource +} + +func (f adapterT) GetImportID() *string { + if f.Spec.Import == nil { + return nil + } + return f.Spec.Import.ID +} + +func (f adapterT) GetImportFilter() *filterT { + if f.Spec.Import == nil { + return nil + } + return f.Spec.Import.Filter +} + +// getResourceName returns the name of the OpenStack resource we should use. +// This method is not implemented as part of APIObjectAdapter as it is intended +// to be used by resource actuators, which don't use the adapter. +func getResourceName(orcObject orcObjectPT) string { + if orcObject.Spec.Resource.Name != nil { + return string(*orcObject.Spec.Resource.Name) + } + return orcObject.Name +} diff --git a/internal/controllers/share/zz_generated.controller.go b/internal/controllers/share/zz_generated.controller.go new file mode 100644 index 000000000..a36eeb8ef --- /dev/null +++ b/internal/controllers/share/zz_generated.controller.go @@ -0,0 +1,45 @@ +// Code generated by resource-generator. DO NOT EDIT. +/* +Copyright The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package share + +import ( + corev1 "k8s.io/api/core/v1" + + "github.com/k-orc/openstack-resource-controller/v2/internal/util/dependency" + orcstrings "github.com/k-orc/openstack-resource-controller/v2/internal/util/strings" +) + +var ( + // NOTE: controllerName must be defined in any controller using this template + + // finalizer is the string this controller adds to an object's Finalizers + finalizer = orcstrings.GetFinalizerName(controllerName) + + // externalObjectFieldOwner is the field owner we use when using + // server-side-apply on objects we don't control + externalObjectFieldOwner = orcstrings.GetSSAFieldOwner(controllerName) + + credentialsDependency = dependency.NewDeletionGuardDependency[*orcObjectListT, *corev1.Secret]( + "spec.cloudCredentialsRef.secretName", + func(obj orcObjectPT) []string { + return []string{obj.Spec.CloudCredentialsRef.SecretName} + }, + finalizer, externalObjectFieldOwner, + dependency.OverrideDependencyName("credentials"), + ) +) diff --git a/internal/osclients/mock/doc.go b/internal/osclients/mock/doc.go index 5ee7aa5da..278552707 100644 --- a/internal/osclients/mock/doc.go +++ b/internal/osclients/mock/doc.go @@ -59,6 +59,9 @@ import ( //go:generate mockgen -package mock -destination=service.go -source=../service.go github.com/k-orc/openstack-resource-controller/internal/osclients/mock ServiceClient //go:generate /usr/bin/env bash -c "cat ../../../hack/boilerplate.go.txt service.go > _service.go && mv _service.go service.go" +//go:generate mockgen -package mock -destination=share.go -source=../share.go github.com/k-orc/openstack-resource-controller/internal/osclients/mock ShareClient +//go:generate /usr/bin/env bash -c "cat ../../../hack/boilerplate.go.txt share.go > _share.go && mv _share.go share.go" + //go:generate mockgen -package mock -destination=sharenetwork.go -source=../sharenetwork.go github.com/k-orc/openstack-resource-controller/internal/osclients/mock ShareNetworkClient //go:generate /usr/bin/env bash -c "cat ../../../hack/boilerplate.go.txt sharenetwork.go > _sharenetwork.go && mv _sharenetwork.go sharenetwork.go" diff --git a/internal/osclients/mock/share.go b/internal/osclients/mock/share.go new file mode 100644 index 000000000..756d39919 --- /dev/null +++ b/internal/osclients/mock/share.go @@ -0,0 +1,131 @@ +/* +Copyright The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by MockGen. DO NOT EDIT. +// Source: ../share.go +// +// Generated by this command: +// +// mockgen -package mock -destination=share.go -source=../share.go github.com/k-orc/openstack-resource-controller/internal/osclients/mock ShareClient +// + +// Package mock is a generated GoMock package. +package mock + +import ( + context "context" + iter "iter" + reflect "reflect" + + shares "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/shares" + gomock "go.uber.org/mock/gomock" +) + +// MockShareClient is a mock of ShareClient interface. +type MockShareClient struct { + ctrl *gomock.Controller + recorder *MockShareClientMockRecorder + isgomock struct{} +} + +// MockShareClientMockRecorder is the mock recorder for MockShareClient. +type MockShareClientMockRecorder struct { + mock *MockShareClient +} + +// NewMockShareClient creates a new mock instance. +func NewMockShareClient(ctrl *gomock.Controller) *MockShareClient { + mock := &MockShareClient{ctrl: ctrl} + mock.recorder = &MockShareClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockShareClient) EXPECT() *MockShareClientMockRecorder { + return m.recorder +} + +// CreateShare mocks base method. +func (m *MockShareClient) CreateShare(ctx context.Context, opts shares.CreateOptsBuilder) (*shares.Share, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateShare", ctx, opts) + ret0, _ := ret[0].(*shares.Share) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateShare indicates an expected call of CreateShare. +func (mr *MockShareClientMockRecorder) CreateShare(ctx, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateShare", reflect.TypeOf((*MockShareClient)(nil).CreateShare), ctx, opts) +} + +// DeleteShare mocks base method. +func (m *MockShareClient) DeleteShare(ctx context.Context, resourceID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteShare", ctx, resourceID) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteShare indicates an expected call of DeleteShare. +func (mr *MockShareClientMockRecorder) DeleteShare(ctx, resourceID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteShare", reflect.TypeOf((*MockShareClient)(nil).DeleteShare), ctx, resourceID) +} + +// GetShare mocks base method. +func (m *MockShareClient) GetShare(ctx context.Context, resourceID string) (*shares.Share, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetShare", ctx, resourceID) + ret0, _ := ret[0].(*shares.Share) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetShare indicates an expected call of GetShare. +func (mr *MockShareClientMockRecorder) GetShare(ctx, resourceID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShare", reflect.TypeOf((*MockShareClient)(nil).GetShare), ctx, resourceID) +} + +// ListShares mocks base method. +func (m *MockShareClient) ListShares(ctx context.Context, listOpts shares.ListOptsBuilder) iter.Seq2[*shares.Share, error] { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListShares", ctx, listOpts) + ret0, _ := ret[0].(iter.Seq2[*shares.Share, error]) + return ret0 +} + +// ListShares indicates an expected call of ListShares. +func (mr *MockShareClientMockRecorder) ListShares(ctx, listOpts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListShares", reflect.TypeOf((*MockShareClient)(nil).ListShares), ctx, listOpts) +} + +// UpdateShare mocks base method. +func (m *MockShareClient) UpdateShare(ctx context.Context, id string, opts shares.UpdateOptsBuilder) (*shares.Share, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateShare", ctx, id, opts) + ret0, _ := ret[0].(*shares.Share) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateShare indicates an expected call of UpdateShare. +func (mr *MockShareClientMockRecorder) UpdateShare(ctx, id, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateShare", reflect.TypeOf((*MockShareClient)(nil).UpdateShare), ctx, id, opts) +} diff --git a/internal/osclients/share.go b/internal/osclients/share.go new file mode 100644 index 000000000..cf7428457 --- /dev/null +++ b/internal/osclients/share.go @@ -0,0 +1,104 @@ +/* +Copyright The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package osclients + +import ( + "context" + "fmt" + "iter" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/shares" + "github.com/gophercloud/utils/v2/openstack/clientconfig" +) + +type ShareClient interface { + ListShares(ctx context.Context, listOpts shares.ListOptsBuilder) iter.Seq2[*shares.Share, error] + CreateShare(ctx context.Context, opts shares.CreateOptsBuilder) (*shares.Share, error) + DeleteShare(ctx context.Context, resourceID string) error + GetShare(ctx context.Context, resourceID string) (*shares.Share, error) + UpdateShare(ctx context.Context, id string, opts shares.UpdateOptsBuilder) (*shares.Share, error) +} + +type shareClient struct{ client *gophercloud.ServiceClient } + +// NewShareClient returns a new OpenStack client. +func NewShareClient(providerClient *gophercloud.ProviderClient, providerClientOpts *clientconfig.ClientOpts) (ShareClient, error) { + client, err := openstack.NewSharedFileSystemV2(providerClient, gophercloud.EndpointOpts{ + Region: providerClientOpts.RegionName, + Availability: clientconfig.GetEndpointType(providerClientOpts.EndpointType), + }) + + if err != nil { + return nil, fmt.Errorf("failed to create share service client: %v", err) + } + + return &shareClient{client}, nil +} + +func (c shareClient) ListShares(ctx context.Context, listOpts shares.ListOptsBuilder) iter.Seq2[*shares.Share, error] { + pager := shares.ListDetail(c.client, listOpts) + return func(yield func(*shares.Share, error) bool) { + _ = pager.EachPage(ctx, yieldPage(shares.ExtractShares, yield)) + } +} + +func (c shareClient) CreateShare(ctx context.Context, opts shares.CreateOptsBuilder) (*shares.Share, error) { + return shares.Create(ctx, c.client, opts).Extract() +} + +func (c shareClient) DeleteShare(ctx context.Context, resourceID string) error { + return shares.Delete(ctx, c.client, resourceID).ExtractErr() +} + +func (c shareClient) GetShare(ctx context.Context, resourceID string) (*shares.Share, error) { + return shares.Get(ctx, c.client, resourceID).Extract() +} + +func (c shareClient) UpdateShare(ctx context.Context, id string, opts shares.UpdateOptsBuilder) (*shares.Share, error) { + return shares.Update(ctx, c.client, id, opts).Extract() +} + +type shareErrorClient struct{ error } + +// NewShareErrorClient returns a ShareClient in which every method returns the given error. +func NewShareErrorClient(e error) ShareClient { + return shareErrorClient{e} +} + +func (e shareErrorClient) ListShares(_ context.Context, _ shares.ListOptsBuilder) iter.Seq2[*shares.Share, error] { + return func(yield func(*shares.Share, error) bool) { + yield(nil, e.error) + } +} + +func (e shareErrorClient) CreateShare(_ context.Context, _ shares.CreateOptsBuilder) (*shares.Share, error) { + return nil, e.error +} + +func (e shareErrorClient) DeleteShare(_ context.Context, _ string) error { + return e.error +} + +func (e shareErrorClient) GetShare(_ context.Context, _ string) (*shares.Share, error) { + return nil, e.error +} + +func (e shareErrorClient) UpdateShare(_ context.Context, _ string, _ shares.UpdateOptsBuilder) (*shares.Share, error) { + return nil, e.error +} diff --git a/internal/scope/mock.go b/internal/scope/mock.go index 8ea474b64..696d9d219 100644 --- a/internal/scope/mock.go +++ b/internal/scope/mock.go @@ -49,6 +49,7 @@ type MockScopeFactory struct { UserClient *mock.MockUserClient VolumeClient *mock.MockVolumeClient VolumeTypeClient *mock.MockVolumeTypeClient + ShareClient *mock.MockShareClient ShareNetworkClient *mock.MockShareNetworkClient clientScopeCreateError error @@ -68,6 +69,7 @@ func NewMockScopeFactory(mockCtrl *gomock.Controller) *MockScopeFactory { roleClient := mock.NewMockRoleClient(mockCtrl) serviceClient := mock.NewMockServiceClient(mockCtrl) userClient := mock.NewMockUserClient(mockCtrl) + shareClient := mock.NewMockShareClient(mockCtrl) sharenetworkClient := mock.NewMockShareNetworkClient(mockCtrl) volumeClient := mock.NewMockVolumeClient(mockCtrl) volumetypeClient := mock.NewMockVolumeTypeClient(mockCtrl) @@ -85,6 +87,7 @@ func NewMockScopeFactory(mockCtrl *gomock.Controller) *MockScopeFactory { NetworkClient: networkClient, RoleClient: roleClient, ServiceClient: serviceClient, + ShareClient: shareClient, ShareNetworkClient: sharenetworkClient, UserClient: userClient, VolumeClient: volumeClient, @@ -143,6 +146,10 @@ func (f *MockScopeFactory) NewServiceClient() (osclients.ServiceClient, error) { return f.ServiceClient, nil } +func (f *MockScopeFactory) NewShareClient() (osclients.ShareClient, error) { + return f.ShareClient, nil +} + func (f *MockScopeFactory) NewShareNetworkClient() (osclients.ShareNetworkClient, error) { return f.ShareNetworkClient, nil } diff --git a/internal/scope/provider.go b/internal/scope/provider.go index 1606e18a1..51cbfe562 100644 --- a/internal/scope/provider.go +++ b/internal/scope/provider.go @@ -185,6 +185,10 @@ func (s *providerScope) NewEndpointClient() (clients.EndpointClient, error) { return clients.NewEndpointClient(s.providerClient, s.providerClientOpts) } +func (s *providerScope) NewShareClient() (clients.ShareClient, error) { + return clients.NewShareClient(s.providerClient, s.providerClientOpts) +} + func (s *providerScope) NewShareNetworkClient() (clients.ShareNetworkClient, error) { return clients.NewShareNetworkClient(s.providerClient, s.providerClientOpts) } diff --git a/internal/scope/scope.go b/internal/scope/scope.go index 0b02b79bd..9c9a1b8d9 100644 --- a/internal/scope/scope.go +++ b/internal/scope/scope.go @@ -60,6 +60,7 @@ type Scope interface { NewNetworkClient() (osclients.NetworkClient, error) NewRoleClient() (osclients.RoleClient, error) NewServiceClient() (osclients.ServiceClient, error) + NewShareClient() (osclients.ShareClient, error) NewShareNetworkClient() (osclients.ShareNetworkClient, error) NewUserClient() (osclients.UserClient, error) NewVolumeClient() (osclients.VolumeClient, error) diff --git a/kuttl-test.yaml b/kuttl-test.yaml index 71fc135ed..4ce56e9e0 100644 --- a/kuttl-test.yaml +++ b/kuttl-test.yaml @@ -21,6 +21,7 @@ testDirs: - ./internal/controllers/server/tests/ - ./internal/controllers/servergroup/tests/ - ./internal/controllers/service/tests/ +- ./internal/controllers/share/tests/ - ./internal/controllers/sharenetwork/tests/ - ./internal/controllers/subnet/tests/ - ./internal/controllers/trunk/tests/ diff --git a/manager b/manager new file mode 100755 index 000000000..603366e25 Binary files /dev/null and b/manager differ diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/share.go b/pkg/clients/applyconfiguration/api/v1alpha1/share.go new file mode 100644 index 000000000..06af5d9eb --- /dev/null +++ b/pkg/clients/applyconfiguration/api/v1alpha1/share.go @@ -0,0 +1,281 @@ +/* +Copyright The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + apiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + internal "github.com/k-orc/openstack-resource-controller/v2/pkg/clients/applyconfiguration/internal" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + managedfields "k8s.io/apimachinery/pkg/util/managedfields" + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// ShareApplyConfiguration represents a declarative configuration of the Share type for use +// with apply. +type ShareApplyConfiguration struct { + v1.TypeMetaApplyConfiguration `json:",inline"` + *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + Spec *ShareSpecApplyConfiguration `json:"spec,omitempty"` + Status *ShareStatusApplyConfiguration `json:"status,omitempty"` +} + +// Share constructs a declarative configuration of the Share type for use with +// apply. +func Share(name, namespace string) *ShareApplyConfiguration { + b := &ShareApplyConfiguration{} + b.WithName(name) + b.WithNamespace(namespace) + b.WithKind("Share") + b.WithAPIVersion("openstack.k-orc.cloud/v1alpha1") + return b +} + +// ExtractShare extracts the applied configuration owned by fieldManager from +// share. If no managedFields are found in share for fieldManager, a +// ShareApplyConfiguration is returned with only the Name, Namespace (if applicable), +// APIVersion and Kind populated. It is possible that no managed fields were found for because other +// field managers have taken ownership of all the fields previously owned by fieldManager, or because +// the fieldManager never owned fields any fields. +// share must be a unmodified Share API object that was retrieved from the Kubernetes API. +// ExtractShare provides a way to perform a extract/modify-in-place/apply workflow. +// Note that an extracted apply configuration will contain fewer fields than what the fieldManager previously +// applied if another fieldManager has updated or force applied any of the previously applied fields. +// Experimental! +func ExtractShare(share *apiv1alpha1.Share, fieldManager string) (*ShareApplyConfiguration, error) { + return extractShare(share, fieldManager, "") +} + +// ExtractShareStatus is the same as ExtractShare except +// that it extracts the status subresource applied configuration. +// Experimental! +func ExtractShareStatus(share *apiv1alpha1.Share, fieldManager string) (*ShareApplyConfiguration, error) { + return extractShare(share, fieldManager, "status") +} + +func extractShare(share *apiv1alpha1.Share, fieldManager string, subresource string) (*ShareApplyConfiguration, error) { + b := &ShareApplyConfiguration{} + err := managedfields.ExtractInto(share, internal.Parser().Type("com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.Share"), fieldManager, b, subresource) + if err != nil { + return nil, err + } + b.WithName(share.Name) + b.WithNamespace(share.Namespace) + + b.WithKind("Share") + b.WithAPIVersion("openstack.k-orc.cloud/v1alpha1") + return b, nil +} +func (b ShareApplyConfiguration) IsApplyConfiguration() {} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *ShareApplyConfiguration) WithKind(value string) *ShareApplyConfiguration { + b.TypeMetaApplyConfiguration.Kind = &value + return b +} + +// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the APIVersion field is set to the value of the last call. +func (b *ShareApplyConfiguration) WithAPIVersion(value string) *ShareApplyConfiguration { + b.TypeMetaApplyConfiguration.APIVersion = &value + return b +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *ShareApplyConfiguration) WithName(value string) *ShareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Name = &value + return b +} + +// WithGenerateName sets the GenerateName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the GenerateName field is set to the value of the last call. +func (b *ShareApplyConfiguration) WithGenerateName(value string) *ShareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.GenerateName = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *ShareApplyConfiguration) WithNamespace(value string) *ShareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Namespace = &value + return b +} + +// WithUID sets the UID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UID field is set to the value of the last call. +func (b *ShareApplyConfiguration) WithUID(value types.UID) *ShareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.UID = &value + return b +} + +// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResourceVersion field is set to the value of the last call. +func (b *ShareApplyConfiguration) WithResourceVersion(value string) *ShareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.ResourceVersion = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *ShareApplyConfiguration) WithGeneration(value int64) *ShareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Generation = &value + return b +} + +// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CreationTimestamp field is set to the value of the last call. +func (b *ShareApplyConfiguration) WithCreationTimestamp(value metav1.Time) *ShareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.CreationTimestamp = &value + return b +} + +// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionTimestamp field is set to the value of the last call. +func (b *ShareApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *ShareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionTimestamp = &value + return b +} + +// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call. +func (b *ShareApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *ShareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionGracePeriodSeconds = &value + return b +} + +// WithLabels puts the entries into the Labels field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Labels field, +// overwriting an existing map entries in Labels field with the same key. +func (b *ShareApplyConfiguration) WithLabels(entries map[string]string) *ShareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Labels == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Labels = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Labels[k] = v + } + return b +} + +// WithAnnotations puts the entries into the Annotations field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Annotations field, +// overwriting an existing map entries in Annotations field with the same key. +func (b *ShareApplyConfiguration) WithAnnotations(entries map[string]string) *ShareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Annotations == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Annotations[k] = v + } + return b +} + +// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the OwnerReferences field. +func (b *ShareApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *ShareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + if values[i] == nil { + panic("nil value passed to WithOwnerReferences") + } + b.ObjectMetaApplyConfiguration.OwnerReferences = append(b.ObjectMetaApplyConfiguration.OwnerReferences, *values[i]) + } + return b +} + +// WithFinalizers adds the given value to the Finalizers field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Finalizers field. +func (b *ShareApplyConfiguration) WithFinalizers(values ...string) *ShareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i]) + } + return b +} + +func (b *ShareApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { + if b.ObjectMetaApplyConfiguration == nil { + b.ObjectMetaApplyConfiguration = &v1.ObjectMetaApplyConfiguration{} + } +} + +// WithSpec sets the Spec field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Spec field is set to the value of the last call. +func (b *ShareApplyConfiguration) WithSpec(value *ShareSpecApplyConfiguration) *ShareApplyConfiguration { + b.Spec = value + return b +} + +// WithStatus sets the Status field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Status field is set to the value of the last call. +func (b *ShareApplyConfiguration) WithStatus(value *ShareStatusApplyConfiguration) *ShareApplyConfiguration { + b.Status = value + return b +} + +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *ShareApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *ShareApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *ShareApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Name +} + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *ShareApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/sharefilter.go b/pkg/clients/applyconfiguration/api/v1alpha1/sharefilter.go new file mode 100644 index 000000000..580a9ffa4 --- /dev/null +++ b/pkg/clients/applyconfiguration/api/v1alpha1/sharefilter.go @@ -0,0 +1,70 @@ +/* +Copyright The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + apiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" +) + +// ShareFilterApplyConfiguration represents a declarative configuration of the ShareFilter type for use +// with apply. +type ShareFilterApplyConfiguration struct { + Name *apiv1alpha1.OpenStackName `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + Status *string `json:"status,omitempty"` + ShareProto *string `json:"shareProto,omitempty"` +} + +// ShareFilterApplyConfiguration constructs a declarative configuration of the ShareFilter type for use with +// apply. +func ShareFilter() *ShareFilterApplyConfiguration { + return &ShareFilterApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *ShareFilterApplyConfiguration) WithName(value apiv1alpha1.OpenStackName) *ShareFilterApplyConfiguration { + b.Name = &value + return b +} + +// WithDescription sets the Description field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Description field is set to the value of the last call. +func (b *ShareFilterApplyConfiguration) WithDescription(value string) *ShareFilterApplyConfiguration { + b.Description = &value + return b +} + +// WithStatus sets the Status field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Status field is set to the value of the last call. +func (b *ShareFilterApplyConfiguration) WithStatus(value string) *ShareFilterApplyConfiguration { + b.Status = &value + return b +} + +// WithShareProto sets the ShareProto field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ShareProto field is set to the value of the last call. +func (b *ShareFilterApplyConfiguration) WithShareProto(value string) *ShareFilterApplyConfiguration { + b.ShareProto = &value + return b +} diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/shareimport.go b/pkg/clients/applyconfiguration/api/v1alpha1/shareimport.go new file mode 100644 index 000000000..c7552627d --- /dev/null +++ b/pkg/clients/applyconfiguration/api/v1alpha1/shareimport.go @@ -0,0 +1,48 @@ +/* +Copyright The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// ShareImportApplyConfiguration represents a declarative configuration of the ShareImport type for use +// with apply. +type ShareImportApplyConfiguration struct { + ID *string `json:"id,omitempty"` + Filter *ShareFilterApplyConfiguration `json:"filter,omitempty"` +} + +// ShareImportApplyConfiguration constructs a declarative configuration of the ShareImport type for use with +// apply. +func ShareImport() *ShareImportApplyConfiguration { + return &ShareImportApplyConfiguration{} +} + +// WithID sets the ID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ID field is set to the value of the last call. +func (b *ShareImportApplyConfiguration) WithID(value string) *ShareImportApplyConfiguration { + b.ID = &value + return b +} + +// WithFilter sets the Filter field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Filter field is set to the value of the last call. +func (b *ShareImportApplyConfiguration) WithFilter(value *ShareFilterApplyConfiguration) *ShareImportApplyConfiguration { + b.Filter = value + return b +} diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/shareresourcespec.go b/pkg/clients/applyconfiguration/api/v1alpha1/shareresourcespec.go new file mode 100644 index 000000000..84bc54919 --- /dev/null +++ b/pkg/clients/applyconfiguration/api/v1alpha1/shareresourcespec.go @@ -0,0 +1,121 @@ +/* +Copyright The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + apiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" +) + +// ShareResourceSpecApplyConfiguration represents a declarative configuration of the ShareResourceSpec type for use +// with apply. +type ShareResourceSpecApplyConfiguration struct { + Name *apiv1alpha1.OpenStackName `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + ShareProto *string `json:"shareProto,omitempty"` + Size *int32 `json:"size,omitempty"` + ShareNetworkRef *apiv1alpha1.KubernetesNameRef `json:"shareNetworkRef,omitempty"` + AvailabilityZone *string `json:"availabilityZone,omitempty"` + ShareType *string `json:"shareType,omitempty"` + Metadata map[string]string `json:"metadata,omitempty"` + IsPublic *bool `json:"isPublic,omitempty"` +} + +// ShareResourceSpecApplyConfiguration constructs a declarative configuration of the ShareResourceSpec type for use with +// apply. +func ShareResourceSpec() *ShareResourceSpecApplyConfiguration { + return &ShareResourceSpecApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *ShareResourceSpecApplyConfiguration) WithName(value apiv1alpha1.OpenStackName) *ShareResourceSpecApplyConfiguration { + b.Name = &value + return b +} + +// WithDescription sets the Description field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Description field is set to the value of the last call. +func (b *ShareResourceSpecApplyConfiguration) WithDescription(value string) *ShareResourceSpecApplyConfiguration { + b.Description = &value + return b +} + +// WithShareProto sets the ShareProto field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ShareProto field is set to the value of the last call. +func (b *ShareResourceSpecApplyConfiguration) WithShareProto(value string) *ShareResourceSpecApplyConfiguration { + b.ShareProto = &value + return b +} + +// WithSize sets the Size field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Size field is set to the value of the last call. +func (b *ShareResourceSpecApplyConfiguration) WithSize(value int32) *ShareResourceSpecApplyConfiguration { + b.Size = &value + return b +} + +// WithShareNetworkRef sets the ShareNetworkRef field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ShareNetworkRef field is set to the value of the last call. +func (b *ShareResourceSpecApplyConfiguration) WithShareNetworkRef(value apiv1alpha1.KubernetesNameRef) *ShareResourceSpecApplyConfiguration { + b.ShareNetworkRef = &value + return b +} + +// WithAvailabilityZone sets the AvailabilityZone field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the AvailabilityZone field is set to the value of the last call. +func (b *ShareResourceSpecApplyConfiguration) WithAvailabilityZone(value string) *ShareResourceSpecApplyConfiguration { + b.AvailabilityZone = &value + return b +} + +// WithShareType sets the ShareType field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ShareType field is set to the value of the last call. +func (b *ShareResourceSpecApplyConfiguration) WithShareType(value string) *ShareResourceSpecApplyConfiguration { + b.ShareType = &value + return b +} + +// WithMetadata puts the entries into the Metadata field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Metadata field, +// overwriting an existing map entries in Metadata field with the same key. +func (b *ShareResourceSpecApplyConfiguration) WithMetadata(entries map[string]string) *ShareResourceSpecApplyConfiguration { + if b.Metadata == nil && len(entries) > 0 { + b.Metadata = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.Metadata[k] = v + } + return b +} + +// WithIsPublic sets the IsPublic field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the IsPublic field is set to the value of the last call. +func (b *ShareResourceSpecApplyConfiguration) WithIsPublic(value bool) *ShareResourceSpecApplyConfiguration { + b.IsPublic = &value + return b +} diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/shareresourcestatus.go b/pkg/clients/applyconfiguration/api/v1alpha1/shareresourcestatus.go new file mode 100644 index 000000000..7e1c9e7ee --- /dev/null +++ b/pkg/clients/applyconfiguration/api/v1alpha1/shareresourcestatus.go @@ -0,0 +1,146 @@ +/* +Copyright The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// ShareResourceStatusApplyConfiguration represents a declarative configuration of the ShareResourceStatus type for use +// with apply. +type ShareResourceStatusApplyConfiguration struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + Status *string `json:"status,omitempty"` + ShareProto *string `json:"shareProto,omitempty"` + Size *int32 `json:"size,omitempty"` + AvailabilityZone *string `json:"availabilityZone,omitempty"` + ShareType *string `json:"shareType,omitempty"` + ShareTypeName *string `json:"shareTypeName,omitempty"` + ShareNetworkID *string `json:"shareNetworkID,omitempty"` + IsPublic *bool `json:"isPublic,omitempty"` + Metadata map[string]string `json:"metadata,omitempty"` + ExportLocations []string `json:"exportLocations,omitempty"` +} + +// ShareResourceStatusApplyConfiguration constructs a declarative configuration of the ShareResourceStatus type for use with +// apply. +func ShareResourceStatus() *ShareResourceStatusApplyConfiguration { + return &ShareResourceStatusApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *ShareResourceStatusApplyConfiguration) WithName(value string) *ShareResourceStatusApplyConfiguration { + b.Name = &value + return b +} + +// WithDescription sets the Description field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Description field is set to the value of the last call. +func (b *ShareResourceStatusApplyConfiguration) WithDescription(value string) *ShareResourceStatusApplyConfiguration { + b.Description = &value + return b +} + +// WithStatus sets the Status field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Status field is set to the value of the last call. +func (b *ShareResourceStatusApplyConfiguration) WithStatus(value string) *ShareResourceStatusApplyConfiguration { + b.Status = &value + return b +} + +// WithShareProto sets the ShareProto field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ShareProto field is set to the value of the last call. +func (b *ShareResourceStatusApplyConfiguration) WithShareProto(value string) *ShareResourceStatusApplyConfiguration { + b.ShareProto = &value + return b +} + +// WithSize sets the Size field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Size field is set to the value of the last call. +func (b *ShareResourceStatusApplyConfiguration) WithSize(value int32) *ShareResourceStatusApplyConfiguration { + b.Size = &value + return b +} + +// WithAvailabilityZone sets the AvailabilityZone field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the AvailabilityZone field is set to the value of the last call. +func (b *ShareResourceStatusApplyConfiguration) WithAvailabilityZone(value string) *ShareResourceStatusApplyConfiguration { + b.AvailabilityZone = &value + return b +} + +// WithShareType sets the ShareType field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ShareType field is set to the value of the last call. +func (b *ShareResourceStatusApplyConfiguration) WithShareType(value string) *ShareResourceStatusApplyConfiguration { + b.ShareType = &value + return b +} + +// WithShareTypeName sets the ShareTypeName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ShareTypeName field is set to the value of the last call. +func (b *ShareResourceStatusApplyConfiguration) WithShareTypeName(value string) *ShareResourceStatusApplyConfiguration { + b.ShareTypeName = &value + return b +} + +// WithShareNetworkID sets the ShareNetworkID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ShareNetworkID field is set to the value of the last call. +func (b *ShareResourceStatusApplyConfiguration) WithShareNetworkID(value string) *ShareResourceStatusApplyConfiguration { + b.ShareNetworkID = &value + return b +} + +// WithIsPublic sets the IsPublic field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the IsPublic field is set to the value of the last call. +func (b *ShareResourceStatusApplyConfiguration) WithIsPublic(value bool) *ShareResourceStatusApplyConfiguration { + b.IsPublic = &value + return b +} + +// WithMetadata puts the entries into the Metadata field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Metadata field, +// overwriting an existing map entries in Metadata field with the same key. +func (b *ShareResourceStatusApplyConfiguration) WithMetadata(entries map[string]string) *ShareResourceStatusApplyConfiguration { + if b.Metadata == nil && len(entries) > 0 { + b.Metadata = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.Metadata[k] = v + } + return b +} + +// WithExportLocations adds the given value to the ExportLocations field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the ExportLocations field. +func (b *ShareResourceStatusApplyConfiguration) WithExportLocations(values ...string) *ShareResourceStatusApplyConfiguration { + for i := range values { + b.ExportLocations = append(b.ExportLocations, values[i]) + } + return b +} diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/sharespec.go b/pkg/clients/applyconfiguration/api/v1alpha1/sharespec.go new file mode 100644 index 000000000..7c471af78 --- /dev/null +++ b/pkg/clients/applyconfiguration/api/v1alpha1/sharespec.go @@ -0,0 +1,79 @@ +/* +Copyright The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + apiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" +) + +// ShareSpecApplyConfiguration represents a declarative configuration of the ShareSpec type for use +// with apply. +type ShareSpecApplyConfiguration struct { + Import *ShareImportApplyConfiguration `json:"import,omitempty"` + Resource *ShareResourceSpecApplyConfiguration `json:"resource,omitempty"` + ManagementPolicy *apiv1alpha1.ManagementPolicy `json:"managementPolicy,omitempty"` + ManagedOptions *ManagedOptionsApplyConfiguration `json:"managedOptions,omitempty"` + CloudCredentialsRef *CloudCredentialsReferenceApplyConfiguration `json:"cloudCredentialsRef,omitempty"` +} + +// ShareSpecApplyConfiguration constructs a declarative configuration of the ShareSpec type for use with +// apply. +func ShareSpec() *ShareSpecApplyConfiguration { + return &ShareSpecApplyConfiguration{} +} + +// WithImport sets the Import field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Import field is set to the value of the last call. +func (b *ShareSpecApplyConfiguration) WithImport(value *ShareImportApplyConfiguration) *ShareSpecApplyConfiguration { + b.Import = value + return b +} + +// WithResource sets the Resource field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Resource field is set to the value of the last call. +func (b *ShareSpecApplyConfiguration) WithResource(value *ShareResourceSpecApplyConfiguration) *ShareSpecApplyConfiguration { + b.Resource = value + return b +} + +// WithManagementPolicy sets the ManagementPolicy field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ManagementPolicy field is set to the value of the last call. +func (b *ShareSpecApplyConfiguration) WithManagementPolicy(value apiv1alpha1.ManagementPolicy) *ShareSpecApplyConfiguration { + b.ManagementPolicy = &value + return b +} + +// WithManagedOptions sets the ManagedOptions field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ManagedOptions field is set to the value of the last call. +func (b *ShareSpecApplyConfiguration) WithManagedOptions(value *ManagedOptionsApplyConfiguration) *ShareSpecApplyConfiguration { + b.ManagedOptions = value + return b +} + +// WithCloudCredentialsRef sets the CloudCredentialsRef field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CloudCredentialsRef field is set to the value of the last call. +func (b *ShareSpecApplyConfiguration) WithCloudCredentialsRef(value *CloudCredentialsReferenceApplyConfiguration) *ShareSpecApplyConfiguration { + b.CloudCredentialsRef = value + return b +} diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/sharestatus.go b/pkg/clients/applyconfiguration/api/v1alpha1/sharestatus.go new file mode 100644 index 000000000..638a60902 --- /dev/null +++ b/pkg/clients/applyconfiguration/api/v1alpha1/sharestatus.go @@ -0,0 +1,66 @@ +/* +Copyright The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// ShareStatusApplyConfiguration represents a declarative configuration of the ShareStatus type for use +// with apply. +type ShareStatusApplyConfiguration struct { + Conditions []v1.ConditionApplyConfiguration `json:"conditions,omitempty"` + ID *string `json:"id,omitempty"` + Resource *ShareResourceStatusApplyConfiguration `json:"resource,omitempty"` +} + +// ShareStatusApplyConfiguration constructs a declarative configuration of the ShareStatus type for use with +// apply. +func ShareStatus() *ShareStatusApplyConfiguration { + return &ShareStatusApplyConfiguration{} +} + +// WithConditions adds the given value to the Conditions field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Conditions field. +func (b *ShareStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *ShareStatusApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithConditions") + } + b.Conditions = append(b.Conditions, *values[i]) + } + return b +} + +// WithID sets the ID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ID field is set to the value of the last call. +func (b *ShareStatusApplyConfiguration) WithID(value string) *ShareStatusApplyConfiguration { + b.ID = &value + return b +} + +// WithResource sets the Resource field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Resource field is set to the value of the last call. +func (b *ShareStatusApplyConfiguration) WithResource(value *ShareResourceStatusApplyConfiguration) *ShareStatusApplyConfiguration { + b.Resource = value + return b +} diff --git a/pkg/clients/applyconfiguration/internal/internal.go b/pkg/clients/applyconfiguration/internal/internal.go index 10461ed9c..4444b0a53 100644 --- a/pkg/clients/applyconfiguration/internal/internal.go +++ b/pkg/clients/applyconfiguration/internal/internal.go @@ -3144,6 +3144,51 @@ var schemaYAML = typed.YAMLObject(`types: - name: resource type: namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ServiceResourceStatus +- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.Share + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + default: {} + - name: spec + type: + namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ShareSpec + default: {} + - name: status + type: + namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ShareStatus + default: {} +- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ShareFilter + map: + fields: + - name: description + type: + scalar: string + - name: name + type: + scalar: string + - name: shareProto + type: + scalar: string + - name: status + type: + scalar: string +- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ShareImport + map: + fields: + - name: filter + type: + namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ShareFilter + - name: id + type: + scalar: string - name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ShareNetwork map: fields: @@ -3271,6 +3316,119 @@ var schemaYAML = typed.YAMLObject(`types: - name: resource type: namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ShareNetworkResourceStatus +- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ShareResourceSpec + map: + fields: + - name: availabilityZone + type: + scalar: string + - name: description + type: + scalar: string + - name: isPublic + type: + scalar: boolean + - name: metadata + type: + map: + elementType: + scalar: string + - name: name + type: + scalar: string + - name: shareNetworkRef + type: + scalar: string + - name: shareProto + type: + scalar: string + default: "" + - name: shareType + type: + scalar: string + - name: size + type: + scalar: numeric +- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ShareResourceStatus + map: + fields: + - name: availabilityZone + type: + scalar: string + - name: description + type: + scalar: string + - name: exportLocations + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: isPublic + type: + scalar: boolean + - name: metadata + type: + map: + elementType: + scalar: string + - name: name + type: + scalar: string + - name: shareNetworkID + type: + scalar: string + - name: shareProto + type: + scalar: string + - name: shareType + type: + scalar: string + - name: shareTypeName + type: + scalar: string + - name: size + type: + scalar: numeric + - name: status + type: + scalar: string +- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ShareSpec + map: + fields: + - name: cloudCredentialsRef + type: + namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.CloudCredentialsReference + default: {} + - name: import + type: + namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ShareImport + - name: managedOptions + type: + namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ManagedOptions + - name: managementPolicy + type: + scalar: string + - name: resource + type: + namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ShareResourceSpec +- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ShareStatus + map: + fields: + - name: conditions + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Condition + elementRelationship: associative + keys: + - type + - name: id + type: + scalar: string + - name: resource + type: + namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ShareResourceStatus - name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.Subnet map: fields: diff --git a/pkg/clients/applyconfiguration/utils.go b/pkg/clients/applyconfiguration/utils.go index 5d8f68cd9..1c03c56d2 100644 --- a/pkg/clients/applyconfiguration/utils.go +++ b/pkg/clients/applyconfiguration/utils.go @@ -380,6 +380,12 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &apiv1alpha1.ServiceSpecApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("ServiceStatus"): return &apiv1alpha1.ServiceStatusApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("Share"): + return &apiv1alpha1.ShareApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("ShareFilter"): + return &apiv1alpha1.ShareFilterApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("ShareImport"): + return &apiv1alpha1.ShareImportApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("ShareNetwork"): return &apiv1alpha1.ShareNetworkApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("ShareNetworkFilter"): @@ -394,6 +400,14 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &apiv1alpha1.ShareNetworkSpecApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("ShareNetworkStatus"): return &apiv1alpha1.ShareNetworkStatusApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("ShareResourceSpec"): + return &apiv1alpha1.ShareResourceSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("ShareResourceStatus"): + return &apiv1alpha1.ShareResourceStatusApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("ShareSpec"): + return &apiv1alpha1.ShareSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("ShareStatus"): + return &apiv1alpha1.ShareStatusApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("Subnet"): return &apiv1alpha1.SubnetApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("SubnetFilter"): diff --git a/pkg/clients/clientset/clientset/typed/api/v1alpha1/api_client.go b/pkg/clients/clientset/clientset/typed/api/v1alpha1/api_client.go index d5c517b1e..e7bbb46d2 100644 --- a/pkg/clients/clientset/clientset/typed/api/v1alpha1/api_client.go +++ b/pkg/clients/clientset/clientset/typed/api/v1alpha1/api_client.go @@ -47,6 +47,7 @@ type OpenstackV1alpha1Interface interface { ServersGetter ServerGroupsGetter ServicesGetter + SharesGetter ShareNetworksGetter SubnetsGetter TrunksGetter @@ -136,6 +137,10 @@ func (c *OpenstackV1alpha1Client) Services(namespace string) ServiceInterface { return newServices(c, namespace) } +func (c *OpenstackV1alpha1Client) Shares(namespace string) ShareInterface { + return newShares(c, namespace) +} + func (c *OpenstackV1alpha1Client) ShareNetworks(namespace string) ShareNetworkInterface { return newShareNetworks(c, namespace) } diff --git a/pkg/clients/clientset/clientset/typed/api/v1alpha1/fake/fake_api_client.go b/pkg/clients/clientset/clientset/typed/api/v1alpha1/fake/fake_api_client.go index f5dcb5da4..23667f457 100644 --- a/pkg/clients/clientset/clientset/typed/api/v1alpha1/fake/fake_api_client.go +++ b/pkg/clients/clientset/clientset/typed/api/v1alpha1/fake/fake_api_client.go @@ -104,6 +104,10 @@ func (c *FakeOpenstackV1alpha1) Services(namespace string) v1alpha1.ServiceInter return newFakeServices(c, namespace) } +func (c *FakeOpenstackV1alpha1) Shares(namespace string) v1alpha1.ShareInterface { + return newFakeShares(c, namespace) +} + func (c *FakeOpenstackV1alpha1) ShareNetworks(namespace string) v1alpha1.ShareNetworkInterface { return newFakeShareNetworks(c, namespace) } diff --git a/pkg/clients/clientset/clientset/typed/api/v1alpha1/fake/fake_share.go b/pkg/clients/clientset/clientset/typed/api/v1alpha1/fake/fake_share.go new file mode 100644 index 000000000..ed44a326a --- /dev/null +++ b/pkg/clients/clientset/clientset/typed/api/v1alpha1/fake/fake_share.go @@ -0,0 +1,49 @@ +/* +Copyright The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + apiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/pkg/clients/applyconfiguration/api/v1alpha1" + typedapiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/pkg/clients/clientset/clientset/typed/api/v1alpha1" + gentype "k8s.io/client-go/gentype" +) + +// fakeShares implements ShareInterface +type fakeShares struct { + *gentype.FakeClientWithListAndApply[*v1alpha1.Share, *v1alpha1.ShareList, *apiv1alpha1.ShareApplyConfiguration] + Fake *FakeOpenstackV1alpha1 +} + +func newFakeShares(fake *FakeOpenstackV1alpha1, namespace string) typedapiv1alpha1.ShareInterface { + return &fakeShares{ + gentype.NewFakeClientWithListAndApply[*v1alpha1.Share, *v1alpha1.ShareList, *apiv1alpha1.ShareApplyConfiguration]( + fake.Fake, + namespace, + v1alpha1.SchemeGroupVersion.WithResource("shares"), + v1alpha1.SchemeGroupVersion.WithKind("Share"), + func() *v1alpha1.Share { return &v1alpha1.Share{} }, + func() *v1alpha1.ShareList { return &v1alpha1.ShareList{} }, + func(dst, src *v1alpha1.ShareList) { dst.ListMeta = src.ListMeta }, + func(list *v1alpha1.ShareList) []*v1alpha1.Share { return gentype.ToPointerSlice(list.Items) }, + func(list *v1alpha1.ShareList, items []*v1alpha1.Share) { list.Items = gentype.FromPointerSlice(items) }, + ), + fake, + } +} diff --git a/pkg/clients/clientset/clientset/typed/api/v1alpha1/generated_expansion.go b/pkg/clients/clientset/clientset/typed/api/v1alpha1/generated_expansion.go index e13858a9c..1defc2c3f 100644 --- a/pkg/clients/clientset/clientset/typed/api/v1alpha1/generated_expansion.go +++ b/pkg/clients/clientset/clientset/typed/api/v1alpha1/generated_expansion.go @@ -56,6 +56,8 @@ type ServerGroupExpansion interface{} type ServiceExpansion interface{} +type ShareExpansion interface{} + type ShareNetworkExpansion interface{} type SubnetExpansion interface{} diff --git a/pkg/clients/clientset/clientset/typed/api/v1alpha1/share.go b/pkg/clients/clientset/clientset/typed/api/v1alpha1/share.go new file mode 100644 index 000000000..e79cde3d9 --- /dev/null +++ b/pkg/clients/clientset/clientset/typed/api/v1alpha1/share.go @@ -0,0 +1,74 @@ +/* +Copyright The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + context "context" + + apiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + applyconfigurationapiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/pkg/clients/applyconfiguration/api/v1alpha1" + scheme "github.com/k-orc/openstack-resource-controller/v2/pkg/clients/clientset/clientset/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + gentype "k8s.io/client-go/gentype" +) + +// SharesGetter has a method to return a ShareInterface. +// A group's client should implement this interface. +type SharesGetter interface { + Shares(namespace string) ShareInterface +} + +// ShareInterface has methods to work with Share resources. +type ShareInterface interface { + Create(ctx context.Context, share *apiv1alpha1.Share, opts v1.CreateOptions) (*apiv1alpha1.Share, error) + Update(ctx context.Context, share *apiv1alpha1.Share, opts v1.UpdateOptions) (*apiv1alpha1.Share, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, share *apiv1alpha1.Share, opts v1.UpdateOptions) (*apiv1alpha1.Share, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*apiv1alpha1.Share, error) + List(ctx context.Context, opts v1.ListOptions) (*apiv1alpha1.ShareList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *apiv1alpha1.Share, err error) + Apply(ctx context.Context, share *applyconfigurationapiv1alpha1.ShareApplyConfiguration, opts v1.ApplyOptions) (result *apiv1alpha1.Share, err error) + // Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus(). + ApplyStatus(ctx context.Context, share *applyconfigurationapiv1alpha1.ShareApplyConfiguration, opts v1.ApplyOptions) (result *apiv1alpha1.Share, err error) + ShareExpansion +} + +// shares implements ShareInterface +type shares struct { + *gentype.ClientWithListAndApply[*apiv1alpha1.Share, *apiv1alpha1.ShareList, *applyconfigurationapiv1alpha1.ShareApplyConfiguration] +} + +// newShares returns a Shares +func newShares(c *OpenstackV1alpha1Client, namespace string) *shares { + return &shares{ + gentype.NewClientWithListAndApply[*apiv1alpha1.Share, *apiv1alpha1.ShareList, *applyconfigurationapiv1alpha1.ShareApplyConfiguration]( + "shares", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *apiv1alpha1.Share { return &apiv1alpha1.Share{} }, + func() *apiv1alpha1.ShareList { return &apiv1alpha1.ShareList{} }, + ), + } +} diff --git a/pkg/clients/informers/externalversions/api/v1alpha1/interface.go b/pkg/clients/informers/externalversions/api/v1alpha1/interface.go index 2e9f92392..c9eef4a4d 100644 --- a/pkg/clients/informers/externalversions/api/v1alpha1/interface.go +++ b/pkg/clients/informers/externalversions/api/v1alpha1/interface.go @@ -62,6 +62,8 @@ type Interface interface { ServerGroups() ServerGroupInformer // Services returns a ServiceInformer. Services() ServiceInformer + // Shares returns a ShareInformer. + Shares() ShareInformer // ShareNetworks returns a ShareNetworkInformer. ShareNetworks() ShareNetworkInformer // Subnets returns a SubnetInformer. @@ -182,6 +184,11 @@ func (v *version) Services() ServiceInformer { return &serviceInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } +// Shares returns a ShareInformer. +func (v *version) Shares() ShareInformer { + return &shareInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // ShareNetworks returns a ShareNetworkInformer. func (v *version) ShareNetworks() ShareNetworkInformer { return &shareNetworkInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/pkg/clients/informers/externalversions/api/v1alpha1/share.go b/pkg/clients/informers/externalversions/api/v1alpha1/share.go new file mode 100644 index 000000000..ad703d511 --- /dev/null +++ b/pkg/clients/informers/externalversions/api/v1alpha1/share.go @@ -0,0 +1,102 @@ +/* +Copyright The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + context "context" + time "time" + + v2apiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + clientset "github.com/k-orc/openstack-resource-controller/v2/pkg/clients/clientset/clientset" + internalinterfaces "github.com/k-orc/openstack-resource-controller/v2/pkg/clients/informers/externalversions/internalinterfaces" + apiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/pkg/clients/listers/api/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// ShareInformer provides access to a shared informer and lister for +// Shares. +type ShareInformer interface { + Informer() cache.SharedIndexInformer + Lister() apiv1alpha1.ShareLister +} + +type shareInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewShareInformer constructs a new informer for Share type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewShareInformer(client clientset.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredShareInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredShareInformer constructs a new informer for Share type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredShareInformer(client clientset.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.OpenstackV1alpha1().Shares(namespace).List(context.Background(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.OpenstackV1alpha1().Shares(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.OpenstackV1alpha1().Shares(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.OpenstackV1alpha1().Shares(namespace).Watch(ctx, options) + }, + }, + &v2apiv1alpha1.Share{}, + resyncPeriod, + indexers, + ) +} + +func (f *shareInformer) defaultInformer(client clientset.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredShareInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *shareInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&v2apiv1alpha1.Share{}, f.defaultInformer) +} + +func (f *shareInformer) Lister() apiv1alpha1.ShareLister { + return apiv1alpha1.NewShareLister(f.Informer().GetIndexer()) +} diff --git a/pkg/clients/informers/externalversions/generic.go b/pkg/clients/informers/externalversions/generic.go index fb3637f1e..2f0c70f6d 100644 --- a/pkg/clients/informers/externalversions/generic.go +++ b/pkg/clients/informers/externalversions/generic.go @@ -91,6 +91,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Openstack().V1alpha1().ServerGroups().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("services"): return &genericInformer{resource: resource.GroupResource(), informer: f.Openstack().V1alpha1().Services().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("shares"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Openstack().V1alpha1().Shares().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("sharenetworks"): return &genericInformer{resource: resource.GroupResource(), informer: f.Openstack().V1alpha1().ShareNetworks().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("subnets"): diff --git a/pkg/clients/listers/api/v1alpha1/expansion_generated.go b/pkg/clients/listers/api/v1alpha1/expansion_generated.go index 4d7043581..25eca7070 100644 --- a/pkg/clients/listers/api/v1alpha1/expansion_generated.go +++ b/pkg/clients/listers/api/v1alpha1/expansion_generated.go @@ -170,6 +170,14 @@ type ServiceListerExpansion interface{} // ServiceNamespaceLister. type ServiceNamespaceListerExpansion interface{} +// ShareListerExpansion allows custom methods to be added to +// ShareLister. +type ShareListerExpansion interface{} + +// ShareNamespaceListerExpansion allows custom methods to be added to +// ShareNamespaceLister. +type ShareNamespaceListerExpansion interface{} + // ShareNetworkListerExpansion allows custom methods to be added to // ShareNetworkLister. type ShareNetworkListerExpansion interface{} diff --git a/pkg/clients/listers/api/v1alpha1/share.go b/pkg/clients/listers/api/v1alpha1/share.go new file mode 100644 index 000000000..53fe71da2 --- /dev/null +++ b/pkg/clients/listers/api/v1alpha1/share.go @@ -0,0 +1,70 @@ +/* +Copyright The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + apiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" +) + +// ShareLister helps list Shares. +// All objects returned here must be treated as read-only. +type ShareLister interface { + // List lists all Shares in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*apiv1alpha1.Share, err error) + // Shares returns an object that can list and get Shares. + Shares(namespace string) ShareNamespaceLister + ShareListerExpansion +} + +// shareLister implements the ShareLister interface. +type shareLister struct { + listers.ResourceIndexer[*apiv1alpha1.Share] +} + +// NewShareLister returns a new ShareLister. +func NewShareLister(indexer cache.Indexer) ShareLister { + return &shareLister{listers.New[*apiv1alpha1.Share](indexer, apiv1alpha1.Resource("share"))} +} + +// Shares returns an object that can list and get Shares. +func (s *shareLister) Shares(namespace string) ShareNamespaceLister { + return shareNamespaceLister{listers.NewNamespaced[*apiv1alpha1.Share](s.ResourceIndexer, namespace)} +} + +// ShareNamespaceLister helps list and get Shares. +// All objects returned here must be treated as read-only. +type ShareNamespaceLister interface { + // List lists all Shares in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*apiv1alpha1.Share, err error) + // Get retrieves the Share from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*apiv1alpha1.Share, error) + ShareNamespaceListerExpansion +} + +// shareNamespaceLister implements the ShareNamespaceLister +// interface. +type shareNamespaceLister struct { + listers.ResourceIndexer[*apiv1alpha1.Share] +} diff --git a/test/apivalidations/share_test.go b/test/apivalidations/share_test.go new file mode 100644 index 000000000..70e53b5e8 --- /dev/null +++ b/test/apivalidations/share_test.go @@ -0,0 +1,122 @@ +/* +Copyright The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package apivalidations + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + applyconfigv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/pkg/clients/applyconfiguration/api/v1alpha1" +) + +const ( + shareName = "share" + shareID = "265c9e4f-0f5a-46e4-9f3f-fb8de25ae120" +) + +func shareStub(namespace *corev1.Namespace) *orcv1alpha1.Share { + obj := &orcv1alpha1.Share{} + obj.Name = shareName + obj.Namespace = namespace.Name + return obj +} + +func testShareResource() *applyconfigv1alpha1.ShareResourceSpecApplyConfiguration { + return applyconfigv1alpha1.ShareResourceSpec(). + WithShareProto("NFS"). + WithSize(1) +} + +func baseSharePatch(obj client.Object) *applyconfigv1alpha1.ShareApplyConfiguration { + return applyconfigv1alpha1.Share(obj.GetName(), obj.GetNamespace()). + WithSpec(applyconfigv1alpha1.ShareSpec(). + WithCloudCredentialsRef(testCredentials())) +} + +func testShareImport() *applyconfigv1alpha1.ShareImportApplyConfiguration { + return applyconfigv1alpha1.ShareImport().WithID(shareID) +} + +var _ = Describe("ORC Share API validations", func() { + var namespace *corev1.Namespace + BeforeEach(func() { + namespace = createNamespace() + }) + + runManagementPolicyTests(func() *corev1.Namespace { return namespace }, managementPolicyTestArgs[*applyconfigv1alpha1.ShareApplyConfiguration]{ + createObject: func(ns *corev1.Namespace) client.Object { return shareStub(ns) }, + basePatch: func(obj client.Object) *applyconfigv1alpha1.ShareApplyConfiguration { + return baseSharePatch(obj) + }, + applyResource: func(p *applyconfigv1alpha1.ShareApplyConfiguration) { + p.Spec.WithResource(testShareResource()) + }, + applyImport: func(p *applyconfigv1alpha1.ShareApplyConfiguration) { + p.Spec.WithImport(testShareImport()) + }, + applyEmptyImport: func(p *applyconfigv1alpha1.ShareApplyConfiguration) { + p.Spec.WithImport(applyconfigv1alpha1.ShareImport()) + }, + applyEmptyFilter: func(p *applyconfigv1alpha1.ShareApplyConfiguration) { + p.Spec.WithImport(applyconfigv1alpha1.ShareImport().WithFilter(applyconfigv1alpha1.ShareFilter())) + }, + applyValidFilter: func(p *applyconfigv1alpha1.ShareApplyConfiguration) { + p.Spec.WithImport(applyconfigv1alpha1.ShareImport().WithFilter(applyconfigv1alpha1.ShareFilter().WithName("foo"))) + }, + applyManaged: func(p *applyconfigv1alpha1.ShareApplyConfiguration) { + p.Spec.WithManagementPolicy(orcv1alpha1.ManagementPolicyManaged) + }, + applyUnmanaged: func(p *applyconfigv1alpha1.ShareApplyConfiguration) { + p.Spec.WithManagementPolicy(orcv1alpha1.ManagementPolicyUnmanaged) + }, + applyManagedOptions: func(p *applyconfigv1alpha1.ShareApplyConfiguration) { + p.Spec.WithManagedOptions(applyconfigv1alpha1.ManagedOptions().WithOnDelete(orcv1alpha1.OnDeleteDetach)) + }, + getManagementPolicy: func(obj client.Object) orcv1alpha1.ManagementPolicy { + return obj.(*orcv1alpha1.Share).Spec.ManagementPolicy + }, + getOnDelete: func(obj client.Object) orcv1alpha1.OnDelete { + return obj.(*orcv1alpha1.Share).Spec.ManagedOptions.OnDelete + }, + }) + + It("should have immutable shareNetworkRef", func(ctx context.Context) { + obj := shareStub(namespace) + patch := baseSharePatch(obj) + patch.Spec.WithResource(testShareResource(). + WithShareNetworkRef("sharenetwork-a")) + Expect(applyObj(ctx, obj, patch)).To(Succeed()) + + patch.Spec.WithResource(testShareResource(). + WithShareNetworkRef("sharenetwork-b")) + Expect(applyObj(ctx, obj, patch)).To(MatchError(ContainSubstring("shareNetworkRef is immutable"))) + }) + + // TODO(scaffolding): Add more resource-specific validation tests. + // Some common things to test: + // - Immutability of fields with `self == oldSelf` validation + // - Enum validation (valid and invalid values) + // - Numeric range validation (min/max bounds) + // - Tag uniqueness (if the resource has tags with listType=set) + // - Format validation (CIDR, UUID, etc.) + // - Cross-field validation rules +}) diff --git a/website/docs/crd-reference.md b/website/docs/crd-reference.md index de769a2d0..56d9cbf6f 100644 --- a/website/docs/crd-reference.md +++ b/website/docs/crd-reference.md @@ -29,6 +29,7 @@ Package v1alpha1 contains API Schema definitions for the openstack v1alpha1 API - [Server](#server) - [ServerGroup](#servergroup) - [Service](#service) +- [Share](#share) - [ShareNetwork](#sharenetwork) - [Subnet](#subnet) - [Trunk](#trunk) @@ -521,6 +522,7 @@ _Appears in:_ - [ServerSpec](#serverspec) - [ServiceSpec](#servicespec) - [ShareNetworkSpec](#sharenetworkspec) +- [ShareSpec](#sharespec) - [SubnetSpec](#subnetspec) - [TrunkSpec](#trunkspec) - [UserSpec](#userspec) @@ -2173,6 +2175,7 @@ _Appears in:_ - [ServerSchedulerHints](#serverschedulerhints) - [ServerVolumeSpec](#servervolumespec) - [ShareNetworkResourceSpec](#sharenetworkresourcespec) +- [ShareResourceSpec](#shareresourcespec) - [SubnetFilter](#subnetfilter) - [SubnetResourceSpec](#subnetresourcespec) - [TrunkFilter](#trunkfilter) @@ -2243,6 +2246,7 @@ _Appears in:_ - [ServerSpec](#serverspec) - [ServiceSpec](#servicespec) - [ShareNetworkSpec](#sharenetworkspec) +- [ShareSpec](#sharespec) - [SubnetSpec](#subnetspec) - [TrunkSpec](#trunkspec) - [UserSpec](#userspec) @@ -2283,6 +2287,7 @@ _Appears in:_ - [ServerSpec](#serverspec) - [ServiceSpec](#servicespec) - [ShareNetworkSpec](#sharenetworkspec) +- [ShareSpec](#sharespec) - [SubnetSpec](#subnetspec) - [TrunkSpec](#trunkspec) - [UserSpec](#userspec) @@ -2592,8 +2597,10 @@ _Appears in:_ - [ServerResourceSpec](#serverresourcespec) - [ServiceFilter](#servicefilter) - [ServiceResourceSpec](#serviceresourcespec) +- [ShareFilter](#sharefilter) - [ShareNetworkFilter](#sharenetworkfilter) - [ShareNetworkResourceSpec](#sharenetworkresourcespec) +- [ShareResourceSpec](#shareresourcespec) - [SubnetFilter](#subnetfilter) - [SubnetResourceSpec](#subnetresourcespec) - [TrunkFilter](#trunkfilter) @@ -4262,6 +4269,65 @@ _Appears in:_ | `resource` _[ServiceResourceStatus](#serviceresourcestatus)_ | resource contains the observed state of the OpenStack resource. | | Optional: \{\}
| +#### Share + + + +Share is the Schema for an ORC resource. + + + + + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `apiVersion` _string_ | `openstack.k-orc.cloud/v1alpha1` | | | +| `kind` _string_ | `Share` | | | +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | Optional: \{\}
| +| `spec` _[ShareSpec](#sharespec)_ | spec specifies the desired state of the resource. | | Required: \{\}
| +| `status` _[ShareStatus](#sharestatus)_ | status defines the observed state of the resource. | | Optional: \{\}
| + + +#### ShareFilter + + + +ShareFilter defines an existing resource by its properties + +_Validation:_ +- MinProperties: 1 + +_Appears in:_ +- [ShareImport](#shareimport) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `name` _[OpenStackName](#openstackname)_ | name of the existing resource | | MaxLength: 255
MinLength: 1
Pattern: `^[^,]+$`
Optional: \{\}
| +| `description` _string_ | description of the existing resource | | MaxLength: 255
MinLength: 1
Optional: \{\}
| +| `status` _string_ | status filters by share status | | MaxLength: 255
Optional: \{\}
| +| `shareProto` _string_ | shareProto filters by share protocol | | MaxLength: 255
Optional: \{\}
| + + +#### ShareImport + + + +ShareImport specifies an existing resource which will be imported instead of +creating a new one + +_Validation:_ +- MaxProperties: 1 +- MinProperties: 1 + +_Appears in:_ +- [ShareSpec](#sharespec) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `id` _string_ | id contains the unique identifier of an existing OpenStack resource. Note
that when specifying an import by ID, the resource MUST already exist.
The ORC object will enter an error state if the resource does not exist. | | Format: uuid
MaxLength: 36
Optional: \{\}
| +| `filter` _[ShareFilter](#sharefilter)_ | filter contains a resource query which is expected to return a single
result. The controller will continue to retry if filter returns no
results. If filter returns multiple results the controller will set an
error state and will not continue to retry. | | MinProperties: 1
Optional: \{\}
| + + #### ShareNetwork @@ -4402,6 +4468,95 @@ _Appears in:_ | `resource` _[ShareNetworkResourceStatus](#sharenetworkresourcestatus)_ | resource contains the observed state of the OpenStack resource. | | Optional: \{\}
| +#### ShareResourceSpec + + + +ShareResourceSpec contains the desired state of the resource. + + + +_Appears in:_ +- [ShareSpec](#sharespec) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `name` _[OpenStackName](#openstackname)_ | name will be the name of the created resource. If not specified, the
name of the ORC object will be used. | | MaxLength: 255
MinLength: 1
Pattern: `^[^,]+$`
Optional: \{\}
| +| `description` _string_ | description is a human-readable description for the resource. | | MaxLength: 255
MinLength: 1
Optional: \{\}
| +| `shareProto` _string_ | shareProto is the shared file system protocol (e.g., NFS, CIFS). | | MaxLength: 255
Required: \{\}
| +| `size` _integer_ | size is the size of the share in GB. | | Minimum: 1
Required: \{\}
| +| `shareNetworkRef` _[KubernetesNameRef](#kubernetesnameref)_ | shareNetworkRef is a reference to the ORC ShareNetwork which this resource is associated with. | | MaxLength: 253
MinLength: 1
Optional: \{\}
| +| `availabilityZone` _string_ | availabilityZone is the availability zone in which to create the share. | | MaxLength: 255
Optional: \{\}
| +| `shareType` _string_ | shareType is the share type to use. If not specified, the default share type is used. | | MaxLength: 255
Optional: \{\}
| +| `metadata` _object (keys:string, values:string)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | Optional: \{\}
| +| `isPublic` _boolean_ | isPublic determines whether the share is public. | | Optional: \{\}
| + + +#### ShareResourceStatus + + + +ShareResourceStatus represents the observed state of the resource. + + + +_Appears in:_ +- [ShareStatus](#sharestatus) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `name` _string_ | name is a Human-readable name for the resource. Might not be unique. | | MaxLength: 1024
Optional: \{\}
| +| `description` _string_ | description is a human-readable description for the resource. | | MaxLength: 1024
Optional: \{\}
| +| `status` _string_ | status is the current status of the share. | | MaxLength: 1024
Optional: \{\}
| +| `shareProto` _string_ | shareProto is the shared file system protocol. | | MaxLength: 1024
Optional: \{\}
| +| `size` _integer_ | size is the size of the share in GB. | | Optional: \{\}
| +| `availabilityZone` _string_ | availabilityZone is the availability zone of the share. | | MaxLength: 1024
Optional: \{\}
| +| `shareType` _string_ | shareType is the UUID of the share type. | | MaxLength: 1024
Optional: \{\}
| +| `shareTypeName` _string_ | shareTypeName is the name of the share type. | | MaxLength: 1024
Optional: \{\}
| +| `shareNetworkID` _string_ | shareNetworkID is the ID of the ShareNetwork to which the resource is associated. | | MaxLength: 1024
Optional: \{\}
| +| `isPublic` _boolean_ | isPublic indicates the visibility of the share. | | Optional: \{\}
| +| `metadata` _object (keys:string, values:string)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | Optional: \{\}
| +| `exportLocations` _string array_ | exportLocations contains the export locations for mounting the share. | | MaxItems: 100
items:MaxLength: 1024
Optional: \{\}
| + + +#### ShareSpec + + + +ShareSpec defines the desired state of an ORC object. + + + +_Appears in:_ +- [Share](#share) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `import` _[ShareImport](#shareimport)_ | import refers to an existing OpenStack resource which will be imported instead of
creating a new one. | | MaxProperties: 1
MinProperties: 1
Optional: \{\}
| +| `resource` _[ShareResourceSpec](#shareresourcespec)_ | resource specifies the desired state of the resource.
resource may not be specified if the management policy is `unmanaged`.
resource must be specified if the management policy is `managed`. | | Optional: \{\}
| +| `managementPolicy` _[ManagementPolicy](#managementpolicy)_ | managementPolicy defines how ORC will treat the object. Valid values are
`managed`: ORC will create, update, and delete the resource; `unmanaged`:
ORC will import an existing resource, and will not apply updates to it or
delete it. | managed | Enum: [managed unmanaged]
Optional: \{\}
| +| `managedOptions` _[ManagedOptions](#managedoptions)_ | managedOptions specifies options which may be applied to managed objects. | | Optional: \{\}
| +| `cloudCredentialsRef` _[CloudCredentialsReference](#cloudcredentialsreference)_ | cloudCredentialsRef points to a secret containing OpenStack credentials | | Required: \{\}
| + + +#### ShareStatus + + + +ShareStatus defines the observed state of an ORC resource. + + + +_Appears in:_ +- [Share](#share) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#condition-v1-meta) array_ | conditions represents the observed status of the object.
Known .status.conditions.type are: "Available", "Progressing"
Available represents the availability of the OpenStack resource. If it is
true then the resource is ready for use.
Progressing indicates whether the controller is still attempting to
reconcile the current state of the OpenStack resource to the desired
state. Progressing will be False either because the desired state has
been achieved, or because some terminal error prevents it from ever being
achieved and the controller is no longer attempting to reconcile. If
Progressing is True, an observer waiting on the resource should continue
to wait. | | MaxItems: 32
Optional: \{\}
| +| `id` _string_ | id is the unique identifier of the OpenStack resource. | | MaxLength: 1024
Optional: \{\}
| +| `resource` _[ShareResourceStatus](#shareresourcestatus)_ | resource contains the observed state of the OpenStack resource. | | Optional: \{\}
| + + #### Subnet