diff --git a/apis/nas/v1alpha1/nas_fs_types.go b/apis/nas/v1alpha1/nas_fs_types.go index 909e863..cc3a541 100644 --- a/apis/nas/v1alpha1/nas_fs_types.go +++ b/apis/nas/v1alpha1/nas_fs_types.go @@ -33,9 +33,9 @@ type NASFileSystemList struct { // +kubebuilder:object:root=true // NASFileSystem is a managed resource that represents an NASFileSystem instance +// +kubebuilder:printcolumn:name="FILE-SYSTEM-ID",type="string",JSONPath=".status.atProvider.fileSystemID" // +kubebuilder:printcolumn:name="READY",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status" // +kubebuilder:printcolumn:name="SYNCED",type="string",JSONPath=".status.conditions[?(@.type=='Synced')].status" -// +kubebuilder:printcolumn:name="WARNING",type="string",JSONPath=".status.atProvider.message" // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" // +kubebuilder:subresource:status // +kubebuilder:resource:scope=Cluster,categories={crossplane,managed,alibaba} diff --git a/apis/nas/v1alpha1/zz_generated.deepcopy.go b/apis/nas/v1alpha1/zz_generated.deepcopy.go index e99ffda..48a8d38 100644 --- a/apis/nas/v1alpha1/zz_generated.deepcopy.go +++ b/apis/nas/v1alpha1/zz_generated.deepcopy.go @@ -101,6 +101,36 @@ func (in *NASFileSystemObservation) DeepCopy() *NASFileSystemObservation { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NASFileSystemParameter) DeepCopyInto(out *NASFileSystemParameter) { *out = *in + if in.FileSystemType != nil { + in, out := &in.FileSystemType, &out.FileSystemType + *out = new(string) + **out = **in + } + if in.ChargeType != nil { + in, out := &in.ChargeType, &out.ChargeType + *out = new(string) + **out = **in + } + if in.StorageType != nil { + in, out := &in.StorageType, &out.StorageType + *out = new(string) + **out = **in + } + if in.ProtocolType != nil { + in, out := &in.ProtocolType, &out.ProtocolType + *out = new(string) + **out = **in + } + if in.VpcID != nil { + in, out := &in.VpcID, &out.VpcID + *out = new(string) + **out = **in + } + if in.VSwitchID != nil { + in, out := &in.VSwitchID, &out.VSwitchID + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NASFileSystemParameter. @@ -117,7 +147,7 @@ func (in *NASFileSystemParameter) DeepCopy() *NASFileSystemParameter { func (in *NASFileSystemSpec) DeepCopyInto(out *NASFileSystemSpec) { *out = *in in.ResourceSpec.DeepCopyInto(&out.ResourceSpec) - out.NASFileSystemParameter = in.NASFileSystemParameter + in.NASFileSystemParameter.DeepCopyInto(&out.NASFileSystemParameter) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NASFileSystemSpec. diff --git a/examples/nas/nas-filesystem.yaml b/examples/nas/nas-filesystem.yaml new file mode 100644 index 0000000..45f8748 --- /dev/null +++ b/examples/nas/nas-filesystem.yaml @@ -0,0 +1,11 @@ +apiVersion: nas.alibaba.crossplane.io/v1alpha1 +kind: NASFileSystem +metadata: + name: na-filesystem-test + namespace: default +spec: + storageType: Performance + protocolType: NFS + writeConnectionSecretToRef: + name: nas-endpoint + namespace: default diff --git a/package/crds/nas.alibaba.crossplane.io_nasfilesystems.yaml b/package/crds/nas.alibaba.crossplane.io_nasfilesystems.yaml index d8e2eef..3ba37ad 100644 --- a/package/crds/nas.alibaba.crossplane.io_nasfilesystems.yaml +++ b/package/crds/nas.alibaba.crossplane.io_nasfilesystems.yaml @@ -19,15 +19,15 @@ spec: scope: Cluster versions: - additionalPrinterColumns: + - jsonPath: .status.atProvider.fileSystemID + name: FILE-SYSTEM-ID + type: string - jsonPath: .status.conditions[?(@.type=='Ready')].status name: READY type: string - jsonPath: .status.conditions[?(@.type=='Synced')].status name: SYNCED type: string - - jsonPath: .status.atProvider.message - name: WARNING - type: string - jsonPath: .metadata.creationTimestamp name: AGE type: date @@ -57,6 +57,8 @@ spec: type: string fileSystemType: type: string + protocolType: + type: string providerConfigRef: description: ProviderConfigReference specifies how the provider that will be used to create, observe, update, and delete this managed resource should be configured. properties: @@ -75,9 +77,11 @@ spec: required: - name type: object + storageType: + type: string vSwitchId: type: string - vpcDd: + vpcId: type: string writeConnectionSecretToRef: description: WriteConnectionSecretToReference specifies the namespace and name of a Secret to which any connection details for this managed resource should be written. Connection details frequently include the endpoint, username, and password required to connect to the managed resource. @@ -92,6 +96,9 @@ spec: - name - namespace type: object + required: + - protocolType + - storageType type: object status: description: NASFileSystemStatus defines the observed state of NASFileSystem @@ -99,6 +106,8 @@ spec: atProvider: description: NASFileSystemObservation is the representation of the current state that is observed. properties: + fileSystemID: + type: string mountTargetDomain: type: string type: object diff --git a/pkg/clients/nas/nas.go b/pkg/clients/nas/nas.go index 5dc5680..7c46963 100644 --- a/pkg/clients/nas/nas.go +++ b/pkg/clients/nas/nas.go @@ -18,7 +18,6 @@ package nas import ( "context" - "fmt" openapi "github.com/alibabacloud-go/darabonba-openapi/client" sdk "github.com/alibabacloud-go/nas-20170626/v2/client" @@ -30,8 +29,8 @@ import ( // ErrCodeNoSuchNASFileSystem is the error code "NoSuchNASFileSystem" returned by SDK const ( - ErrCodeNoSuchNASFileSystem = "NoSuchNASFileSystem" errFailedToCreateNASClient = "failed to crate NAS client" + errCodeFileSystemNotExist = "InvalidFileSystem.NotFound" ) // ClientInterface will help fakeOSSClient in unit tests @@ -47,13 +46,13 @@ type SDKClient struct { } // NewClient will create OSS client -func NewClient(ctx context.Context, region string, accessKeyID string, accessKeySecret string, securityToken string) (*SDKClient, error) { +func NewClient(ctx context.Context, endpoint string, accessKeyID string, accessKeySecret string, securityToken string) (*SDKClient, error) { config := &openapi.Config{ AccessKeyId: &accessKeyID, AccessKeySecret: &accessKeySecret, SecurityToken: &securityToken, + Endpoint: &endpoint, } - config.Endpoint = tea.String(fmt.Sprintf("nas.%s.aliyuncs.com", region)) client, err := sdk.NewClient(config) if err != nil { return nil, errors.Wrap(err, errFailedToCreateNASClient) @@ -104,7 +103,7 @@ func (c *SDKClient) DeleteFileSystem(fileSystemID string) error { } // GenerateObservation generates NASFileSystemObservation from fileSystem information -func GenerateObservation(r sdk.DescribeFileSystemsResponse) v1alpha1.NASFileSystemObservation { +func GenerateObservation(r *sdk.DescribeFileSystemsResponse) v1alpha1.NASFileSystemObservation { var domain string if len(r.Body.FileSystems.FileSystem) == 0 { return v1alpha1.NASFileSystemObservation{} @@ -122,13 +121,23 @@ func GenerateObservation(r sdk.DescribeFileSystemsResponse) v1alpha1.NASFileSyst // IsUpdateToDate checks whether cr is up to date func IsUpdateToDate(cr *v1alpha1.NASFileSystem, fsResponse *sdk.DescribeFileSystemsResponse) bool { - if len(fsResponse.Body.FileSystems.FileSystem) == 0 { + if *fsResponse.Body.TotalCount == 0 { return false } fs := fsResponse.Body.FileSystems.FileSystem[0] - if cr.Spec.StorageType == fs.StorageType && cr.Spec.ProtocolType == fs.ProtocolType && - cr.Spec.ChargeType == fs.ChargeType && cr.Spec.FileSystemType == fs.FileSystemType { + if *cr.Spec.StorageType == *fs.StorageType && *cr.Spec.ProtocolType == *fs.ProtocolType { + return true + } + return false +} + +// IsNotFoundError helper function to test for SLS project not found error +func IsNotFoundError(err error) bool { + if err == nil { + return false + } + if e, ok := errors.Cause(err).(*tea.SDKError); ok && (*e.Code == errCodeFileSystemNotExist) { return true } return false diff --git a/pkg/controller/nas/nas_controller.go b/pkg/controller/nas/nas_controller.go index 3da5bd5..af420ec 100644 --- a/pkg/controller/nas/nas_controller.go +++ b/pkg/controller/nas/nas_controller.go @@ -22,7 +22,6 @@ import ( xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" "github.com/crossplane/crossplane-runtime/pkg/event" "github.com/crossplane/crossplane-runtime/pkg/logging" - "github.com/crossplane/crossplane-runtime/pkg/meta" "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/pkg/errors" @@ -133,19 +132,23 @@ func (e *External) Observe(ctx context.Context, mg resource.Managed) (managed.Ex return managed.ExternalObservation{}, errors.New(errNotNASFileSystem) } - if cr.Status.AtProvider.FileSystemID == "" { + fsID := cr.Status.AtProvider.FileSystemID + if fsID == "" { return managed.ExternalObservation{ ResourceExists: false, }, nil } - fsID := meta.GetExternalName(cr) filesystem, err := e.ExternalClient.DescribeFileSystems(&fsID, cr.Spec.FileSystemType, cr.Spec.VpcID) if err != nil { - return managed.ExternalObservation{}, errors.Wrap(err, errFailedToDescribeNASFileSystem) + // Managed resource `NASFileSystem` is special, the identifier of if `name` is different to the cloud resource identifier `FileSystemID` + if nasclient.IsNotFoundError(err) { + return managed.ExternalObservation{ResourceExists: false, ResourceUpToDate: true}, nil + } + return managed.ExternalObservation{ResourceExists: false}, nil } - cr.Status.AtProvider = nasclient.GenerateObservation(*filesystem) + cr.Status.AtProvider = nasclient.GenerateObservation(filesystem) var upToDate = nasclient.IsUpdateToDate(cr, filesystem) if upToDate { cr.SetConditions(xpv1.Available()) @@ -177,8 +180,11 @@ func (e *External) Create(ctx context.Context, mg resource.Managed) (managed.Ext if err != nil { return managed.ExternalCreation{}, errors.Wrap(err, errFailedToCreateNASFileSystem) } - // The name of this managed resource could not be regarded as its external name, so set it as FileSystem ID - meta.SetExternalName(cr, *res.Body.FileSystemId) + fsRes, err := e.ExternalClient.DescribeFileSystems(res.Body.FileSystemId, cr.Spec.FileSystemType, cr.Spec.VpcID) + if err != nil { + return managed.ExternalCreation{}, errors.Wrap(err, errFailedToDescribeNASFileSystem) + } + cr.Status.AtProvider = nasclient.GenerateObservation(fsRes) return managed.ExternalCreation{ConnectionDetails: GetConnectionDetails(cr)}, nil } @@ -194,7 +200,7 @@ func (e *External) Delete(ctx context.Context, mg resource.Managed) error { return errors.New(errNotNASFileSystem) } cr.SetConditions(xpv1.Deleting()) - if err := e.ExternalClient.DeleteFileSystem(meta.GetExternalName(cr)); err != nil { + if err := e.ExternalClient.DeleteFileSystem(cr.Status.AtProvider.FileSystemID); err != nil { return errors.Wrap(err, errFailedToDeleteNASFileSystem) } return nil diff --git a/pkg/controller/nas/nas_controller_test.go b/pkg/controller/nas/nas_controller_test.go index db6e776..f17adee 100644 --- a/pkg/controller/nas/nas_controller_test.go +++ b/pkg/controller/nas/nas_controller_test.go @@ -140,7 +140,7 @@ func TestCreate(t *testing.T) { validCR := &v1alpha1.NASFileSystem{Spec: v1alpha1.NASFileSystemSpec{}} validCR.Spec.StorageType = pointer.StringPtr("standard") - validCR.Spec.ProtocolType = pointer.StringPtr("standard") + validCR.Spec.ProtocolType = pointer.StringPtr("nfs") validCR.ObjectMeta.Annotations = map[string]string{meta.AnnotationKeyExternalName: "def"} type want struct { diff --git a/pkg/util/endpoint.go b/pkg/util/endpoint.go index 2d79756..02174a6 100644 --- a/pkg/util/endpoint.go +++ b/pkg/util/endpoint.go @@ -22,6 +22,7 @@ import ( "github.com/pkg/errors" "k8s.io/apimachinery/pkg/runtime" + nasapi "github.com/crossplane/provider-alibaba/apis/nas/v1alpha1" ossapi "github.com/crossplane/provider-alibaba/apis/oss/v1alpha1" ) @@ -47,6 +48,8 @@ func GetEndpoint(res runtime.Object, region string) (string, error) { switch res.GetObjectKind().GroupVersionKind().Kind { case ossapi.BucketKind: endpoint = fmt.Sprintf("http://oss-%s.%s", region, Domain) + case nasapi.NASFileSystemKind: + endpoint = fmt.Sprintf("nas.%s.aliyuncs.com", region) default: return "", errors.New(errCloudResourceNotSupported) }