diff --git a/apis/sls/v1alpha1/groupversion_info.go b/apis/sls/v1alpha1/groupversion_info.go index 48af4c1..e97f7ee 100644 --- a/apis/sls/v1alpha1/groupversion_info.go +++ b/apis/sls/v1alpha1/groupversion_info.go @@ -90,6 +90,15 @@ var ( // MachineGroupVersionKind is the group, version and kind of MachineGroup MachineGroupVersionKind = GroupVersion.WithKind(MachineGroupKind) + + // MachineGroupBindingKind is the kind of MachineGroupBinding + MachineGroupBindingKind = reflect.TypeOf(MachineGroupBinding{}).Name() + + // MachineGroupBindingGroupKind is the group and kind of MachineGroupBinding + MachineGroupBindingGroupKind = schema.GroupKind{Group: GroupVersion.Group, Kind: MachineGroupBindingKind}.String() + + // MachineGroupBindingGroupVersionKind is the group, version and kind of MachineGroupBinding + MachineGroupBindingGroupVersionKind = GroupVersion.WithKind(MachineGroupBindingKind) ) func init() { @@ -98,4 +107,5 @@ func init() { SchemeBuilder.Register(&Logtail{}, &LogtailList{}) SchemeBuilder.Register(&LogstoreIndex{}, &LogstoreIndexList{}) SchemeBuilder.Register(&MachineGroup{}, &MachineGroupList{}) + SchemeBuilder.Register(&MachineGroupBinding{}, &MachineGroupBindingList{}) } diff --git a/apis/sls/v1alpha1/machineGroupBinding_types.go b/apis/sls/v1alpha1/machineGroupBinding_types.go new file mode 100644 index 0000000..8aee92f --- /dev/null +++ b/apis/sls/v1alpha1/machineGroupBinding_types.go @@ -0,0 +1,79 @@ +/* + + Copyright 2021 The Crossplane 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 ( + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// MachineGroupBindingSpec defines the desired state of SLS MachineGroupBinding +type MachineGroupBindingSpec struct { + xpv1.ResourceSpec `json:",inline"` + + // ForProvider field is where use set parameters for SLS MachineGroupBinding + ForProvider MachineGroupBindingParameters `json:"forProvider"` +} + +// MachineGroupBindingObservation is the representation of the current state that is observed. +type MachineGroupBindingObservation struct { + Configs []string `json:"configs"` +} + +// MachineGroupBindingStatus defines the observed state of SLS MachineGroupBinding +type MachineGroupBindingStatus struct { + xpv1.ResourceStatus `json:",inline"` + AtProvider MachineGroupBindingObservation `json:"atProvider,omitempty"` +} + +// MachineGroupBindingParameters define the desired state of an SLS store. +type MachineGroupBindingParameters struct { + // SLS project name + // +kubebuilder:validation:MinLength:=3 + // +kubebuilder:validation:MaxLength:=63 + ProjectName *string `json:"projectName"` + + GroupName *string `json:"groupName"` + + ConfigName *string `json:"configName"` +} + +// +kubebuilder:object:root=true + +// MachineGroupBinding is the Schema for the SLS MachineGroupBindings API +// +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="AGE",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster,categories={crossplane,managed,alibaba} +type MachineGroupBinding struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec MachineGroupBindingSpec `json:"spec"` + Status MachineGroupBindingStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// MachineGroupBindingList contains a list of MachineGroupBinding +type MachineGroupBindingList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []MachineGroupBinding `json:"items"` +} diff --git a/apis/sls/v1alpha1/zz_generated.deepcopy.go b/apis/sls/v1alpha1/zz_generated.deepcopy.go index 61fc1e3..fc662d5 100644 --- a/apis/sls/v1alpha1/zz_generated.deepcopy.go +++ b/apis/sls/v1alpha1/zz_generated.deepcopy.go @@ -618,6 +618,149 @@ func (in *MachineGroup) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MachineGroupBinding) DeepCopyInto(out *MachineGroupBinding) { + *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 MachineGroupBinding. +func (in *MachineGroupBinding) DeepCopy() *MachineGroupBinding { + if in == nil { + return nil + } + out := new(MachineGroupBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MachineGroupBinding) 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 *MachineGroupBindingList) DeepCopyInto(out *MachineGroupBindingList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]MachineGroupBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineGroupBindingList. +func (in *MachineGroupBindingList) DeepCopy() *MachineGroupBindingList { + if in == nil { + return nil + } + out := new(MachineGroupBindingList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MachineGroupBindingList) 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 *MachineGroupBindingObservation) DeepCopyInto(out *MachineGroupBindingObservation) { + *out = *in + if in.Configs != nil { + in, out := &in.Configs, &out.Configs + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineGroupBindingObservation. +func (in *MachineGroupBindingObservation) DeepCopy() *MachineGroupBindingObservation { + if in == nil { + return nil + } + out := new(MachineGroupBindingObservation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MachineGroupBindingParameters) DeepCopyInto(out *MachineGroupBindingParameters) { + *out = *in + if in.ProjectName != nil { + in, out := &in.ProjectName, &out.ProjectName + *out = new(string) + **out = **in + } + if in.GroupName != nil { + in, out := &in.GroupName, &out.GroupName + *out = new(string) + **out = **in + } + if in.ConfigName != nil { + in, out := &in.ConfigName, &out.ConfigName + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineGroupBindingParameters. +func (in *MachineGroupBindingParameters) DeepCopy() *MachineGroupBindingParameters { + if in == nil { + return nil + } + out := new(MachineGroupBindingParameters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MachineGroupBindingSpec) DeepCopyInto(out *MachineGroupBindingSpec) { + *out = *in + in.ResourceSpec.DeepCopyInto(&out.ResourceSpec) + in.ForProvider.DeepCopyInto(&out.ForProvider) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineGroupBindingSpec. +func (in *MachineGroupBindingSpec) DeepCopy() *MachineGroupBindingSpec { + if in == nil { + return nil + } + out := new(MachineGroupBindingSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MachineGroupBindingStatus) DeepCopyInto(out *MachineGroupBindingStatus) { + *out = *in + in.ResourceStatus.DeepCopyInto(&out.ResourceStatus) + in.AtProvider.DeepCopyInto(&out.AtProvider) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineGroupBindingStatus. +func (in *MachineGroupBindingStatus) DeepCopy() *MachineGroupBindingStatus { + if in == nil { + return nil + } + out := new(MachineGroupBindingStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MachineGroupList) DeepCopyInto(out *MachineGroupList) { *out = *in diff --git a/apis/sls/v1alpha1/zz_generated.managed.go b/apis/sls/v1alpha1/zz_generated.managed.go index ab27ec2..7d4edd6 100644 --- a/apis/sls/v1alpha1/zz_generated.managed.go +++ b/apis/sls/v1alpha1/zz_generated.managed.go @@ -244,6 +244,62 @@ func (mg *MachineGroup) SetWriteConnectionSecretToReference(r *xpv1.SecretRefere mg.Spec.WriteConnectionSecretToReference = r } +// GetCondition of this MachineGroupBinding. +func (mg *MachineGroupBinding) GetCondition(ct xpv1.ConditionType) xpv1.Condition { + return mg.Status.GetCondition(ct) +} + +// GetDeletionPolicy of this MachineGroupBinding. +func (mg *MachineGroupBinding) GetDeletionPolicy() xpv1.DeletionPolicy { + return mg.Spec.DeletionPolicy +} + +// GetProviderConfigReference of this MachineGroupBinding. +func (mg *MachineGroupBinding) GetProviderConfigReference() *xpv1.Reference { + return mg.Spec.ProviderConfigReference +} + +/* +GetProviderReference of this MachineGroupBinding. +Deprecated: Use GetProviderConfigReference. +*/ +func (mg *MachineGroupBinding) GetProviderReference() *xpv1.Reference { + return mg.Spec.ProviderReference +} + +// GetWriteConnectionSecretToReference of this MachineGroupBinding. +func (mg *MachineGroupBinding) GetWriteConnectionSecretToReference() *xpv1.SecretReference { + return mg.Spec.WriteConnectionSecretToReference +} + +// SetConditions of this MachineGroupBinding. +func (mg *MachineGroupBinding) SetConditions(c ...xpv1.Condition) { + mg.Status.SetConditions(c...) +} + +// SetDeletionPolicy of this MachineGroupBinding. +func (mg *MachineGroupBinding) SetDeletionPolicy(r xpv1.DeletionPolicy) { + mg.Spec.DeletionPolicy = r +} + +// SetProviderConfigReference of this MachineGroupBinding. +func (mg *MachineGroupBinding) SetProviderConfigReference(r *xpv1.Reference) { + mg.Spec.ProviderConfigReference = r +} + +/* +SetProviderReference of this MachineGroupBinding. +Deprecated: Use SetProviderConfigReference. +*/ +func (mg *MachineGroupBinding) SetProviderReference(r *xpv1.Reference) { + mg.Spec.ProviderReference = r +} + +// SetWriteConnectionSecretToReference of this MachineGroupBinding. +func (mg *MachineGroupBinding) SetWriteConnectionSecretToReference(r *xpv1.SecretReference) { + mg.Spec.WriteConnectionSecretToReference = r +} + // GetCondition of this Project. func (mg *Project) GetCondition(ct xpv1.ConditionType) xpv1.Condition { return mg.Status.GetCondition(ct) diff --git a/apis/sls/v1alpha1/zz_generated.managedlist.go b/apis/sls/v1alpha1/zz_generated.managedlist.go index 89e5888..580f447 100644 --- a/apis/sls/v1alpha1/zz_generated.managedlist.go +++ b/apis/sls/v1alpha1/zz_generated.managedlist.go @@ -47,6 +47,15 @@ func (l *LogtailList) GetItems() []resource.Managed { return items } +// GetItems of this MachineGroupBindingList. +func (l *MachineGroupBindingList) GetItems() []resource.Managed { + items := make([]resource.Managed, len(l.Items)) + for i := range l.Items { + items[i] = &l.Items[i] + } + return items +} + // GetItems of this MachineGroupList. func (l *MachineGroupList) GetItems() []resource.Managed { items := make([]resource.Managed, len(l.Items)) diff --git a/examples/sls/machine-group-bingding.yaml b/examples/sls/machine-group-bingding.yaml new file mode 100644 index 0000000..b2e3185 --- /dev/null +++ b/examples/sls/machine-group-bingding.yaml @@ -0,0 +1,13 @@ +apiVersion: sls.alibaba.crossplane.io/v1alpha1 +kind: MachineGroupBinding +metadata: + name: sls-machine-group-binding + namespace: default +spec: + forProvider: + projectName: crossplane-poc + groupName: sls-machinegroup-test + configName: sls-logtail-test + writeConnectionSecretToRef: + name: machine-group-binding + namespace: default diff --git a/package/crds/sls.alibaba.crossplane.io_machinegroupbindings.yaml b/package/crds/sls.alibaba.crossplane.io_machinegroupbindings.yaml new file mode 100644 index 0000000..65d0e98 --- /dev/null +++ b/package/crds/sls.alibaba.crossplane.io_machinegroupbindings.yaml @@ -0,0 +1,158 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.3.0 + creationTimestamp: null + name: machinegroupbindings.sls.alibaba.crossplane.io +spec: + group: sls.alibaba.crossplane.io + names: + categories: + - crossplane + - managed + - alibaba + kind: MachineGroupBinding + listKind: MachineGroupBindingList + plural: machinegroupbindings + singular: machinegroupbinding + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=='Ready')].status + name: READY + type: string + - jsonPath: .status.conditions[?(@.type=='Synced')].status + name: SYNCED + type: string + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: MachineGroupBinding is the Schema for the SLS MachineGroupBindings API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: MachineGroupBindingSpec defines the desired state of SLS MachineGroupBinding + properties: + deletionPolicy: + description: DeletionPolicy specifies what will happen to the underlying external when this managed resource is deleted - either "Delete" or "Orphan" the external resource. The "Delete" policy is the default when no policy is specified. + enum: + - Orphan + - Delete + type: string + forProvider: + description: ForProvider field is where use set parameters for SLS MachineGroupBinding + properties: + configName: + type: string + groupName: + type: string + projectName: + description: SLS project name + maxLength: 63 + minLength: 3 + type: string + required: + - configName + - groupName + - projectName + type: object + providerConfigRef: + description: ProviderConfigReference specifies how the provider that will be used to create, observe, update, and delete this managed resource should be configured. + properties: + name: + description: Name of the referenced object. + type: string + required: + - name + type: object + providerRef: + description: 'ProviderReference specifies the provider that will be used to create, observe, update, and delete this managed resource. Deprecated: Please use ProviderConfigReference, i.e. `providerConfigRef`' + properties: + name: + description: Name of the referenced object. + type: string + required: + - name + type: object + 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. + properties: + name: + description: Name of the secret. + type: string + namespace: + description: Namespace of the secret. + type: string + required: + - name + - namespace + type: object + required: + - forProvider + type: object + status: + description: MachineGroupBindingStatus defines the observed state of SLS MachineGroupBinding + properties: + atProvider: + description: MachineGroupBindingObservation is the representation of the current state that is observed. + properties: + configs: + items: + type: string + type: array + required: + - configs + type: object + conditions: + description: Conditions of the resource. + items: + description: A Condition that may apply to a resource. + properties: + lastTransitionTime: + description: LastTransitionTime is the last time this condition transitioned from one status to another. + format: date-time + type: string + message: + description: A Message containing details about this condition's last transition from one status to another, if any. + type: string + reason: + description: A Reason for this condition's last transition from one status to another. + type: string + status: + description: Status of this condition; is it currently True, False, or Unknown? + type: string + type: + description: Type of this condition. At most one of each condition type may apply to a resource at any point in time. + type: string + required: + - lastTransitionTime + - reason + - status + - type + type: object + type: array + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/pkg/clients/sls/machineGroup.go b/pkg/clients/sls/machineGroup.go index 4369622..e6e402d 100644 --- a/pkg/clients/sls/machineGroup.go +++ b/pkg/clients/sls/machineGroup.go @@ -34,6 +34,13 @@ var ( ErrCreateMachineGroup = "failed to create a Logtail MachineGroup" // ErrDeleteMachineGroup is the error when failed to delete the resource ErrDeleteMachineGroup = "failed to delete the Logtail MachineGroup" + + // ErrGetAppliedConfigs is the error when getting configs from a machine group + ErrGetAppliedConfigs = "failed to get applied configs from a machine group" + // ErrApplyConfigToMachineGroup is the error when applying a config to a machine group + ErrApplyConfigToMachineGroup = "failed to apply a config to a machine group" + // ErrRemoveConfigFromMachineGroup is the error when removing a config from a machine group + ErrRemoveConfigFromMachineGroup = "failed to remove a config from a machine group" ) // DescribeMachineGroup describes SLS Logtail MachineGroup @@ -109,3 +116,31 @@ func IsMachineGroupNotFoundError(err error) bool { } return false } + +// GetAppliedConfigs gets applied configs to a machine group +func (c *LogClient) GetAppliedConfigs(projectName *string, + groupName *string) ([]string, error) { + configs, err := c.Client.GetAppliedConfigs(*projectName, *groupName) + return configs, errors.Wrap(err, ErrGetAppliedConfigs) +} + +// ApplyConfigToMachineGroup applied a config to a machine group +func (c *LogClient) ApplyConfigToMachineGroup(projectName, + groupName, confName *string) error { + err := c.Client.ApplyConfigToMachineGroup(*projectName, *confName, *groupName) + return errors.Wrap(err, ErrApplyConfigToMachineGroup) +} + +// RemoveConfigFromMachineGroup remove a config from a machine group +func (c *LogClient) RemoveConfigFromMachineGroup(projectName, + groupName, confName *string) error { + err := c.Client.RemoveConfigFromMachineGroup(*projectName, *confName, *groupName) + return errors.Wrap(err, ErrRemoveConfigFromMachineGroup) +} + +// GenerateMachineGroupBindingObservation is used to produce observation message +func GenerateMachineGroupBindingObservation(configs []string) v1alpha1.MachineGroupBindingObservation { + return v1alpha1.MachineGroupBindingObservation{ + Configs: configs, + } +} diff --git a/pkg/clients/sls/sls.go b/pkg/clients/sls/sls.go index b504b92..b70bf26 100644 --- a/pkg/clients/sls/sls.go +++ b/pkg/clients/sls/sls.go @@ -80,6 +80,10 @@ type LogClientInterface interface { CreateMachineGroup(name string, param v1alpha1.MachineGroupParameters) error UpdateMachineGroup(project, logstore *string, machineGroup *sdk.MachineGroup) error DeleteMachineGroup(project *string, logstore string) error + + GetAppliedConfigs(projectName *string, groupName *string) ([]string, error) + ApplyConfigToMachineGroup(projectName, groupName, confName *string) error + RemoveConfigFromMachineGroup(projectName, groupName, confName *string) error } // LogClient is the SDK client of SLS diff --git a/pkg/controller/alibaba.go b/pkg/controller/alibaba.go index c640639..9202e9d 100644 --- a/pkg/controller/alibaba.go +++ b/pkg/controller/alibaba.go @@ -40,6 +40,7 @@ func Setup(mgr ctrl.Manager, l logging.Logger) error { sls.SetupLogtail, sls.SetupIndex, sls.SetupMachineGroup, + sls.SetupMachineGroupBinding, oss.SetupBucket, nas.SetupNASFileSystem, nas.SetupNASMountTarget, diff --git a/pkg/controller/sls/machineGroupBinding_controller.go b/pkg/controller/sls/machineGroupBinding_controller.go new file mode 100644 index 0000000..a5e7b11 --- /dev/null +++ b/pkg/controller/sls/machineGroupBinding_controller.go @@ -0,0 +1,195 @@ +/* +Copyright 2021 The Crossplane 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 sls + +import ( + "context" + "strings" + + 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" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + aliv1alpha1 "github.com/crossplane/provider-alibaba/apis/sls/v1alpha1" + "github.com/crossplane/provider-alibaba/apis/v1alpha1" + slsclient "github.com/crossplane/provider-alibaba/pkg/clients/sls" + "github.com/crossplane/provider-alibaba/pkg/util" +) + +const ( + errCreateMachineGroupBinding = "failed to create MachineGroupBinding" + errDeleteMachineGroupBinding = "failed to delete MachineGroupBinding" + errDescribeMachineGroupBinding = "failed to describe MachineGroupBinding" + errNotMachineGroupBinding = "managed resource is not a MachineGroupBinding custom resource" +) + +// SetupMachineGroupBinding adds a controller that reconciles MachineGroupBinding +func SetupMachineGroupBinding(mgr ctrl.Manager, l logging.Logger) error { + name := managed.ControllerName(aliv1alpha1.MachineGroupBindingGroupKind) + return ctrl.NewControllerManagedBy(mgr). + Named(name). + For(&aliv1alpha1.MachineGroupBinding{}). + Complete(managed.NewReconciler(mgr, + resource.ManagedKind(aliv1alpha1.MachineGroupBindingGroupVersionKind), + managed.WithLogger(l.WithValues("controller", name)), + managed.WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name))), + managed.WithExternalConnecter(&machineGroupBindingConnector{ + client: mgr.GetClient(), + usage: resource.NewProviderConfigUsageTracker(mgr.GetClient(), &v1alpha1.ProviderConfigUsage{}), + NewClientFn: slsclient.NewClient, + }))) +} + +// machineGroupBindingConnector stores Kubernetes client and SLS client +type machineGroupBindingConnector struct { + client client.Client + usage resource.Tracker + NewClientFn func(accessKeyID, accessKeySecret, securityToken, region string) *slsclient.LogClient +} + +// Connect initials cloud resource client +func (c *machineGroupBindingConnector) Connect(ctx context.Context, mg resource.Managed) (managed.ExternalClient, error) { + cr, ok := mg.(*aliv1alpha1.MachineGroupBinding) + if !ok { + return nil, errors.New(errNotMachineGroupBinding) + } + + var ( + sel *xpv1.SecretKeySelector + region string + ) + + switch { + case cr.GetProviderConfigReference() != nil: + if err := c.usage.Track(ctx, mg); err != nil { + return nil, errors.Wrap(err, errTrackUsage) + } + + pc := &v1alpha1.ProviderConfig{} + if err := c.client.Get(ctx, types.NamespacedName{Name: cr.Spec.ProviderConfigReference.Name}, pc); err != nil { + return nil, errors.Wrap(err, errGetProviderConfig) + } + if s := pc.Spec.Credentials.Source; s != xpv1.CredentialsSourceSecret { + return nil, errors.Errorf(errFmtUnsupportedCredSource, s) + } + sel = pc.Spec.Credentials.SecretRef + region = pc.Spec.Region + default: + return nil, errors.New(errNoProvider) + } + + if sel == nil { + return nil, errors.New(errNoConnectionSecret) + } + + s := &corev1.Secret{} + nn := types.NamespacedName{Namespace: sel.Namespace, Name: sel.Name} + if err := c.client.Get(ctx, nn, s); err != nil { + return nil, errors.Wrap(err, errGetConnectionSecret) + } + + slsClient := c.NewClientFn(string(s.Data[util.AccessKeyID]), string(s.Data[util.AccessKeySecret]), string(s.Data[util.SecurityToken]), region) + return &machineGroupBindingExternal{client: slsClient}, nil +} + +// machineGroupBindingExternal includes external SLS client +type machineGroupBindingExternal struct { + client slsclient.LogClientInterface +} + +// Observe managed resource MachineGroupBinding +func (e *machineGroupBindingExternal) Observe(ctx context.Context, mg resource.Managed) (managed.ExternalObservation, error) { + cr, ok := mg.(*aliv1alpha1.MachineGroupBinding) + if !ok { + return managed.ExternalObservation{}, errors.New(errNotMachineGroupBinding) + } + + if meta.GetExternalName(mg) == "" { + return managed.ExternalObservation{ + ResourceExists: false, + }, nil + } + + configs, err := e.client.GetAppliedConfigs(cr.Spec.ForProvider.ProjectName, cr.Spec.ForProvider.GroupName) + if err != nil { + return managed.ExternalObservation{ResourceExists: false}, errors.Wrap(err, errDescribeMachineGroupBinding) + } + if len(configs) == 0 { + return managed.ExternalObservation{ResourceExists: false, ResourceUpToDate: true}, nil + } + cr.Status.AtProvider = slsclient.GenerateMachineGroupBindingObservation(configs) + cr.SetConditions(xpv1.Available()) + + return managed.ExternalObservation{ + ResourceExists: true, + ResourceUpToDate: true, + ConnectionDetails: GetMachineGroupBindingConnectionDetails(configs), + }, nil +} + +// Create managed resource MachineGroupBinding +func (e *machineGroupBindingExternal) Create(ctx context.Context, mg resource.Managed) (managed.ExternalCreation, error) { + cr, ok := mg.(*aliv1alpha1.MachineGroupBinding) + if !ok { + return managed.ExternalCreation{}, errors.New(errNotMachineGroupBinding) + } + cr.SetConditions(xpv1.Creating()) + + err := e.client.ApplyConfigToMachineGroup(cr.Spec.ForProvider.ProjectName, cr.Spec.ForProvider.GroupName, + cr.Spec.ForProvider.ConfigName) + if err != nil { + return managed.ExternalCreation{}, errors.Wrap(err, errCreateMachineGroupBinding) + } + + return managed.ExternalCreation{}, nil +} + +// Update managed resource MachineGroupBinding +func (e *machineGroupBindingExternal) Update(ctx context.Context, mg resource.Managed) (managed.ExternalUpdate, error) { + // TODO(zzxwll) need to add Update logic + return managed.ExternalUpdate{}, nil +} + +// Delete managed resource MachineGroupBinding which will remove config from a machine group +func (e *machineGroupBindingExternal) Delete(ctx context.Context, mg resource.Managed) error { + cr, ok := mg.(*aliv1alpha1.MachineGroupBinding) + if !ok { + return errors.New(errNotMachineGroupBinding) + } + cr.SetConditions(xpv1.Deleting()) + if err := e.client.RemoveConfigFromMachineGroup(cr.Spec.ForProvider.ProjectName, cr.Spec.ForProvider.GroupName, + cr.Spec.ForProvider.ConfigName); err != nil { + return errors.Wrap(err, errDeleteMachineGroupBinding) + } + return nil +} + +// GetMachineGroupBindingConnectionDetails generates connection details +func GetMachineGroupBindingConnectionDetails(configs []string) managed.ConnectionDetails { + cd := managed.ConnectionDetails{ + "Configs": []byte(strings.Join(configs, ", ")), + } + return cd +} diff --git a/pkg/controller/sls/machineGroupBinding_controller_test.go b/pkg/controller/sls/machineGroupBinding_controller_test.go new file mode 100644 index 0000000..78198da --- /dev/null +++ b/pkg/controller/sls/machineGroupBinding_controller_test.go @@ -0,0 +1,269 @@ +/* + + Copyright 2021 The Crossplane 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 sls + +import ( + "context" + "testing" + + "github.com/crossplane/crossplane-runtime/pkg/meta" + "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" + "github.com/crossplane/crossplane-runtime/pkg/resource" + "github.com/crossplane/crossplane-runtime/pkg/test" + "github.com/google/go-cmp/cmp" + "github.com/pkg/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + slsv1alpha1 "github.com/crossplane/provider-alibaba/apis/sls/v1alpha1" +) + +var ( + mgbProject = "abc" + mgbGroup = "test-group" + mgbConfig = "test-config" + mgbBadProject = "def" + notExistedProject = "not-found-abc" + mgbOtherError = "Some other error" + validMachineGroupBindingCR = &slsv1alpha1.MachineGroupBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: store, + Annotations: map[string]string{meta.AnnotationKeyExternalName: mgbProject}, + }, + Spec: slsv1alpha1.MachineGroupBindingSpec{ + ForProvider: slsv1alpha1.MachineGroupBindingParameters{ + ProjectName: &mgbProject, + GroupName: &mgbGroup, + ConfigName: &mgbConfig, + }, + }, + Status: slsv1alpha1.MachineGroupBindingStatus{ + AtProvider: slsv1alpha1.MachineGroupBindingObservation{ + Configs: []string{mgbConfig}, + }, + }, + } +) + +func (c *fakeSDKClient) GetAppliedConfigs(projectName *string, groupName *string) ([]string, error) { + switch *projectName { + case mgbProject: + return []string{mgbConfig}, nil + case notExistedProject: + return nil, nil + case mgbBadProject: + return nil, errors.New(mgbOtherError) + default: + return nil, nil + } +} + +func (c *fakeSDKClient) ApplyConfigToMachineGroup(projectName, groupName, confName *string) error { + return nil +} + +func (c *fakeSDKClient) RemoveConfigFromMachineGroup(projectName, groupName, confName *string) error { + return nil +} + +func TestMachineGroupBindingObserve(t *testing.T) { + var ( + ctx = context.Background() + ) + + type want struct { + o managed.ExternalObservation + err error + } + + cases := map[string]struct { + reason string + mg resource.Managed + want want + }{ + "NotMachineGroupBinding": { + reason: "We should return an error if the supplied managed resource is not MachineGroupBinding", + mg: nil, + want: want{ + o: managed.ExternalObservation{}, + err: errors.New(errNotMachineGroupBinding), + }, + }, + "ExternalNameIsNotSet": { + reason: "MachineGroupBinding's external name is not set", + mg: &slsv1alpha1.MachineGroupBinding{}, + want: want{ + o: managed.ExternalObservation{ + ResourceExists: false, + }, + err: nil, + }, + }, + "FailedToGetConfigs": { + reason: "failed to get configs from a machine group", + mg: &slsv1alpha1.MachineGroupBinding{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{meta.AnnotationKeyExternalName: mgbBadProject}, + }, + Spec: slsv1alpha1.MachineGroupBindingSpec{ + ForProvider: slsv1alpha1.MachineGroupBindingParameters{ + ProjectName: &mgbBadProject, + }, + }, + }, + want: want{ + o: managed.ExternalObservation{ + ResourceExists: false, + }, + err: errors.Wrap(errors.New(mgbOtherError), errDescribeMachineGroupBinding), + }, + }, + "MachineGroupBindingNotFound": { + reason: "MachineGroupBinding name could not be found", + mg: &slsv1alpha1.MachineGroupBinding{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{meta.AnnotationKeyExternalName: notExistedProject}, + }, + Spec: slsv1alpha1.MachineGroupBindingSpec{ + ForProvider: slsv1alpha1.MachineGroupBindingParameters{ + ProjectName: ¬ExistedProject, + }, + }, + }, + want: want{ + o: managed.ExternalObservation{ + ResourceExists: false, + ResourceUpToDate: true, + }, + err: nil, + }, + }, + "MachineGroupBindingSuccessfullyFound": { + reason: "Observing MachineGroupBinding successfully should return an ExternalObservation and nil error", + mg: validMachineGroupBindingCR, + want: want{ + o: managed.ExternalObservation{ + ResourceExists: true, + ResourceUpToDate: true, + ConnectionDetails: GetMachineGroupBindingConnectionDetails([]string{mgbConfig})}, + err: nil, + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + external := &machineGroupBindingExternal{client: &fakeSDKClient{}} + got, err := external.Observe(ctx, tc.mg) + if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { + t.Errorf("\n%s\ne.Observe(...): -want error, +got error:\n%s\n", tc.reason, diff) + } + if diff := cmp.Diff(tc.want.o, got); diff != "" { + t.Errorf("\n%s\ne.Observe(...): -want, +got:\n%s\n", tc.reason, diff) + } + }) + } +} + +func TestApplyConfigToMachineGroup(t *testing.T) { + var ( + ctx = context.Background() + ) + + type want struct { + o managed.ExternalCreation + err error + } + + cases := map[string]struct { + reason string + mg resource.Managed + want want + }{ + "NotMachineGroupBinding": { + reason: "Not MachineGroupBinding object", + mg: nil, + want: want{ + o: managed.ExternalCreation{}, + err: errors.New(errNotMachineGroupBinding), + }, + }, + "Success": { + reason: "Creating MachineGroupBinding successfully", + mg: validMachineGroupBindingCR, + want: want{ + o: managed.ExternalCreation{}, + err: nil, + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + external := &machineGroupBindingExternal{client: &fakeSDKClient{}} + got, err := external.Create(ctx, tc.mg) + if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { + t.Errorf("\n%s\ne.Create(...): -want error, +got error:\n%s\n", tc.reason, diff) + } + if diff := cmp.Diff(tc.want.o, got); diff != "" { + t.Errorf("\n%s\ne.Create(...): -want, +got:\n%s\n", tc.reason, diff) + } + }) + } +} + +func TestRemoveConfigFromMachineGroup(t *testing.T) { + var ( + ctx = context.Background() + ) + + type want struct { + err error + } + + cases := map[string]struct { + reason string + mg resource.Managed + want want + }{ + "NotMachineGroupBinding": { + reason: "Not MachineGroupBinding object", + mg: nil, + want: want{ + err: errors.New(errNotMachineGroupBinding), + }, + }, + "Success": { + reason: "Creating MachineGroupBinding successfully", + mg: validMachineGroupBindingCR, + want: want{ + err: nil, + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + external := &machineGroupBindingExternal{client: &fakeSDKClient{}} + err := external.Delete(ctx, tc.mg) + if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { + t.Errorf("\n%s\ne.Delete(...): -want error, +got error:\n%s\n", tc.reason, diff) + } + }) + } +}