diff --git a/Dockerfile b/Dockerfile index 29aa8075..d8234863 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM golang:1.19.3 as builder +FROM golang:1.20.6 as builder WORKDIR /workspace # Copy the Go Modules manifests diff --git a/Makefile b/Makefile index 00ca1e9d..300c029f 100644 --- a/Makefile +++ b/Makefile @@ -378,7 +378,7 @@ $(KUSTOMIZE): ## Download kustomize locally if necessary. GOBIN=$(BIN_DIR)/ $(GO_INSTALL) sigs.k8s.io/kustomize/kustomize/v4 $(KUSTOMIZE_BIN) v4.5.2 $(GINKGO): ## Build ginkgo. - GOBIN=$(BIN_DIR)/ $(GO_INSTALL) github.com/onsi/ginkgo/v2/ginkgo $(GINKGO_BIN) v2.9.2 + GOBIN=$(BIN_DIR)/ $(GO_INSTALL) github.com/onsi/ginkgo/v2/ginkgo $(GINKGO_BIN) v2.11.0 $(GOLANGCI_LINT): ## Build golanci-lint. GOBIN=$(BIN_DIR)/ $(GO_INSTALL) github.com/golangci/golangci-lint/cmd/golangci-lint $(GOLANGCI_LINT_BIN) v1.44.0 diff --git a/api/v1beta2/ocicluster_webhook.go b/api/v1beta2/ocicluster_webhook.go index 400d3cb6..a276898c 100644 --- a/api/v1beta2/ocicluster_webhook.go +++ b/api/v1beta2/ocicluster_webhook.go @@ -29,6 +29,7 @@ import ( "k8s.io/apimachinery/pkg/util/validation/field" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) var clusterlogger = ctrl.Log.WithName("ocicluster-resource") @@ -58,7 +59,7 @@ func (c *OCICluster) SetupWebhookWithManager(mgr ctrl.Manager) error { } // ValidateCreate implements webhook.Validator so a webhook will be registered for the type. -func (c *OCICluster) ValidateCreate() error { +func (c *OCICluster) ValidateCreate() (admission.Warnings, error) { clusterlogger.Info("validate update cluster", "name", c.Name) var allErrs field.ErrorList @@ -66,28 +67,28 @@ func (c *OCICluster) ValidateCreate() error { allErrs = append(allErrs, c.validate(nil)...) if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid(c.GroupVersionKind().GroupKind(), c.Name, allErrs) + return nil, apierrors.NewInvalid(c.GroupVersionKind().GroupKind(), c.Name, allErrs) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type. -func (c *OCICluster) ValidateDelete() error { +func (c *OCICluster) ValidateDelete() (admission.Warnings, error) { clusterlogger.Info("validate delete cluster", "name", c.Name) - return nil + return nil, nil } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. -func (c *OCICluster) ValidateUpdate(old runtime.Object) error { +func (c *OCICluster) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { clusterlogger.Info("validate update cluster", "name", c.Name) var allErrs field.ErrorList oldCluster, ok := old.(*OCICluster) if !ok { - return apierrors.NewBadRequest(fmt.Sprintf("expected an OCICluster but got a %T", old)) + return nil, apierrors.NewBadRequest(fmt.Sprintf("expected an OCICluster but got a %T", old)) } if c.Spec.Region != oldCluster.Spec.Region { @@ -105,10 +106,10 @@ func (c *OCICluster) ValidateUpdate(old runtime.Object) error { allErrs = append(allErrs, c.validate(oldCluster)...) if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid(c.GroupVersionKind().GroupKind(), c.Name, allErrs) + return nil, apierrors.NewInvalid(c.GroupVersionKind().GroupKind(), c.Name, allErrs) } func (c *OCICluster) validate(old *OCICluster) field.ErrorList { diff --git a/api/v1beta2/ocicluster_webhook_test.go b/api/v1beta2/ocicluster_webhook_test.go index a8e89cbb..db335471 100644 --- a/api/v1beta2/ocicluster_webhook_test.go +++ b/api/v1beta2/ocicluster_webhook_test.go @@ -439,11 +439,12 @@ func TestOCICluster_ValidateCreate(t *testing.T) { g := gomega.NewWithT(t) if test.expectErr { - err := test.c.ValidateCreate() + _, err := test.c.ValidateCreate() g.Expect(err).NotTo(gomega.Succeed()) g.Expect(strings.Contains(err.Error(), test.errorMgsShouldContain)).To(gomega.BeTrue()) } else { - g.Expect(test.c.ValidateCreate()).To(gomega.Succeed()) + _, err := test.c.ValidateCreate() + g.Expect(err).To(gomega.Succeed()) } }) } @@ -562,11 +563,12 @@ func TestOCICluster_ValidateUpdate(t *testing.T) { g := gomega.NewWithT(t) if test.expectErr { - err := test.c.ValidateUpdate(test.old) + _, err := test.c.ValidateUpdate(test.old) g.Expect(err).NotTo(gomega.Succeed()) g.Expect(strings.Contains(err.Error(), test.errorMgsShouldContain)).To(gomega.BeTrue()) } else { - g.Expect(test.c.ValidateUpdate(test.old)).To(gomega.Succeed()) + _, err := test.c.ValidateUpdate(test.old) + g.Expect(err).To(gomega.Succeed()) } }) } diff --git a/api/v1beta2/ocimachinetemplate_webhook.go b/api/v1beta2/ocimachinetemplate_webhook.go index c33b5936..0c70586a 100644 --- a/api/v1beta2/ocimachinetemplate_webhook.go +++ b/api/v1beta2/ocimachinetemplate_webhook.go @@ -24,6 +24,7 @@ import ( "k8s.io/apimachinery/pkg/util/validation/field" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) var ( @@ -39,7 +40,7 @@ func (m *OCIMachineTemplate) SetupWebhookWithManager(mgr ctrl.Manager) error { } // ValidateCreate implements webhook.Validator so a webhook will be registered for the type. -func (m *OCIMachineTemplate) ValidateCreate() error { +func (m *OCIMachineTemplate) ValidateCreate() (admission.Warnings, error) { clusterlogger.Info("validate create machinetemplate", "name", m.Name) var allErrs field.ErrorList @@ -47,21 +48,21 @@ func (m *OCIMachineTemplate) ValidateCreate() error { allErrs = append(allErrs, m.validate()...) if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid(m.GroupVersionKind().GroupKind(), m.Name, allErrs) + return nil, apierrors.NewInvalid(m.GroupVersionKind().GroupKind(), m.Name, allErrs) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type. -func (m *OCIMachineTemplate) ValidateDelete() error { +func (m *OCIMachineTemplate) ValidateDelete() (admission.Warnings, error) { clusterlogger.Info("validate delete machinetemplate", "name", m.Name) - return nil + return nil, nil } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. -func (m *OCIMachineTemplate) ValidateUpdate(old runtime.Object) error { +func (m *OCIMachineTemplate) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { clusterlogger.Info("validate update machinetemplate", "name", m.Name) var allErrs field.ErrorList @@ -69,10 +70,10 @@ func (m *OCIMachineTemplate) ValidateUpdate(old runtime.Object) error { allErrs = append(allErrs, m.validate()...) if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid(m.GroupVersionKind().GroupKind(), m.Name, allErrs) + return nil, apierrors.NewInvalid(m.GroupVersionKind().GroupKind(), m.Name, allErrs) } func (m *OCIMachineTemplate) validate() field.ErrorList { diff --git a/api/v1beta2/ocimachinetemplate_webhook_test.go b/api/v1beta2/ocimachinetemplate_webhook_test.go index 0667f6c6..da66cabb 100644 --- a/api/v1beta2/ocimachinetemplate_webhook_test.go +++ b/api/v1beta2/ocimachinetemplate_webhook_test.go @@ -99,11 +99,12 @@ func TestOCIMachineTemplate_ValidateCreate(t *testing.T) { g := gomega.NewWithT(t) if test.expectErr { - err := test.inputTemplate.ValidateCreate() + _, err := test.inputTemplate.ValidateCreate() g.Expect(err).NotTo(gomega.Succeed()) g.Expect(strings.Contains(err.Error(), test.errorField)).To(gomega.BeTrue()) } else { - g.Expect(test.inputTemplate.ValidateCreate()).To(gomega.Succeed()) + _, err := test.inputTemplate.ValidateCreate() + g.Expect(err).To(gomega.Succeed()) } }) } @@ -115,11 +116,12 @@ func TestOCIMachineTemplate_ValidateUpdate(t *testing.T) { g := gomega.NewWithT(t) if test.expectErr { - err := test.inputTemplate.ValidateUpdate(nil) + _, err := test.inputTemplate.ValidateUpdate(nil) g.Expect(err).NotTo(gomega.Succeed()) g.Expect(strings.Contains(err.Error(), test.errorField)).To(gomega.BeTrue()) } else { - g.Expect(test.inputTemplate.ValidateUpdate(nil)).To(gomega.Succeed()) + _, err := test.inputTemplate.ValidateUpdate(nil) + g.Expect(err).To(gomega.Succeed()) } }) } diff --git a/api/v1beta2/ocimanagedcluster_webhook.go b/api/v1beta2/ocimanagedcluster_webhook.go index 7980f81d..c16ffe20 100644 --- a/api/v1beta2/ocimanagedcluster_webhook.go +++ b/api/v1beta2/ocimanagedcluster_webhook.go @@ -27,6 +27,7 @@ import ( "k8s.io/apimachinery/pkg/util/validation/field" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) var managedclusterlogger = ctrl.Log.WithName("ocimanagedcluster-resource") @@ -114,7 +115,7 @@ func (c *OCIManagedCluster) SetupWebhookWithManager(mgr ctrl.Manager) error { } // ValidateCreate implements webhook.Validator so a webhook will be registered for the type. -func (c *OCIManagedCluster) ValidateCreate() error { +func (c *OCIManagedCluster) ValidateCreate() (admission.Warnings, error) { managedclusterlogger.Info("validate create cluster", "name", c.Name) var allErrs field.ErrorList @@ -122,28 +123,28 @@ func (c *OCIManagedCluster) ValidateCreate() error { allErrs = append(allErrs, c.validate(nil)...) if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid(c.GroupVersionKind().GroupKind(), c.Name, allErrs) + return nil, apierrors.NewInvalid(c.GroupVersionKind().GroupKind(), c.Name, allErrs) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type. -func (c *OCIManagedCluster) ValidateDelete() error { +func (c *OCIManagedCluster) ValidateDelete() (admission.Warnings, error) { managedclusterlogger.Info("validate delete cluster", "name", c.Name) - return nil + return nil, nil } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. -func (c *OCIManagedCluster) ValidateUpdate(old runtime.Object) error { +func (c *OCIManagedCluster) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { managedclusterlogger.Info("validate update cluster", "name", c.Name) var allErrs field.ErrorList oldCluster, ok := old.(*OCIManagedCluster) if !ok { - return apierrors.NewBadRequest(fmt.Sprintf("expected an OCIManagedCluster but got a %T", old)) + return nil, apierrors.NewBadRequest(fmt.Sprintf("expected an OCIManagedCluster but got a %T", old)) } if c.Spec.Region != oldCluster.Spec.Region { @@ -161,10 +162,10 @@ func (c *OCIManagedCluster) ValidateUpdate(old runtime.Object) error { allErrs = append(allErrs, c.validate(oldCluster)...) if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid(c.GroupVersionKind().GroupKind(), c.Name, allErrs) + return nil, apierrors.NewInvalid(c.GroupVersionKind().GroupKind(), c.Name, allErrs) } func (c *OCIManagedCluster) validate(old *OCIManagedCluster) field.ErrorList { diff --git a/api/v1beta2/ocimanagedcluster_webhook_test.go b/api/v1beta2/ocimanagedcluster_webhook_test.go index 35dfbaea..1c25c9ad 100644 --- a/api/v1beta2/ocimanagedcluster_webhook_test.go +++ b/api/v1beta2/ocimanagedcluster_webhook_test.go @@ -464,11 +464,12 @@ func TestOCIManagedCluster_ValidateCreate(t *testing.T) { g := gomega.NewWithT(t) if test.expectErr { - err := test.c.ValidateCreate() + _, err := test.c.ValidateCreate() g.Expect(err).NotTo(gomega.Succeed()) g.Expect(strings.Contains(err.Error(), test.errorMgsShouldContain)).To(gomega.BeTrue()) } else { - g.Expect(test.c.ValidateCreate()).To(gomega.Succeed()) + _, err := test.c.ValidateCreate() + g.Expect(err).To(gomega.Succeed()) } }) } @@ -587,11 +588,12 @@ func TestOCIManagedCluster_ValidateUpdate(t *testing.T) { g := gomega.NewWithT(t) if test.expectErr { - err := test.c.ValidateUpdate(test.old) + _, err := test.c.ValidateUpdate(test.old) g.Expect(err).NotTo(gomega.Succeed()) g.Expect(strings.Contains(err.Error(), test.errorMgsShouldContain)).To(gomega.BeTrue()) } else { - g.Expect(test.c.ValidateUpdate(test.old)).To(gomega.Succeed()) + _, err := test.c.ValidateUpdate(test.old) + g.Expect(err).To(gomega.Succeed()) } }) } diff --git a/api/v1beta2/ocimanagedcontrolplane_webhook.go b/api/v1beta2/ocimanagedcontrolplane_webhook.go index b486fc07..e66f6530 100644 --- a/api/v1beta2/ocimanagedcontrolplane_webhook.go +++ b/api/v1beta2/ocimanagedcontrolplane_webhook.go @@ -22,6 +22,7 @@ import ( "k8s.io/apimachinery/pkg/util/validation/field" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) var managedcplanelogger = ctrl.Log.WithName("ocimanagedcontrolplane-resource") @@ -50,21 +51,21 @@ func (c *OCIManagedControlPlane) SetupWebhookWithManager(mgr ctrl.Manager) error Complete() } -func (c *OCIManagedControlPlane) ValidateCreate() error { +func (c *OCIManagedControlPlane) ValidateCreate() (admission.Warnings, error) { var allErrs field.ErrorList if len(c.Name) > 31 { allErrs = append(allErrs, field.Invalid(field.NewPath("Name"), c.Name, "Name cannot be more than 31 characters")) } if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid(c.GroupVersionKind().GroupKind(), c.Name, allErrs) + return nil, apierrors.NewInvalid(c.GroupVersionKind().GroupKind(), c.Name, allErrs) } -func (c *OCIManagedControlPlane) ValidateUpdate(old runtime.Object) error { - return nil +func (c *OCIManagedControlPlane) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + return nil, nil } -func (c *OCIManagedControlPlane) ValidateDelete() error { - return nil +func (c *OCIManagedControlPlane) ValidateDelete() (admission.Warnings, error) { + return nil, nil } diff --git a/api/v1beta2/ocimanagedcontrolplane_webhook_test.go b/api/v1beta2/ocimanagedcontrolplane_webhook_test.go index af0ea8a2..bac388be 100644 --- a/api/v1beta2/ocimanagedcontrolplane_webhook_test.go +++ b/api/v1beta2/ocimanagedcontrolplane_webhook_test.go @@ -85,11 +85,12 @@ func TestOCIManagedControlPlane_ValidateCreate(t *testing.T) { g := gomega.NewWithT(t) if test.expectErr { - err := test.c.ValidateCreate() + _, err := test.c.ValidateCreate() g.Expect(err).NotTo(gomega.Succeed()) g.Expect(strings.Contains(err.Error(), test.errorMgsShouldContain)).To(gomega.BeTrue()) } else { - g.Expect(test.c.ValidateCreate()).To(gomega.Succeed()) + _, err := test.c.ValidateCreate() + g.Expect(err).To(gomega.Succeed()) } }) } diff --git a/cloud/scope/machine_pool.go b/cloud/scope/machine_pool.go index da12b19c..7231cd1c 100644 --- a/cloud/scope/machine_pool.go +++ b/cloud/scope/machine_pool.go @@ -20,6 +20,7 @@ import ( "context" "encoding/base64" "fmt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "reflect" "strconv" "strings" @@ -86,7 +87,7 @@ func NewMachinePoolScope(params MachinePoolScopeParams) (*MachinePoolScope, erro if err != nil { return nil, errors.Wrap(err, "failed to init patch helper") } - + params.OCIMachinePool.Status.InfrastructureMachineKind = "OCIMachinePoolMachine" return &MachinePoolScope{ Logger: params.Logger, Client: params.Client, @@ -153,7 +154,7 @@ func (m *MachinePoolScope) GetWorkerMachineSubnet() *string { return nil } -// ListMachinePoolInstances returns the WorkerRole core.Subnet id for the cluster +// ListMachinePoolInstances returns the list of instances belonging to an instance pool func (m *MachinePoolScope) ListMachinePoolInstances(ctx context.Context) ([]core.InstanceSummary, error) { poolOcid := m.OCIMachinePool.Spec.OCID if poolOcid == nil { @@ -187,22 +188,38 @@ func (m *MachinePoolScope) ListMachinePoolInstances(ctx context.Context) ([]core } // SetListandSetMachinePoolInstances retrieves a machine pools instances and sets them in the ProviderIDList -func (m *MachinePoolScope) SetListandSetMachinePoolInstances(ctx context.Context) (int32, error) { +func (m *MachinePoolScope) SetListandSetMachinePoolInstances(ctx context.Context) ([]infrav2exp.OCIMachinePoolMachine, error) { poolInstanceSummaries, err := m.ListMachinePoolInstances(ctx) if err != nil { - return 0, err + return nil, err } - providerIDList := make([]string, len(poolInstanceSummaries)) + machines := make([]infrav2exp.OCIMachinePoolMachine, 0) - for i, instance := range poolInstanceSummaries { - if *instance.State == "Running" { - providerIDList[i] = m.OCIClusterAccesor.GetProviderID(*instance.Id) + for _, instance := range poolInstanceSummaries { + // deleted nodes should not be considered + if strings.EqualFold(*instance.State, "TERMINATED") { + continue + } + ready := false + if strings.EqualFold(*instance.State, "RUNNING") { + ready = true } + machines = append(machines, infrav2exp.OCIMachinePoolMachine{ + ObjectMeta: metav1.ObjectMeta{ + Name: *instance.DisplayName, + }, + Spec: infrav2exp.OCIMachinePoolMachineSpec{ + OCID: instance.Id, + ProviderID: common.String(m.OCIClusterAccesor.GetProviderID(*instance.Id)), + InstanceName: instance.DisplayName, + MachineType: infrav2exp.SelfManaged, + }, + Status: infrav2exp.OCIMachinePoolMachineStatus{ + Ready: ready, + }, + }) } - - m.OCIMachinePool.Spec.ProviderIDList = providerIDList - - return int32(len(providerIDList)), nil + return machines, nil } // GetBootstrapData returns the bootstrap data from the secret in the Machine's bootstrap.dataSecretName. diff --git a/cloud/scope/machine_pool_test.go b/cloud/scope/machine_pool_test.go index 12c5b395..23487192 100644 --- a/cloud/scope/machine_pool_test.go +++ b/cloud/scope/machine_pool_test.go @@ -126,7 +126,7 @@ func TestInstanceConfigCreate(t *testing.T) { }, Spec: infrav2exp.OCIMachinePoolSpec{}, } - client := fake.NewClientBuilder().WithObjects(secret, machinePool).Build() + client := fake.NewClientBuilder().WithStatusSubresource(machinePool).WithObjects(secret, machinePool).Build() ms, err = NewMachinePoolScope(MachinePoolScopeParams{ ComputeManagementClient: computeManagementClient, OCIMachinePool: machinePool, @@ -486,7 +486,7 @@ func TestInstancePoolCreate(t *testing.T) { }, Spec: infrav2exp.OCIMachinePoolSpec{}, } - client := fake.NewClientBuilder().WithObjects(secret, machinePool).Build() + client := fake.NewClientBuilder().WithStatusSubresource(machinePool).WithObjects(secret, machinePool).Build() ms, err = NewMachinePoolScope(MachinePoolScopeParams{ ComputeManagementClient: computeManagementClient, OCIMachinePool: machinePool, diff --git a/cloud/scope/managed_machine_pool.go b/cloud/scope/managed_machine_pool.go index e28cd435..3a19db13 100644 --- a/cloud/scope/managed_machine_pool.go +++ b/cloud/scope/managed_machine_pool.go @@ -91,6 +91,7 @@ func NewManagedMachinePoolScope(params ManagedMachinePoolScopeParams) (*ManagedM if err != nil { return nil, errors.Wrap(err, "failed to init patch helper") } + params.OCIManagedMachinePool.Status.InfrastructureMachineKind = "OCIMachinePoolMachine" return &ManagedMachinePoolScope{ Logger: params.Logger, diff --git a/cloud/scope/virtual_machine_pool.go b/cloud/scope/virtual_machine_pool.go index b86e465d..1e4e7f4e 100644 --- a/cloud/scope/virtual_machine_pool.go +++ b/cloud/scope/virtual_machine_pool.go @@ -20,6 +20,7 @@ import ( "context" "encoding/json" "fmt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/go-logr/logr" infrastructurev1beta2 "github.com/oracle/cluster-api-provider-oci/api/v1beta2" @@ -88,7 +89,7 @@ func NewVirtualMachinePoolScope(params VirtualMachinePoolScopeParams) (*VirtualM if err != nil { return nil, errors.Wrap(err, "failed to init patch helper") } - + params.OCIVirtualMachinePool.Status.InfrastructureMachineKind = "OCIMachinePoolMachine" return &VirtualMachinePoolScope{ Logger: params.Logger, Client: params.Client, @@ -133,8 +134,8 @@ func (m *VirtualMachinePoolScope) GetWorkerMachineSubnet() *string { } // ListandSetMachinePoolInstances retrieves a virtual node pools instances and sets them in the ProviderIDList -func (m *VirtualMachinePoolScope) ListandSetMachinePoolInstances(ctx context.Context, nodePool *oke.VirtualNodePool) (int32, error) { - providerIDList := make([]string, 0) +func (m *VirtualMachinePoolScope) ListandSetMachinePoolInstances(ctx context.Context, nodePool *oke.VirtualNodePool) ([]infrav2exp.OCIMachinePoolMachine, error) { + machines := make([]infrav2exp.OCIMachinePoolMachine, 0) var page *string for { reqList := oke.ListVirtualNodesRequest{ @@ -144,12 +145,32 @@ func (m *VirtualMachinePoolScope) ListandSetMachinePoolInstances(ctx context.Con response, err := m.ContainerEngineClient.ListVirtualNodes(ctx, reqList) if err != nil { - return 0, err + return machines, err } - for _, i := range response.Items { - if i.LifecycleState == oke.VirtualNodeLifecycleStateActive { - providerIDList = append(providerIDList, *i.Id) + for _, node := range response.Items { + // deleted nodes should not be considered + if node.LifecycleState == oke.VirtualNodeLifecycleStateDeleted || + node.LifecycleState == oke.VirtualNodeLifecycleStateFailed { + continue } + ready := false + if node.LifecycleState == oke.VirtualNodeLifecycleStateActive { + ready = true + } + machines = append(machines, infrav2exp.OCIMachinePoolMachine{ + ObjectMeta: metav1.ObjectMeta{ + Name: *node.DisplayName, + }, + Spec: infrav2exp.OCIMachinePoolMachineSpec{ + OCID: node.Id, + ProviderID: node.Id, + InstanceName: node.DisplayName, + MachineType: infrav2exp.Virtual, + }, + Status: infrav2exp.OCIMachinePoolMachineStatus{ + Ready: ready, + }, + }) } if response.OpcNextPage == nil { break @@ -157,8 +178,7 @@ func (m *VirtualMachinePoolScope) ListandSetMachinePoolInstances(ctx context.Con page = response.OpcNextPage } } - m.OCIVirtualMachinePool.Spec.ProviderIDList = providerIDList - return int32(len(providerIDList)), nil + return machines, nil } // IsResourceCreatedByClusterAPI determines if the virtual node pool was created by the cluster using the diff --git a/cloud/util/suite_test.go b/cloud/util/suite_test.go index 77412f55..3b056e41 100644 --- a/cloud/util/suite_test.go +++ b/cloud/util/suite_test.go @@ -21,9 +21,11 @@ import ( "testing" infrastructurev1beta2 "github.com/oracle/cluster-api-provider-oci/api/v1beta2" + infrav2exp "github.com/oracle/cluster-api-provider-oci/exp/api/v1beta2" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/kubernetes/scheme" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" ) func TestMain(m *testing.M) { @@ -36,4 +38,6 @@ func TestMain(m *testing.M) { func setup() { utilruntime.Must(infrastructurev1beta2.AddToScheme(scheme.Scheme)) utilruntime.Must(clusterv1.AddToScheme(scheme.Scheme)) + utilruntime.Must(infrav2exp.AddToScheme(scheme.Scheme)) + utilruntime.Must(expclusterv1.AddToScheme(scheme.Scheme)) } diff --git a/cloud/util/util.go b/cloud/util/util.go index 3ec13b72..f630c2c0 100644 --- a/cloud/util/util.go +++ b/cloud/util/util.go @@ -22,16 +22,24 @@ import ( "fmt" "reflect" + "github.com/go-logr/logr" infrastructurev1beta2 "github.com/oracle/cluster-api-provider-oci/api/v1beta2" "github.com/oracle/cluster-api-provider-oci/cloud/config" "github.com/oracle/cluster-api-provider-oci/cloud/scope" + infrav2exp "github.com/oracle/cluster-api-provider-oci/exp/api/v1beta2" "github.com/oracle/oci-go-sdk/v65/common" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" + "sigs.k8s.io/cluster-api/util" + "sigs.k8s.io/cluster-api/util/labels/format" + "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) // GetClusterIdentityFromRef returns the OCIClusterIdentity referenced by the OCICluster. @@ -254,3 +262,142 @@ func CreateClientProviderFromClusterIdentity(ctx context.Context, client client. } return clientProvider, nil } + +// CreateMachinePoolMachinesIfNotExists creates the machine pool machines if not exists. This method lists the existing +// machines in the clusters and does a diff, and creates any missing machines based ont he spec provided. +func CreateMachinePoolMachinesIfNotExists(ctx context.Context, params MachineParams) error { + + machineList, err := getMachinepoolMachines(ctx, params.Client, params.MachinePool, params.Cluster, params.Namespace) + if err != nil { + return err + } + + instanceNameToMachinePoolMachine := make(map[string]infrav2exp.OCIMachinePoolMachine) + for _, machine := range machineList.Items { + instanceNameToMachinePoolMachine[*machine.Spec.OCID] = machine + } + + for _, specMachine := range params.SpecInfraMachines { + if actualMachine, exists := instanceNameToMachinePoolMachine[*specMachine.Spec.OCID]; exists { + if !reflect.DeepEqual(specMachine.Status.Ready, actualMachine.Status.Ready) { + params.Logger.Info("Setting status of machine to active", "machine", actualMachine.Name) + + helper, err := patch.NewHelper(&actualMachine, params.Client) + if err != nil { + return err + } + actualMachine.Status.Ready = true + err = helper.Patch(ctx, &actualMachine) + if err != nil { + return err + } + } + continue + } + + labels := map[string]string{ + clusterv1.ClusterNameLabel: params.Cluster.Name, + clusterv1.MachinePoolNameLabel: format.MustFormatValue(params.MachinePool.Name), + } + infraMachine := &infrav2exp.OCIMachinePoolMachine{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: params.Namespace, + Name: specMachine.Name, + Labels: labels, + Annotations: make(map[string]string), + // set the parent to infra machinepool till the capi machine reconciler changes it to capi machinepool machine + OwnerReferences: []metav1.OwnerReference{ + { + Kind: params.InfraMachinePoolKind, + Name: params.InfraMachinePoolName, + APIVersion: infrav2exp.GroupVersion.String(), + UID: params.InfraMachinePoolUID, + }, + }, + }, + Spec: infrav2exp.OCIMachinePoolMachineSpec{ + OCID: specMachine.Spec.OCID, + ProviderID: specMachine.Spec.ProviderID, + InstanceName: specMachine.Spec.InstanceName, + MachineType: specMachine.Spec.MachineType, + }, + } + infraMachine.Status.Ready = specMachine.Status.Ready + controllerutil.AddFinalizer(infraMachine, infrav2exp.MachinePoolMachineFinalizer) + params.Logger.Info("Creating machinepool machine", "machine", infraMachine.Name, "instanceName", specMachine.Name) + + if err := params.Client.Create(ctx, infraMachine); err != nil { + return errors.Wrap(err, "failed to create machine") + } + } + + return nil +} + +func getMachinepoolMachines(ctx context.Context, c client.Client, machinePool *expclusterv1.MachinePool, cluster *clusterv1.Cluster, namespace string) (*infrav2exp.OCIMachinePoolMachineList, error) { + machineList := &infrav2exp.OCIMachinePoolMachineList{} + labels := map[string]string{ + clusterv1.ClusterNameLabel: cluster.Name, + clusterv1.MachinePoolNameLabel: format.MustFormatValue(machinePool.Name), + } + if err := c.List(ctx, machineList, client.InNamespace(namespace), client.MatchingLabels(labels)); err != nil { + return nil, err + } + + return machineList, nil +} + +// DeleteOrphanedMachinePoolMachines deletes the machine pool machines which are not required. This method lists the +// existing machines in the clusters and does a diff with the spec and deletes any machines which are missing from the spec. +func DeleteOrphanedMachinePoolMachines(ctx context.Context, params MachineParams) error { + machineList, err := getMachinepoolMachines(ctx, params.Client, params.MachinePool, params.Cluster, params.Namespace) + if err != nil { + return err + } + + // create a set of instances in the spec, which will be used for lookup later + instanceSpecSet := map[string]struct{}{} + for _, specMachine := range params.SpecInfraMachines { + instanceSpecSet[*specMachine.Spec.OCID] = struct{}{} + } + + for i := range machineList.Items { + machinePoolMachine := &machineList.Items[i] + // lookup if the machinepool machine is not in the spec, if not delete the underlying machine + if _, ok := instanceSpecSet[*machinePoolMachine.Spec.OCID]; !ok { + machine, err := util.GetOwnerMachine(ctx, params.Client, machinePoolMachine.ObjectMeta) + if err != nil { + if apierrors.IsNotFound(err) { + params.Logger.Info("Machinepool machine has not been created", "machine", machinePoolMachine.Name) + continue + } + return errors.Wrapf(err, "failed to get owner Machine for machinepool machine %s/%s", machinePoolMachine.Namespace, machinePoolMachine.Name) + } + if machine == nil { + return errors.Errorf("Machinepool %s/%s has no parent Machine, will reattempt deletion once parent Machine is present", machinePoolMachine.Namespace, machinePoolMachine.Name) + } + params.Logger.Info("Deleting machinepool machine", "machine", machine.Name) + if err := params.Client.Delete(ctx, machine); err != nil { + return errors.Wrapf(err, "failed to delete machinepool machine %s/%s", machine.Namespace, machine.Name) + } + } else { + params.Logger.Info("Keeping machinepool, nothing to do", "machine", machinePoolMachine.Name, "namespace", machinePoolMachine.Namespace) + } + } + + return nil +} + +// MachineParams specifies the params required to create or delete machinepool machines. +// Infra machine pool specifed below refers to OCIManagedMachinePool/OCIMachinePool/OCIVirtualMachinePool +type MachineParams struct { + Client client.Client // the kubernetes client + MachinePool *expclusterv1.MachinePool // the corresponding machinepool + Cluster *clusterv1.Cluster // the corresponding cluster + InfraMachinePoolName string // the name of the infra machinepool corresponding(can be managed/self managed/virtual) + InfraMachinePoolKind string // the kind of infra machinepool(can be managed/self managed/virtual) + InfraMachinePoolUID types.UID // the UID of the infra machinepool + Namespace string // the namespace in which machinepool machine has to be created + SpecInfraMachines []infrav2exp.OCIMachinePoolMachine // the spec of actual machines in the pool + Logger *logr.Logger // the logger which has to be used +} diff --git a/cloud/util/util_test.go b/cloud/util/util_test.go index ad8e608d..4da6eb60 100644 --- a/cloud/util/util_test.go +++ b/cloud/util/util_test.go @@ -17,6 +17,7 @@ package util import ( "context" + "errors" "reflect" "testing" @@ -24,10 +25,17 @@ import ( infrastructurev1beta2 "github.com/oracle/cluster-api-provider-oci/api/v1beta2" "github.com/oracle/cluster-api-provider-oci/cloud/config" "github.com/oracle/cluster-api-provider-oci/cloud/scope" + infrav2exp "github.com/oracle/cluster-api-provider-oci/exp/api/v1beta2" + "github.com/oracle/oci-go-sdk/v65/common" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/klog/v2/klogr" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/client/interceptor" ) func TestGetClusterIdentityFromRef(t *testing.T) { @@ -304,7 +312,6 @@ func TestInitClientsAndRegion(t *testing.T) { if err != nil { t.Error(err) } - testCases := []struct { name string namespace string @@ -453,3 +460,422 @@ func TestInitClientsAndRegion(t *testing.T) { }) } } + +func TestCreateManagedMachinesIfNotExists(t *testing.T) { + log := klogr.New() + type test struct { + name string + errorExpected bool + namespace string + client client.Client + machinePool *expclusterv1.MachinePool + cluster *clusterv1.Cluster + clusterAccessor scope.OCIClusterAccessor + clientProvider *scope.ClientProvider + specMachines []infrav2exp.OCIMachinePoolMachine + machineTypEnum infrav2exp.MachineTypeEnum + infraMachinePoolName string + infraMachinePoolKind string + infraMachinePoolUid types.UID + errorMessage string + setup func(t *test) + validate func(g *WithT, t *test) + createPoolMachines []infrav2exp.OCIMachinePoolMachine + } + testCases := []test{ + { + name: "create machine", + namespace: "default", + infraMachinePoolName: "test", + errorExpected: false, + machineTypEnum: infrav2exp.SelfManaged, + machinePool: &expclusterv1.MachinePool{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + }, + cluster: &clusterv1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + }, + specMachines: []infrav2exp.OCIMachinePoolMachine{ + { + Spec: infrav2exp.OCIMachinePoolMachineSpec{ + OCID: common.String("id-1"), + InstanceName: common.String("name-1"), + ProviderID: common.String("oci://id-1"), + MachineType: infrav2exp.SelfManaged, + }, + }, + { + Spec: infrav2exp.OCIMachinePoolMachineSpec{ + OCID: common.String("id-2"), + InstanceName: common.String("name-2"), + ProviderID: common.String("oci://id-2"), + MachineType: infrav2exp.SelfManaged, + }, + }, + }, + setup: func(t *test) { + t.client = interceptor.NewClient(fake.NewClientBuilder().WithObjects().Build(), interceptor.Funcs{ + Create: func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.CreateOption) error { + m := obj.(*infrav2exp.OCIMachinePoolMachine) + t.createPoolMachines = append(t.createPoolMachines, *m) + return nil + }, + }) + }, + validate: func(g *WithT, t *test) { + g.Expect(len(t.createPoolMachines)).To(Equal(2)) + machine := t.createPoolMachines[0] + g.Expect(machine.Spec.MachineType).To(Equal(infrav2exp.SelfManaged)) + g.Expect(*machine.Spec.InstanceName).To(Equal("name-1")) + g.Expect(*machine.Spec.ProviderID).To(Equal("oci://id-1")) + g.Expect(*machine.Spec.OCID).To(Equal("id-1")) + machine = t.createPoolMachines[1] + g.Expect(machine.Spec.MachineType).To(Equal(infrav2exp.SelfManaged)) + g.Expect(*machine.Spec.InstanceName).To(Equal("name-2")) + g.Expect(*machine.Spec.ProviderID).To(Equal("oci://id-2")) + g.Expect(*machine.Spec.OCID).To(Equal("id-2")) + }, + }, + { + name: "machine exists", + namespace: "default", + infraMachinePoolName: "test", + errorExpected: false, + machineTypEnum: infrav2exp.SelfManaged, + machinePool: &expclusterv1.MachinePool{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + }, + cluster: &clusterv1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + }, + specMachines: []infrav2exp.OCIMachinePoolMachine{ + { + Spec: infrav2exp.OCIMachinePoolMachineSpec{ + OCID: common.String("id-1"), + InstanceName: common.String("name-1"), + ProviderID: common.String("oci://id-1"), + MachineType: infrav2exp.SelfManaged, + }, + }, + }, + setup: func(t *test) { + t.client = interceptor.NewClient(fake.NewClientBuilder().WithObjects(&infrav2exp.OCIMachinePoolMachine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + Labels: map[string]string{ + clusterv1.ClusterNameLabel: "test", + clusterv1.MachinePoolNameLabel: "test", + }, + }, + Spec: infrav2exp.OCIMachinePoolMachineSpec{ + OCID: common.String("id-1"), + InstanceName: common.String("name-1"), + ProviderID: common.String("oci://id-1"), + MachineType: infrav2exp.SelfManaged, + }, + }).Build(), interceptor.Funcs{ + Create: func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.CreateOption) error { + m := obj.(*infrav2exp.OCIMachinePoolMachine) + t.createPoolMachines = append(t.createPoolMachines, *m) + return nil + }, + }) + }, + validate: func(g *WithT, t *test) { + g.Expect(len(t.createPoolMachines)).To(Equal(0)) + }, + }, + { + name: "ready status patch", + namespace: "default", + infraMachinePoolName: "test", + errorExpected: false, + machineTypEnum: infrav2exp.SelfManaged, + machinePool: &expclusterv1.MachinePool{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + }, + cluster: &clusterv1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + }, + specMachines: []infrav2exp.OCIMachinePoolMachine{ + { + Spec: infrav2exp.OCIMachinePoolMachineSpec{ + OCID: common.String("id-1"), + InstanceName: common.String("name-1"), + ProviderID: common.String("oci://id-1"), + MachineType: infrav2exp.SelfManaged, + }, + Status: infrav2exp.OCIMachinePoolMachineStatus{ + Ready: true, + }, + }, + }, + setup: func(t *test) { + m := &infrav2exp.OCIMachinePoolMachine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + Labels: map[string]string{ + clusterv1.ClusterNameLabel: "test", + clusterv1.MachinePoolNameLabel: "test", + }, + }, + Spec: infrav2exp.OCIMachinePoolMachineSpec{ + OCID: common.String("id-1"), + InstanceName: common.String("name-1"), + ProviderID: common.String("oci://id-1"), + MachineType: infrav2exp.SelfManaged, + }, + } + t.client = interceptor.NewClient(fake.NewClientBuilder().WithStatusSubresource(m).WithObjects(m).Build(), interceptor.Funcs{ + SubResourcePatch: func(ctx context.Context, client client.Client, subResourceName string, obj client.Object, patch client.Patch, opts ...client.SubResourcePatchOption) error { + m := obj.(*infrav2exp.OCIMachinePoolMachine) + t.createPoolMachines = append(t.createPoolMachines, *m) + return nil + }, + }) + }, + validate: func(g *WithT, t *test) { + g.Expect(len(t.createPoolMachines)).To(Equal(1)) + }, + }, + } + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + tt.setup(&tt) + params := MachineParams{ + tt.client, tt.machinePool, + tt.cluster, tt.infraMachinePoolName, tt.infraMachinePoolKind, tt.infraMachinePoolUid, tt.namespace, tt.specMachines, &log, + } + err := CreateMachinePoolMachinesIfNotExists(context.Background(), params) + if tt.errorExpected { + g.Expect(err).To(Not(BeNil())) + g.Expect(err.Error()).To(Equal(tt.errorMessage)) + } else { + g.Expect(err).To(BeNil()) + } + if tt.validate != nil { + tt.validate(g, &tt) + } + }) + } +} + +func TestDeleteManagedMachinesIfNotExists(t *testing.T) { + log := klogr.New() + type test struct { + name string + errorExpected bool + namespace string + client client.Client + machinePool *expclusterv1.MachinePool + cluster *clusterv1.Cluster + clusterAccessor scope.OCIClusterAccessor + clientProvider *scope.ClientProvider + specMachines []infrav2exp.OCIMachinePoolMachine + machineTypEnum infrav2exp.MachineTypeEnum + infraMachinePoolName string + infraMachinePoolKind string + infraMachinePoolUid types.UID + errorMessage string + setup func(t *test) + validate func(g *WithT, t *test) + deletePoolMachines []clusterv1.Machine + } + testCases := []test{ + { + name: "machine delete", + namespace: "default", + infraMachinePoolName: "test", + errorExpected: false, + machineTypEnum: infrav2exp.SelfManaged, + machinePool: &expclusterv1.MachinePool{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + }, + cluster: &clusterv1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + }, + setup: func(t *test) { + t.client = interceptor.NewClient(fake.NewClientBuilder().WithObjects(&infrav2exp.OCIMachinePoolMachine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + Labels: map[string]string{ + clusterv1.ClusterNameLabel: "test", + clusterv1.MachinePoolNameLabel: "test", + }, + OwnerReferences: []metav1.OwnerReference{ + { + Kind: "Machine", + Name: "test-machine", + APIVersion: clusterv1.GroupVersion.String(), + }, + }, + }, + Spec: infrav2exp.OCIMachinePoolMachineSpec{ + OCID: common.String("id-1"), + InstanceName: common.String("name-1"), + ProviderID: common.String("oci://id-1"), + MachineType: infrav2exp.SelfManaged, + }, + }, &clusterv1.Machine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-machine", + Namespace: "default", + Labels: map[string]string{ + clusterv1.ClusterNameLabel: "test", + clusterv1.MachinePoolNameLabel: "test", + }, + }, + Spec: clusterv1.MachineSpec{}, + }).Build(), interceptor.Funcs{ + Delete: func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.DeleteOption) error { + m := obj.(*clusterv1.Machine) + t.deletePoolMachines = append(t.deletePoolMachines, *m) + return nil + }, + }) + }, + validate: func(g *WithT, t *test) { + g.Expect(len(t.deletePoolMachines)).To(Equal(1)) + g.Expect(t.deletePoolMachines[0].Name).To(Equal("test-machine")) + }, + }, + { + name: "machine delete, no owner", + namespace: "default", + infraMachinePoolName: "test", + errorExpected: false, + machineTypEnum: infrav2exp.SelfManaged, + machinePool: &expclusterv1.MachinePool{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + }, + cluster: &clusterv1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + }, + setup: func(t *test) { + t.client = interceptor.NewClient(fake.NewClientBuilder().WithObjects(&infrav2exp.OCIMachinePoolMachine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + Labels: map[string]string{ + clusterv1.ClusterNameLabel: "test", + clusterv1.MachinePoolNameLabel: "test", + }, + OwnerReferences: []metav1.OwnerReference{ + { + Kind: "Machine", + Name: "test-machine", + APIVersion: clusterv1.GroupVersion.String(), + }, + }, + }, + Spec: infrav2exp.OCIMachinePoolMachineSpec{ + OCID: common.String("id-1"), + InstanceName: common.String("name-1"), + ProviderID: common.String("oci://id-1"), + MachineType: infrav2exp.SelfManaged, + }, + }).Build(), interceptor.Funcs{ + Delete: func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.DeleteOption) error { + m := obj.(*clusterv1.Machine) + t.deletePoolMachines = append(t.deletePoolMachines, *m) + return nil + }, + }) + }, + }, + { + name: "machine delete, other error", + namespace: "default", + infraMachinePoolName: "test", + errorExpected: true, + machineTypEnum: infrav2exp.SelfManaged, + machinePool: &expclusterv1.MachinePool{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + }, + cluster: &clusterv1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + }, + setup: func(t *test) { + t.client = interceptor.NewClient(fake.NewClientBuilder().WithObjects(&infrav2exp.OCIMachinePoolMachine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + Labels: map[string]string{ + clusterv1.ClusterNameLabel: "test", + clusterv1.MachinePoolNameLabel: "test", + }, + OwnerReferences: []metav1.OwnerReference{ + { + Kind: "Machine", + Name: "test-machine", + APIVersion: clusterv1.GroupVersion.String(), + }, + }, + }, + Spec: infrav2exp.OCIMachinePoolMachineSpec{ + OCID: common.String("id-1"), + InstanceName: common.String("name-1"), + ProviderID: common.String("oci://id-1"), + MachineType: infrav2exp.SelfManaged, + }, + }).Build(), interceptor.Funcs{ + Delete: func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.DeleteOption) error { + m := obj.(*clusterv1.Machine) + t.deletePoolMachines = append(t.deletePoolMachines, *m) + return nil + }, + Get: func(ctx context.Context, client client.WithWatch, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { + return errors.New("another error") + }, + }) + }, + }, + } + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + tt.setup(&tt) + params := MachineParams{ + tt.client, tt.machinePool, + tt.cluster, tt.infraMachinePoolName, tt.infraMachinePoolKind, tt.infraMachinePoolUid, tt.namespace, tt.specMachines, &log, + } + err := DeleteOrphanedMachinePoolMachines(context.Background(), params) + if tt.errorExpected { + g.Expect(err).To(Not(BeNil())) + } else { + g.Expect(err).To(BeNil()) + } + if tt.validate != nil { + tt.validate(g, &tt) + } + }) + } +} diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_ocimachinepoolmachines.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_ocimachinepoolmachines.yaml new file mode 100644 index 00000000..5df3d87d --- /dev/null +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_ocimachinepoolmachines.yaml @@ -0,0 +1,199 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: ocimachinepoolmachines.infrastructure.cluster.x-k8s.io +spec: + group: infrastructure.cluster.x-k8s.io + names: + kind: OCIMachinePoolMachine + listKind: OCIMachinePoolMachineList + plural: ocimachinepoolmachines + singular: ocimachinepoolmachine + scope: Namespaced + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + 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: OCIMachinePoolMachineSpec defines the desired state of OCIMachinePoolMachine + properties: + instanceName: + description: InstanceName is the name of the instance. + type: string + machineType: + description: MachineType is the type of the machine. + type: string + ocid: + description: OCID is the OCID of the associated instance. + type: string + providerID: + description: ProviderID is the Oracle Cloud Identifier of the associated + instance. + type: string + type: object + status: + description: OCIMachinePoolMachineStatus defines the observed state of + OCIMachinePoolMachine + properties: + conditions: + description: Conditions defines current service state of the OCIMachinePool. + items: + description: Condition defines an observation of a Cluster API resource + operational state. + properties: + lastTransitionTime: + description: 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: A human readable message indicating details about + the transition. This field may be empty. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. The specific API may choose whether or not this + field is considered a guaranteed API. This field may not be + empty. + type: string + severity: + description: Severity provides an explicit classification of + Reason code, so the users or machines can immediately understand + the current situation and act accordingly. The Severity field + MUST be set only when Status=False. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase or in foo.example.com/CamelCase. + Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + ready: + description: Flag set to true when machine is ready. + type: boolean + type: object + type: object + served: true + storage: false + subresources: + status: {} + - name: v1beta2 + schema: + openAPIV3Schema: + 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: OCIMachinePoolMachineSpec defines the desired state of OCIMachinePoolMachine + properties: + instanceName: + description: InstanceName is the name of the instance. + type: string + machineType: + description: MachineType is the type of the machine. + type: string + ocid: + description: OCID is the OCID of the associated instance. + type: string + providerID: + description: ProviderID is Oracle Cloud Identifier of the associated + instance. + type: string + type: object + status: + description: OCIMachinePoolMachineStatus defines the observed state of + OCIMachinePoolMachine + properties: + conditions: + description: Conditions defines current service state of the OCIMachinePool. + items: + description: Condition defines an observation of a Cluster API resource + operational state. + properties: + lastTransitionTime: + description: 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: A human readable message indicating details about + the transition. This field may be empty. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. The specific API may choose whether or not this + field is considered a guaranteed API. This field may not be + empty. + type: string + severity: + description: Severity provides an explicit classification of + Reason code, so the users or machines can immediately understand + the current situation and act accordingly. The Severity field + MUST be set only when Status=False. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase or in foo.example.com/CamelCase. + Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + ready: + description: Flag set to true when machine is ready. + type: boolean + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_ocimachinepools.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_ocimachinepools.yaml index bf9e25c7..a2c49c73 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_ocimachinepools.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_ocimachinepools.yaml @@ -719,6 +719,10 @@ spec: description: MachineStatusError defines errors states for Machine objects. type: string + infrastructureMachineKind: + description: InfrastructureMachineKind is the kind of the infrastructure + resources behind MachinePool Machines. + type: string ready: description: Ready is true when the provider resource is ready. type: boolean @@ -1435,6 +1439,10 @@ spec: description: MachineStatusError defines errors states for Machine objects. type: string + infrastructureMachineKind: + description: InfrastructureMachineKind is the kind of the infrastructure + resources behind MachinePool Machines. + type: string ready: description: Ready is true when the provider resource is ready. type: boolean diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_ocimanagedmachinepools.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_ocimanagedmachinepools.yaml index 9c48e0cd..6b3808e5 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_ocimanagedmachinepools.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_ocimanagedmachinepools.yaml @@ -265,6 +265,10 @@ spec: description: MachineStatusError defines errors states for Machine objects. type: string + infrastructureMachineKind: + description: InfrastructureMachineKind is the kind of the infrastructure + resources behind MachinePool Machines. + type: string ready: type: boolean replicas: @@ -549,6 +553,10 @@ spec: description: MachineStatusError defines errors states for Machine objects. type: string + infrastructureMachineKind: + description: InfrastructureMachineKind is the kind of the infrastructure + resources behind MachinePool Machines. + type: string ready: type: boolean replicas: diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_ocivirtualmachinepools.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_ocivirtualmachinepools.yaml index 1677d74f..fa0ecfbe 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_ocivirtualmachinepools.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_ocivirtualmachinepools.yaml @@ -189,6 +189,10 @@ spec: description: FailureReason will contains the CAPI MachinePoolStatusFailure if the virtual machine pool has hit an error condition. type: string + infrastructureMachineKind: + description: InfrastructureMachineKind is the kind of the infrastructure + resources behind MachinePool Machines. + type: string ready: type: boolean replicas: @@ -375,6 +379,10 @@ spec: description: FailureReason will contains the CAPI MachinePoolStatusFailure if the virtual machine pool has hit an error condition. type: string + infrastructureMachineKind: + description: InfrastructureMachineKind is the kind of the infrastructure + resources behind MachinePool Machines. + type: string ready: type: boolean replicas: diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 9b489c57..5ec6f957 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -19,6 +19,7 @@ resources: - bases/infrastructure.cluster.x-k8s.io_ocimanagedclustertemplates.yaml - bases/infrastructure.cluster.x-k8s.io_ociclusteridentities.yaml - bases/infrastructure.cluster.x-k8s.io_ocivirtualmachinepools.yaml +- bases/infrastructure.cluster.x-k8s.io_ocimachinepoolmachines.yaml #+kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: @@ -34,6 +35,7 @@ patchesStrategicMerge: - patches/webhook_in_ocimanagedcontrolplanes.yaml - patches/webhook_in_ociclusteridentities.yaml - patches/webhook_in_ocivirtualmachinepools.yaml +- patches/webhook_in_ocimachinepoolmachines.yaml # +kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. @@ -48,6 +50,7 @@ patchesStrategicMerge: - patches/cainjection_in_ocimanagedcontrolplanes.yaml - patches/cainjection_in_ociclusteridentities.yaml - patches/cainjection_in_ocivirtualmachinepools.yaml +- patches/cainjection_in_ocimachinepoolmachines.yaml # +kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/patches/cainjection_in_ocimachinepoolmachines.yaml b/config/crd/patches/cainjection_in_ocimachinepoolmachines.yaml new file mode 100644 index 00000000..1833bba7 --- /dev/null +++ b/config/crd/patches/cainjection_in_ocimachinepoolmachines.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: ocimachinepoolmachines.infrastructure.cluster.x-k8s.io diff --git a/config/crd/patches/webhook_in_ocimachinepoolmachines.yaml b/config/crd/patches/webhook_in_ocimachinepoolmachines.yaml new file mode 100644 index 00000000..d6c1d0a6 --- /dev/null +++ b/config/crd/patches/webhook_in_ocimachinepoolmachines.yaml @@ -0,0 +1,17 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: ocimachinepoolmachines.infrastructure.cluster.x-k8s.io +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 + - v1beta1 diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index b5607d00..3d3f7471 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -31,6 +31,7 @@ spec: - "--metrics-bind-address=127.0.0.1:8080" - "--logging-format=${LOG_FORMAT:=text}" - "--init-oci-clients-on-startup=${INIT_OCI_CLIENTS_ON_STARTUP:=true}" + - "--enable-instance-metadata-service-lookup=${ENABLE_INSTANCE_METADATA_SERVICE_LOOKUP:=false}" image: controller:latest name: manager securityContext: diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 57329dd4..49d4a1e9 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -115,6 +115,7 @@ rules: - get - list - watch + - delete - apiGroups: - cluster.x-k8s.io resources: @@ -253,6 +254,26 @@ rules: - infrastructure.cluster.x-k8s.io resources: - ocivirtualmachinepools/status + verbs: + - get + - patch + - update +- apiGroups: + - infrastructure.cluster.x-k8s.io + resources: + - ocimachinepoolmachines + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - infrastructure.cluster.x-k8s.io + resources: + - ocimachinepoolmachines/status verbs: - get - patch diff --git a/controllers/ocicluster_controller.go b/controllers/ocicluster_controller.go index 987a4e4e..779b08e1 100644 --- a/controllers/ocicluster_controller.go +++ b/controllers/ocicluster_controller.go @@ -21,9 +21,8 @@ import ( "fmt" "strings" - "github.com/oracle/cluster-api-provider-oci/api/v1beta2" - "github.com/go-logr/logr" + "github.com/oracle/cluster-api-provider-oci/api/v1beta2" infrastructurev1beta2 "github.com/oracle/cluster-api-provider-oci/api/v1beta2" "github.com/oracle/cluster-api-provider-oci/cloud/scope" cloudutil "github.com/oracle/cluster-api-provider-oci/cloud/util" @@ -40,13 +39,13 @@ import ( "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/cluster-api/util/predicates" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" ) // OCIClusterReconciler reconciles a OciCluster object @@ -268,33 +267,31 @@ func (r *OCIClusterReconciler) reconcile(ctx context.Context, logger logr.Logger // SetupWithManager sets up the controller with the Manager. func (r *OCIClusterReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error { log := ctrl.LoggerFrom(ctx) - c, err := ctrl.NewControllerManagedBy(mgr). + err := ctrl.NewControllerManagedBy(mgr). WithOptions(options). For(&infrastructurev1beta2.OCICluster{}). WithEventFilter(predicates.ResourceNotPaused(log)). // don't queue reconcile if resource is paused WithEventFilter(predicates.ResourceIsNotExternallyManaged(log)). //the externally managed cluster won't be reconciled - Build(r) + Watches( + &clusterv1.Cluster{}, + handler.EnqueueRequestsFromMapFunc(r.clusterToInfrastructureMapFunc(log)), + builder.WithPredicates( + predicates.ClusterUnpaused(log), + predicates.ResourceNotPausedAndHasFilterLabel(log, ""), + ), + ). + Complete(r) if err != nil { return errors.Wrapf(err, "error creating controller") } - // Add a watch on clusterv1.Cluster object for unpause notifications. - if err = c.Watch( - &source.Kind{Type: &clusterv1.Cluster{}}, - handler.EnqueueRequestsFromMapFunc(r.clusterToInfrastructureMapFunc(ctx, log)), - predicates.ClusterUnpaused(log), - predicates.ResourceNotPausedAndHasFilterLabel(log, ""), - ); err != nil { - return errors.Wrapf(err, "failed adding a watch for ready clusters") - } - return nil } // ClusterToInfrastructureMapFunc returns a handler.ToRequestsFunc that watches for // Cluster events and returns reconciliation requests for an infrastructure provider object. -func (r *OCIClusterReconciler) clusterToInfrastructureMapFunc(ctx context.Context, log logr.Logger) handler.MapFunc { - return func(o client.Object) []reconcile.Request { +func (r *OCIClusterReconciler) clusterToInfrastructureMapFunc(log logr.Logger) handler.MapFunc { + return func(ctx context.Context, o client.Object) []reconcile.Request { c, ok := o.(*clusterv1.Cluster) if !ok { return nil diff --git a/controllers/ocimachine_controller.go b/controllers/ocimachine_controller.go index ebc72e61..87e4d776 100644 --- a/controllers/ocimachine_controller.go +++ b/controllers/ocimachine_controller.go @@ -40,13 +40,13 @@ import ( "sigs.k8s.io/cluster-api/util/conditions" "sigs.k8s.io/cluster-api/util/predicates" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" ) // OCIMachineReconciler reconciles a OciMachine object @@ -172,44 +172,41 @@ func (r *OCIMachineReconciler) Reconcile(ctx context.Context, req ctrl.Request) // SetupWithManager sets up the controller with the Manager. func (r *OCIMachineReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error { - c, err := ctrl.NewControllerManagedBy(mgr). + clusterToObjectFunc, err := util.ClusterToTypedObjectsMapper(r.Client, &infrastructurev1beta2.OCIMachineList{}, mgr.GetScheme()) + if err != nil { + return errors.Wrapf(err, "failed to create mapper for Cluster to OCIMachines") + } + err = ctrl.NewControllerManagedBy(mgr). WithOptions(options). For(&infrastructurev1beta2.OCIMachine{}). + WithEventFilter(predicates.ResourceNotPaused(ctrl.LoggerFrom(ctx))). Watches( - &source.Kind{Type: &clusterv1.Machine{}}, + &clusterv1.Machine{}, handler.EnqueueRequestsFromMapFunc(util.MachineToInfrastructureMapFunc(infrastructurev1beta2. GroupVersion.WithKind(scope.OCIMachineKind))), ). Watches( - &source.Kind{Type: &infrastructurev1beta2.OCICluster{}}, - handler.EnqueueRequestsFromMapFunc(r.OCIClusterToOCIMachines(ctx)), + &infrastructurev1beta2.OCICluster{}, + handler.EnqueueRequestsFromMapFunc(r.OCIClusterToOCIMachines()), ). - WithEventFilter(predicates.ResourceNotPaused(ctrl.LoggerFrom(ctx))). // don't queue reconcile if resource is paused - Build(r) + Watches( + &clusterv1.Cluster{}, + handler.EnqueueRequestsFromMapFunc(clusterToObjectFunc), + builder.WithPredicates( + predicates.ClusterUnpausedAndInfrastructureReady(ctrl.LoggerFrom(ctx)), + ), + ). + // don't queue reconcile if resource is paused + Complete(r) if err != nil { return errors.Wrapf(err, "error creating controller") } - - clusterToObjectFunc, err := util.ClusterToObjectsMapper(r.Client, &infrastructurev1beta2.OCIMachineList{}, mgr.GetScheme()) - if err != nil { - return errors.Wrapf(err, "failed to create mapper for Cluster to OCIMachines") - } - - // Add a watch on clusterv1.Cluster object for unpause & ready notifications. - if err := c.Watch( - &source.Kind{Type: &clusterv1.Cluster{}}, - handler.EnqueueRequestsFromMapFunc(clusterToObjectFunc), - predicates.ClusterUnpausedAndInfrastructureReady(ctrl.LoggerFrom(ctx)), - ); err != nil { - return errors.Wrapf(err, "failed adding a watch for ready clusters") - } - return nil } -func (r *OCIMachineReconciler) OCIClusterToOCIMachines(ctx context.Context) handler.MapFunc { - log := ctrl.LoggerFrom(ctx) - return func(o client.Object) []ctrl.Request { +func (r *OCIMachineReconciler) OCIClusterToOCIMachines() handler.MapFunc { + return func(ctx context.Context, o client.Object) []ctrl.Request { + log := ctrl.LoggerFrom(ctx) result := []ctrl.Request{} c, ok := o.(*infrastructurev1beta2.OCICluster) diff --git a/controllers/ocimachine_controller_test.go b/controllers/ocimachine_controller_test.go index 394d0069..988b839d 100644 --- a/controllers/ocimachine_controller_test.go +++ b/controllers/ocimachine_controller_test.go @@ -22,19 +22,17 @@ import ( "testing" "time" - "github.com/oracle/cluster-api-provider-oci/cloud/ociutil" - "github.com/oracle/cluster-api-provider-oci/cloud/services/networkloadbalancer/mock_nlb" - "github.com/oracle/cluster-api-provider-oci/cloud/services/vcn/mock_vcn" - "github.com/oracle/oci-go-sdk/v65/networkloadbalancer" - "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/golang/mock/gomock" . "github.com/onsi/gomega" infrastructurev1beta2 "github.com/oracle/cluster-api-provider-oci/api/v1beta2" + "github.com/oracle/cluster-api-provider-oci/cloud/ociutil" "github.com/oracle/cluster-api-provider-oci/cloud/scope" "github.com/oracle/cluster-api-provider-oci/cloud/services/compute/mock_compute" + "github.com/oracle/cluster-api-provider-oci/cloud/services/networkloadbalancer/mock_nlb" + "github.com/oracle/cluster-api-provider-oci/cloud/services/vcn/mock_vcn" "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/core" + "github.com/oracle/oci-go-sdk/v65/networkloadbalancer" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -42,7 +40,9 @@ import ( "k8s.io/client-go/tools/record" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" "sigs.k8s.io/cluster-api/util/conditions" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" ) @@ -117,7 +117,7 @@ func TestMachineReconciliation(t *testing.T) { defer teardown(t, g) setup(t, g) - client := fake.NewClientBuilder().WithObjects(tc.objects...).Build() + client := fake.NewClientBuilder().WithStatusSubresource(tc.objects...).WithObjects(tc.objects...).Build() r = OCIMachineReconciler{ Client: client, Scheme: runtime.NewScheme(), @@ -592,6 +592,7 @@ func TestMachineReconciliationDelete(t *testing.T) { ociMachine = getOciMachine() now := metav1.NewTime(time.Now()) ociMachine.DeletionTimestamp = &now + controllerutil.AddFinalizer(ociMachine, infrastructurev1beta2.MachineFinalizer) client := fake.NewClientBuilder().WithObjects(getSecret(), getMachine(), ociMachine, getCluster(), getOCICluster()).Build() clientProvider, err := scope.MockNewClientProvider(scope.MockOCIClients{ ComputeClient: computeClient, @@ -660,6 +661,7 @@ func TestMachineReconciliationDeletionNormal(t *testing.T) { now := metav1.NewTime(time.Now()) ociMachine = getOciMachine() ociMachine.DeletionTimestamp = &now + controllerutil.AddFinalizer(ociMachine, infrastructurev1beta2.MachineFinalizer) machine := getMachine() machine.Spec.Bootstrap.DataSecretName = common.String("bootstrap") mockCtrl = gomock.NewController(t) diff --git a/controllers/ocimanagedcluster_controller.go b/controllers/ocimanagedcluster_controller.go index c626a573..8b6df6d7 100644 --- a/controllers/ocimanagedcluster_controller.go +++ b/controllers/ocimanagedcluster_controller.go @@ -38,13 +38,13 @@ import ( "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/cluster-api/util/predicates" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" ) // OCIManagedClusterReconciler reconciles a OciCluster object @@ -275,38 +275,39 @@ func (r *OCIManagedClusterReconciler) reconcile(ctx context.Context, logger logr // SetupWithManager sets up the controller with the Manager. func (r *OCIManagedClusterReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error { log := ctrl.LoggerFrom(ctx) - ociManagedControlPlaneMapper, err := OCIManagedControlPlaneToOCIManagedClusterMapper(ctx, r.Client, log) - c, err := ctrl.NewControllerManagedBy(mgr). + ociManagedControlPlaneMapper, err := OCIManagedControlPlaneToOCIManagedClusterMapper(r.Client, log) + if err != nil { + return err + } + err = ctrl.NewControllerManagedBy(mgr). WithOptions(options). For(&infrastructurev1beta2.OCIManagedCluster{}). WithEventFilter(predicates.ResourceNotPaused(log)). // don't queue reconcile if resource is paused // watch OCIManagedControlPlane resources Watches( - &source.Kind{Type: &infrastructurev1beta2.OCIManagedControlPlane{}}, + &infrastructurev1beta2.OCIManagedControlPlane{}, handler.EnqueueRequestsFromMapFunc(ociManagedControlPlaneMapper), ). - Build(r) + Watches( + &clusterv1.Cluster{}, + handler.EnqueueRequestsFromMapFunc(r.clusterToInfrastructureMapFunc(log)), + builder.WithPredicates( + predicates.ClusterUnpaused(log), + predicates.ResourceNotPausedAndHasFilterLabel(log, ""), + ), + ). + Complete(r) if err != nil { return errors.Wrapf(err, "error creating controller") } - // Add a watch on clusterv1.Cluster object for unpause notifications. - if err = c.Watch( - &source.Kind{Type: &clusterv1.Cluster{}}, - handler.EnqueueRequestsFromMapFunc(r.clusterToInfrastructureMapFunc(ctx, log)), - predicates.ClusterUnpaused(log), - predicates.ResourceNotPausedAndHasFilterLabel(log, ""), - ); err != nil { - return errors.Wrapf(err, "failed adding a watch for ready clusters") - } - return nil } // ClusterToInfrastructureMapFunc returns a handler.ToRequestsFunc that watches for // Cluster events and returns reconciliation requests for an infrastructure provider object. -func (r *OCIManagedClusterReconciler) clusterToInfrastructureMapFunc(ctx context.Context, log logr.Logger) handler.MapFunc { - return func(o client.Object) []reconcile.Request { +func (r *OCIManagedClusterReconciler) clusterToInfrastructureMapFunc(log logr.Logger) handler.MapFunc { + return func(ctx context.Context, o client.Object) []reconcile.Request { c, ok := o.(*clusterv1.Cluster) if !ok { return nil @@ -438,8 +439,8 @@ func (r *OCIManagedClusterReconciler) reconcileDelete(ctx context.Context, logge return reconcile.Result{}, nil } -func OCIManagedControlPlaneToOCIManagedClusterMapper(ctx context.Context, c client.Client, log logr.Logger) (handler.MapFunc, error) { - return func(o client.Object) []ctrl.Request { +func OCIManagedControlPlaneToOCIManagedClusterMapper(c client.Client, log logr.Logger) (handler.MapFunc, error) { + return func(ctx context.Context, o client.Object) []ctrl.Request { ociManagedControlPlane, ok := o.(*infrastructurev1beta2.OCIManagedControlPlane) if !ok { log.Error(errors.Errorf("expected an OCIManagedControlPlane, got %T instead", o), "failed to map OCIManagedControlPlane") diff --git a/controllers/ocimanagedcluster_controlplane_controller.go b/controllers/ocimanagedcluster_controlplane_controller.go index 166e0b4c..c696c7ef 100644 --- a/controllers/ocimanagedcluster_controlplane_controller.go +++ b/controllers/ocimanagedcluster_controlplane_controller.go @@ -40,13 +40,13 @@ import ( "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/cluster-api/util/predicates" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" ) // OCIManagedClusterControlPlaneReconciler reconciles a OCIManagedControlPlane object @@ -252,34 +252,37 @@ func (r *OCIManagedClusterControlPlaneReconciler) reconcile(ctx context.Context, // SetupWithManager sets up the controller with the Manager. func (r *OCIManagedClusterControlPlaneReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error { log := ctrl.LoggerFrom(ctx) - ociManagedClusterMapper, err := OCIManagedClusterToOCIManagedControlPlaneMapper(ctx, r.Client, log) - c, err := ctrl.NewControllerManagedBy(mgr). + ociManagedClusterMapper, err := OCIManagedClusterToOCIManagedControlPlaneMapper(r.Client, log) + if err != nil { + return err + } + err = ctrl.NewControllerManagedBy(mgr). WithOptions(options). For(&infrastructurev1beta2.OCIManagedControlPlane{}). Watches( - &source.Kind{Type: &infrastructurev1beta2.OCIManagedCluster{}}, + &infrastructurev1beta2.OCIManagedCluster{}, handler.EnqueueRequestsFromMapFunc(ociManagedClusterMapper), ). - Build(r) + Watches( + &clusterv1.Cluster{}, + handler.EnqueueRequestsFromMapFunc(ClusterToOCIManagedControlPlaneMapper()), + builder.WithPredicates( + predicates.ClusterUnpaused(log), + predicates.ResourceNotPausedAndHasFilterLabel(log, ""), + ), + ). + Complete(r) if err != nil { return errors.Wrapf(err, "error creating controller") } - if err = c.Watch( - &source.Kind{Type: &clusterv1.Cluster{}}, - handler.EnqueueRequestsFromMapFunc(ClusterToOCIManagedControlPlaneMapper()), - predicates.ResourceNotPausedAndHasFilterLabel(log, ""), - ); err != nil { - return errors.Wrapf(err, "failed adding a watch for ready clusters") - } - return nil } // ClusterToInfrastructureMapFunc returns a handler.ToRequestsFunc that watches for // Cluster events and returns reconciliation requests for an infrastructure provider object. -func (r *OCIManagedClusterControlPlaneReconciler) clusterToInfrastructureMapFunc(ctx context.Context, log logr.Logger) handler.MapFunc { - return func(o client.Object) []reconcile.Request { +func (r *OCIManagedClusterControlPlaneReconciler) clusterToInfrastructureMapFunc(log logr.Logger) handler.MapFunc { + return func(ctx context.Context, o client.Object) []reconcile.Request { c, ok := o.(*clusterv1.Cluster) if !ok { return nil @@ -369,8 +372,8 @@ func (r *OCIManagedClusterControlPlaneReconciler) reconcileDelete(ctx context.Co } } -func OCIManagedClusterToOCIManagedControlPlaneMapper(ctx context.Context, c client.Client, log logr.Logger) (handler.MapFunc, error) { - return func(o client.Object) []ctrl.Request { +func OCIManagedClusterToOCIManagedControlPlaneMapper(c client.Client, log logr.Logger) (handler.MapFunc, error) { + return func(ctx context.Context, o client.Object) []ctrl.Request { ociCluster, ok := o.(*infrastructurev1beta2.OCIManagedCluster) if !ok { log.Error(errors.Errorf("expected an OCIManagedCluster, got %T instead", o), "failed to map OCIManagedCluster") @@ -413,7 +416,7 @@ func OCIManagedClusterToOCIManagedControlPlaneMapper(ctx context.Context, c clie } func ClusterToOCIManagedControlPlaneMapper() handler.MapFunc { - return func(o client.Object) []ctrl.Request { + return func(ctx context.Context, o client.Object) []ctrl.Request { cluster, ok := o.(*clusterv1.Cluster) if !ok { return nil diff --git a/exp/api/v1beta1/ocimachinepool_machine_types.go b/exp/api/v1beta1/ocimachinepool_machine_types.go new file mode 100644 index 00000000..14023b8f --- /dev/null +++ b/exp/api/v1beta1/ocimachinepool_machine_types.go @@ -0,0 +1,86 @@ +/* +Copyright (c) 2022 Oracle and/or its affiliates. + +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 v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" +) + +// +kubebuilder:object:generate=true +// +groupName=infrastructure.cluster.x-k8s.io + +const ( + Managed MachineTypeEnum = "MANAGED_TYPE" + Virtual MachineTypeEnum = "VIRTUAL_TYPE" + SelfManaged MachineTypeEnum = "SELF_MANAGED_TYPE" +) + +type MachineTypeEnum string + +// OCIMachinePoolMachineSpec defines the desired state of OCIMachinePoolMachine +type OCIMachinePoolMachineSpec struct { + // ProviderID is the Oracle Cloud Identifier of the associated instance. + // +optional + ProviderID *string `json:"providerID,omitempty"` + + // OCID is the OCID of the associated instance. + // +optional + OCID *string `json:"ocid,omitempty"` + + // InstanceName is the name of the instance. + // +optional + InstanceName *string `json:"instanceName,omitempty"` + + // MachineType is the type of the machine. + MachineType MachineTypeEnum `json:"machineType,omitempty"` +} + +// OCIMachinePoolMachineStatus defines the observed state of OCIMachinePoolMachine +type OCIMachinePoolMachineStatus struct { + // Flag set to true when machine is ready. + // +optional + Ready bool `json:"ready,omitempty"` + + // Conditions defines current service state of the OCIMachinePool. + // +optional + Conditions clusterv1.Conditions `json:"conditions,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +type OCIMachinePoolMachine struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec OCIMachinePoolMachineSpec `json:"spec,omitempty"` + Status OCIMachinePoolMachineStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// OCIMachinePoolMachineList contains a list of OCIMachinePoolMachine. +type OCIMachinePoolMachineList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []OCIMachinePoolMachine `json:"items"` +} + +func init() { + SchemeBuilder.Register(&OCIMachinePoolMachine{}, &OCIMachinePoolMachineList{}) +} diff --git a/exp/api/v1beta1/ocimachinepool_types.go b/exp/api/v1beta1/ocimachinepool_types.go index 42bd559d..1abcca86 100644 --- a/exp/api/v1beta1/ocimachinepool_types.go +++ b/exp/api/v1beta1/ocimachinepool_types.go @@ -206,6 +206,10 @@ type OCIMachinePoolStatus struct { FailureReason *errors.MachineStatusError `json:"failureReason,omitempty"` FailureMessage *string `json:"failureMessage,omitempty"` + + // InfrastructureMachineKind is the kind of the infrastructure resources behind MachinePool Machines. + // +optional + InfrastructureMachineKind string `json:"infrastructureMachineKind,omitempty"` } //+kubebuilder:object:root=true diff --git a/exp/api/v1beta1/ocimachinepoolmachine_conversion.go b/exp/api/v1beta1/ocimachinepoolmachine_conversion.go new file mode 100644 index 00000000..e71d279f --- /dev/null +++ b/exp/api/v1beta1/ocimachinepoolmachine_conversion.go @@ -0,0 +1,55 @@ +/* +Copyright (c) 2021, 2022 Oracle and/or its affiliates. + +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 v1beta1 + +import ( + "github.com/oracle/cluster-api-provider-oci/exp/api/v1beta2" + utilconversion "sigs.k8s.io/cluster-api/util/conversion" + "sigs.k8s.io/controller-runtime/pkg/conversion" +) + +// ConvertTo converts the v1beta1 OCIMachinePoolMachine receiver to a v1beta2 OCIMachinePoolMachine. +func (src *OCIMachinePoolMachine) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*v1beta2.OCIMachinePoolMachine) + + if err := Convert_v1beta1_OCIMachinePoolMachine_To_v1beta2_OCIMachinePoolMachine(src, dst, nil); err != nil { + return err + } + + restored := &v1beta2.OCIMachinePoolMachine{} + if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + return err + } + + return nil +} + +// ConvertFrom converts receiver to a v1beta2 OCIMachinePoolMachine. +func (r *OCIMachinePoolMachine) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*v1beta2.OCIMachinePoolMachine) + + if err := Convert_v1beta2_OCIMachinePoolMachine_To_v1beta1_OCIMachinePoolMachine(src, r, nil); err != nil { + return err + } + + // Preserve Hub data on down-conversion. + if err := utilconversion.MarshalData(src, r); err != nil { + return err + } + + return nil +} diff --git a/exp/api/v1beta1/ocimanagedmachinepool_types.go b/exp/api/v1beta1/ocimanagedmachinepool_types.go index fc694eee..124675ef 100644 --- a/exp/api/v1beta1/ocimanagedmachinepool_types.go +++ b/exp/api/v1beta1/ocimanagedmachinepool_types.go @@ -200,6 +200,10 @@ type OCIManagedMachinePoolStatus struct { FailureReason *errors.MachineStatusError `json:"failureReason,omitempty"` FailureMessages []string `json:"failureMessages,omitempty"` + + // InfrastructureMachineKind is the kind of the infrastructure resources behind MachinePool Machines. + // +optional + InfrastructureMachineKind string `json:"infrastructureMachineKind,omitempty"` } //+kubebuilder:object:root=true diff --git a/exp/api/v1beta1/ocivirtualmachinepool_types.go b/exp/api/v1beta1/ocivirtualmachinepool_types.go index 23a4f0d3..f9417761 100644 --- a/exp/api/v1beta1/ocivirtualmachinepool_types.go +++ b/exp/api/v1beta1/ocivirtualmachinepool_types.go @@ -113,6 +113,10 @@ type OCIVirtualMachinePoolStatus struct { // FailureMessages contains the verbose erorr messages related to the virtual machine pool failures. // +optional FailureMessages []string `json:"failureMessages,omitempty"` + + // InfrastructureMachineKind is the kind of the infrastructure resources behind MachinePool Machines. + // +optional + InfrastructureMachineKind string `json:"infrastructureMachineKind,omitempty"` } //+kubebuilder:object:root=true diff --git a/exp/api/v1beta1/zz_generated.conversion.go b/exp/api/v1beta1/zz_generated.conversion.go index ca21e436..bdd646a2 100644 --- a/exp/api/v1beta1/zz_generated.conversion.go +++ b/exp/api/v1beta1/zz_generated.conversion.go @@ -160,6 +160,46 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*OCIMachinePoolMachine)(nil), (*v1beta2.OCIMachinePoolMachine)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_OCIMachinePoolMachine_To_v1beta2_OCIMachinePoolMachine(a.(*OCIMachinePoolMachine), b.(*v1beta2.OCIMachinePoolMachine), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.OCIMachinePoolMachine)(nil), (*OCIMachinePoolMachine)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_OCIMachinePoolMachine_To_v1beta1_OCIMachinePoolMachine(a.(*v1beta2.OCIMachinePoolMachine), b.(*OCIMachinePoolMachine), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*OCIMachinePoolMachineList)(nil), (*v1beta2.OCIMachinePoolMachineList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_OCIMachinePoolMachineList_To_v1beta2_OCIMachinePoolMachineList(a.(*OCIMachinePoolMachineList), b.(*v1beta2.OCIMachinePoolMachineList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.OCIMachinePoolMachineList)(nil), (*OCIMachinePoolMachineList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_OCIMachinePoolMachineList_To_v1beta1_OCIMachinePoolMachineList(a.(*v1beta2.OCIMachinePoolMachineList), b.(*OCIMachinePoolMachineList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*OCIMachinePoolMachineSpec)(nil), (*v1beta2.OCIMachinePoolMachineSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_OCIMachinePoolMachineSpec_To_v1beta2_OCIMachinePoolMachineSpec(a.(*OCIMachinePoolMachineSpec), b.(*v1beta2.OCIMachinePoolMachineSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.OCIMachinePoolMachineSpec)(nil), (*OCIMachinePoolMachineSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_OCIMachinePoolMachineSpec_To_v1beta1_OCIMachinePoolMachineSpec(a.(*v1beta2.OCIMachinePoolMachineSpec), b.(*OCIMachinePoolMachineSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*OCIMachinePoolMachineStatus)(nil), (*v1beta2.OCIMachinePoolMachineStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_OCIMachinePoolMachineStatus_To_v1beta2_OCIMachinePoolMachineStatus(a.(*OCIMachinePoolMachineStatus), b.(*v1beta2.OCIMachinePoolMachineStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.OCIMachinePoolMachineStatus)(nil), (*OCIMachinePoolMachineStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_OCIMachinePoolMachineStatus_To_v1beta1_OCIMachinePoolMachineStatus(a.(*v1beta2.OCIMachinePoolMachineStatus), b.(*OCIMachinePoolMachineStatus), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*OCIMachinePoolSpec)(nil), (*v1beta2.OCIMachinePoolSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_OCIMachinePoolSpec_To_v1beta2_OCIMachinePoolSpec(a.(*OCIMachinePoolSpec), b.(*v1beta2.OCIMachinePoolSpec), scope) }); err != nil { @@ -750,6 +790,108 @@ func Convert_v1beta2_OCIMachinePoolList_To_v1beta1_OCIMachinePoolList(in *v1beta return autoConvert_v1beta2_OCIMachinePoolList_To_v1beta1_OCIMachinePoolList(in, out, s) } +func autoConvert_v1beta1_OCIMachinePoolMachine_To_v1beta2_OCIMachinePoolMachine(in *OCIMachinePoolMachine, out *v1beta2.OCIMachinePoolMachine, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta1_OCIMachinePoolMachineSpec_To_v1beta2_OCIMachinePoolMachineSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta1_OCIMachinePoolMachineStatus_To_v1beta2_OCIMachinePoolMachineStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_OCIMachinePoolMachine_To_v1beta2_OCIMachinePoolMachine is an autogenerated conversion function. +func Convert_v1beta1_OCIMachinePoolMachine_To_v1beta2_OCIMachinePoolMachine(in *OCIMachinePoolMachine, out *v1beta2.OCIMachinePoolMachine, s conversion.Scope) error { + return autoConvert_v1beta1_OCIMachinePoolMachine_To_v1beta2_OCIMachinePoolMachine(in, out, s) +} + +func autoConvert_v1beta2_OCIMachinePoolMachine_To_v1beta1_OCIMachinePoolMachine(in *v1beta2.OCIMachinePoolMachine, out *OCIMachinePoolMachine, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta2_OCIMachinePoolMachineSpec_To_v1beta1_OCIMachinePoolMachineSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta2_OCIMachinePoolMachineStatus_To_v1beta1_OCIMachinePoolMachineStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_OCIMachinePoolMachine_To_v1beta1_OCIMachinePoolMachine is an autogenerated conversion function. +func Convert_v1beta2_OCIMachinePoolMachine_To_v1beta1_OCIMachinePoolMachine(in *v1beta2.OCIMachinePoolMachine, out *OCIMachinePoolMachine, s conversion.Scope) error { + return autoConvert_v1beta2_OCIMachinePoolMachine_To_v1beta1_OCIMachinePoolMachine(in, out, s) +} + +func autoConvert_v1beta1_OCIMachinePoolMachineList_To_v1beta2_OCIMachinePoolMachineList(in *OCIMachinePoolMachineList, out *v1beta2.OCIMachinePoolMachineList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + out.Items = *(*[]v1beta2.OCIMachinePoolMachine)(unsafe.Pointer(&in.Items)) + return nil +} + +// Convert_v1beta1_OCIMachinePoolMachineList_To_v1beta2_OCIMachinePoolMachineList is an autogenerated conversion function. +func Convert_v1beta1_OCIMachinePoolMachineList_To_v1beta2_OCIMachinePoolMachineList(in *OCIMachinePoolMachineList, out *v1beta2.OCIMachinePoolMachineList, s conversion.Scope) error { + return autoConvert_v1beta1_OCIMachinePoolMachineList_To_v1beta2_OCIMachinePoolMachineList(in, out, s) +} + +func autoConvert_v1beta2_OCIMachinePoolMachineList_To_v1beta1_OCIMachinePoolMachineList(in *v1beta2.OCIMachinePoolMachineList, out *OCIMachinePoolMachineList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + out.Items = *(*[]OCIMachinePoolMachine)(unsafe.Pointer(&in.Items)) + return nil +} + +// Convert_v1beta2_OCIMachinePoolMachineList_To_v1beta1_OCIMachinePoolMachineList is an autogenerated conversion function. +func Convert_v1beta2_OCIMachinePoolMachineList_To_v1beta1_OCIMachinePoolMachineList(in *v1beta2.OCIMachinePoolMachineList, out *OCIMachinePoolMachineList, s conversion.Scope) error { + return autoConvert_v1beta2_OCIMachinePoolMachineList_To_v1beta1_OCIMachinePoolMachineList(in, out, s) +} + +func autoConvert_v1beta1_OCIMachinePoolMachineSpec_To_v1beta2_OCIMachinePoolMachineSpec(in *OCIMachinePoolMachineSpec, out *v1beta2.OCIMachinePoolMachineSpec, s conversion.Scope) error { + out.ProviderID = (*string)(unsafe.Pointer(in.ProviderID)) + out.OCID = (*string)(unsafe.Pointer(in.OCID)) + out.InstanceName = (*string)(unsafe.Pointer(in.InstanceName)) + out.MachineType = v1beta2.MachineTypeEnum(in.MachineType) + return nil +} + +// Convert_v1beta1_OCIMachinePoolMachineSpec_To_v1beta2_OCIMachinePoolMachineSpec is an autogenerated conversion function. +func Convert_v1beta1_OCIMachinePoolMachineSpec_To_v1beta2_OCIMachinePoolMachineSpec(in *OCIMachinePoolMachineSpec, out *v1beta2.OCIMachinePoolMachineSpec, s conversion.Scope) error { + return autoConvert_v1beta1_OCIMachinePoolMachineSpec_To_v1beta2_OCIMachinePoolMachineSpec(in, out, s) +} + +func autoConvert_v1beta2_OCIMachinePoolMachineSpec_To_v1beta1_OCIMachinePoolMachineSpec(in *v1beta2.OCIMachinePoolMachineSpec, out *OCIMachinePoolMachineSpec, s conversion.Scope) error { + out.ProviderID = (*string)(unsafe.Pointer(in.ProviderID)) + out.OCID = (*string)(unsafe.Pointer(in.OCID)) + out.InstanceName = (*string)(unsafe.Pointer(in.InstanceName)) + out.MachineType = MachineTypeEnum(in.MachineType) + return nil +} + +// Convert_v1beta2_OCIMachinePoolMachineSpec_To_v1beta1_OCIMachinePoolMachineSpec is an autogenerated conversion function. +func Convert_v1beta2_OCIMachinePoolMachineSpec_To_v1beta1_OCIMachinePoolMachineSpec(in *v1beta2.OCIMachinePoolMachineSpec, out *OCIMachinePoolMachineSpec, s conversion.Scope) error { + return autoConvert_v1beta2_OCIMachinePoolMachineSpec_To_v1beta1_OCIMachinePoolMachineSpec(in, out, s) +} + +func autoConvert_v1beta1_OCIMachinePoolMachineStatus_To_v1beta2_OCIMachinePoolMachineStatus(in *OCIMachinePoolMachineStatus, out *v1beta2.OCIMachinePoolMachineStatus, s conversion.Scope) error { + out.Ready = in.Ready + out.Conditions = *(*clusterapiapiv1beta1.Conditions)(unsafe.Pointer(&in.Conditions)) + return nil +} + +// Convert_v1beta1_OCIMachinePoolMachineStatus_To_v1beta2_OCIMachinePoolMachineStatus is an autogenerated conversion function. +func Convert_v1beta1_OCIMachinePoolMachineStatus_To_v1beta2_OCIMachinePoolMachineStatus(in *OCIMachinePoolMachineStatus, out *v1beta2.OCIMachinePoolMachineStatus, s conversion.Scope) error { + return autoConvert_v1beta1_OCIMachinePoolMachineStatus_To_v1beta2_OCIMachinePoolMachineStatus(in, out, s) +} + +func autoConvert_v1beta2_OCIMachinePoolMachineStatus_To_v1beta1_OCIMachinePoolMachineStatus(in *v1beta2.OCIMachinePoolMachineStatus, out *OCIMachinePoolMachineStatus, s conversion.Scope) error { + out.Ready = in.Ready + out.Conditions = *(*clusterapiapiv1beta1.Conditions)(unsafe.Pointer(&in.Conditions)) + return nil +} + +// Convert_v1beta2_OCIMachinePoolMachineStatus_To_v1beta1_OCIMachinePoolMachineStatus is an autogenerated conversion function. +func Convert_v1beta2_OCIMachinePoolMachineStatus_To_v1beta1_OCIMachinePoolMachineStatus(in *v1beta2.OCIMachinePoolMachineStatus, out *OCIMachinePoolMachineStatus, s conversion.Scope) error { + return autoConvert_v1beta2_OCIMachinePoolMachineStatus_To_v1beta1_OCIMachinePoolMachineStatus(in, out, s) +} + func autoConvert_v1beta1_OCIMachinePoolSpec_To_v1beta2_OCIMachinePoolSpec(in *OCIMachinePoolSpec, out *v1beta2.OCIMachinePoolSpec, s conversion.Scope) error { out.ProviderID = (*string)(unsafe.Pointer(in.ProviderID)) out.OCID = (*string)(unsafe.Pointer(in.OCID)) @@ -788,6 +930,7 @@ func autoConvert_v1beta1_OCIMachinePoolStatus_To_v1beta2_OCIMachinePoolStatus(in out.Conditions = *(*clusterapiapiv1beta1.Conditions)(unsafe.Pointer(&in.Conditions)) out.FailureReason = (*errors.MachineStatusError)(unsafe.Pointer(in.FailureReason)) out.FailureMessage = (*string)(unsafe.Pointer(in.FailureMessage)) + out.InfrastructureMachineKind = in.InfrastructureMachineKind return nil } @@ -802,6 +945,7 @@ func autoConvert_v1beta2_OCIMachinePoolStatus_To_v1beta1_OCIMachinePoolStatus(in out.Conditions = *(*clusterapiapiv1beta1.Conditions)(unsafe.Pointer(&in.Conditions)) out.FailureReason = (*errors.MachineStatusError)(unsafe.Pointer(in.FailureReason)) out.FailureMessage = (*string)(unsafe.Pointer(in.FailureMessage)) + out.InfrastructureMachineKind = in.InfrastructureMachineKind return nil } @@ -928,6 +1072,7 @@ func autoConvert_v1beta1_OCIManagedMachinePoolStatus_To_v1beta2_OCIManagedMachin out.Replicas = in.Replicas out.FailureReason = (*errors.MachineStatusError)(unsafe.Pointer(in.FailureReason)) out.FailureMessages = *(*[]string)(unsafe.Pointer(&in.FailureMessages)) + out.InfrastructureMachineKind = in.InfrastructureMachineKind return nil } @@ -942,6 +1087,7 @@ func autoConvert_v1beta2_OCIManagedMachinePoolStatus_To_v1beta1_OCIManagedMachin out.Replicas = in.Replicas out.FailureReason = (*errors.MachineStatusError)(unsafe.Pointer(in.FailureReason)) out.FailureMessages = *(*[]string)(unsafe.Pointer(&in.FailureMessages)) + out.InfrastructureMachineKind = in.InfrastructureMachineKind return nil } @@ -1164,6 +1310,7 @@ func autoConvert_v1beta1_OCIVirtualMachinePoolStatus_To_v1beta2_OCIVirtualMachin out.Replicas = in.Replicas out.FailureReason = (*errors.MachinePoolStatusFailure)(unsafe.Pointer(in.FailureReason)) out.FailureMessages = *(*[]string)(unsafe.Pointer(&in.FailureMessages)) + out.InfrastructureMachineKind = in.InfrastructureMachineKind return nil } @@ -1178,6 +1325,7 @@ func autoConvert_v1beta2_OCIVirtualMachinePoolStatus_To_v1beta1_OCIVirtualMachin out.Replicas = in.Replicas out.FailureReason = (*errors.MachinePoolStatusFailure)(unsafe.Pointer(in.FailureReason)) out.FailureMessages = *(*[]string)(unsafe.Pointer(&in.FailureMessages)) + out.InfrastructureMachineKind = in.InfrastructureMachineKind return nil } diff --git a/exp/api/v1beta1/zz_generated.deepcopy.go b/exp/api/v1beta1/zz_generated.deepcopy.go index 467c6095..d5951691 100644 --- a/exp/api/v1beta1/zz_generated.deepcopy.go +++ b/exp/api/v1beta1/zz_generated.deepcopy.go @@ -22,9 +22,9 @@ limitations under the License. package v1beta1 import ( - apiv1beta1 "github.com/oracle/cluster-api-provider-oci/api/v1beta1" + cluster_api_provider_ociapiv1beta1 "github.com/oracle/cluster-api-provider-oci/api/v1beta1" "k8s.io/apimachinery/pkg/runtime" - cluster_apiapiv1beta1 "sigs.k8s.io/cluster-api/api/v1beta1" + apiv1beta1 "sigs.k8s.io/cluster-api/api/v1beta1" "sigs.k8s.io/cluster-api/errors" ) @@ -48,27 +48,27 @@ func (in *InstanceConfiguration) DeepCopyInto(out *InstanceConfiguration) { } if in.InstanceVnicConfiguration != nil { in, out := &in.InstanceVnicConfiguration, &out.InstanceVnicConfiguration - *out = new(apiv1beta1.NetworkDetails) + *out = new(cluster_api_provider_ociapiv1beta1.NetworkDetails) (*in).DeepCopyInto(*out) } if in.PlatformConfig != nil { in, out := &in.PlatformConfig, &out.PlatformConfig - *out = new(apiv1beta1.PlatformConfig) + *out = new(cluster_api_provider_ociapiv1beta1.PlatformConfig) (*in).DeepCopyInto(*out) } if in.AgentConfig != nil { in, out := &in.AgentConfig, &out.AgentConfig - *out = new(apiv1beta1.LaunchInstanceAgentConfig) + *out = new(cluster_api_provider_ociapiv1beta1.LaunchInstanceAgentConfig) (*in).DeepCopyInto(*out) } if in.PreemptibleInstanceConfig != nil { in, out := &in.PreemptibleInstanceConfig, &out.PreemptibleInstanceConfig - *out = new(apiv1beta1.PreemptibleInstanceConfig) + *out = new(cluster_api_provider_ociapiv1beta1.PreemptibleInstanceConfig) (*in).DeepCopyInto(*out) } if in.AvailabilityConfig != nil { in, out := &in.AvailabilityConfig, &out.AvailabilityConfig - *out = new(apiv1beta1.LaunchInstanceAvailabilityConfig) + *out = new(cluster_api_provider_ociapiv1beta1.LaunchInstanceAvailabilityConfig) (*in).DeepCopyInto(*out) } if in.DedicatedVmHostId != nil { @@ -78,12 +78,12 @@ func (in *InstanceConfiguration) DeepCopyInto(out *InstanceConfiguration) { } if in.LaunchOptions != nil { in, out := &in.LaunchOptions, &out.LaunchOptions - *out = new(apiv1beta1.LaunchOptions) + *out = new(cluster_api_provider_ociapiv1beta1.LaunchOptions) (*in).DeepCopyInto(*out) } if in.InstanceOptions != nil { in, out := &in.InstanceOptions, &out.InstanceOptions - *out = new(apiv1beta1.InstanceOptions) + *out = new(cluster_api_provider_ociapiv1beta1.InstanceOptions) (*in).DeepCopyInto(*out) } if in.IsPvEncryptionInTransitEnabled != nil { @@ -439,6 +439,117 @@ func (in *OCIMachinePoolList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OCIMachinePoolMachine) DeepCopyInto(out *OCIMachinePoolMachine) { + *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 OCIMachinePoolMachine. +func (in *OCIMachinePoolMachine) DeepCopy() *OCIMachinePoolMachine { + if in == nil { + return nil + } + out := new(OCIMachinePoolMachine) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OCIMachinePoolMachine) 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 *OCIMachinePoolMachineList) DeepCopyInto(out *OCIMachinePoolMachineList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]OCIMachinePoolMachine, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIMachinePoolMachineList. +func (in *OCIMachinePoolMachineList) DeepCopy() *OCIMachinePoolMachineList { + if in == nil { + return nil + } + out := new(OCIMachinePoolMachineList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OCIMachinePoolMachineList) 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 *OCIMachinePoolMachineSpec) DeepCopyInto(out *OCIMachinePoolMachineSpec) { + *out = *in + if in.ProviderID != nil { + in, out := &in.ProviderID, &out.ProviderID + *out = new(string) + **out = **in + } + if in.OCID != nil { + in, out := &in.OCID, &out.OCID + *out = new(string) + **out = **in + } + if in.InstanceName != nil { + in, out := &in.InstanceName, &out.InstanceName + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIMachinePoolMachineSpec. +func (in *OCIMachinePoolMachineSpec) DeepCopy() *OCIMachinePoolMachineSpec { + if in == nil { + return nil + } + out := new(OCIMachinePoolMachineSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OCIMachinePoolMachineStatus) DeepCopyInto(out *OCIMachinePoolMachineStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(apiv1beta1.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIMachinePoolMachineStatus. +func (in *OCIMachinePoolMachineStatus) DeepCopy() *OCIMachinePoolMachineStatus { + if in == nil { + return nil + } + out := new(OCIMachinePoolMachineStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OCIMachinePoolSpec) DeepCopyInto(out *OCIMachinePoolSpec) { *out = *in @@ -480,7 +591,7 @@ func (in *OCIMachinePoolStatus) DeepCopyInto(out *OCIMachinePoolStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make(cluster_apiapiv1beta1.Conditions, len(*in)) + *out = make(apiv1beta1.Conditions, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -640,7 +751,7 @@ func (in *OCIManagedMachinePoolStatus) DeepCopyInto(out *OCIManagedMachinePoolSt *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make(cluster_apiapiv1beta1.Conditions, len(*in)) + *out = make(apiv1beta1.Conditions, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -878,7 +989,7 @@ func (in *OCIVirtualMachinePoolStatus) DeepCopyInto(out *OCIVirtualMachinePoolSt *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make(cluster_apiapiv1beta1.Conditions, len(*in)) + *out = make(apiv1beta1.Conditions, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } diff --git a/exp/api/v1beta2/conversion.go b/exp/api/v1beta2/conversion.go index d4080ebf..7071c365 100644 --- a/exp/api/v1beta2/conversion.go +++ b/exp/api/v1beta2/conversion.go @@ -33,3 +33,9 @@ func (*OCIVirtualMachinePool) Hub() {} // Hub marks OCIVirtualMachinePool as a conversion hub. func (*OCIVirtualMachinePoolList) Hub() {} + +// Hub marks OCIManagedMachinePoolMachine as a conversion hub. +func (*OCIMachinePoolMachine) Hub() {} + +// Hub marks OCIManagedMachinePoolMachineList as a conversion hub. +func (*OCIMachinePoolMachineList) Hub() {} diff --git a/exp/api/v1beta2/ocimachinepool_machine_types.go b/exp/api/v1beta2/ocimachinepool_machine_types.go new file mode 100644 index 00000000..fe975442 --- /dev/null +++ b/exp/api/v1beta2/ocimachinepool_machine_types.go @@ -0,0 +1,90 @@ +/* +Copyright (c) 2022 Oracle and/or its affiliates. + +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 v1beta2 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" +) + +// +kubebuilder:object:generate=true +// +groupName=infrastructure.cluster.x-k8s.io + +const ( + Managed MachineTypeEnum = "MANAGED_TYPE" + Virtual MachineTypeEnum = "VIRTUAL_TYPE" + SelfManaged MachineTypeEnum = "SELF_MANAGED_TYPE" + // MachinePoolMachineFinalizer is the finalizer for managed machine pool. + MachinePoolMachineFinalizer = "ocimachinepoolmachine.infrastructure.cluster.x-k8s.io" +) + +type MachineTypeEnum string + +// OCIMachinePoolMachineSpec defines the desired state of OCIMachinePoolMachine +type OCIMachinePoolMachineSpec struct { + // ProviderID is Oracle Cloud Identifier of the associated instance. + // +optional + ProviderID *string `json:"providerID,omitempty"` + + // OCID is the OCID of the associated instance. + // +optional + OCID *string `json:"ocid,omitempty"` + + // InstanceName is the name of the instance. + // +optional + InstanceName *string `json:"instanceName,omitempty"` + + // MachineType is the type of the machine. + MachineType MachineTypeEnum `json:"machineType,omitempty"` +} + +// OCIMachinePoolMachineStatus defines the observed state of OCIMachinePoolMachine +type OCIMachinePoolMachineStatus struct { + // Flag set to true when machine is ready. + // +optional + Ready bool `json:"ready,omitempty"` + + // Conditions defines current service state of the OCIMachinePool. + // +optional + Conditions clusterv1.Conditions `json:"conditions,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +// +kubebuilder:storageversion + +type OCIMachinePoolMachine struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec OCIMachinePoolMachineSpec `json:"spec,omitempty"` + Status OCIMachinePoolMachineStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:storageversion + +// OCIMachinePoolMachineList contains a list of OCIMachinePoolMachine. +type OCIMachinePoolMachineList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []OCIMachinePoolMachine `json:"items"` +} + +func init() { + SchemeBuilder.Register(&OCIMachinePoolMachine{}, &OCIMachinePoolMachineList{}) +} diff --git a/exp/api/v1beta2/ocimachinepool_types.go b/exp/api/v1beta2/ocimachinepool_types.go index 9100918b..926c1ae9 100644 --- a/exp/api/v1beta2/ocimachinepool_types.go +++ b/exp/api/v1beta2/ocimachinepool_types.go @@ -205,6 +205,10 @@ type OCIMachinePoolStatus struct { FailureReason *errors.MachineStatusError `json:"failureReason,omitempty"` FailureMessage *string `json:"failureMessage,omitempty"` + + // InfrastructureMachineKind is the kind of the infrastructure resources behind MachinePool Machines. + // +optional + InfrastructureMachineKind string `json:"infrastructureMachineKind,omitempty"` } //+kubebuilder:object:root=true diff --git a/exp/api/v1beta2/ocimanagedmachinepool_types.go b/exp/api/v1beta2/ocimanagedmachinepool_types.go index 9691e58c..5c57bd0f 100644 --- a/exp/api/v1beta2/ocimanagedmachinepool_types.go +++ b/exp/api/v1beta2/ocimanagedmachinepool_types.go @@ -223,6 +223,10 @@ type OCIManagedMachinePoolStatus struct { FailureReason *errors.MachineStatusError `json:"failureReason,omitempty"` FailureMessages []string `json:"failureMessages,omitempty"` + + // InfrastructureMachineKind is the kind of the infrastructure resources behind MachinePool Machines. + // +optional + InfrastructureMachineKind string `json:"infrastructureMachineKind,omitempty"` } //+kubebuilder:object:root=true diff --git a/exp/api/v1beta2/ocimanagedmachinepool_webhook.go b/exp/api/v1beta2/ocimanagedmachinepool_webhook.go index 2cf43230..b40d0366 100644 --- a/exp/api/v1beta2/ocimanagedmachinepool_webhook.go +++ b/exp/api/v1beta2/ocimanagedmachinepool_webhook.go @@ -27,6 +27,7 @@ import ( "sigs.k8s.io/cluster-api/util/version" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) var logger = ctrl.Log.WithName("ocimachinepool-resource") @@ -60,16 +61,16 @@ func (m *OCIManagedMachinePool) Default() { } } -func (m *OCIManagedMachinePool) ValidateCreate() error { +func (m *OCIManagedMachinePool) ValidateCreate() (admission.Warnings, error) { var allErrs field.ErrorList if len(m.Name) > 31 { allErrs = append(allErrs, field.Invalid(field.NewPath("Name"), m.Name, "Name cannot be more than 31 characters")) } allErrs = m.validateVersion(allErrs) if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid(m.GroupVersionKind().GroupKind(), m.Name, allErrs) + return nil, apierrors.NewInvalid(m.GroupVersionKind().GroupKind(), m.Name, allErrs) } func (m *OCIManagedMachinePool) validateVersion(allErrs field.ErrorList) field.ErrorList { @@ -88,11 +89,11 @@ func (m *OCIManagedMachinePool) validateVersion(allErrs field.ErrorList) field.E return allErrs } -func (m *OCIManagedMachinePool) ValidateUpdate(old runtime.Object) error { +func (m *OCIManagedMachinePool) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { var allErrs field.ErrorList oldManagedMachinePool, ok := old.(*OCIManagedMachinePool) if !ok { - return apierrors.NewBadRequest(fmt.Sprintf("expected an OCIManagedMachinePool but got a %T", old)) + return nil, apierrors.NewBadRequest(fmt.Sprintf("expected an OCIManagedMachinePool but got a %T", old)) } allErrs = m.validateVersion(allErrs) @@ -113,13 +114,13 @@ func (m *OCIManagedMachinePool) ValidateUpdate(old runtime.Object) error { } } if len(allErrs) == 0 { - return nil + return nil, nil } - return apierrors.NewInvalid(m.GroupVersionKind().GroupKind(), m.Name, allErrs) + return nil, apierrors.NewInvalid(m.GroupVersionKind().GroupKind(), m.Name, allErrs) } -func (m *OCIManagedMachinePool) ValidateDelete() error { - return nil +func (m *OCIManagedMachinePool) ValidateDelete() (admission.Warnings, error) { + return nil, nil } func (m *OCIManagedMachinePool) getImageId() *string { diff --git a/exp/api/v1beta2/ocimanagedmachinepool_webhook_test.go b/exp/api/v1beta2/ocimanagedmachinepool_webhook_test.go index 9f538f7f..cbb57529 100644 --- a/exp/api/v1beta2/ocimanagedmachinepool_webhook_test.go +++ b/exp/api/v1beta2/ocimanagedmachinepool_webhook_test.go @@ -135,11 +135,12 @@ func TestOCIManagedMachinePool_ValidateCreate(t *testing.T) { g := gomega.NewWithT(t) if test.expectErr { - err := test.m.ValidateCreate() + _, err := test.m.ValidateCreate() g.Expect(err).NotTo(gomega.Succeed()) g.Expect(strings.Contains(err.Error(), test.errorMgsShouldContain)).To(gomega.BeTrue()) } else { - g.Expect(test.m.ValidateCreate()).To(gomega.Succeed()) + _, err := test.m.ValidateCreate() + g.Expect(err).To(gomega.Succeed()) } }) } @@ -283,7 +284,7 @@ func TestOCIManagedMachinePool_ValidateUpdate(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { g := gomega.NewWithT(t) - err := test.m.ValidateUpdate(test.old) + _, err := test.m.ValidateUpdate(test.old) if test.expectErr { g.Expect(err).NotTo(gomega.Succeed()) g.Expect(strings.Contains(err.Error(), test.errorMgsShouldContain)).To(gomega.BeTrue()) diff --git a/exp/api/v1beta2/ocivirtualmachinepool_types.go b/exp/api/v1beta2/ocivirtualmachinepool_types.go index 7c4b9ea0..b5c25c32 100644 --- a/exp/api/v1beta2/ocivirtualmachinepool_types.go +++ b/exp/api/v1beta2/ocivirtualmachinepool_types.go @@ -113,6 +113,10 @@ type OCIVirtualMachinePoolStatus struct { // FailureMessages contains the verbose erorr messages related to the virtual machine pool failures. // +optional FailureMessages []string `json:"failureMessages,omitempty"` + + // InfrastructureMachineKind is the kind of the infrastructure resources behind MachinePool Machines. + // +optional + InfrastructureMachineKind string `json:"infrastructureMachineKind,omitempty"` } //+kubebuilder:object:root=true diff --git a/exp/api/v1beta2/ocivirtualmachinepool_webhook.go b/exp/api/v1beta2/ocivirtualmachinepool_webhook.go index 9f980b80..172904e4 100644 --- a/exp/api/v1beta2/ocivirtualmachinepool_webhook.go +++ b/exp/api/v1beta2/ocivirtualmachinepool_webhook.go @@ -21,6 +21,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) var virtualMpLogger = ctrl.Log.WithName("ocivirtualmachinepool-resource") @@ -51,14 +52,14 @@ func (m *OCIVirtualMachinePool) Default() { } } -func (m *OCIVirtualMachinePool) ValidateCreate() error { - return nil +func (m *OCIVirtualMachinePool) ValidateCreate() (admission.Warnings, error) { + return nil, nil } -func (m *OCIVirtualMachinePool) ValidateUpdate(old runtime.Object) error { - return nil +func (m *OCIVirtualMachinePool) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + return nil, nil } -func (m *OCIVirtualMachinePool) ValidateDelete() error { - return nil +func (m *OCIVirtualMachinePool) ValidateDelete() (admission.Warnings, error) { + return nil, nil } diff --git a/exp/api/v1beta2/zz_generated.deepcopy.go b/exp/api/v1beta2/zz_generated.deepcopy.go index bd6f4d1c..ab5d8c5d 100644 --- a/exp/api/v1beta2/zz_generated.deepcopy.go +++ b/exp/api/v1beta2/zz_generated.deepcopy.go @@ -469,6 +469,117 @@ func (in *OCIMachinePoolList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OCIMachinePoolMachine) DeepCopyInto(out *OCIMachinePoolMachine) { + *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 OCIMachinePoolMachine. +func (in *OCIMachinePoolMachine) DeepCopy() *OCIMachinePoolMachine { + if in == nil { + return nil + } + out := new(OCIMachinePoolMachine) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OCIMachinePoolMachine) 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 *OCIMachinePoolMachineList) DeepCopyInto(out *OCIMachinePoolMachineList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]OCIMachinePoolMachine, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIMachinePoolMachineList. +func (in *OCIMachinePoolMachineList) DeepCopy() *OCIMachinePoolMachineList { + if in == nil { + return nil + } + out := new(OCIMachinePoolMachineList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OCIMachinePoolMachineList) 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 *OCIMachinePoolMachineSpec) DeepCopyInto(out *OCIMachinePoolMachineSpec) { + *out = *in + if in.ProviderID != nil { + in, out := &in.ProviderID, &out.ProviderID + *out = new(string) + **out = **in + } + if in.OCID != nil { + in, out := &in.OCID, &out.OCID + *out = new(string) + **out = **in + } + if in.InstanceName != nil { + in, out := &in.InstanceName, &out.InstanceName + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIMachinePoolMachineSpec. +func (in *OCIMachinePoolMachineSpec) DeepCopy() *OCIMachinePoolMachineSpec { + if in == nil { + return nil + } + out := new(OCIMachinePoolMachineSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OCIMachinePoolMachineStatus) DeepCopyInto(out *OCIMachinePoolMachineStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(v1beta1.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIMachinePoolMachineStatus. +func (in *OCIMachinePoolMachineStatus) DeepCopy() *OCIMachinePoolMachineStatus { + if in == nil { + return nil + } + out := new(OCIMachinePoolMachineStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OCIMachinePoolSpec) DeepCopyInto(out *OCIMachinePoolSpec) { *out = *in diff --git a/exp/controllers/ocimachinepool_controller.go b/exp/controllers/ocimachinepool_controller.go index 19eb8e8a..70883ec1 100644 --- a/exp/controllers/ocimachinepool_controller.go +++ b/exp/controllers/ocimachinepool_controller.go @@ -45,13 +45,13 @@ import ( "sigs.k8s.io/cluster-api/util/conditions" "sigs.k8s.io/cluster-api/util/predicates" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" ) // OCIMachinePoolReconciler reconciles a OCIMachinePool object @@ -180,37 +180,36 @@ func (r *OCIMachinePoolReconciler) Reconcile(ctx context.Context, req ctrl.Reque // SetupWithManager sets up the controller with the Manager. func (r *OCIMachinePoolReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error { logger := log.FromContext(ctx) - c, err := ctrl.NewControllerManagedBy(mgr). + clusterToObjectFunc, err := util.ClusterToTypedObjectsMapper(r.Client, &expV1Beta1.OCIMachinePoolList{}, mgr.GetScheme()) + if err != nil { + return errors.Wrapf(err, "failed to create mapper for Cluster to OCIMachinePool") + } + err = ctrl.NewControllerManagedBy(mgr). WithOptions(options). For(&infrav2exp.OCIMachinePool{}). + WithEventFilter(predicates.ResourceNotPaused(ctrl.LoggerFrom(ctx))). Watches( - &source.Kind{Type: &expclusterv1.MachinePool{}}, + &expclusterv1.MachinePool{}, handler.EnqueueRequestsFromMapFunc(machinePoolToInfrastructureMapFunc(infrav2exp. GroupVersion.WithKind(scope.OCIMachinePoolKind), logger)), ). - WithEventFilter(predicates.ResourceNotPaused(ctrl.LoggerFrom(ctx))). - Build(r) + Watches( + &clusterv1.Cluster{}, + handler.EnqueueRequestsFromMapFunc(clusterToObjectFunc), + builder.WithPredicates( + predicates.ClusterUnpausedAndInfrastructureReady(ctrl.LoggerFrom(ctx)), + ), + ). + Complete(r) if err != nil { return errors.Wrapf(err, "error creating controller") } - clusterToObjectFunc, err := util.ClusterToObjectsMapper(r.Client, &expV1Beta1.OCIMachinePoolList{}, mgr.GetScheme()) - if err != nil { - return errors.Wrapf(err, "failed to create mapper for Cluster to OCIMachines") - } - // Add a watch on clusterv1.Cluster object for unpause & ready notifications. - if err := c.Watch( - &source.Kind{Type: &clusterv1.Cluster{}}, - handler.EnqueueRequestsFromMapFunc(clusterToObjectFunc), - predicates.ClusterUnpausedAndInfrastructureReady(ctrl.LoggerFrom(ctx)), - ); err != nil { - return errors.Wrapf(err, "failed adding a watch for ready clusters") - } return nil } func machinePoolToInfrastructureMapFunc(gvk schema.GroupVersionKind, logger logr.Logger) handler.MapFunc { - return func(o client.Object) []reconcile.Request { + return func(ctx context.Context, o client.Object) []reconcile.Request { m, ok := o.(*expclusterv1.MachinePool) if !ok { panic(fmt.Sprintf("Expected a MachinePool but got a %T", o)) @@ -340,10 +339,26 @@ func (r *OCIMachinePoolReconciler) reconcileNormal(ctx context.Context, logger l "Instance pool is in ready state") conditions.MarkTrue(machinePoolScope.OCIMachinePool, infrav2exp.InstancePoolReadyCondition) - instanceCount, err := machinePoolScope.SetListandSetMachinePoolInstances(ctx) + machines, err := machinePoolScope.SetListandSetMachinePoolInstances(ctx) + if err != nil { + return reconcile.Result{}, err + } + if err != nil { + return reconcile.Result{}, err + } + providerIDList := make([]string, 0) + for _, machine := range machines { + if machine.Status.Ready { + providerIDList = append(providerIDList, *machine.Spec.ProviderID) + } + } + machinePoolScope.OCIMachinePool.Spec.ProviderIDList = providerIDList + + err = r.reconcileMachines(ctx, err, machinePoolScope, machines) if err != nil { return reconcile.Result{}, err } + instancePool, err = machinePoolScope.UpdatePool(ctx, instancePool) if err != nil { r.Recorder.Eventf(machinePoolScope.OCIMachinePool, corev1.EventTypeWarning, "FailedUpdate", "Failed to update instance pool: %v", err) @@ -354,7 +369,7 @@ func (r *OCIMachinePoolReconciler) reconcileNormal(ctx context.Context, logger l if err != nil { return ctrl.Result{}, err } - machinePoolScope.SetReplicaCount(instanceCount) + machinePoolScope.SetReplicaCount(int32(len(providerIDList))) machinePoolScope.SetReady() default: conditions.MarkFalse(machinePoolScope.OCIMachinePool, infrav2exp.InstancePoolReadyCondition, infrav2exp.InstancePoolProvisionFailedReason, clusterv1.ConditionSeverityError, "") @@ -364,8 +379,9 @@ func (r *OCIMachinePoolReconciler) reconcileNormal(ctx context.Context, logger l "Instance pool has invalid lifecycle state %s", instancePool.LifecycleState) return reconcile.Result{}, errors.New(fmt.Sprintf("instance pool has invalid lifecycle state %s", instancePool.LifecycleState)) } - - return ctrl.Result{}, nil + // we reconcile every 5 minutes in case any reconciliation have happened behind the scenes by Instancepool service on + // the instance pool(removing unhealthy nodes etc) which has to be percolated to machinepool machines + return reconcile.Result{RequeueAfter: 300 * time.Second}, nil } func (r *OCIMachinePoolReconciler) reconcileDelete(ctx context.Context, machinePoolScope *scope.MachinePoolScope) (_ ctrl.Result, reterr error) { @@ -431,3 +447,29 @@ func (r *OCIMachinePoolReconciler) reconcileDelete(ctx context.Context, machineP controllerutil.RemoveFinalizer(machinePoolScope.OCIMachinePool, infrav2exp.MachinePoolFinalizer) return ctrl.Result{}, nil } + +func (r *OCIMachinePoolReconciler) reconcileMachines(ctx context.Context, err error, machinePoolScope *scope.MachinePoolScope, specInfraMachines []infrav2exp.OCIMachinePoolMachine) error { + params := cloudutil.MachineParams{ + Cluster: machinePoolScope.Cluster, + MachinePool: machinePoolScope.MachinePool, + InfraMachinePoolName: machinePoolScope.OCIMachinePool.Name, + Namespace: machinePoolScope.OCIMachinePool.Namespace, + SpecInfraMachines: specInfraMachines, + Client: r.Client, + Logger: machinePoolScope.Logger, + InfraMachinePoolKind: machinePoolScope.OCIMachinePool.Kind, + InfraMachinePoolUID: machinePoolScope.OCIMachinePool.UID, + } + err = cloudutil.CreateMachinePoolMachinesIfNotExists(ctx, params) + if err != nil { + conditions.MarkFalse(machinePoolScope.OCIMachinePool, clusterv1.ReadyCondition, "FailedToDeleteOrphanedMachines", clusterv1.ConditionSeverityWarning, err.Error()) + return errors.Wrap(err, "failed to create missing machines") + } + + err = cloudutil.DeleteOrphanedMachinePoolMachines(ctx, params) + if err != nil { + conditions.MarkFalse(machinePoolScope.OCIMachinePool, clusterv1.ReadyCondition, "FailedToDeleteOrphanedMachines", clusterv1.ConditionSeverityWarning, err.Error()) + return errors.Wrap(err, "failed to delete orphaned machines") + } + return nil +} diff --git a/exp/controllers/ocimachinepool_controller_test.go b/exp/controllers/ocimachinepool_controller_test.go index f6909bc2..d4879719 100644 --- a/exp/controllers/ocimachinepool_controller_test.go +++ b/exp/controllers/ocimachinepool_controller_test.go @@ -39,6 +39,7 @@ import ( "sigs.k8s.io/cluster-api/util/conditions" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/client/interceptor" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" ) @@ -211,7 +212,7 @@ func TestReconciliationFunction(t *testing.T) { computeManagementClient = mock_computemanagement.NewMockClient(mockCtrl) machinePool := getMachinePool() ociMachinePool = getOCIMachinePool() - client := fake.NewClientBuilder().WithObjects(getSecret(), ociMachinePool).Build() + client := fake.NewClientBuilder().WithStatusSubresource(ociMachinePool).WithObjects(getSecret(), ociMachinePool).Build() ociCluster := getOCIClusterWithOwner() ms, err = scope.NewMachinePoolScope(scope.MachinePoolScopeParams{ ComputeManagementClient: computeManagementClient, @@ -235,27 +236,31 @@ func TestReconciliationFunction(t *testing.T) { teardown := func(t *testing.T, g *WithT) { mockCtrl.Finish() } - tests := []struct { + type test struct { name string errorExpected bool expectedEvent string eventNotExpected string conditionAssertion []conditionAssertion - testSpecificSetup func(machinePoolScope *scope.MachinePoolScope, computeManagementClient *mock_computemanagement.MockClient) + testSpecificSetup func(t *test, machinePoolScope *scope.MachinePoolScope, computeManagementClient *mock_computemanagement.MockClient) expectedFailureMessages []string - }{ + createPoolMachines []infrav2exp.OCIMachinePoolMachine + deletePoolMachines []clusterv1.Machine + validate func(g *WithT, t *test) + } + tests := []test{ { name: "bootstrap data not ready", errorExpected: false, conditionAssertion: []conditionAssertion{{infrav2exp.InstancePoolReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityInfo, infrastructurev1beta2.WaitingForBootstrapDataReason}}, - testSpecificSetup: func(machinePoolScope *scope.MachinePoolScope, computeManagementClient *mock_computemanagement.MockClient) { + testSpecificSetup: func(t *test, machinePoolScope *scope.MachinePoolScope, computeManagementClient *mock_computemanagement.MockClient) { }, }, { name: "instance pool create", errorExpected: false, conditionAssertion: []conditionAssertion{{infrav2exp.LaunchTemplateReadyCondition, corev1.ConditionTrue, "", ""}}, - testSpecificSetup: func(machinePoolScope *scope.MachinePoolScope, computeManagementClient *mock_computemanagement.MockClient) { + testSpecificSetup: func(t *test, machinePoolScope *scope.MachinePoolScope, computeManagementClient *mock_computemanagement.MockClient) { ms.OCIMachinePool.Spec.InstanceConfiguration = infrav2exp.InstanceConfiguration{ Shape: common.String("test-shape"), InstanceConfigurationId: common.String("test"), @@ -300,11 +305,128 @@ func TestReconciliationFunction(t *testing.T) { name: "instance pool running", errorExpected: false, conditionAssertion: []conditionAssertion{{infrav2exp.LaunchTemplateReadyCondition, corev1.ConditionTrue, "", ""}, {infrav2exp.InstancePoolReadyCondition, corev1.ConditionTrue, "", ""}}, - testSpecificSetup: func(machinePoolScope *scope.MachinePoolScope, computeManagementClient *mock_computemanagement.MockClient) { + testSpecificSetup: func(t *test, machinePoolScope *scope.MachinePoolScope, computeManagementClient *mock_computemanagement.MockClient) { + ms.OCIMachinePool.Spec.InstanceConfiguration = infrav2exp.InstanceConfiguration{ + Shape: common.String("test-shape"), + InstanceConfigurationId: common.String("test"), + } + r.Client = interceptor.NewClient(fake.NewClientBuilder().WithObjects(getSecret(), ociMachinePool).Build(), interceptor.Funcs{ + Create: func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.CreateOption) error { + m := obj.(*infrav2exp.OCIMachinePoolMachine) + t.createPoolMachines = append(t.createPoolMachines, *m) + return nil + }, + }) + ms.MachinePool.Spec.Template.Spec.Bootstrap.DataSecretName = common.String("bootstrap") + ms.OCIMachinePool.Spec.OCID = common.String("pool-id") + computeManagementClient.EXPECT().GetInstanceConfiguration(gomock.Any(), gomock.Eq(core.GetInstanceConfigurationRequest{ + InstanceConfigurationId: common.String("test"), + })). + Return(core.GetInstanceConfigurationResponse{ + InstanceConfiguration: core.InstanceConfiguration{ + Id: common.String("test"), + InstanceDetails: core.ComputeInstanceDetails{ + LaunchDetails: &core.InstanceConfigurationLaunchInstanceDetails{ + DefinedTags: definedTagsInterface, + FreeformTags: tags, + CompartmentId: common.String("test-compartment"), + Shape: common.String("test-shape"), + CreateVnicDetails: &core.InstanceConfigurationCreateVnicDetails{ + FreeformTags: tags, + NsgIds: []string{"worker-nsg-id"}, + SubnetId: common.String("worker-subnet-id"), + }, + SourceDetails: core.InstanceConfigurationInstanceSourceViaImageDetails{}, + Metadata: map[string]string{"user_data": "dGVzdA=="}, + }, + }, + }, + }, nil) + + computeManagementClient.EXPECT().GetInstancePool(gomock.Any(), gomock.Any()). + Return(core.GetInstancePoolResponse{ + InstancePool: core.InstancePool{ + LifecycleState: core.InstancePoolLifecycleStateRunning, + Id: common.String("pool-id"), + InstanceConfigurationId: common.String("test"), + Size: common.Int(3), + }, + }, nil) + computeManagementClient.EXPECT().ListInstancePoolInstances(gomock.Any(), gomock.Any()). + Return(core.ListInstancePoolInstancesResponse{ + Items: []core.InstanceSummary{{ + Id: common.String("id-1"), + State: common.String("Running"), + DisplayName: common.String("name-1"), + }}, + }, nil) + computeManagementClient.EXPECT().ListInstanceConfigurations(gomock.Any(), gomock.Any()). + Return(core.ListInstanceConfigurationsResponse{}, nil) + }, + validate: func(g *WithT, t *test) { + g.Expect(len(t.createPoolMachines)).To(Equal(1)) + machine := t.createPoolMachines[0] + g.Expect(machine.Spec.MachineType).To(Equal(infrav2exp.SelfManaged)) + g.Expect(*machine.Spec.InstanceName).To(Equal("name-1")) + g.Expect(*machine.Spec.ProviderID).To(Equal("oci://id-1")) + g.Expect(*machine.Spec.OCID).To(Equal("id-1")) + }, + }, + { + name: "delete unwanted machinepool machine", + errorExpected: false, + conditionAssertion: []conditionAssertion{{infrav2exp.LaunchTemplateReadyCondition, corev1.ConditionTrue, "", ""}, {infrav2exp.InstancePoolReadyCondition, corev1.ConditionTrue, "", ""}}, + testSpecificSetup: func(t *test, machinePoolScope *scope.MachinePoolScope, computeManagementClient *mock_computemanagement.MockClient) { ms.OCIMachinePool.Spec.InstanceConfiguration = infrav2exp.InstanceConfiguration{ Shape: common.String("test-shape"), InstanceConfigurationId: common.String("test"), } + fakeClient := fake.NewClientBuilder().WithObjects(&infrav2exp.OCIMachinePoolMachine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + Labels: map[string]string{ + clusterv1.ClusterNameLabel: "test-cluster", + clusterv1.MachinePoolNameLabel: "test", + }, + OwnerReferences: []metav1.OwnerReference{ + { + Kind: "Machine", + Name: "test", + APIVersion: clusterv1.GroupVersion.String(), + }, + }, + }, + Spec: infrav2exp.OCIMachinePoolMachineSpec{ + OCID: common.String("id-2"), + InstanceName: common.String("name-2"), + ProviderID: common.String("id-2"), + MachineType: infrav2exp.Managed, + }, + }, &clusterv1.Machine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + Labels: map[string]string{ + clusterv1.ClusterNameLabel: "oci-cluster", + clusterv1.MachinePoolNameLabel: "test", + }, + }, + Spec: clusterv1.MachineSpec{}, + }).Build() + t.deletePoolMachines = make([]clusterv1.Machine, 0) + r.Client = interceptor.NewClient(fakeClient, interceptor.Funcs{ + Create: func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.CreateOption) error { + m := obj.(*infrav2exp.OCIMachinePoolMachine) + t.createPoolMachines = append(t.createPoolMachines, *m) + return nil + }, + Delete: func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.DeleteOption) error { + m := obj.(*clusterv1.Machine) + t.deletePoolMachines = append(t.deletePoolMachines, *m) + return nil + }, + }) ms.MachinePool.Spec.Template.Spec.Bootstrap.DataSecretName = common.String("bootstrap") ms.OCIMachinePool.Spec.OCID = common.String("pool-id") computeManagementClient.EXPECT().GetInstanceConfiguration(gomock.Any(), gomock.Eq(core.GetInstanceConfigurationRequest{ @@ -343,19 +465,32 @@ func TestReconciliationFunction(t *testing.T) { computeManagementClient.EXPECT().ListInstancePoolInstances(gomock.Any(), gomock.Any()). Return(core.ListInstancePoolInstancesResponse{ Items: []core.InstanceSummary{{ - Id: common.String("id-1"), - State: common.String("Running"), + Id: common.String("id-1"), + State: common.String("Running"), + DisplayName: common.String("name-1"), }}, }, nil) computeManagementClient.EXPECT().ListInstanceConfigurations(gomock.Any(), gomock.Any()). Return(core.ListInstanceConfigurationsResponse{}, nil) }, + validate: func(g *WithT, t *test) { + g.Expect(len(t.createPoolMachines)).To(Equal(1)) + machine := t.createPoolMachines[0] + g.Expect(machine.Spec.MachineType).To(Equal(infrav2exp.SelfManaged)) + g.Expect(*machine.Spec.InstanceName).To(Equal("name-1")) + g.Expect(*machine.Spec.ProviderID).To(Equal("oci://id-1")) + g.Expect(*machine.Spec.OCID).To(Equal("id-1")) + + g.Expect(len(t.deletePoolMachines)).To(Equal(1)) + deleteMachine := t.deletePoolMachines[0] + g.Expect(deleteMachine.Name).To(Equal("test")) + }, }, { name: "instance pool failed", errorExpected: true, conditionAssertion: []conditionAssertion{{infrav2exp.LaunchTemplateReadyCondition, corev1.ConditionTrue, "", ""}, {infrav2exp.InstancePoolReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityError, infrav2exp.InstancePoolProvisionFailedReason}}, - testSpecificSetup: func(machinePoolScope *scope.MachinePoolScope, computeManagementClient *mock_computemanagement.MockClient) { + testSpecificSetup: func(t *test, machinePoolScope *scope.MachinePoolScope, computeManagementClient *mock_computemanagement.MockClient) { ms.OCIMachinePool.Spec.InstanceConfiguration = infrav2exp.InstanceConfiguration{ Shape: common.String("test-shape"), InstanceConfigurationId: common.String("test"), @@ -404,7 +539,7 @@ func TestReconciliationFunction(t *testing.T) { g := NewWithT(t) defer teardown(t, g) setup(t, g) - tc.testSpecificSetup(ms, computeManagementClient) + tc.testSpecificSetup(&tc, ms, computeManagementClient) ctx := context.Background() _, err := r.reconcileNormal(ctx, log.FromContext(ctx), ms) if len(tc.conditionAssertion) > 0 { @@ -421,6 +556,9 @@ func TestReconciliationFunction(t *testing.T) { if len(tc.expectedFailureMessages) > 0 { g.Expect(tc.expectedFailureMessages).To(Equal(ms.OCIMachinePool.Status.FailureMessage)) } + if tc.validate != nil { + tc.validate(g, &tc) + } }) } } diff --git a/exp/controllers/ocimachinepoolmachine_controller.go b/exp/controllers/ocimachinepoolmachine_controller.go new file mode 100644 index 00000000..6043648c --- /dev/null +++ b/exp/controllers/ocimachinepoolmachine_controller.go @@ -0,0 +1,84 @@ +/* +Copyright (c) 2023 Oracle and/or its affiliates. + +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 controllers + +import ( + "context" + + "github.com/oracle/cluster-api-provider-oci/cloud/scope" + infrav2exp "github.com/oracle/cluster-api-provider-oci/exp/api/v1beta2" + "github.com/pkg/errors" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/tools/record" + "sigs.k8s.io/cluster-api/util/patch" + "sigs.k8s.io/cluster-api/util/predicates" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +type OCIMachinePoolMachineReconciler struct { + client.Client + Scheme *runtime.Scheme + Recorder record.EventRecorder + ClientProvider *scope.ClientProvider + Region string +} + +//+kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=ocimachinepoolmachines,verbs=get;list;watch;create;update;patch;delete + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the machinepoolmachines closer to the desired state. +func (r *OCIMachinePoolMachineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, reterr error) { + // currently, we only remove the finalizer on delete + // at a later point, when machine pool machine feature improves with integration with autoscaler and deployment + // orchestration etc, we should improve the below logic to actually delete the machine pool machine from the underlying + // infra object such as OKE nodepool or instance pool etc + ociMachinePoolMachine := &infrav2exp.OCIMachinePoolMachine{} + err := r.Get(ctx, req.NamespacedName, ociMachinePoolMachine) + if err != nil { + if apierrors.IsNotFound(err) { + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + helper, err := patch.NewHelper(ociMachinePoolMachine, r.Client) + defer func() { + if err := helper.Patch(ctx, ociMachinePoolMachine); err != nil && reterr == nil { + reterr = err + } + }() + if !ociMachinePoolMachine.DeletionTimestamp.IsZero() { + controllerutil.RemoveFinalizer(ociMachinePoolMachine, infrav2exp.MachinePoolMachineFinalizer) + } + return ctrl.Result{}, nil +} + +func (r *OCIMachinePoolMachineReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error { + err := ctrl.NewControllerManagedBy(mgr). + WithOptions(options). + For(&infrav2exp.OCIMachinePoolMachine{}). + WithEventFilter(predicates.ResourceNotPaused(ctrl.LoggerFrom(ctx))). + Complete(r) + + if err != nil { + return errors.Wrapf(err, "error creating controller") + } + return nil +} diff --git a/exp/controllers/ocimanaged_machinepool_controller.go b/exp/controllers/ocimanaged_machinepool_controller.go index 7803077d..5f7ee036 100644 --- a/exp/controllers/ocimanaged_machinepool_controller.go +++ b/exp/controllers/ocimanaged_machinepool_controller.go @@ -19,6 +19,7 @@ package controllers import ( "context" "fmt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "time" "github.com/go-logr/logr" @@ -26,6 +27,7 @@ import ( "github.com/oracle/cluster-api-provider-oci/cloud/ociutil" "github.com/oracle/cluster-api-provider-oci/cloud/scope" cloudutil "github.com/oracle/cluster-api-provider-oci/cloud/util" + expV1Beta1 "github.com/oracle/cluster-api-provider-oci/exp/api/v1beta1" infrav2exp "github.com/oracle/cluster-api-provider-oci/exp/api/v1beta2" "github.com/oracle/oci-go-sdk/v65/common" oke "github.com/oracle/oci-go-sdk/v65/containerengine" @@ -43,6 +45,7 @@ import ( "sigs.k8s.io/cluster-api/util/conditions" "sigs.k8s.io/cluster-api/util/predicates" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/apiutil" "sigs.k8s.io/controller-runtime/pkg/controller" @@ -50,7 +53,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" ) // OCIManagedMachinePoolReconciler reconciles a OCIManagedMachinePool object @@ -85,7 +87,6 @@ func (r *OCIManagedMachinePoolReconciler) Reconcile(ctx context.Context, req ctr } return ctrl.Result{}, err } - // Fetch the CAPI MachinePool machinePool, err := getOwnerMachinePool(ctx, r.Client, ociManagedMachinePool.ObjectMeta) if err != nil { @@ -184,25 +185,35 @@ func (r *OCIManagedMachinePoolReconciler) SetupWithManager(ctx context.Context, return errors.Wrapf(err, "failed to find GVK for OCIManagedMachinePool") } managedControlPlaneToManagedMachinePoolMap := managedClusterToManagedMachinePoolMapFunc(r.Client, gvk, logger) + clusterToObjectFunc, err := util.ClusterToTypedObjectsMapper(r.Client, &expV1Beta1.OCIManagedMachinePoolList{}, mgr.GetScheme()) + if err != nil { + return errors.Wrapf(err, "failed to create mapper for Cluster to OCIManagedMachinePool") + } return ctrl.NewControllerManagedBy(mgr). WithOptions(options). For(&infrav2exp.OCIManagedMachinePool{}). Watches( - &source.Kind{Type: &expclusterv1.MachinePool{}}, + &expclusterv1.MachinePool{}, handler.EnqueueRequestsFromMapFunc(machinePoolToInfrastructureMapFunc(infrastructurev1beta2. GroupVersion.WithKind(scope.OCIManagedMachinePoolKind), logger)), ). Watches( - &source.Kind{Type: &infrastructurev1beta2.OCIManagedCluster{}}, + &infrastructurev1beta2.OCIManagedCluster{}, handler.EnqueueRequestsFromMapFunc(managedControlPlaneToManagedMachinePoolMap), ). + Watches( + &clusterv1.Cluster{}, + handler.EnqueueRequestsFromMapFunc(clusterToObjectFunc), + builder.WithPredicates( + predicates.ClusterUnpausedAndInfrastructureReady(ctrl.LoggerFrom(ctx)), + ), + ). WithEventFilter(predicates.ResourceNotPaused(ctrl.LoggerFrom(ctx))). Complete(r) } func managedClusterToManagedMachinePoolMapFunc(c client.Client, gvk schema.GroupVersionKind, log logr.Logger) handler.MapFunc { - return func(o client.Object) []reconcile.Request { - ctx := context.Background() + return func(ctx context.Context, o client.Object) []reconcile.Request { ociCluster, ok := o.(*infrastructurev1beta2.OCIManagedCluster) if !ok { panic(fmt.Sprintf("Expected a OCIManagedControlPlane but got a %T", o)) @@ -233,7 +244,7 @@ func managedClusterToManagedMachinePoolMapFunc(c client.Client, gvk schema.Group var results []ctrl.Request for i := range managedPoolForClusterList.Items { - managedPool := mapFunc(&managedPoolForClusterList.Items[i]) + managedPool := mapFunc(ctx, &managedPoolForClusterList.Items[i]) results = append(results, managedPool...) } @@ -290,10 +301,18 @@ func (r *OCIManagedMachinePoolReconciler) reconcileNormal(ctx context.Context, l switch nodePool.LifecycleState { case oke.NodePoolLifecycleStateCreating: machinePoolScope.Info("Node Pool is creating") + err = r.reconcileManagedMachines(ctx, err, machinePoolScope, nodePool) + if err != nil { + return reconcile.Result{}, err + } conditions.MarkFalse(machinePoolScope.OCIManagedMachinePool, infrav2exp.NodePoolReadyCondition, infrav2exp.NodePoolNotReadyReason, clusterv1.ConditionSeverityInfo, "") return reconcile.Result{RequeueAfter: 30 * time.Second}, nil case oke.NodePoolLifecycleStateUpdating: machinePoolScope.Info("Node Pool is updating") + err = r.reconcileManagedMachines(ctx, err, machinePoolScope, nodePool) + if err != nil { + return reconcile.Result{}, err + } return reconcile.Result{RequeueAfter: 30 * time.Second}, nil case oke.NodePoolLifecycleStateActive: machinePoolScope.Info("Node pool is active") @@ -314,8 +333,14 @@ func (r *OCIManagedMachinePoolReconciler) reconcileNormal(ctx context.Context, l if isUpdated { return reconcile.Result{RequeueAfter: 30 * time.Second}, nil } + err = r.reconcileManagedMachines(ctx, err, machinePoolScope, nodePool) + if err != nil { + return reconcile.Result{}, err + } - return reconcile.Result{}, nil + // we reconcile every 5 minutes in case any reconciliation have happened behind the scenes by OKE service on + // the node pool(removing unhealthy nodes etc) which has to be percolated to machinepool machines + return reconcile.Result{RequeueAfter: 300 * time.Second}, nil default: err := errors.Errorf("Node Pool status %s is unexpected", nodePool.LifecycleState) machinePoolScope.OCIManagedMachinePool.Status.FailureMessages = append(machinePoolScope.OCIManagedMachinePool.Status.FailureMessages, err.Error()) @@ -326,6 +351,58 @@ func (r *OCIManagedMachinePoolReconciler) reconcileNormal(ctx context.Context, l } } +func (r *OCIManagedMachinePoolReconciler) reconcileManagedMachines(ctx context.Context, err error, machinePoolScope *scope.ManagedMachinePoolScope, nodePool *oke.NodePool) error { + specInfraMachines := make([]infrav2exp.OCIMachinePoolMachine, 0) + for _, node := range nodePool.Nodes { + // deleted/failing nodes should not be added to spec + machinePoolScope.Info(string(node.LifecycleState)) + if node.LifecycleState == oke.NodeLifecycleStateDeleted || node.LifecycleState == oke.NodeLifecycleStateFailing { + continue + } + ready := false + if node.LifecycleState == oke.NodeLifecycleStateActive { + ready = true + } + specInfraMachines = append(specInfraMachines, infrav2exp.OCIMachinePoolMachine{ + ObjectMeta: metav1.ObjectMeta{ + Name: *node.Name, + }, + Spec: infrav2exp.OCIMachinePoolMachineSpec{ + OCID: node.Id, + ProviderID: node.Id, + InstanceName: node.Name, + MachineType: infrav2exp.Managed, + }, + Status: infrav2exp.OCIMachinePoolMachineStatus{ + Ready: ready, + }, + }) + } + params := cloudutil.MachineParams{ + Cluster: machinePoolScope.Cluster, + MachinePool: machinePoolScope.MachinePool, + InfraMachinePoolName: machinePoolScope.OCIManagedMachinePool.Name, + Namespace: machinePoolScope.OCIManagedMachinePool.Namespace, + SpecInfraMachines: specInfraMachines, + Client: r.Client, + Logger: machinePoolScope.Logger, + InfraMachinePoolKind: machinePoolScope.OCIManagedMachinePool.Kind, + InfraMachinePoolUID: machinePoolScope.OCIManagedMachinePool.UID, + } + err = cloudutil.CreateMachinePoolMachinesIfNotExists(ctx, params) + if err != nil { + conditions.MarkFalse(machinePoolScope.OCIManagedMachinePool, clusterv1.ReadyCondition, "FailedToDeleteOrphanedMachines", clusterv1.ConditionSeverityWarning, err.Error()) + return errors.Wrap(err, "failed to create missing machines") + } + + err = cloudutil.DeleteOrphanedMachinePoolMachines(ctx, params) + if err != nil { + conditions.MarkFalse(machinePoolScope.OCIManagedMachinePool, clusterv1.ReadyCondition, "FailedToDeleteOrphanedMachines", clusterv1.ConditionSeverityWarning, err.Error()) + return errors.Wrap(err, "failed to delete orphaned machines") + } + return nil +} + func (r *OCIManagedMachinePoolReconciler) reconcileDelete(ctx context.Context, machinePoolScope *scope.ManagedMachinePoolScope) (_ ctrl.Result, reterr error) { machinePoolScope.Info("Handling deleted OCIMachinePool") machinePool := machinePoolScope.OCIManagedMachinePool diff --git a/exp/controllers/ocimanaged_machinepool_controller_test.go b/exp/controllers/ocimanaged_machinepool_controller_test.go index 61d8617c..ad78672e 100644 --- a/exp/controllers/ocimanaged_machinepool_controller_test.go +++ b/exp/controllers/ocimanaged_machinepool_controller_test.go @@ -18,11 +18,11 @@ package controllers import ( "context" - infrastructurev1beta2 "github.com/oracle/cluster-api-provider-oci/api/v1beta2" "testing" "github.com/golang/mock/gomock" . "github.com/onsi/gomega" + infrastructurev1beta2 "github.com/oracle/cluster-api-provider-oci/api/v1beta2" "github.com/oracle/cluster-api-provider-oci/cloud/ociutil" "github.com/oracle/cluster-api-provider-oci/cloud/scope" "github.com/oracle/cluster-api-provider-oci/cloud/services/containerengine/mock_containerengine" @@ -39,6 +39,7 @@ import ( "sigs.k8s.io/cluster-api/util/conditions" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/client/interceptor" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" ) @@ -146,6 +147,7 @@ func TestNormalReconciliationFunction(t *testing.T) { ociManagedMachinePool *infrav2exp.OCIManagedMachinePool okeClient *mock_containerengine.MockClient ms *scope.ManagedMachinePoolScope + k8sClient client.WithWatch ) tags := make(map[string]string) @@ -155,7 +157,7 @@ func TestNormalReconciliationFunction(t *testing.T) { setup := func(t *testing.T, g *WithT) { var err error mockCtrl = gomock.NewController(t) - client := fake.NewClientBuilder().WithObjects(getSecret()).Build() + k8sClient = interceptor.NewClient(fake.NewClientBuilder().WithObjects(getSecret()).Build(), interceptor.Funcs{}) okeClient = mock_containerengine.NewMockClient(mockCtrl) machinePool := getMachinePool() ociManagedMachinePool = getOCIManagedMachinePool() @@ -172,7 +174,7 @@ func TestNormalReconciliationFunction(t *testing.T) { ContainerEngineClient: okeClient, OCIManagedCluster: ociCluster, Cluster: getCluster(), - Client: client, + Client: k8sClient, OCIManagedMachinePool: ociManagedMachinePool, MachinePool: machinePool, OCIManagedControlPlane: &ociManagedControlPlane, @@ -180,7 +182,7 @@ func TestNormalReconciliationFunction(t *testing.T) { recorder = record.NewFakeRecorder(2) r = OCIManagedMachinePoolReconciler{ - Client: client, + Client: k8sClient, Scheme: runtime.NewScheme(), Recorder: recorder, } @@ -189,20 +191,109 @@ func TestNormalReconciliationFunction(t *testing.T) { teardown := func(t *testing.T, g *WithT) { mockCtrl.Finish() } - tests := []struct { + type test struct { name string errorExpected bool expectedEvent string eventNotExpected string conditionAssertion []conditionAssertion - testSpecificSetup func(machinePoolScope *scope.ManagedMachinePoolScope, okeClient *mock_containerengine.MockClient) + testSpecificSetup func(t *test, machinePoolScope *scope.ManagedMachinePoolScope, okeClient *mock_containerengine.MockClient) + validate func(g *WithT, t *test) expectedFailureMessages []string - }{ + createPoolMachines []infrav2exp.OCIMachinePoolMachine + deletePoolMachines []clusterv1.Machine + } + tests := []test{ { name: "node pool in creating state", errorExpected: false, conditionAssertion: []conditionAssertion{{infrav2exp.NodePoolReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityInfo, infrav2exp.NodePoolNotReadyReason}}, - testSpecificSetup: func(machinePoolScope *scope.ManagedMachinePoolScope, okeClient *mock_containerengine.MockClient) { + testSpecificSetup: func(t *test, machinePoolScope *scope.ManagedMachinePoolScope, okeClient *mock_containerengine.MockClient) { + t.createPoolMachines = make([]infrav2exp.OCIMachinePoolMachine, 0) + r.Client = interceptor.NewClient(fake.NewClientBuilder().WithObjects(getSecret()).Build(), interceptor.Funcs{ + Create: func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.CreateOption) error { + m := obj.(*infrav2exp.OCIMachinePoolMachine) + t.createPoolMachines = append(t.createPoolMachines, *m) + return nil + }, + }) + okeClient.EXPECT().GetNodePool(gomock.Any(), gomock.Eq(oke.GetNodePoolRequest{ + NodePoolId: common.String("test"), + })). + Return(oke.GetNodePoolResponse{ + NodePool: oke.NodePool{ + Id: common.String("test"), + LifecycleState: oke.NodePoolLifecycleStateCreating, + FreeformTags: tags, + Nodes: []oke.Node{{ + Id: common.String("id-1"), + Name: common.String("name-1"), + LifecycleState: oke.NodeLifecycleStateCreating, + }}, + }, + }, nil) + }, + validate: func(g *WithT, t *test) { + g.Expect(len(t.createPoolMachines)).To(Equal(1)) + machine := t.createPoolMachines[0] + g.Expect(machine.Spec.MachineType).To(Equal(infrav2exp.Managed)) + g.Expect(*machine.Spec.InstanceName).To(Equal("name-1")) + g.Expect(*machine.Spec.ProviderID).To(Equal("id-1")) + g.Expect(*machine.Spec.OCID).To(Equal("id-1")) + }, + }, + { + name: "delete unwanted machinepool machine", + errorExpected: false, + conditionAssertion: []conditionAssertion{{infrav2exp.NodePoolReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityInfo, infrav2exp.NodePoolNotReadyReason}}, + testSpecificSetup: func(t *test, machinePoolScope *scope.ManagedMachinePoolScope, okeClient *mock_containerengine.MockClient) { + t.createPoolMachines = make([]infrav2exp.OCIMachinePoolMachine, 0) + fakeClient := fake.NewClientBuilder().WithObjects(&infrav2exp.OCIMachinePoolMachine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + Labels: map[string]string{ + clusterv1.ClusterNameLabel: "test-cluster", + clusterv1.MachinePoolNameLabel: "test", + }, + OwnerReferences: []metav1.OwnerReference{ + { + Kind: "Machine", + Name: "test", + APIVersion: clusterv1.GroupVersion.String(), + }, + }, + }, + Spec: infrav2exp.OCIMachinePoolMachineSpec{ + OCID: common.String("id-2"), + InstanceName: common.String("name-2"), + ProviderID: common.String("id-2"), + MachineType: infrav2exp.Managed, + }, + }, &clusterv1.Machine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + Labels: map[string]string{ + clusterv1.ClusterNameLabel: "oci-cluster", + clusterv1.MachinePoolNameLabel: "test", + }, + }, + Spec: clusterv1.MachineSpec{}, + }).Build() + t.deletePoolMachines = make([]clusterv1.Machine, 0) + r.Client = interceptor.NewClient(fakeClient, interceptor.Funcs{ + Create: func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.CreateOption) error { + m := obj.(*infrav2exp.OCIMachinePoolMachine) + t.createPoolMachines = append(t.createPoolMachines, *m) + return nil + }, + Delete: func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.DeleteOption) error { + m := obj.(*clusterv1.Machine) + t.deletePoolMachines = append(t.deletePoolMachines, *m) + return nil + }, + }) okeClient.EXPECT().GetNodePool(gomock.Any(), gomock.Eq(oke.GetNodePoolRequest{ NodePoolId: common.String("test"), })). @@ -211,16 +302,33 @@ func TestNormalReconciliationFunction(t *testing.T) { Id: common.String("test"), LifecycleState: oke.NodePoolLifecycleStateCreating, FreeformTags: tags, + Nodes: []oke.Node{{ + Id: common.String("id-1"), + Name: common.String("name-1"), + LifecycleState: oke.NodeLifecycleStateCreating, + }}, }, }, nil) }, + validate: func(g *WithT, t *test) { + g.Expect(len(t.createPoolMachines)).To(Equal(1)) + createMachine := t.createPoolMachines[0] + g.Expect(createMachine.Spec.MachineType).To(Equal(infrav2exp.Managed)) + g.Expect(*createMachine.Spec.InstanceName).To(Equal("name-1")) + g.Expect(*createMachine.Spec.ProviderID).To(Equal("id-1")) + g.Expect(*createMachine.Spec.OCID).To(Equal("id-1")) + + g.Expect(len(t.deletePoolMachines)).To(Equal(1)) + deleteMachine := t.deletePoolMachines[0] + g.Expect(deleteMachine.Name).To(Equal("test")) + }, }, { name: "node pool create", errorExpected: false, expectedEvent: "Created new Node Pool: test", conditionAssertion: []conditionAssertion{{infrav2exp.NodePoolReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityInfo, infrav2exp.NodePoolNotReadyReason}}, - testSpecificSetup: func(machinePoolScope *scope.ManagedMachinePoolScope, okeClient *mock_containerengine.MockClient) { + testSpecificSetup: func(t *test, machinePoolScope *scope.ManagedMachinePoolScope, okeClient *mock_containerengine.MockClient) { ociManagedMachinePool.Spec.ID = nil okeClient.EXPECT().ListNodePools(gomock.Any(), gomock.Any()). Return(oke.ListNodePoolsResponse{}, nil) @@ -249,7 +357,7 @@ func TestNormalReconciliationFunction(t *testing.T) { name: "node pool is created, no update", errorExpected: false, conditionAssertion: []conditionAssertion{{infrav2exp.NodePoolReadyCondition, corev1.ConditionTrue, "", ""}}, - testSpecificSetup: func(machinePoolScope *scope.ManagedMachinePoolScope, okeClient *mock_containerengine.MockClient) { + testSpecificSetup: func(t *test, machinePoolScope *scope.ManagedMachinePoolScope, okeClient *mock_containerengine.MockClient) { okeClient.EXPECT().GetNodePool(gomock.Any(), gomock.Eq(oke.GetNodePoolRequest{ NodePoolId: common.String("test"), })). @@ -305,10 +413,10 @@ func TestNormalReconciliationFunction(t *testing.T) { }, }, { - name: "node pool in created, pdate", + name: "node pool in created, update", errorExpected: false, conditionAssertion: []conditionAssertion{{infrav2exp.NodePoolReadyCondition, corev1.ConditionTrue, "", ""}}, - testSpecificSetup: func(machinePoolScope *scope.ManagedMachinePoolScope, okeClient *mock_containerengine.MockClient) { + testSpecificSetup: func(t *test, machinePoolScope *scope.ManagedMachinePoolScope, okeClient *mock_containerengine.MockClient) { okeClient.EXPECT().GetNodePool(gomock.Any(), gomock.Eq(oke.GetNodePoolRequest{ NodePoolId: common.String("test"), })). @@ -370,7 +478,7 @@ func TestNormalReconciliationFunction(t *testing.T) { errorExpected: true, expectedFailureMessages: []string{"test error!", "Node Pool status FAILED is unexpected"}, conditionAssertion: []conditionAssertion{{infrav2exp.NodePoolReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityError, infrav2exp.NodePoolProvisionFailedReason}}, - testSpecificSetup: func(machinePoolScope *scope.ManagedMachinePoolScope, okeClient *mock_containerengine.MockClient) { + testSpecificSetup: func(t *test, machinePoolScope *scope.ManagedMachinePoolScope, okeClient *mock_containerengine.MockClient) { okeClient.EXPECT().GetNodePool(gomock.Any(), gomock.Eq(oke.GetNodePoolRequest{ NodePoolId: common.String("test"), })). @@ -393,7 +501,7 @@ func TestNormalReconciliationFunction(t *testing.T) { { name: "node pool in update state", errorExpected: false, - testSpecificSetup: func(machinePoolScope *scope.ManagedMachinePoolScope, okeClient *mock_containerengine.MockClient) { + testSpecificSetup: func(t *test, machinePoolScope *scope.ManagedMachinePoolScope, okeClient *mock_containerengine.MockClient) { okeClient.EXPECT().GetNodePool(gomock.Any(), gomock.Eq(oke.GetNodePoolRequest{ NodePoolId: common.String("test"), })). @@ -413,7 +521,7 @@ func TestNormalReconciliationFunction(t *testing.T) { g := NewWithT(t) defer teardown(t, g) setup(t, g) - tc.testSpecificSetup(ms, okeClient) + tc.testSpecificSetup(&tc, ms, okeClient) ctx := context.Background() _, err := r.reconcileNormal(ctx, log.FromContext(ctx), ms) if len(tc.conditionAssertion) > 0 { @@ -430,6 +538,9 @@ func TestNormalReconciliationFunction(t *testing.T) { if len(tc.expectedFailureMessages) > 0 { g.Expect(tc.expectedFailureMessages).To(Equal(ms.OCIManagedMachinePool.Status.FailureMessages)) } + if tc.validate != nil { + tc.validate(g, &tc) + } }) } } diff --git a/exp/controllers/ocivirtual_machinepool_controller.go b/exp/controllers/ocivirtual_machinepool_controller.go index 5835479b..aba0ac60 100644 --- a/exp/controllers/ocivirtual_machinepool_controller.go +++ b/exp/controllers/ocivirtual_machinepool_controller.go @@ -43,6 +43,7 @@ import ( "sigs.k8s.io/cluster-api/util/conditions" "sigs.k8s.io/cluster-api/util/predicates" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/apiutil" "sigs.k8s.io/controller-runtime/pkg/controller" @@ -50,7 +51,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" ) // OCIVirtualMachinePoolReconciler reconciles a OCIVirtualMachinePool object @@ -183,26 +183,36 @@ func (r *OCIVirtualMachinePoolReconciler) SetupWithManager(ctx context.Context, if err != nil { return errors.Wrapf(err, "failed to find GVK for OCIVirtualMachinePool") } + clusterToObjectFunc, err := util.ClusterToTypedObjectsMapper(r.Client, &infrav2exp.OCIVirtualMachinePoolList{}, mgr.GetScheme()) + if err != nil { + return errors.Wrapf(err, "failed to create mapper for Cluster to OCIVirtualMachinePool") + } managedClusterToVirtualMachinePoolMapFunc := managedClusterToVirtualMachinePoolMapFunc(r.Client, gvk, logger) return ctrl.NewControllerManagedBy(mgr). WithOptions(options). For(&infrav2exp.OCIVirtualMachinePool{}). Watches( - &source.Kind{Type: &expclusterv1.MachinePool{}}, + &expclusterv1.MachinePool{}, handler.EnqueueRequestsFromMapFunc(machinePoolToInfrastructureMapFunc(infrastructurev1beta2. GroupVersion.WithKind(scope.OCIVirtualMachinePoolKind), logger)), ). Watches( - &source.Kind{Type: &infrastructurev1beta2.OCIManagedCluster{}}, + &infrastructurev1beta2.OCIManagedCluster{}, handler.EnqueueRequestsFromMapFunc(managedClusterToVirtualMachinePoolMapFunc), ). + Watches( + &clusterv1.Cluster{}, + handler.EnqueueRequestsFromMapFunc(clusterToObjectFunc), + builder.WithPredicates( + predicates.ClusterUnpausedAndInfrastructureReady(ctrl.LoggerFrom(ctx)), + ), + ). WithEventFilter(predicates.ResourceNotPaused(ctrl.LoggerFrom(ctx))). Complete(r) } func managedClusterToVirtualMachinePoolMapFunc(c client.Client, gvk schema.GroupVersionKind, log logr.Logger) handler.MapFunc { - return func(o client.Object) []reconcile.Request { - ctx := context.Background() + return func(ctx context.Context, o client.Object) []reconcile.Request { ociCluster, ok := o.(*infrastructurev1beta2.OCIManagedCluster) if !ok { panic(fmt.Sprintf("Expected a OCIManagedControlPlane but got a %T", o)) @@ -233,7 +243,7 @@ func managedClusterToVirtualMachinePoolMapFunc(c client.Client, gvk schema.Group var results []ctrl.Request for i := range managedPoolForClusterList.Items { - managedPool := mapFunc(&managedPoolForClusterList.Items[i]) + managedPool := mapFunc(ctx, &managedPoolForClusterList.Items[i]) results = append(results, managedPool...) } @@ -287,11 +297,23 @@ func (r *OCIVirtualMachinePoolReconciler) reconcileNormal(ctx context.Context, l return reconcile.Result{RequeueAfter: 30 * time.Second}, nil case oke.VirtualNodePoolLifecycleStateActive: machinePoolScope.Info("Virtual Node pool is active") - instanceCount, err := machinePoolScope.ListandSetMachinePoolInstances(ctx, nodePool) + machines, err := machinePoolScope.ListandSetMachinePoolInstances(ctx, nodePool) + if err != nil { + return reconcile.Result{}, err + } + providerIDList := make([]string, 0) + for _, machine := range machines { + if machine.Status.Ready { + providerIDList = append(providerIDList, *machine.Spec.ProviderID) + } + } + machinePoolScope.OCIVirtualMachinePool.Spec.ProviderIDList = providerIDList + machinePoolScope.SetReplicaCount(int32(len(providerIDList))) + + err = r.reconcileVirtualMachines(ctx, err, machinePoolScope, machines) if err != nil { return reconcile.Result{}, err } - machinePoolScope.SetReplicaCount(instanceCount) machinePoolScope.OCIVirtualMachinePool.Status.Ready = true // record the event only when pool goes from not ready to ready state r.Recorder.Eventf(machinePoolScope.OCIVirtualMachinePool, corev1.EventTypeNormal, "VirtualNodePoolReady", @@ -305,7 +327,9 @@ func (r *OCIVirtualMachinePoolReconciler) reconcileNormal(ctx context.Context, l return reconcile.Result{RequeueAfter: 30 * time.Second}, nil } - return reconcile.Result{}, nil + // we reconcile every 5 minutes in case any reconciliation have happened behind the scenes by OKE service on + // the virtual node pool(removing unhealthy nodes etc) which has to be percolated to machinepool machines + return reconcile.Result{RequeueAfter: 300 * time.Second}, nil default: err := errors.Errorf("Virtual Node Pool status %s is unexpected", nodePool.LifecycleState) machinePoolScope.OCIVirtualMachinePool.Status.FailureMessages = append(machinePoolScope.OCIVirtualMachinePool.Status.FailureMessages, err.Error()) @@ -366,3 +390,29 @@ func (r *OCIVirtualMachinePoolReconciler) reconcileDelete(ctx context.Context, m } } } + +func (r *OCIVirtualMachinePoolReconciler) reconcileVirtualMachines(ctx context.Context, err error, machinePoolScope *scope.VirtualMachinePoolScope, specInfraMachines []infrav2exp.OCIMachinePoolMachine) error { + params := cloudutil.MachineParams{ + Cluster: machinePoolScope.Cluster, + MachinePool: machinePoolScope.MachinePool, + InfraMachinePoolName: machinePoolScope.OCIVirtualMachinePool.Name, + Namespace: machinePoolScope.OCIVirtualMachinePool.Namespace, + SpecInfraMachines: specInfraMachines, + Client: r.Client, + Logger: machinePoolScope.Logger, + InfraMachinePoolKind: machinePoolScope.OCIVirtualMachinePool.Kind, + InfraMachinePoolUID: machinePoolScope.OCIVirtualMachinePool.UID, + } + err = cloudutil.CreateMachinePoolMachinesIfNotExists(ctx, params) + if err != nil { + conditions.MarkFalse(machinePoolScope.OCIVirtualMachinePool, clusterv1.ReadyCondition, "FailedToDeleteOrphanedMachines", clusterv1.ConditionSeverityWarning, err.Error()) + return errors.Wrap(err, "failed to create missing machines") + } + + err = cloudutil.DeleteOrphanedMachinePoolMachines(ctx, params) + if err != nil { + conditions.MarkFalse(machinePoolScope.OCIVirtualMachinePool, clusterv1.ReadyCondition, "FailedToDeleteOrphanedMachines", clusterv1.ConditionSeverityWarning, err.Error()) + return errors.Wrap(err, "failed to delete orphaned machines") + } + return nil +} diff --git a/exp/controllers/ocivirtual_machinepool_controller_test.go b/exp/controllers/ocivirtual_machinepool_controller_test.go index c0308f4b..943fc608 100644 --- a/exp/controllers/ocivirtual_machinepool_controller_test.go +++ b/exp/controllers/ocivirtual_machinepool_controller_test.go @@ -39,6 +39,7 @@ import ( "sigs.k8s.io/cluster-api/util/conditions" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/client/interceptor" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" ) @@ -189,20 +190,24 @@ func TestNormalReconciliationFunctionForVirtualMP(t *testing.T) { teardown := func(t *testing.T, g *WithT) { mockCtrl.Finish() } - tests := []struct { + type test struct { name string errorExpected bool expectedEvent string eventNotExpected string conditionAssertion []conditionAssertion - testSpecificSetup func(machinePoolScope *scope.VirtualMachinePoolScope, okeClient *mock_containerengine.MockClient) + testSpecificSetup func(t *test, machinePoolScope *scope.VirtualMachinePoolScope, okeClient *mock_containerengine.MockClient) expectedFailureMessages []string - }{ + createPoolMachines []infrav2exp.OCIMachinePoolMachine + deletePoolMachines []clusterv1.Machine + validate func(g *WithT, t *test) + } + tests := []test{ { name: "virtual node pool in creating state", errorExpected: false, conditionAssertion: []conditionAssertion{{infrav2exp.VirtualNodePoolReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityInfo, infrav2exp.VirtualNodePoolNotReadyReason}}, - testSpecificSetup: func(machinePoolScope *scope.VirtualMachinePoolScope, okeClient *mock_containerengine.MockClient) { + testSpecificSetup: func(t *test, machinePoolScope *scope.VirtualMachinePoolScope, okeClient *mock_containerengine.MockClient) { okeClient.EXPECT().GetVirtualNodePool(gomock.Any(), gomock.Eq(oke.GetVirtualNodePoolRequest{ VirtualNodePoolId: common.String("test"), })). @@ -220,7 +225,7 @@ func TestNormalReconciliationFunctionForVirtualMP(t *testing.T) { errorExpected: false, expectedEvent: "Created new Virtual Node Pool: test", conditionAssertion: []conditionAssertion{{infrav2exp.VirtualNodePoolReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityInfo, infrav2exp.VirtualNodePoolNotReadyReason}}, - testSpecificSetup: func(machinePoolScope *scope.VirtualMachinePoolScope, okeClient *mock_containerengine.MockClient) { + testSpecificSetup: func(t *test, machinePoolScope *scope.VirtualMachinePoolScope, okeClient *mock_containerengine.MockClient) { ociVirtualMachinePool.Spec.ID = nil okeClient.EXPECT().ListVirtualNodePools(gomock.Any(), gomock.Any()). Return(oke.ListVirtualNodePoolsResponse{}, nil) @@ -249,7 +254,14 @@ func TestNormalReconciliationFunctionForVirtualMP(t *testing.T) { name: "virtual node pool is created, no update", errorExpected: false, conditionAssertion: []conditionAssertion{{infrav2exp.VirtualNodePoolReadyCondition, corev1.ConditionTrue, "", ""}}, - testSpecificSetup: func(machinePoolScope *scope.VirtualMachinePoolScope, okeClient *mock_containerengine.MockClient) { + testSpecificSetup: func(t *test, machinePoolScope *scope.VirtualMachinePoolScope, okeClient *mock_containerengine.MockClient) { + r.Client = interceptor.NewClient(fake.NewClientBuilder().WithObjects(getSecret(), ociVirtualMachinePool).Build(), interceptor.Funcs{ + Create: func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.CreateOption) error { + m := obj.(*infrav2exp.OCIMachinePoolMachine) + t.createPoolMachines = append(t.createPoolMachines, *m) + return nil + }, + }) okeClient.EXPECT().GetVirtualNodePool(gomock.Any(), gomock.Eq(oke.GetVirtualNodePoolRequest{ VirtualNodePoolId: common.String("test"), })). @@ -290,14 +302,143 @@ func TestNormalReconciliationFunctionForVirtualMP(t *testing.T) { okeClient.EXPECT().ListVirtualNodes(gomock.Any(), gomock.Eq(oke.ListVirtualNodesRequest{ VirtualNodePoolId: common.String("id"), - })).Return(oke.ListVirtualNodesResponse{}, nil) + })).Return(oke.ListVirtualNodesResponse{ + Items: []oke.VirtualNodeSummary{ + { + Id: common.String("id-1"), + LifecycleState: oke.VirtualNodeLifecycleStateActive, + DisplayName: common.String("name-1"), + }, + }, + }, nil) + }, + validate: func(g *WithT, t *test) { + g.Expect(len(t.createPoolMachines)).To(Equal(1)) + machine := t.createPoolMachines[0] + g.Expect(machine.Spec.MachineType).To(Equal(infrav2exp.Virtual)) + g.Expect(*machine.Spec.InstanceName).To(Equal("name-1")) + g.Expect(*machine.Spec.ProviderID).To(Equal("id-1")) + g.Expect(*machine.Spec.OCID).To(Equal("id-1")) + }, + }, + { + name: "delete unwanted machinepool machine", + errorExpected: false, + conditionAssertion: []conditionAssertion{{infrav2exp.VirtualNodePoolReadyCondition, corev1.ConditionTrue, "", ""}}, + testSpecificSetup: func(t *test, machinePoolScope *scope.VirtualMachinePoolScope, okeClient *mock_containerengine.MockClient) { + fakeClient := fake.NewClientBuilder().WithObjects(&infrav2exp.OCIMachinePoolMachine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + Labels: map[string]string{ + clusterv1.ClusterNameLabel: "test-cluster", + clusterv1.MachinePoolNameLabel: "test", + }, + OwnerReferences: []metav1.OwnerReference{ + { + Kind: "Machine", + Name: "test", + APIVersion: clusterv1.GroupVersion.String(), + }, + }, + }, + Spec: infrav2exp.OCIMachinePoolMachineSpec{ + OCID: common.String("id-2"), + InstanceName: common.String("name-2"), + ProviderID: common.String("id-2"), + MachineType: infrav2exp.Managed, + }, + }, &clusterv1.Machine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + Labels: map[string]string{ + clusterv1.ClusterNameLabel: "oci-cluster", + clusterv1.MachinePoolNameLabel: "test", + }, + }, + Spec: clusterv1.MachineSpec{}, + }).Build() + r.Client = interceptor.NewClient(fakeClient, interceptor.Funcs{ + Create: func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.CreateOption) error { + m := obj.(*infrav2exp.OCIMachinePoolMachine) + t.createPoolMachines = append(t.createPoolMachines, *m) + return nil + }, + Delete: func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.DeleteOption) error { + m := obj.(*clusterv1.Machine) + t.deletePoolMachines = append(t.deletePoolMachines, *m) + return nil + }, + }) + okeClient.EXPECT().GetVirtualNodePool(gomock.Any(), gomock.Eq(oke.GetVirtualNodePoolRequest{ + VirtualNodePoolId: common.String("test"), + })). + Return(oke.GetVirtualNodePoolResponse{ + VirtualNodePool: oke.VirtualNodePool{ + Id: common.String("id"), + LifecycleState: oke.VirtualNodePoolLifecycleStateActive, + ClusterId: common.String("cluster-id"), + DisplayName: common.String("test"), + CompartmentId: common.String("test-compartment"), + KubernetesVersion: common.String("v1.24.5"), + InitialVirtualNodeLabels: []oke.InitialVirtualNodeLabel{{ + Key: common.String("key"), + Value: common.String("value"), + }}, + Taints: []oke.Taint{{ + Key: common.String("key"), + Value: common.String("value"), + Effect: common.String("effect"), + }}, + PlacementConfigurations: []oke.PlacementConfiguration{ + { + AvailabilityDomain: common.String("test-ad"), + SubnetId: common.String("subnet-id"), + FaultDomain: []string{"fd-1", "fd-2"}, + }, + }, + NsgIds: []string{"nsg-id"}, + PodConfiguration: &oke.PodConfiguration{ + NsgIds: []string{"pod-nsg-id"}, + Shape: common.String("pod-shape"), + SubnetId: common.String("pod-subnet-id"), + }, + Size: common.Int(3), + FreeformTags: tags, + }, + }, nil) + + okeClient.EXPECT().ListVirtualNodes(gomock.Any(), gomock.Eq(oke.ListVirtualNodesRequest{ + VirtualNodePoolId: common.String("id"), + })).Return(oke.ListVirtualNodesResponse{ + Items: []oke.VirtualNodeSummary{ + { + Id: common.String("id-1"), + LifecycleState: oke.VirtualNodeLifecycleStateActive, + DisplayName: common.String("name-1"), + }, + }, + }, nil) + }, + validate: func(g *WithT, t *test) { + g.Expect(len(t.createPoolMachines)).To(Equal(1)) + machine := t.createPoolMachines[0] + g.Expect(machine.Spec.MachineType).To(Equal(infrav2exp.Virtual)) + g.Expect(*machine.Spec.InstanceName).To(Equal("name-1")) + g.Expect(*machine.Spec.ProviderID).To(Equal("id-1")) + g.Expect(*machine.Spec.OCID).To(Equal("id-1")) + + g.Expect(len(t.deletePoolMachines)).To(Equal(1)) + deleteMachine := t.deletePoolMachines[0] + g.Expect(deleteMachine.Name).To(Equal("test")) }, }, { name: "virtual node pool in created, update", errorExpected: false, conditionAssertion: []conditionAssertion{{infrav2exp.VirtualNodePoolReadyCondition, corev1.ConditionTrue, "", ""}}, - testSpecificSetup: func(machinePoolScope *scope.VirtualMachinePoolScope, okeClient *mock_containerengine.MockClient) { + testSpecificSetup: func(t *test, machinePoolScope *scope.VirtualMachinePoolScope, okeClient *mock_containerengine.MockClient) { okeClient.EXPECT().GetVirtualNodePool(gomock.Any(), gomock.Eq(oke.GetVirtualNodePoolRequest{ VirtualNodePoolId: common.String("test"), })). @@ -346,7 +487,7 @@ func TestNormalReconciliationFunctionForVirtualMP(t *testing.T) { errorExpected: true, expectedFailureMessages: []string{"Virtual Node Pool status FAILED is unexpected"}, conditionAssertion: []conditionAssertion{{infrav2exp.VirtualNodePoolReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityError, infrav2exp.VirtualNodePoolProvisionFailedReason}}, - testSpecificSetup: func(machinePoolScope *scope.VirtualMachinePoolScope, okeClient *mock_containerengine.MockClient) { + testSpecificSetup: func(t *test, machinePoolScope *scope.VirtualMachinePoolScope, okeClient *mock_containerengine.MockClient) { okeClient.EXPECT().GetVirtualNodePool(gomock.Any(), gomock.Eq(oke.GetVirtualNodePoolRequest{ VirtualNodePoolId: common.String("test"), })). @@ -362,7 +503,7 @@ func TestNormalReconciliationFunctionForVirtualMP(t *testing.T) { { name: "virtual node pool in update state", errorExpected: false, - testSpecificSetup: func(machinePoolScope *scope.VirtualMachinePoolScope, okeClient *mock_containerengine.MockClient) { + testSpecificSetup: func(t *test, machinePoolScope *scope.VirtualMachinePoolScope, okeClient *mock_containerengine.MockClient) { okeClient.EXPECT().GetVirtualNodePool(gomock.Any(), gomock.Eq(oke.GetVirtualNodePoolRequest{ VirtualNodePoolId: common.String("test"), })). @@ -382,7 +523,7 @@ func TestNormalReconciliationFunctionForVirtualMP(t *testing.T) { g := NewWithT(t) defer teardown(t, g) setup(t, g) - tc.testSpecificSetup(ms, okeClient) + tc.testSpecificSetup(&tc, ms, okeClient) ctx := context.Background() _, err := r.reconcileNormal(ctx, log.FromContext(ctx), ms) if len(tc.conditionAssertion) > 0 { @@ -399,6 +540,9 @@ func TestNormalReconciliationFunctionForVirtualMP(t *testing.T) { if len(tc.expectedFailureMessages) > 0 { g.Expect(tc.expectedFailureMessages).To(Equal(ms.OCIVirtualMachinePool.Status.FailureMessages)) } + if tc.validate != nil { + tc.validate(g, &tc) + } }) } } diff --git a/go.mod b/go.mod index 2aced89f..eef71dbc 100644 --- a/go.mod +++ b/go.mod @@ -1,27 +1,28 @@ module github.com/oracle/cluster-api-provider-oci -go 1.19 +go 1.20 require ( - github.com/go-logr/logr v1.2.3 + github.com/go-logr/logr v1.2.4 github.com/golang/mock v1.6.0 github.com/google/gofuzz v1.2.0 - github.com/onsi/ginkgo/v2 v2.9.2 - github.com/onsi/gomega v1.27.5 + github.com/onsi/ginkgo/v2 v2.11.0 + github.com/onsi/gomega v1.27.8 github.com/oracle/oci-go-sdk/v65 v65.40.1 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.15.1 + github.com/prometheus/client_golang v1.16.0 github.com/spf13/pflag v1.0.5 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.26.1 - k8s.io/apimachinery v0.26.1 - k8s.io/client-go v0.26.1 - k8s.io/component-base v0.26.1 - k8s.io/klog/v2 v2.80.1 - k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 - sigs.k8s.io/cluster-api v1.4.3 - sigs.k8s.io/cluster-api/test v1.4.3 - sigs.k8s.io/controller-runtime v0.14.5 + k8s.io/api v0.27.2 + k8s.io/apimachinery v0.27.2 + k8s.io/client-go v0.27.2 + k8s.io/component-base v0.27.2 + k8s.io/klog/v2 v2.90.1 + k8s.io/utils v0.0.0-20230209194617-a36077c30491 + sigs.k8s.io/cluster-api v1.5.0 + sigs.k8s.io/cluster-api/test v1.5.0 + sigs.k8s.io/controller-runtime v0.15.0 + sigs.k8s.io/kind v0.20.0 ) require ( @@ -31,6 +32,7 @@ require ( github.com/Masterminds/semver/v3 v3.2.0 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/Microsoft/go-winio v0.5.0 // indirect + github.com/adrg/xdg v0.4.0 // indirect github.com/alessio/shellescape v1.4.1 // indirect github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect @@ -42,17 +44,17 @@ require ( github.com/coredns/corefile-migration v1.0.20 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/distribution v2.8.2+incompatible // indirect - github.com/docker/docker v20.10.24+incompatible // indirect + github.com/docker/docker v24.0.5+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.4.0 // indirect github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46 // indirect - github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/emicklei/go-restful/v3 v3.10.2 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/go-logr/zapr v1.2.3 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-logr/zapr v1.2.4 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.1 // indirect github.com/go-openapi/swag v0.22.3 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gobuffalo/flect v1.0.2 // indirect @@ -65,13 +67,13 @@ require ( github.com/google/go-cmp v0.5.9 // indirect github.com/google/go-github/v48 v48.2.0 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect + github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 // indirect github.com/google/uuid v1.3.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/huandu/xstrings v1.3.3 // indirect github.com/imdario/mergo v0.3.13 // indirect - github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/magiconair/properties v1.8.7 // indirect @@ -87,47 +89,45 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.6 // indirect - github.com/prometheus/client_model v0.3.0 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.42.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect github.com/shopspring/decimal v1.3.1 // indirect - github.com/sirupsen/logrus v1.8.1 // indirect github.com/sony/gobreaker v0.5.0 // indirect - github.com/spf13/afero v1.9.3 // indirect - github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/cobra v1.6.1 // indirect + github.com/spf13/afero v1.9.5 // indirect + github.com/spf13/cast v1.5.1 // indirect + github.com/spf13/cobra v1.7.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/viper v1.15.0 // indirect + github.com/spf13/viper v1.16.0 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/valyala/fastjson v1.6.4 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.8.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/crypto v0.3.0 // indirect - golang.org/x/net v0.8.0 // indirect - golang.org/x/oauth2 v0.6.0 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/term v0.6.0 // indirect - golang.org/x/text v0.8.0 // indirect + golang.org/x/crypto v0.11.0 // indirect + golang.org/x/net v0.12.0 // indirect + golang.org/x/oauth2 v0.10.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/term v0.10.0 // indirect + golang.org/x/text v0.11.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.7.0 // indirect - gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect + golang.org/x/tools v0.9.3 // indirect + gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect - google.golang.org/protobuf v1.30.0 // indirect + google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.26.1 // indirect - k8s.io/apiserver v0.26.1 // indirect - k8s.io/cluster-bootstrap v0.25.0 // indirect - k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect - sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect - sigs.k8s.io/kind v0.18.0 // indirect + k8s.io/apiextensions-apiserver v0.27.2 // indirect + k8s.io/apiserver v0.27.2 // indirect + k8s.io/cluster-bootstrap v0.27.2 // indirect + k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) -replace sigs.k8s.io/cluster-api => sigs.k8s.io/cluster-api v1.4.3 +replace sigs.k8s.io/cluster-api => sigs.k8s.io/cluster-api v1.5.0 diff --git a/go.sum b/go.sum index 4affb2be..8699fe41 100644 --- a/go.sum +++ b/go.sum @@ -53,6 +53,8 @@ github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBa github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls= +github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= @@ -102,6 +104,7 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -109,8 +112,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.24+incompatible h1:Ugvxm7a8+Gz6vqQYQQ2W7GYq5EUPaAiuPgIfVyI3dYE= -github.com/docker/docker v20.10.24+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY= +github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= @@ -118,8 +121,8 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46 h1:7QPwrLT79GlD5sizHf27aoY2RTvw62mO6x7mxkScNk0= github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46/go.mod h1:esf2rsHFNlZlxsqsZDojNBcnNs5REqIvRrWRHqX0vEU= -github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= -github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE= +github.com/emicklei/go-restful/v3 v3.10.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -127,7 +130,6 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= @@ -135,7 +137,7 @@ github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2Vvl github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= @@ -147,17 +149,14 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= -github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= -github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= +github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= +github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -243,8 +242,8 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 h1:SJ+NtwL6QaZ21U+IrK7d0gGgpjGGvd2kz+FzTHVzdqI= github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2/go.mod h1:Tv1PlzqC9t8wNnpPdctvtSUOPUUg4SHeE6vR1Ir2hmg= @@ -290,8 +289,8 @@ github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -311,16 +310,16 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -348,7 +347,7 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -361,10 +360,10 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU= -github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= -github.com/onsi/gomega v1.27.5 h1:T/X6I0RNFw/kTqgfkZPcQ5KU6vCnWNBGdtrIx2dpGeQ= -github.com/onsi/gomega v1.27.5/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= +github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= +github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= +github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= @@ -376,8 +375,8 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= -github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -388,26 +387,26 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= -github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -418,8 +417,6 @@ github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFR github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -427,16 +424,16 @@ github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= -github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= +github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -444,8 +441,8 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= -github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= +github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= +github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -461,8 +458,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= @@ -491,14 +489,13 @@ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -510,9 +507,10 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -548,6 +546,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -585,10 +584,11 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -598,8 +598,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= -golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= +golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= +golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -657,18 +657,20 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -679,8 +681,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -703,7 +705,6 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -740,15 +741,16 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= -gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= +gomodules.xyz/jsonpatch/v2 v2.3.0 h1:8NFhfS6gzxNqjLIYnZxg319wZ5Qjnx4m/CcX+Klzazc= +gomodules.xyz/jsonpatch/v2 v2.3.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -814,8 +816,8 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef h1:uQ2vjV/sHTsWSqdKeLqmwitzgvjMl7o4IdtHwUDXSJY= -google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -848,14 +850,15 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= @@ -878,7 +881,7 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -886,39 +889,39 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.26.1 h1:f+SWYiPd/GsiWwVRz+NbFyCgvv75Pk9NK6dlkZgpCRQ= -k8s.io/api v0.26.1/go.mod h1:xd/GBNgR0f707+ATNyPmQ1oyKSgndzXij81FzWGsejg= -k8s.io/apiextensions-apiserver v0.26.1 h1:cB8h1SRk6e/+i3NOrQgSFij1B2S0Y0wDoNl66bn8RMI= -k8s.io/apiextensions-apiserver v0.26.1/go.mod h1:AptjOSXDGuE0JICx/Em15PaoO7buLwTs0dGleIHixSM= -k8s.io/apimachinery v0.26.1 h1:8EZ/eGJL+hY/MYCNwhmDzVqq2lPl3N3Bo8rvweJwXUQ= -k8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= -k8s.io/apiserver v0.26.1 h1:6vmnAqCDO194SVCPU3MU8NcDgSqsUA62tBUSWrFXhsc= -k8s.io/apiserver v0.26.1/go.mod h1:wr75z634Cv+sifswE9HlAo5FQ7UoUauIICRlOE+5dCg= -k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU= -k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE= -k8s.io/cluster-bootstrap v0.25.0 h1:KJ2/r0dV+bLfTK5EBobAVKvjGel3N4Qqh3bvnzh9qPk= -k8s.io/cluster-bootstrap v0.25.0/go.mod h1:x/TCtY3EiuR/rODkA3SvVQT3uSssQLf9cXcmSjdDTe0= -k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4= -k8s.io/component-base v0.26.1/go.mod h1:VHrLR0b58oC035w6YQiBSbtsf0ThuSwXP+p5dD/kAWU= -k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= -k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= -k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= -k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= -k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/api v0.27.2 h1:+H17AJpUMvl+clT+BPnKf0E3ksMAzoBBg7CntpSuADo= +k8s.io/api v0.27.2/go.mod h1:ENmbocXfBT2ADujUXcBhHV55RIT31IIEvkntP6vZKS4= +k8s.io/apiextensions-apiserver v0.27.2 h1:iwhyoeS4xj9Y7v8YExhUwbVuBhMr3Q4bd/laClBV6Bo= +k8s.io/apiextensions-apiserver v0.27.2/go.mod h1:Oz9UdvGguL3ULgRdY9QMUzL2RZImotgxvGjdWRq6ZXQ= +k8s.io/apimachinery v0.27.2 h1:vBjGaKKieaIreI+oQwELalVG4d8f3YAMNpWLzDXkxeg= +k8s.io/apimachinery v0.27.2/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= +k8s.io/apiserver v0.27.2 h1:p+tjwrcQEZDrEorCZV2/qE8osGTINPuS5ZNqWAvKm5E= +k8s.io/apiserver v0.27.2/go.mod h1:EsOf39d75rMivgvvwjJ3OW/u9n1/BmUMK5otEOJrb1Y= +k8s.io/client-go v0.27.2 h1:vDLSeuYvCHKeoQRhCXjxXO45nHVv2Ip4Fe0MfioMrhE= +k8s.io/client-go v0.27.2/go.mod h1:tY0gVmUsHrAmjzHX9zs7eCjxcBsf8IiNe7KQ52biTcQ= +k8s.io/cluster-bootstrap v0.27.2 h1:OL3onrOwrUD7NQxBUqQwTl1Uu2GQKCkw9BMHpc4PbiA= +k8s.io/cluster-bootstrap v0.27.2/go.mod h1:b++PF0mjUOiTKdPQFlDw7p4V2VquANZ8SfhAwzxZJFM= +k8s.io/component-base v0.27.2 h1:neju+7s/r5O4x4/txeUONNTS9r1HsPbyoPBAtHsDCpo= +k8s.io/component-base v0.27.2/go.mod h1:5UPk7EjfgrfgRIuDBFtsEFAe4DAvP3U+M8RTzoSJkpo= +k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= +k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= +k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= +k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/cluster-api v1.4.3 h1:QSeKr3qnWPtVp+EMQZQduKoi5WDwUhAtBRreEukkcy0= -sigs.k8s.io/cluster-api v1.4.3/go.mod h1:/SeFds4NXJ+Gp2etqHyoNuO6yoxTfVq6Zmd2OGxd/qM= -sigs.k8s.io/cluster-api/test v1.4.3 h1:xXhvOLp5zBKn2Yf4PQckFwxGxFrKgkgUI74ikU5Jh/c= -sigs.k8s.io/cluster-api/test v1.4.3/go.mod h1:xGTJsJkbXMNmumhErEAH/e7GmnE2sGfm/rcOzJZSdbw= -sigs.k8s.io/controller-runtime v0.14.5 h1:6xaWFqzT5KuAQ9ufgUaj1G/+C4Y1GRkhrxl+BJ9i+5s= -sigs.k8s.io/controller-runtime v0.14.5/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/kind v0.18.0 h1:ahgZdVV1pdhXlYe1f+ztISakT23KdrBl/NFY9JMygzs= -sigs.k8s.io/kind v0.18.0/go.mod h1:Qqp8AiwOlMZmJWs37Hgs31xcbiYXjtXlRBSftcnZXQk= +sigs.k8s.io/cluster-api v1.5.0 h1:pwXvzScbAwnrB7EWHTApzW+VQfrj2OSrWAQDC9+bcbU= +sigs.k8s.io/cluster-api v1.5.0/go.mod h1:ZSEP01t8oT6104gB4ljsOwwp5uJcI8SWy8IFp2HUvrc= +sigs.k8s.io/cluster-api/test v1.5.0 h1:ePfrh7S+eaVWy+D0ca4AjzhBqrhXxRE9NTr5te4hCa0= +sigs.k8s.io/cluster-api/test v1.5.0/go.mod h1:Ii5Mh9oVq7QJEtiUkGg9mM2qojjWxvsqmL8TMlwZViM= +sigs.k8s.io/controller-runtime v0.15.0 h1:ML+5Adt3qZnMSYxZ7gAverBLNPSMQEibtzAgp0UPojU= +sigs.k8s.io/controller-runtime v0.15.0/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/kind v0.20.0 h1:f0sc3v9mQbGnjBUaqSFST1dwIuiikKVGgoTwpoP33a8= +sigs.k8s.io/kind v0.20.0/go.mod h1:aBlbxg08cauDgZ612shr017/rZwqd7AS563FvpWKPVs= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= diff --git a/main.go b/main.go index 7b7e515b..1baada5f 100644 --- a/main.go +++ b/main.go @@ -31,6 +31,7 @@ import ( expcontrollers "github.com/oracle/cluster-api-provider-oci/exp/controllers" "github.com/oracle/cluster-api-provider-oci/feature" "github.com/oracle/cluster-api-provider-oci/version" + "github.com/oracle/oci-go-sdk/v65/common" "github.com/spf13/pflag" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" @@ -44,23 +45,17 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" + "sigs.k8s.io/controller-runtime/pkg/webhook" ) var ( - scheme = runtime.NewScheme() - setupLog = ctrl.Log.WithName("setup") - logOptions = logs.NewOptions() - webhookPort int - webhookCertDir string - - // Flags for reconciler concurrency - ociClusterConcurrency int - ociMachineConcurrency int - ociMachinePoolConcurrency int - initOciClientsOnStartup bool + scheme = runtime.NewScheme() + setupLog = ctrl.Log.WithName("setup") + logOptions = logs.NewOptions() ) const ( @@ -88,6 +83,13 @@ func main() { var watchNamespace string var probeAddr string var webhookPort int + var webhookCertDir string + // Flags for reconciler concurrency + var ociClusterConcurrency int + var ociMachineConcurrency int + var ociMachinePoolConcurrency int + var initOciClientsOnStartup bool + var enableInstanceMetadataServiceLookup bool fs := pflag.CommandLine logs.AddFlags(fs, logs.SkipLoggingConfigurationFlags()) @@ -159,6 +161,12 @@ func main() { "", "Namespace that the controller watches to reconcile cluster-api objects. If unspecified, the controller watches for cluster-api objects across all namespaces.", ) + flag.BoolVar( + &enableInstanceMetadataServiceLookup, + "enable-instance-metadata-service-lookup", + false, + "Initialize OCI clients on startup", + ) opts := zap.Options{ Development: true, @@ -179,9 +187,12 @@ func main() { ctrl.SetLogger(klog.Background()) mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ - Scheme: scheme, - MetricsBindAddress: metricsAddr, - Port: webhookPort, + Scheme: scheme, + MetricsBindAddress: metricsAddr, + WebhookServer: webhook.NewServer(webhook.Options{ + Port: webhookPort, + CertDir: webhookCertDir, + }), HealthProbeBindAddress: probeAddr, LeaderElection: enableLeaderElection, LeaderElectionID: "controller-leader-elect-capoci", @@ -190,8 +201,9 @@ func main() { LeaseDuration: &leaderElectionLeaseDuration, RenewDeadline: &leaderElectionRenewDeadline, RetryPeriod: &leaderElectionRetryPeriod, - CertDir: webhookCertDir, - Namespace: watchNamespace, + Cache: cache.Options{ + Namespaces: []string{watchNamespace}, + }, }) if err != nil { setupLog.Error(err, "unable to start manager") @@ -240,6 +252,9 @@ func main() { os.Exit(1) } } + if enableInstanceMetadataServiceLookup { + common.EnableInstanceMetadataServiceLookup() + } if err = (&controllers.OCIClusterReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), @@ -321,6 +336,17 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", scope.OCIVirtualMachinePoolKind) os.Exit(1) } + + if err = (&expcontrollers.OCIMachinePoolMachineReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Region: region, + ClientProvider: clientProvider, + Recorder: mgr.GetEventRecorderFor("ocimachinepoolmachine-controller"), + }).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: ociClusterConcurrency}); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "OCIMachinePoolMachine") + os.Exit(1) + } } if err = (&infrastructurev1beta2.OCICluster{}).SetupWebhookWithManager(mgr); err != nil { diff --git a/test/e2e/config/e2e_conf.yaml b/test/e2e/config/e2e_conf.yaml index c8a0bfdb..4bc18149 100644 --- a/test/e2e/config/e2e_conf.yaml +++ b/test/e2e/config/e2e_conf.yaml @@ -8,8 +8,8 @@ providers: - name: cluster-api type: CoreProvider versions: - - name: v1.4.3 - value: https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.4.3/core-components.yaml + - name: v1.5.0 + value: https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.5.0/core-components.yaml type: url files: - sourcePath: "../data/shared/v1beta1/metadata.yaml" @@ -21,8 +21,8 @@ providers: - name: kubeadm type: BootstrapProvider versions: - - name: v1.4.3 - value: https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.4.3/bootstrap-components.yaml + - name: v1.5.0 + value: https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.5.0/bootstrap-components.yaml type: url files: - sourcePath: "../data/shared/v1beta1/metadata.yaml" @@ -34,8 +34,8 @@ providers: - name: kubeadm type: ControlPlaneProvider versions: - - name: v1.4.3 - value: https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.4.3/control-plane-components.yaml + - name: v1.5.0 + value: https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.5.0/control-plane-components.yaml type: url files: - sourcePath: "../data/shared/v1beta1/metadata.yaml" diff --git a/test/e2e/data/shared/v1beta1/metadata.yaml b/test/e2e/data/shared/v1beta1/metadata.yaml index 4a1df451..46725596 100644 --- a/test/e2e/data/shared/v1beta1/metadata.yaml +++ b/test/e2e/data/shared/v1beta1/metadata.yaml @@ -2,5 +2,5 @@ apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3 kind: Metadata releaseSeries: - major: 1 - minor: 4 + minor: 5 contract: v1beta1 diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 2e4e73c6..1017bc9a 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -24,8 +24,10 @@ import ( "encoding/base64" "flag" "fmt" + "k8s.io/klog/v2" "os" "path/filepath" + ctrl "sigs.k8s.io/controller-runtime" "strconv" "strings" "testing" @@ -139,6 +141,9 @@ func init() { func TestE2E(t *testing.T) { RegisterFailHandler(Fail) + // Before all ParallelNodes. + + ctrl.SetLogger(klog.Background()) RunSpecs(t, "capoci-e2e") } @@ -146,8 +151,6 @@ func TestE2E(t *testing.T) { // Using a SynchronizedBeforeSuite for controlling how to create resources shared across ParallelNodes (~ginkgo threads). // The local clusterctl repository & the bootstrap cluster are created once and shared across all the tests. var _ = SynchronizedBeforeSuite(func() []byte { - // Before all ParallelNodes. - Expect(configPath).To(BeAnExistingFile(), "Invalid test suite argument. e2e.config should be an existing file.") Expect(os.MkdirAll(artifactFolder, 0755)).To(Succeed(), "Invalid test suite argument. Can't create e2e.artifacts-folder %q", artifactFolder) diff --git a/test/e2e/managed_cluster_test.go b/test/e2e/managed_cluster_test.go index 0ad796f3..dac6a6e0 100644 --- a/test/e2e/managed_cluster_test.go +++ b/test/e2e/managed_cluster_test.go @@ -141,7 +141,7 @@ var _ = Describe("Managed Workload cluster creation", func() { WaitForMachinePools: e2eConfig.GetIntervals(specName, "wait-machine-pool-nodes"), WaitForMachineDeployments: e2eConfig.GetIntervals(specName, "wait-worker-nodes"), } - input.WaitForControlPlaneInitialized = func(ctx context.Context, input clusterctl.ApplyClusterTemplateAndWaitInput, result *clusterctl.ApplyClusterTemplateAndWaitResult) { + input.WaitForControlPlaneInitialized = func(ctx context.Context, input clusterctl.ApplyCustomClusterTemplateAndWaitInput, result *clusterctl.ApplyCustomClusterTemplateAndWaitResult) { Expect(ctx).NotTo(BeNil(), "ctx is required for DiscoveryAndWaitForControlPlaneInitialized") lister := input.ClusterProxy.GetClient() Expect(lister).ToNot(BeNil(), "Invalid argument. input.Lister can't be nil when calling DiscoveryAndWaitForControlPlaneInitialized") @@ -155,12 +155,14 @@ var _ = Describe("Managed Workload cluster creation", func() { g.Expect(controlPlane.Status.Ready).To(BeTrue()) }, input.WaitForControlPlaneIntervals...).Should(Succeed(), "Couldn't get the control plane ready status for the cluster %s", klog.KObj(result.Cluster)) } - input.WaitForControlPlaneMachinesReady = func(ctx context.Context, input clusterctl.ApplyClusterTemplateAndWaitInput, result *clusterctl.ApplyClusterTemplateAndWaitResult) { + input.WaitForControlPlaneMachinesReady = func(ctx context.Context, input clusterctl.ApplyCustomClusterTemplateAndWaitInput, result *clusterctl.ApplyCustomClusterTemplateAndWaitResult) { // Not applicable } clusterctl.ApplyClusterTemplateAndWait(ctx, input, result) + validateMachinePoolMachines(ctx, result.Cluster, bootstrapClusterProxy, result.MachinePools) + By("Scaling the machine pool up") framework.ScaleMachinePoolAndWait(ctx, framework.ScaleMachinePoolAndWaitInput{ ClusterProxy: bootstrapClusterProxy, @@ -169,6 +171,7 @@ var _ = Describe("Managed Workload cluster creation", func() { MachinePools: result.MachinePools, WaitForMachinePoolToScale: e2eConfig.GetIntervals(specName, "wait-machine-pool-nodes"), }) + validateMachinePoolMachines(ctx, result.Cluster, bootstrapClusterProxy, result.MachinePools) By("Scaling the machine pool down") framework.ScaleMachinePoolAndWait(ctx, framework.ScaleMachinePoolAndWaitInput{ @@ -178,6 +181,7 @@ var _ = Describe("Managed Workload cluster creation", func() { MachinePools: result.MachinePools, WaitForMachinePoolToScale: e2eConfig.GetIntervals(specName, "wait-machine-pool-nodes"), }) + validateMachinePoolMachines(ctx, result.Cluster, bootstrapClusterProxy, result.MachinePools) upgradeControlPlaneVersionSpec(ctx, bootstrapClusterProxy.GetClient(), clusterName, namespace.Name, e2eConfig.GetIntervals(specName, "wait-control-plane")) }) @@ -203,7 +207,7 @@ var _ = Describe("Managed Workload cluster creation", func() { WaitForMachinePools: e2eConfig.GetIntervals(specName, "wait-machine-pool-nodes"), WaitForMachineDeployments: e2eConfig.GetIntervals(specName, "wait-worker-nodes"), } - input.WaitForControlPlaneInitialized = func(ctx context.Context, input clusterctl.ApplyClusterTemplateAndWaitInput, result *clusterctl.ApplyClusterTemplateAndWaitResult) { + input.WaitForControlPlaneInitialized = func(ctx context.Context, input clusterctl.ApplyCustomClusterTemplateAndWaitInput, result *clusterctl.ApplyCustomClusterTemplateAndWaitResult) { Expect(ctx).NotTo(BeNil(), "ctx is required for DiscoveryAndWaitForControlPlaneInitialized") lister := input.ClusterProxy.GetClient() Expect(lister).ToNot(BeNil(), "Invalid argument. input.Lister can't be nil when calling DiscoveryAndWaitForControlPlaneInitialized") @@ -217,7 +221,7 @@ var _ = Describe("Managed Workload cluster creation", func() { g.Expect(controlPlane.Status.Ready).To(BeTrue()) }, input.WaitForControlPlaneIntervals...).Should(Succeed(), "Couldn't get the control plane ready status for the cluster %s", klog.KObj(result.Cluster)) } - input.WaitForControlPlaneMachinesReady = func(ctx context.Context, input clusterctl.ApplyClusterTemplateAndWaitInput, result *clusterctl.ApplyClusterTemplateAndWaitResult) { + input.WaitForControlPlaneMachinesReady = func(ctx context.Context, input clusterctl.ApplyCustomClusterTemplateAndWaitInput, result *clusterctl.ApplyCustomClusterTemplateAndWaitResult) { // Not applicable } @@ -245,7 +249,7 @@ var _ = Describe("Managed Workload cluster creation", func() { WaitForMachinePools: e2eConfig.GetIntervals(specName, "wait-machine-pool-nodes"), WaitForMachineDeployments: e2eConfig.GetIntervals(specName, "wait-worker-nodes"), } - input.WaitForControlPlaneInitialized = func(ctx context.Context, input clusterctl.ApplyClusterTemplateAndWaitInput, result *clusterctl.ApplyClusterTemplateAndWaitResult) { + input.WaitForControlPlaneInitialized = func(ctx context.Context, input clusterctl.ApplyCustomClusterTemplateAndWaitInput, result *clusterctl.ApplyCustomClusterTemplateAndWaitResult) { Expect(ctx).NotTo(BeNil(), "ctx is required for DiscoveryAndWaitForControlPlaneInitialized") lister := input.ClusterProxy.GetClient() Expect(lister).ToNot(BeNil(), "Invalid argument. input.Lister can't be nil when calling DiscoveryAndWaitForControlPlaneInitialized") @@ -259,7 +263,7 @@ var _ = Describe("Managed Workload cluster creation", func() { g.Expect(controlPlane.Status.Ready).To(BeTrue()) }, input.WaitForControlPlaneIntervals...).Should(Succeed(), "Couldn't get the control plane ready status for the cluster %s", klog.KObj(result.Cluster)) } - input.WaitForControlPlaneMachinesReady = func(ctx context.Context, input clusterctl.ApplyClusterTemplateAndWaitInput, result *clusterctl.ApplyClusterTemplateAndWaitResult) { + input.WaitForControlPlaneMachinesReady = func(ctx context.Context, input clusterctl.ApplyCustomClusterTemplateAndWaitInput, result *clusterctl.ApplyCustomClusterTemplateAndWaitResult) { // Not applicable } @@ -290,7 +294,7 @@ var _ = Describe("Managed Workload cluster creation", func() { WaitForMachinePools: e2eConfig.GetIntervals(specName, "wait-machine-pool-nodes"), WaitForMachineDeployments: e2eConfig.GetIntervals(specName, "wait-worker-nodes"), } - input.WaitForControlPlaneInitialized = func(ctx context.Context, input clusterctl.ApplyClusterTemplateAndWaitInput, result *clusterctl.ApplyClusterTemplateAndWaitResult) { + input.WaitForControlPlaneInitialized = func(ctx context.Context, input clusterctl.ApplyCustomClusterTemplateAndWaitInput, result *clusterctl.ApplyCustomClusterTemplateAndWaitResult) { Expect(ctx).NotTo(BeNil(), "ctx is required for DiscoveryAndWaitForControlPlaneInitialized") lister := input.ClusterProxy.GetClient() Expect(lister).ToNot(BeNil(), "Invalid argument. input.Lister can't be nil when calling DiscoveryAndWaitForControlPlaneInitialized") @@ -304,12 +308,13 @@ var _ = Describe("Managed Workload cluster creation", func() { g.Expect(controlPlane.Status.Ready).To(BeTrue()) }, input.WaitForControlPlaneIntervals...).Should(Succeed(), "Couldn't get the control plane ready status for the cluster %s", klog.KObj(result.Cluster)) } - input.WaitForControlPlaneMachinesReady = func(ctx context.Context, input clusterctl.ApplyClusterTemplateAndWaitInput, result *clusterctl.ApplyClusterTemplateAndWaitResult) { + input.WaitForControlPlaneMachinesReady = func(ctx context.Context, input clusterctl.ApplyCustomClusterTemplateAndWaitInput, result *clusterctl.ApplyCustomClusterTemplateAndWaitResult) { // Not applicable } clusterctl.ApplyClusterTemplateAndWait(ctx, input, result) + validateMachinePoolMachines(ctx, result.Cluster, bootstrapClusterProxy, result.MachinePools) controlPlane := GetOCIManagedControlPlaneByCluster(ctx, bootstrapClusterProxy.GetClient(), clusterName, namespace.Name) Expect(controlPlane).To(Not(BeNil())) clusterOcid := controlPlane.Spec.ID @@ -343,7 +348,7 @@ var _ = Describe("Managed Workload cluster creation", func() { WaitForMachineDeployments: e2eConfig.GetIntervals(specName, "wait-worker-nodes"), WaitForMachinePools: e2eConfig.GetIntervals(specName, "wait-machine-pool-nodes"), } - input.WaitForControlPlaneInitialized = func(ctx context.Context, input clusterctl.ApplyClusterTemplateAndWaitInput, result *clusterctl.ApplyClusterTemplateAndWaitResult) { + input.WaitForControlPlaneInitialized = func(ctx context.Context, input clusterctl.ApplyCustomClusterTemplateAndWaitInput, result *clusterctl.ApplyCustomClusterTemplateAndWaitResult) { Expect(ctx).NotTo(BeNil(), "ctx is required for DiscoveryAndWaitForControlPlaneInitialized") lister := input.ClusterProxy.GetClient() Expect(lister).ToNot(BeNil(), "Invalid argument. input.Lister can't be nil when calling DiscoveryAndWaitForControlPlaneInitialized") @@ -357,11 +362,12 @@ var _ = Describe("Managed Workload cluster creation", func() { g.Expect(controlPlane.Status.Ready).To(BeTrue()) }, input.WaitForControlPlaneIntervals...).Should(Succeed(), "Couldn't get the control plane ready status for the cluster %s", klog.KObj(result.Cluster)) } - input.WaitForControlPlaneMachinesReady = func(ctx context.Context, input clusterctl.ApplyClusterTemplateAndWaitInput, result *clusterctl.ApplyClusterTemplateAndWaitResult) { + input.WaitForControlPlaneMachinesReady = func(ctx context.Context, input clusterctl.ApplyCustomClusterTemplateAndWaitInput, result *clusterctl.ApplyCustomClusterTemplateAndWaitResult) { // Not applicable } clusterctl.ApplyClusterTemplateAndWait(ctx, input, result) + validateMachinePoolMachines(ctx, result.Cluster, bootstrapClusterProxy, result.MachinePools) }) }) @@ -470,6 +476,34 @@ func updateMachinePoolVersion(ctx context.Context, cluster *clusterv1.Cluster, c }, waitInterval...).Should(Equal(1), "Timed out waiting for all MachinePool %s instances to be upgraded to Kubernetes version %s", klog.KObj(machinePool), managedKubernetesUpgradeVersion) } +func validateMachinePoolMachines(ctx context.Context, cluster *clusterv1.Cluster, clusterProxy framework.ClusterProxy, machinePools []*expv1.MachinePool) { + Eventually(func() error { + lister := clusterProxy.GetClient() + for _, pool := range machinePools { + machineList := &infrav2exp.OCIMachinePoolMachineList{} + labels := map[string]string{ + clusterv1.ClusterNameLabel: cluster.Name, + clusterv1.MachinePoolNameLabel: pool.Name, + } + if err := lister.List(ctx, machineList, client.InNamespace(cluster.Namespace), client.MatchingLabels(labels)); err != nil { + return err + } + + if len(machineList.Items) != int(*pool.Spec.Replicas) { + return errors.New(fmt.Sprintf("Infra machines does not equal machine pool replicas for machinepool %s", pool.Name)) + } + for _, managedMachine := range machineList.Items { + _, err := util.GetOwnerMachine(ctx, lister, managedMachine.ObjectMeta) + if err != nil { + return err + } + } + Logf("Machinepool machines created successfully for machinepool %s", pool.Name) + } + return nil + }, retryableOperationTimeout, retryableOperationInterval).Should(Succeed(), "Machinepool machines were not created properly") +} + // getMachinePoolInstanceVersions returns the Kubernetes versions of the machine pool instances. // This method was forked because we need to lookup the kubeconfig with each call // as the tokens are refreshed in case of OKE @@ -481,7 +515,7 @@ func getMachinePoolInstanceVersions(ctx context.Context, clusterProxy framework. for i, instance := range instances { node := &corev1.Node{} var nodeGetError error - err := wait.PollImmediate(100*time.Millisecond, 10*time.Second, func() (bool, error) { + err := wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, 10*time.Second, true, func(ctx context.Context) (bool, error) { nodeGetError = clusterProxy.GetWorkloadCluster(ctx, cluster.Namespace, cluster.Name). GetClient().Get(ctx, client.ObjectKey{Name: instance.Name}, node) if nodeGetError != nil {