From d6756706d02620112448b597499320a1ed2f7236 Mon Sep 17 00:00:00 2001 From: jharrod Date: Tue, 11 Jun 2024 16:23:54 -0600 Subject: [PATCH 01/10] create pending cloud and cluster --- Makefile | 2 +- .../connector/astra_connect_natless.go | 24 +- app/register/register.go | 540 +++++++++++++++++- app/register/register_test.go | 23 +- common/constants.go | 2 + details/k8s/cluster_type_checker.go | 4 +- details/k8s/k8s_util.go | 19 +- details/k8s/precheck/k8s_version.go | 2 +- .../api/v1/astraconnector_types.go | 2 + .../controllers/astraconnector_controller.go | 98 +++- .../controllers/status_strings.go | 3 + mocks/ClusterRegisterUtil.go | 105 +++- mocks/ClusterTypeCheckerInterface.go | 15 +- mocks/Deployer.go | 110 +++- mocks/HTTPClient.go | 20 +- mocks/K8sUtilInterface.go | 65 ++- mocks/SetWarning.go | 15 +- mocks/getK8sResources.go | 22 +- 18 files changed, 923 insertions(+), 148 deletions(-) diff --git a/Makefile b/Makefile index 38ccdf92..447de3e4 100644 --- a/Makefile +++ b/Makefile @@ -148,7 +148,7 @@ envtest: ## Download envtest-setup locally if necessary. MOCKERY = $(shell pwd)/bin/mockery install-mockery: ## Download mockery locally if necessary: https://github.com/vektra/mockery - $(call go-get-tool,$(MOCKERY),github.com/vektra/mockery/v2@v2.19.0) + $(call go-get-tool,$(MOCKERY),github.com/vektra/mockery/v2@v2.43.2) GOLANGCI_LINT = $(shell pwd)/bin/golangci-lint install-golangci-lint: ## Download golangci-lint locally if necessary: https://github.com/golangci/golangci-lint diff --git a/app/deployer/connector/astra_connect_natless.go b/app/deployer/connector/astra_connect_natless.go index 851a72ab..26a5e0e1 100644 --- a/app/deployer/connector/astra_connect_natless.go +++ b/app/deployer/connector/astra_connect_natless.go @@ -40,6 +40,8 @@ func (d *AstraConnectDeployer) GetDeploymentObjects(m *v1.AstraConnector, ctx co var imageRegistry string var containerImage string var connectorImage string + var clusterId string + var cloudId string if m.Spec.ImageRegistry.Name != "" { imageRegistry = m.Spec.ImageRegistry.Name } else { @@ -52,15 +54,21 @@ func (d *AstraConnectDeployer) GetDeploymentObjects(m *v1.AstraConnector, ctx co containerImage = common.ConnectorImageTag } - connectorImage = fmt.Sprintf("%s/astra-connector:%s", imageRegistry, containerImage) - log.Info("Using AstraConnector image", "image", connectorImage) + if m.Spec.Astra.CloudId != "" { + cloudId = m.Spec.Astra.CloudId + } else { + cloudId = m.Status.CloudId + } - if m.Spec.Astra.ClusterId == "" && m.Spec.Astra.ClusterName == "" { - err := fmt.Errorf("clusterID and clusterName both cannot be empty") - log.Error(err, "Bad config") - return nil, nil, err + if m.Spec.Astra.ClusterId != "" { + clusterId = m.Spec.Astra.ClusterId + } else { + clusterId = m.Status.ClusterId } + connectorImage = fmt.Sprintf("%s/astra-connector:%s", imageRegistry, containerImage) + log.Info("Using AstraConnector image", "image", connectorImage) + dep := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: common.AstraConnectName, @@ -106,11 +114,11 @@ func (d *AstraConnectDeployer) GetDeploymentObjects(m *v1.AstraConnector, ctx co }, { Name: "CLOUD_ID", - Value: m.Spec.Astra.CloudId, + Value: cloudId, }, { Name: "CLUSTER_ID", - Value: m.Spec.Astra.ClusterId, + Value: clusterId, }, { Name: "HOST_ALIAS_IP", diff --git a/app/register/register.go b/app/register/register.go index 701056f3..0f7512c2 100644 --- a/app/register/register.go +++ b/app/register/register.go @@ -28,6 +28,26 @@ import ( v1 "github.com/NetApp-Polaris/astra-connector-operator/details/operator-sdk/api/v1" ) +type ConnectorInstallState string +type ClusterState string +type ClusterHeartbeatState string +type ClusterManagedState string +type CloudType string + +const ( + ConnectorInstallStatePending ConnectorInstallState = "pending" + ConnectorInstallStateInstalled ConnectorInstallState = "installed" + + ClusterStatePending ClusterState = "pending" + + ClusterManagedStateManaged ClusterManagedState = "managed" + + ApiCloudType = "application/astra-cloud" + ApiCloudVersion = "1.1" + + CloudTypePrivate CloudType = "private" +) + // HTTPClient interface used for request and to facilitate testing type HTTPClient interface { Do(req *http.Request) (*http.Response, error) @@ -91,6 +111,8 @@ type ClusterRegisterUtil interface { GetAPITokenFromSecret(secretName string) (string, string, error) IsClusterManaged() (bool, string, error) SetHttpClient(disableTls bool, astraHost string) error + RegisterCloud() (string, string, error) + RegisterCluster(cloudId string, k8sServiceId string) (string, string, error) } type clusterRegisterUtil struct { @@ -100,10 +122,12 @@ type clusterRegisterUtil struct { K8sUtil k8s.K8sUtilInterface Ctx context.Context Log logr.Logger + ApiToken string + AstraHostUrl string } -func NewClusterRegisterUtil(astraConnector *v1.AstraConnector, client HTTPClient, k8sClient client.Client, k8sUtil k8s.K8sUtilInterface, log logr.Logger, ctx context.Context) ClusterRegisterUtil { - return &clusterRegisterUtil{ +func NewClusterRegisterUtil(astraConnector *v1.AstraConnector, client HTTPClient, k8sClient client.Client, k8sUtil k8s.K8sUtilInterface, log logr.Logger, ctx context.Context) (ClusterRegisterUtil, error) { + c := clusterRegisterUtil{ AstraConnector: astraConnector, Client: client, K8sClient: k8sClient, @@ -111,6 +135,17 @@ func NewClusterRegisterUtil(astraConnector *v1.AstraConnector, client HTTPClient Log: log, Ctx: ctx, } + var err error + c.ApiToken, _, err = c.GetAPITokenFromSecret(c.AstraConnector.Spec.Astra.TokenRef) + if err != nil { + return nil, err + } + c.AstraHostUrl = GetAstraHostURL(astraConnector) + err = c.SetHttpClient(astraConnector.Spec.Astra.SkipTLSValidation, c.AstraHostUrl) + if err != nil { + return nil, err + } + return &c, nil } // ****************************** @@ -188,18 +223,12 @@ func (c clusterRegisterUtil) SetHttpClient(disableTls bool, astraHost string) er } func (c clusterRegisterUtil) IsClusterManaged() (bool, string, error) { - apiToken, errorReason, err := c.GetAPITokenFromSecret(c.AstraConnector.Spec.Astra.TokenRef) - if err != nil { - return false, errorReason, err - } - - astraHost := GetAstraHostURL(c.AstraConnector) - url := fmt.Sprintf("%s/accounts/%s/topology/v1/managedClusters/%s", astraHost, c.AstraConnector.Spec.Astra.AccountId, c.AstraConnector.Spec.Astra.ClusterId) + url := fmt.Sprintf("%s/accounts/%s/topology/v1/managedClusters/%s", c.AstraHostUrl, c.AstraConnector.Spec.Astra.AccountId, c.AstraConnector.Spec.Astra.ClusterId) c.Log.WithValues("ClusterId", c.AstraConnector.Spec.Astra.ClusterId). Info("Checking if cluster is managed") - headerMap := HeaderMap{Authorization: fmt.Sprintf("Bearer %s", apiToken)} + headerMap := HeaderMap{Authorization: fmt.Sprintf("Bearer %s", c.ApiToken)} response, err, cancel := DoRequest(c.Ctx, c.Client, http.MethodGet, url, nil, headerMap, c.Log) defer cancel() if err != nil { @@ -235,21 +264,30 @@ func (c clusterRegisterUtil) IsClusterManaged() (bool, string, error) { } type Cluster struct { - Type string `json:"type,omitempty"` - Version string `json:"version,omitempty"` - ID string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - ManagedState string `json:"managedState,omitempty"` - ClusterType string `json:"clusterType,omitempty"` - CloudID string `json:"cloudID,omitempty"` - PrivateRouteID string `json:"privateRouteID,omitempty"` - ConnectorCapabilities []string `json:"connectorCapabilities,omitempty"` - ConnectorInstall string `json:"connectorInstall,omitempty"` - TridentManagedStateDesired string `json:"tridentManagedStateDesired,omitempty"` - ApiServiceID string `json:"apiServiceID,omitempty"` -} - -type GetClustersResponse struct { + Type string `json:"type,omitempty"` + Version string `json:"version,omitempty"` + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + State ClusterState `json:"state,omitempty"` + ManagedState ClusterManagedState `json:"managedState,omitempty"` + ClusterType string `json:"clusterType,omitempty"` + CloudID string `json:"cloudID,omitempty"` + PrivateRouteID string `json:"privateRouteID,omitempty"` + ConnectorCapabilities []string `json:"connectorCapabilities,omitempty"` + ConnectorInstall ConnectorInstallState `json:"connectorInstall,omitempty"` + TridentManagedStateDesired string `json:"tridentManagedStateDesired,omitempty"` + ApiServiceID string `json:"apiServiceID,omitempty"` + ClusterVersion string `json:"clusterVersion,omitempty"` + ClusterVersionString string `json:"clusterVersionString,omitempty"` + LastHeartbeat string `json:"lastHeartbeat,omitempty"` + HeartbeatState string `json:"heartbeatState,omitempty"` + ConnectorNamespace string `json:"connectorNamespace,omitempty"` + AstraControlURL string `json:"astraControlURL,omitempty"` + HostAliasIP string `json:"hostAliasIP,omitempty"` + APITokenRef string `json:"apiTokenRef,omitempty"` +} + +type ClusterList struct { Items []Cluster `json:"items"` } @@ -260,6 +298,51 @@ type ClusterInfo struct { ConnectorInstall string } +type Cloud struct { + ID string `json:"id,omitempty"` + Type string `json:"type"` + Version string `json:"version"` + Name string `json:"name"` + CloudType CloudType `json:"cloudType"` +} + +type CloudList struct { + Items []Cloud `json:"items"` +} + +func (c clusterRegisterUtil) CreateManagedCluster(managedCluster *Cluster) (string, error) { + if managedCluster == nil { + errMsg := "the given managedCluster is nil" + return errMsg, errors.New(errMsg) + } + if managedCluster.ID == "" { + errMsg := "error creating managed cluster, no clusterId provided" + return errMsg, errors.New(errMsg) + } + + managedCluster.Type = "application/astra-managedCluster" + managedCluster.Version = "1.2" + + body, err := json.Marshal(managedCluster) + if err != nil { + errMsg := fmt.Sprintf("failed to marshal ManagedCluster: %s", err) + return errMsg, errors.New(errMsg) + } + + url := fmt.Sprintf("%s/accounts/%s/topology/v1/managedClusters", c.AstraHostUrl, c.AstraConnector.Spec.Astra.AccountId) + + headerMap := HeaderMap{Authorization: fmt.Sprintf("Bearer %s", c.ApiToken)} + response, err, cancel := DoRequest(c.Ctx, c.Client, http.MethodPost, url, body, headerMap, c.Log) + defer cancel() + if err != nil { + return CreateErrorMsg("CreateManagedCluster", "POST /managedClusters error", url, "", "", err), err + } + if response.StatusCode != http.StatusCreated { + return CreateErrorMsg("CreateManagedCluster", "POST /managedClusters non 201 response", url, response.Status, "", err), err + } + return "", nil +} + // GetAPITokenFromSecret Gets Secret provided in the ACC Spec and returns api token string of the data in secret func (c clusterRegisterUtil) GetAPITokenFromSecret(secretName string) (string, string, error) { secret := &coreV1.Secret{} @@ -299,3 +382,410 @@ func CreateErrorMsg(functionName, action, url, status, responseBody string, err return errorMsg } + +func (c clusterRegisterUtil) CloudExists() (bool, string, error) { + url := fmt.Sprintf("%s/accounts/%s/topology/v1/clouds/%s", c.AstraHostUrl, c.AstraConnector.Spec.Astra.AccountId, c.AstraConnector.Spec.Astra.CloudId) + + c.Log.WithValues("CloudId", c.AstraConnector.Spec.Astra.CloudId). + Info("Checking if cloud exists") + + headerMap := HeaderMap{Authorization: fmt.Sprintf("Bearer %s", c.ApiToken)} + response, err, cancel := DoRequest(c.Ctx, c.Client, http.MethodGet, url, nil, headerMap, c.Log) + defer cancel() + if err != nil { + return false, CreateErrorMsg("CloudExists", "GET /clouds error", url, "", "", err), err + } + if response.StatusCode != 200 { + return false, CreateErrorMsg("CloudExists", "GET /clouds non 200 response", url, response.Status, "", err), err + } + + return true, "", nil +} + +func (c clusterRegisterUtil) GetClouds() ([]Cloud, string, error) { + url := fmt.Sprintf("%s/accounts/%s/topology/v1/clouds", c.AstraHostUrl, c.AstraConnector.Spec.Astra.AccountId) + + headerMap := HeaderMap{Authorization: fmt.Sprintf("Bearer %s", c.ApiToken)} + response, err, cancel := DoRequest(c.Ctx, c.Client, http.MethodGet, url, nil, headerMap, c.Log) + defer cancel() + if err != nil { + return nil, CreateErrorMsg("PrivateCloudExists", "GET /clouds error", url, "", "", err), err + } + if response.StatusCode != 200 { + return nil, CreateErrorMsg("PrivateCloudExists", "GET /clouds non 200 response", url, response.Status, "", err), err + } + + respBody, err := c.readResponseBody(response) + if err != nil { + return nil, CreateErrorMsg("PrivateCloudExists", "parse GET /clouds response", url, response.Status, "", err), err + } + + clouds := &CloudList{} + err = json.Unmarshal(respBody, &clouds) + if err != nil { + return nil, CreateErrorMsg("PrivateCloudExists", "unmarshal response from GET call", url, response.Status, string(respBody), err), err + } + return clouds.Items, "", nil +} + +func (c clusterRegisterUtil) PrivateCloudExists() (*Cloud, string, error) { + clouds, errMsg, err := c.GetClouds() + if err != nil { + return nil, errMsg, err + } + for _, cloud := range clouds { + if cloud.CloudType == CloudTypePrivate && cloud.Name == "private" { + return &cloud, "", nil + } + } + return nil, "", nil +} + +func (c clusterRegisterUtil) CreateCloud(cloud *Cloud) (*Cloud, string, error) { + if cloud.ID != "" { + errMsg := "cannot specify ID for post" + return nil, errMsg, fmt.Errorf(errMsg) + } + body, err := json.Marshal(cloud) + if err != nil { + errMsg := "failed to marshall cloud object" + return nil, errMsg, errors.New(errMsg) + } + + url := fmt.Sprintf("%s/accounts/%s/topology/v1/clouds", c.AstraHostUrl, c.AstraConnector.Spec.Astra.AccountId) + + headerMap := HeaderMap{Authorization: fmt.Sprintf("Bearer %s", c.ApiToken)} + response, err, cancel := DoRequest(c.Ctx, c.Client, http.MethodPost, url, body, headerMap, c.Log) + defer cancel() + if err != nil { + return nil, CreateErrorMsg("CreateCloud", "POST /clouds error", url, "", "", err), err + } + if response.StatusCode != http.StatusCreated { + return nil, CreateErrorMsg("CreateCloud", "POST /clouds non 201 response", url, response.Status, "", err), err + } + respBody, err := c.readResponseBody(response) + if err != nil { + return nil, CreateErrorMsg("CreateCloud", "parse POST /clouds response", url, response.Status, "", err), err + } + + newCloud := &Cloud{} + err = json.Unmarshal(respBody, &newCloud) + if err != nil { + return nil, CreateErrorMsg("CreateCloud", "unmarshal response from POST call", url, response.Status, string(respBody), err), err + } + + return newCloud, "", nil +} + +func (c clusterRegisterUtil) RegisterCloud() (string, string, error) { + // If CloudID was included in the spec, check if it exists + if c.AstraConnector.Spec.Astra.CloudId != "" { + exists, errMsg, err := c.CloudExists() + if err != nil { + c.Log.WithValues("cloudId", c.AstraConnector.Spec.Astra.CloudId).Error(err, "error checking if cloud exists") + return "", errMsg, err + } + if !exists { + errMsg := fmt.Sprintf("cloud '%s' does not exist", c.AstraConnector.Spec.Astra.CloudId) + return "", errMsg, errors.New(errMsg) + } + if exists { + return c.AstraConnector.Spec.Astra.CloudId, "", nil + } + } + + // If cloudID wasn't included in the spec, check if "private" cloud exists + existingCloud, errMsg, err := c.PrivateCloudExists() + if err != nil { + return "", errMsg, fmt.Errorf("cannot determine if private cloud exists: %w", err) + } + if existingCloud != nil { + c.Log.Info("Private cloud already exists", "cloudID", existingCloud.ID) + return existingCloud.ID, "", nil + } + + // If the private cloud type doesn't exist, create one. + cloud := Cloud{ + Type: ApiCloudType, + Version: ApiCloudVersion, + Name: "private", + CloudType: CloudTypePrivate, + } + createdCloud, errMsg, err := c.CreateCloud(&cloud) + if err != nil { + return "", errMsg, fmt.Errorf("cannot create cloud: %w", err) + } + + c.Log.WithValues("cloudId", createdCloud.ID).Info("Created private cloud") + return createdCloud.ID, "", nil +} + +func (c clusterRegisterUtil) ListClusters() ([]Cluster, string, error) { + url := fmt.Sprintf("%s/topology/v1/clusters", c.AstraHostUrl) + headerMap := HeaderMap{Authorization: fmt.Sprintf("Bearer %s", c.ApiToken)} + response, err, cancel := DoRequest(c.Ctx, c.Client, http.MethodGet, url, nil, headerMap, c.Log) + defer cancel() + if err != nil { + return nil, CreateErrorMsg("ListClusters", "GET /clusters error", url, "", "", err), err + } + if response.StatusCode != http.StatusOK { + return nil, CreateErrorMsg("ListClusters", "GET /clusters non 201 response", url, response.Status, "", err), err + } + respBody, err := c.readResponseBody(response) + if err != nil { + return nil, CreateErrorMsg("ListClusters", "GET /clusters response", url, response.Status, "", err), err + } + + clusters := &ClusterList{} + err = json.Unmarshal(respBody, &clusters) + if err != nil { + return nil, CreateErrorMsg("ListClusters", "unmarshal response from GET call", url, response.Status, string(respBody), err), err + } + return clusters.Items, "", nil +} + +func (c clusterRegisterUtil) GetCluster(cloudId, clusterId string) (*Cluster, string, error) { + url := fmt.Sprintf("%s/accounts/%s/topology/v1/clouds/%s/clusters/%s", c.AstraHostUrl, c.AstraConnector.Spec.Astra.AccountId, cloudId, clusterId) + headerMap := HeaderMap{Authorization: fmt.Sprintf("Bearer %s", c.ApiToken)} + response, err, cancel := DoRequest(c.Ctx, c.Client, http.MethodGet, url, nil, headerMap, c.Log) + defer cancel() + if err != nil { + return nil, CreateErrorMsg("GetCluster", "GET /clusters error", url, "", "", err), err + } + if response.StatusCode != http.StatusOK { + return nil, CreateErrorMsg("GetCluster", "GET /clusters non 201 response", url, response.Status, "", err), err + } + respBody, err := c.readResponseBody(response) + if err != nil { + return nil, CreateErrorMsg("GetCluster", "GET /clusters response", url, response.Status, "", err), err + } + + cluster := &Cluster{} + err = json.Unmarshal(respBody, &cluster) + if err != nil { + return nil, CreateErrorMsg("GetCluster", "unmarshal response from GET call", url, response.Status, string(respBody), err), err + } + return cluster, "", nil +} + +func (c clusterRegisterUtil) CreateCluster(inputCluster *Cluster, cloudId string) (*Cluster, string, error) { + url := fmt.Sprintf("%s/accounts/%s/topology/v1/clouds/%s/clusters", c.AstraHostUrl, c.AstraConnector.Spec.Astra.AccountId, cloudId) + headerMap := HeaderMap{Authorization: fmt.Sprintf("Bearer %s", c.ApiToken)} + body, err := json.Marshal(inputCluster) + if err != nil { + errMsg := "failed to marshall cluster object" + return nil, errMsg, errors.New(errMsg) + } + response, err, cancel := DoRequest(c.Ctx, c.Client, http.MethodPost, url, body, headerMap, c.Log) + defer cancel() + if err != nil { + return nil, CreateErrorMsg("CreateCluster", "POST /clusters error", url, "", "", err), err + } + if response.StatusCode != http.StatusCreated { + return nil, CreateErrorMsg("CreateCluster", "POST /clusters non 201 response", url, response.Status, "", err), err + } + respBody, err := c.readResponseBody(response) + if err != nil { + return nil, CreateErrorMsg("CreateCluster", "POST /clusters response", url, response.Status, "", err), err + } + + cluster := &Cluster{} + err = json.Unmarshal(respBody, &cluster) + if err != nil { + return nil, CreateErrorMsg("CreateCluster", "unmarshal response from POST call", url, response.Status, string(respBody), err), err + } + return cluster, "", nil +} + +func (c clusterRegisterUtil) checkForDuplicateApiServiceIDs(k8sApiServiceId string) (*Cluster, string, error) { + clusters, errMsg, err := c.ListClusters() + if err != nil { + return nil, errMsg, fmt.Errorf("error listing clusters from Astra Control: %w", err) + } + + for _, cluster := range clusters { + sameApiServiceID := cluster.ApiServiceID == k8sApiServiceId + isV2Cluster := cluster.ConnectorInstall == "" + + if sameApiServiceID { + if isV2Cluster { + c.Log.WithValues("matchingClusterId", cluster.ID). + WithValues("matchingClusterName", cluster.Name). + Info("Found matching v2 cluster, letting it through") + } else { + c.Log.WithValues("matchingClusterId", cluster.ID). + WithValues("matchingClusterName", cluster.Name). + Info("Found duplicate v3 cluster") + return &cluster, "", nil + } + } + } + return nil, "", nil +} + +func (c clusterRegisterUtil) checkClusterRegistrationStatus(cloudId, clusterId string, k8sApiServiceId string) (isAlreadyRegistered bool, err error) { + cluster, _, err := c.GetCluster(cloudId, clusterId) + if err != nil { + return false, fmt.Errorf("error getting cluster from Astra Control: %w", err) + } + return c.checkRegistrationStatus(cluster, k8sApiServiceId) +} + +func (c clusterRegisterUtil) checkRegistrationStatus(cluster *Cluster, k8sApiServiceID string) (isAlreadyRegistered bool, err error) { + if cluster.ApiServiceID != "" && cluster.ApiServiceID != k8sApiServiceID { + return false, fmt.Errorf("cluster is incompatible: " + + "cluster record's apiServiceID does not match current cluster's apiServiceID") + } + + if cluster.ApiServiceID == "" && cluster.ConnectorInstall != ConnectorInstallStatePending { + return false, fmt.Errorf("cluster is incompatible: "+ + "cluster record's apiServiceID is empty but connectorInstall is not '%s'", + ConnectorInstallStatePending) + } + + if cluster.ConnectorInstall == "" { + return false, fmt.Errorf("cluster is incompatible: " + + "connectorInstall field cannot be empty") + } + + if cluster.ConnectorInstall == ConnectorInstallStateInstalled { + if cluster.ManagedState != ClusterManagedStateManaged { + return false, fmt.Errorf("cluster is incompatible: "+ + "connectorInstall is '%s' but cluster state is not '%s'", + ConnectorInstallStateInstalled, ClusterManagedStateManaged) + } + return true, nil + } + + if cluster.ConnectorInstall != ConnectorInstallStatePending { + return false, fmt.Errorf("cluster is incompatible: "+ + "expected connectorInstall field to be '%s' or '%s' but got '%s'", + ConnectorInstallStatePending, ConnectorInstallStateInstalled, cluster.ConnectorInstall) + } + + if cluster.State != ClusterStatePending { + err := fmt.Errorf("cluster is incompatible: "+ + "expected cluster state field to be '%s' but got '%s'", + ClusterStatePending, cluster.State) + + return false, err + } + + return false, nil +} + +// RegisterCluster runs through the necessary steps to establish a connection with Astra Control and ensure +// both the cluster and its record in Astra Control are valid, in which case the cluster will be managed +// (if it hasn't been already). +func (c clusterRegisterUtil) RegisterCluster(cloudId string, k8sApiServiceId string) (string, string, error) { + + // If this connector is started without a particular clusterID, create a pending cluster + // record with the provided clusterName. + clusterId := "" + if c.AstraConnector.Spec.Astra.ClusterId == "" { + // Check for a duplicate cluster (by ApiServiceID) before continuing + c.Log.Info("Checking for duplicate cluster records") + + dupeCluster, errMsg, err := c.checkForDuplicateApiServiceIDs(k8sApiServiceId) //todo move this out + if dupeCluster == nil && err != nil { + // Legit error + return "", errMsg, fmt.Errorf("error checking for duplicate apiServiceID records: %w", err) + } else if dupeCluster != nil && err == nil { + // Found a duplicate cluster, check if the state of the cluster is already managed. + registered, err := c.checkRegistrationStatus(dupeCluster, k8sApiServiceId) + if err != nil { + errMsg := fmt.Sprintf("unable to check duplicate cluster registration status") + return "", errMsg, fmt.Errorf("%s: %w", errMsg, err) + } + if registered { + c.Log.WithValues("clusterId", dupeCluster.ID).Info("Found duplicate cluster record already registered") + return dupeCluster.ID, "", nil + } + } else { + // Cluster not found, go create a new pending cluster record + c.Log.WithValues("clusterName", c.AstraConnector.Spec.Astra.ClusterName). + Info("Creating a new pending cluster record") + + pendingCluster := &Cluster{} + pendingCluster.Type = "application/astra-cluster" + pendingCluster.Version = "1.6" + pendingCluster.CloudID = cloudId + pendingCluster.Name = c.AstraConnector.Spec.Astra.ClusterName + pendingCluster.ConnectorInstall = ConnectorInstallStatePending + pendingCluster.ApiServiceID = k8sApiServiceId + + cluster, errMsg, err := c.CreateCluster(pendingCluster, cloudId) + if err != nil { + return "", errMsg, fmt.Errorf("unable to create pending cluster record: %w", err) + } + clusterId = cluster.ID + } + } else { + clusterId = c.AstraConnector.Spec.Astra.ClusterId + c.Log.Info("Checking if Astra Control cluster is already registered") + isAlreadyRegistered, err := c.checkClusterRegistrationStatus(cloudId, c.AstraConnector.Spec.Astra.ClusterId, k8sApiServiceId) + if err != nil { + errMsg := fmt.Sprintf("error checking if cluster is already registered: %s", err) + return "", errMsg, errors.New(errMsg) + } + if isAlreadyRegistered { + c.Log.Info("Cluster is already registered, skipping registration.") + return c.AstraConnector.Spec.Astra.ClusterId, "", nil + } + + // Check for a duplicate cluster (by ApiServiceID) before continuing + c.Log.Info("Checking for duplicate cluster records") + dupeCluster, errMsg, err := c.checkForDuplicateApiServiceIDs(k8sApiServiceId) + if dupeCluster == nil && err != nil { + // Legit error + return "", errMsg, fmt.Errorf("error checking for duplicate apiServiceID records: %w", err) + } + if dupeCluster != nil { + errMsg = "duplicate cluster found in Astra Control: apiServiceID matches but the " + + "clusterId does not" + return "", errMsg, fmt.Errorf(errMsg) + } + } + + fullVersion, semanticVersion, err := c.K8sUtil.VersionGet() + if err != nil { + errMsg := "failed to get k8s version of host cluster" + c.Log.Error(err, "failed to get k8s version of host cluster") + return "", errMsg, fmt.Errorf("%s: %w", errMsg, err) + } + + clusterTypeChecker := k8s.NewClusterTypeChecker(c.K8sUtil, c.Log) + clusterType := clusterTypeChecker.DetermineClusterType() + + c.Log.WithValues("K8sDistro", clusterType).Info("Detected Kubernetes distro") + + c.Log.WithValues( + "K8sApiServiceId", k8sApiServiceId, + "clusterType", clusterType, + "clusterK8sVersion", fullVersion, + "astraURL", c.AstraConnector.Spec.NatsSyncClient.CloudBridgeURL, + "connectorNamespace", c.AstraConnector.Namespace, + ).Info("Managing cluster in Astra Control") + + managedCluster := &Cluster{ + ConnectorInstall: ConnectorInstallStateInstalled, + ConnectorCapabilities: common.GetConnectorCapabilities(), + ApiServiceID: k8sApiServiceId, + ConnectorNamespace: c.AstraConnector.Namespace, + AstraControlURL: c.AstraConnector.Spec.NatsSyncClient.CloudBridgeURL, + APITokenRef: c.AstraConnector.Spec.Astra.TokenRef, + ClusterType: clusterType, + ClusterVersion: semanticVersion, + ClusterVersionString: fullVersion, + ID: clusterId, + } + if errMsg, err := c.CreateManagedCluster(managedCluster); err != nil { + return "", errMsg, err + } + + c.Log.WithValues( + "clusterId", clusterId, + "clusterName", managedCluster.Name, + ).Info("Registration completed successfully") + return clusterId, "", nil +} diff --git a/app/register/register_test.go b/app/register/register_test.go index 621464c0..b5015dda 100644 --- a/app/register/register_test.go +++ b/app/register/register_test.go @@ -1,7 +1,6 @@ /* - * Copyright (c) 2024. NetApp, Inc. All Rights Reserved. +* Copyright (c) 2024. NetApp, Inc. All Rights Reserved. */ - package register_test import ( @@ -53,7 +52,7 @@ type AstraConnectorInput struct { invalidHostDetails bool } -func createClusterRegister(astraConnectorInput AstraConnectorInput) (register.ClusterRegisterUtil, *mocks.HTTPClient, string, client.Client) { +func createClusterRegister(astraConnectorInput AstraConnectorInput) (register.ClusterRegisterUtil, *mocks.HTTPClient, string, client.Client, error) { log := testutil.CreateLoggerForTesting() mockHttpClient := &mocks.HTTPClient{} fakeClient := testutil.CreateFakeClient() @@ -106,15 +105,19 @@ func createClusterRegister(astraConnectorInput AstraConnectorInput) (register.Cl astraConnector.Spec.NatsSyncClient.HostAliasIP = testIP } - clusterRegisterUtil := register.NewClusterRegisterUtil(astraConnector, mockHttpClient, fakeClient, k8sUtil, log, context.Background()) - return clusterRegisterUtil, mockHttpClient, apiTokenSecret, fakeClient + clusterRegisterUtil, err := register.NewClusterRegisterUtil(astraConnector, mockHttpClient, fakeClient, k8sUtil, log, context.Background()) + if err != nil { + return nil, nil, "", nil, err + } + return clusterRegisterUtil, mockHttpClient, apiTokenSecret, fakeClient, nil } // Tests func TestGetAPITokenFromSecret(t *testing.T) { t.Run("GetAPITokenFromSecret__SecretNotPresentReturnsError", func(t *testing.T) { - clusterRegisterUtil, _, _, _ := createClusterRegister(AstraConnectorInput{}) + clusterRegisterUtil, _, _, _, err := createClusterRegister(AstraConnectorInput{}) + assert.NoError(t, err) apiToken, errorReason, err := clusterRegisterUtil.GetAPITokenFromSecret("astra-token") assert.Equal(t, apiToken, "") @@ -123,7 +126,8 @@ func TestGetAPITokenFromSecret(t *testing.T) { }) t.Run("GetAPITokenFromSecret__SecretInvalidReturnsError", func(t *testing.T) { - clusterRegisterUtil, _, apiTokenSecret, fakeClient := createClusterRegister(AstraConnectorInput{}) + clusterRegisterUtil, _, apiTokenSecret, fakeClient, err := createClusterRegister(AstraConnectorInput{}) + assert.NoError(t, err) secret := &coreV1.Secret{ ObjectMeta: metaV1.ObjectMeta{ @@ -136,7 +140,7 @@ func TestGetAPITokenFromSecret(t *testing.T) { } // creating secret - err := fakeClient.Create(ctx, secret) + err = fakeClient.Create(ctx, secret) assert.NoError(t, err) apiToken, errorReason, err := clusterRegisterUtil.GetAPITokenFromSecret(apiTokenSecret) @@ -147,7 +151,8 @@ func TestGetAPITokenFromSecret(t *testing.T) { }) t.Run("GetAPITokenFromSecret__ReturnsApiToken", func(t *testing.T) { - clusterRegisterUtil, _, apiTokenSecret, _ := createClusterRegister(AstraConnectorInput{createTokenSecret: true}) + clusterRegisterUtil, _, apiTokenSecret, _, err := createClusterRegister(AstraConnectorInput{createTokenSecret: true}) + assert.NoError(t, err) apiToken, errorReason, err := clusterRegisterUtil.GetAPITokenFromSecret(apiTokenSecret) assert.Equal(t, apiToken, "auth-token") diff --git a/common/constants.go b/common/constants.go index 22be003b..1fdaa6f5 100644 --- a/common/constants.go +++ b/common/constants.go @@ -30,6 +30,7 @@ const ( ConnectorNeptuneCapability = "neptuneV1" ConnectorV2Capability = "connectorV2" // V2 refers specifically to Arch 3.0 connector and beyond ConnectorWatcherCapability = "watcherV1" + ConnectorNeptuneV1 = "neptuneV1" RbacProxyImage = "kube-rbac-proxy:v0.14.1" ) @@ -60,6 +61,7 @@ func GetConnectorCapabilities() []string { capabilities := []string{ ConnectorV2Capability, ConnectorWatcherCapability, + ConnectorNeptuneV1, } if conf.Config.FeatureFlags().DeployNeptune() { diff --git a/details/k8s/cluster_type_checker.go b/details/k8s/cluster_type_checker.go index 3dab69a2..247b34c4 100644 --- a/details/k8s/cluster_type_checker.go +++ b/details/k8s/cluster_type_checker.go @@ -185,7 +185,7 @@ func (c *ClusterTypeChecker) isAKSFlavor() bool { // isGKEFlavor - checks for 'gke' in the client git version // i.e. "gitVersion": "v1.20.6-gke.1000", func (c *ClusterTypeChecker) isGKEFlavor() bool { - version, err := c.K8sUtil.VersionGet() + version, _, err := c.K8sUtil.VersionGet() if err != nil { return false } @@ -194,7 +194,7 @@ func (c *ClusterTypeChecker) isGKEFlavor() bool { } func (c *ClusterTypeChecker) isEKSFlavor() bool { - version, err := c.K8sUtil.VersionGet() + version, _, err := c.K8sUtil.VersionGet() if err != nil { return false } diff --git a/details/k8s/k8s_util.go b/details/k8s/k8s_util.go index ffc4a308..f4b98af4 100644 --- a/details/k8s/k8s_util.go +++ b/details/k8s/k8s_util.go @@ -7,6 +7,7 @@ package k8s import ( "context" "github.com/go-logr/logr" + semver "github.com/hashicorp/go-version" "github.com/pkg/errors" rbacv1 "k8s.io/api/rbac/v1" apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" @@ -30,7 +31,7 @@ type K8sUtil struct { type K8sUtilInterface interface { CreateOrUpdateResource(context.Context, client.Object, client.Object, controllerutil.MutateFn) (string, error) DeleteResource(context.Context, client.Object) error - VersionGet() (string, error) + VersionGet() (string, string, error) IsCRDInstalled(string) bool RESTGet(string) ([]byte, error) K8sClientset() kubernetes.Interface @@ -69,21 +70,27 @@ func isNamespaceScoped(obj client.Object) bool { } // VersionGet returns the server version of the k8s cluster. -func (r *K8sUtil) VersionGet() (string, error) { +func (r *K8sUtil) VersionGet() (string, string, error) { restConfig, err := ctrl.GetConfig() if err != nil { - return "", errors.Wrap(err, "error getting kubeconfig") + return "", "", errors.Wrap(err, "error getting kubeconfig") } dClient, err := discovery.NewDiscoveryClientForConfig(restConfig) if err != nil { - return "", errors.Wrap(err, "error creating discovery client") + return "", "", errors.Wrap(err, "error creating discovery client") } versionInfo, err := dClient.ServerVersion() if err != nil { - return "", errors.Wrap(err, "error getting server version") + return "", "", errors.Wrap(err, "error getting server version") + } + fullVersionString := versionInfo.String() + semanticVersion, err := semver.NewSemver(fullVersionString) + if err != nil { + r.Log.Error(err, "failed to parse k8s server version") + return "", "", err } r.Log.V(3).Info("versionInfo", "versionInfo", versionInfo) - return versionInfo.GitVersion, nil + return fullVersionString, semanticVersion.String(), nil } // IsCRDInstalled returns the server version of the k8s cluster. diff --git a/details/k8s/precheck/k8s_version.go b/details/k8s/precheck/k8s_version.go index 9b99bdf2..c2799115 100644 --- a/details/k8s/precheck/k8s_version.go +++ b/details/k8s/precheck/k8s_version.go @@ -15,7 +15,7 @@ const ( ) func (p *PrecheckClient) RunK8sVersionCheck() error { - versionString, err := p.k8sUtil.VersionGet() + versionString, _, err := p.k8sUtil.VersionGet() if err != nil { p.log.Error(err, "failed to get k8s version of host cluster") return err diff --git a/details/operator-sdk/api/v1/astraconnector_types.go b/details/operator-sdk/api/v1/astraconnector_types.go index d6de5063..1179c019 100644 --- a/details/operator-sdk/api/v1/astraconnector_types.go +++ b/details/operator-sdk/api/v1/astraconnector_types.go @@ -99,6 +99,8 @@ type AstraConnectorSpec struct { // AstraConnectorStatus defines the observed state of AstraConnector type AstraConnectorStatus struct { NatsSyncClient NatsSyncClientStatus `json:"natsSyncClient"` + ClusterId string `json:"clusterId"` + CloudId string `json:"cloudId"` } // NatsSyncClientStatus defines the observed state of NatsSyncClient diff --git a/details/operator-sdk/controllers/astraconnector_controller.go b/details/operator-sdk/controllers/astraconnector_controller.go index d9b1cb15..ee09ea6c 100644 --- a/details/operator-sdk/controllers/astraconnector_controller.go +++ b/details/operator-sdk/controllers/astraconnector_controller.go @@ -8,6 +8,7 @@ import ( "context" "encoding/json" "fmt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "net/http" "sigs.k8s.io/controller-runtime/pkg/builder" "strings" @@ -182,13 +183,61 @@ func (r *AstraConnectorController) Reconcile(ctx context.Context, req ctrl.Reque var connectorResults ctrl.Result var deployError error + if astraConnector.Spec.Astra.ClusterId == "" && astraConnector.Spec.Astra.ClusterName == "" { + err := fmt.Errorf("clusterID and clusterName both cannot be empty") + log.Error(err, "Bad config") + natsSyncClientStatus.Status = ErrorClusterIdAndNameEmpty + _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) + return ctrl.Result{RequeueAfter: time.Minute * conf.Config.ErrorTimeout()}, err + } + + registerUtil, err := register.NewClusterRegisterUtil(astraConnector, &http.Client{}, r.Client, nil, log, context.Background()) + if err != nil { + natsSyncClientStatus.Status = ErrorInitiatingRegistration + _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) + return ctrl.Result{RequeueAfter: time.Minute * conf.Config.ErrorTimeout()}, fmt.Errorf("error getting Kubernetes API service ID: %w", err) + } + + cloudID, errMsg, err := registerUtil.RegisterCloud() + if err != nil { + natsSyncClientStatus.Status = ErrorGetK8sServiceId + _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) + return ctrl.Result{RequeueAfter: time.Minute * conf.Config.ErrorTimeout()}, fmt.Errorf("error getting Kubernetes API service ID: %w", err) + } + astraConnector.Status.CloudId = cloudID + _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) + + log.Info("Getting Kubernetes API service ID") + k8sServiceId, err := r.getK8sApiServiceID(ctx) + if err != nil { + natsSyncClientStatus.Status = ErrorGetK8sServiceId + _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) + return ctrl.Result{RequeueAfter: time.Minute * conf.Config.ErrorTimeout()}, fmt.Errorf("error getting Kubernetes API service ID: %w", err) + } + + clusterId, errMsg, err := registerUtil.RegisterCluster(cloudID, k8sServiceId) + if err != nil { + natsSyncClientStatus.Status = errMsg + _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) + return ctrl.Result{RequeueAfter: time.Minute * conf.Config.ErrorTimeout()}, fmt.Errorf("error getting Kubernetes API service ID: %w", err) + } + astraConnector.Status.ClusterId = clusterId + _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) + connectorResults, deployError = r.deployNatlessConnector(ctx, astraConnector, &natsSyncClientStatus) + if deployError != nil { + // Note: Returning nil in error since we want to wait for the requeue to happen + // non nil errors triggers the requeue right away + log.Error(err, "Error deploying NatsConnector, requeueing after delay", "delay", conf.Config.ErrorTimeout()) + return connectorResults, nil + } // Wait for the cluster to become managed (aka "registered") natsSyncClientStatus.Status = WaitForClusterManagedState _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) - isManaged, err := waitForManagedCluster(astraConnector, r.Client, log) - if !isManaged { + + err = waitForManagedCluster(registerUtil, log) + if err != nil { log.Error(err, "timed out waiting for cluster to become managed, requeueing after delay", "delay", conf.Config.ErrorTimeout()) natsSyncClientStatus.Status = ErrorClusterUnmanaged _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) @@ -198,7 +247,7 @@ func (r *AstraConnectorController) Reconcile(ctx context.Context, req ctrl.Reque log.Info("Cluster is managed") // ASUP Setup - err = r.createASUPCR(ctx, astraConnector, astraConnector.Spec.Astra.ClusterId) + err = r.createASUPCR(ctx, astraConnector, clusterId) if err != nil { log.Error(err, FailedASUPCreation) natsSyncClientStatus.Status = FailedASUPCreation @@ -210,13 +259,6 @@ func (r *AstraConnectorController) Reconcile(ctx context.Context, req ctrl.Reque natsSyncClientStatus.AstraClusterId = astraConnector.Spec.Astra.ClusterId natsSyncClientStatus.Status = RegisteredWithAstra _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) - - if deployError != nil { - // Note: Returning nil in error since we want to wait for the requeue to happen - // non nil errors triggers the requeue right away - log.Error(err, "Error deploying NatsConnector, requeueing after delay", "delay", conf.Config.ErrorTimeout()) - return connectorResults, nil - } } if natsSyncClientStatus.AstraClusterId != "" { @@ -329,22 +371,13 @@ func (r *AstraConnectorController) waitForStatusUpdate(astraConnector *v1.AstraC return err } -func waitForManagedCluster(astraConnector *v1.AstraConnector, client client.Client, log logr.Logger) (bool, error) { - registerUtil := register.NewClusterRegisterUtil(astraConnector, &http.Client{}, client, nil, log, context.Background()) - // SetHttpClient should be in the New func above but would require a larger refactor - // Setup TLS if enabled, setup hostAliasIP if used - err := registerUtil.SetHttpClient(astraConnector.Spec.Astra.SkipTLSValidation, register.GetAstraHostURL(astraConnector)) - if err != nil { - return false, fmt.Errorf("failed to setup HTTP client: %w", err) - } - +func waitForManagedCluster(registerUtil register.ClusterRegisterUtil, log logr.Logger) error { maxRetries := 5 waitTime := 3 * time.Second - var isManaged bool for i := 1; i <= maxRetries; i++ { - isManaged, _, err = registerUtil.IsClusterManaged() + isManaged, _, err := registerUtil.IsClusterManaged() if isManaged { - break + return nil } if err != nil { log.Error(err, "encountered error while checking for cluster management") @@ -352,5 +385,24 @@ func waitForManagedCluster(astraConnector *v1.AstraConnector, client client.Clie log.Info("cluster not yet managed", "retiresLeft", maxRetries-i) time.Sleep(waitTime) } - return isManaged, err + return errors.New("timed out waiting for cluster to become managed") +} + +func (r *AstraConnectorController) getK8sApiServiceID(ctx context.Context) (string, error) { + name := "kubernetes" + namespace := "default" + + if r.Clientset == nil { + return "", fmt.Errorf("the Kubernetes clientset is nil") + } + + service, err := r.Clientset.CoreV1().Services(namespace).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return "", fmt.Errorf("error getting '%s' service from '%s' namespace: %w", name, namespace, err) + } + if service == nil { + return "", fmt.Errorf("the service returned by the Kubernetes client is nil") + } + + return string(service.UID), nil } diff --git a/details/operator-sdk/controllers/status_strings.go b/details/operator-sdk/controllers/status_strings.go index e0efede4..221a8312 100644 --- a/details/operator-sdk/controllers/status_strings.go +++ b/details/operator-sdk/controllers/status_strings.go @@ -26,6 +26,9 @@ const ( ErrorCreateRoles = "Error creating Roles %s/%s" ErrorCreateClusterRoles = "Error creating ClusterRoles %s/%s" ErrorClusterUnmanaged = "Timed out waiting for cluster to become managed" + ErrorGetK8sServiceId = "Error getting K8s service ID" + ErrorClusterIdAndNameEmpty = "Error: clusterID and clusterName cannot both be empty" + ErrorInitiatingRegistration = "Error initiating cluster registration" FailedFinalizerAdd = "Failed to add finalizer" FailedFinalizerRemove = "Failed to remove finalizer" diff --git a/mocks/ClusterRegisterUtil.go b/mocks/ClusterRegisterUtil.go index 3053a2e1..0acea981 100644 --- a/mocks/ClusterRegisterUtil.go +++ b/mocks/ClusterRegisterUtil.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.19.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks @@ -13,21 +13,28 @@ type ClusterRegisterUtil struct { func (_m *ClusterRegisterUtil) GetAPITokenFromSecret(secretName string) (string, string, error) { ret := _m.Called(secretName) + if len(ret) == 0 { + panic("no return value specified for GetAPITokenFromSecret") + } + var r0 string + var r1 string + var r2 error + if rf, ok := ret.Get(0).(func(string) (string, string, error)); ok { + return rf(secretName) + } if rf, ok := ret.Get(0).(func(string) string); ok { r0 = rf(secretName) } else { r0 = ret.Get(0).(string) } - var r1 string if rf, ok := ret.Get(1).(func(string) string); ok { r1 = rf(secretName) } else { r1 = ret.Get(1).(string) } - var r2 error if rf, ok := ret.Get(2).(func(string) error); ok { r2 = rf(secretName) } else { @@ -41,21 +48,63 @@ func (_m *ClusterRegisterUtil) GetAPITokenFromSecret(secretName string) (string, func (_m *ClusterRegisterUtil) IsClusterManaged() (bool, string, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for IsClusterManaged") + } + var r0 bool + var r1 string + var r2 error + if rf, ok := ret.Get(0).(func() (bool, string, error)); ok { + return rf() + } if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() } else { r0 = ret.Get(0).(bool) } - var r1 string if rf, ok := ret.Get(1).(func() string); ok { r1 = rf() } else { r1 = ret.Get(1).(string) } + if rf, ok := ret.Get(2).(func() error); ok { + r2 = rf() + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// RegisterCloud provides a mock function with given fields: +func (_m *ClusterRegisterUtil) RegisterCloud() (string, string, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for RegisterCloud") + } + + var r0 string + var r1 string var r2 error + if rf, ok := ret.Get(0).(func() (string, string, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func() string); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(string) + } + if rf, ok := ret.Get(2).(func() error); ok { r2 = rf() } else { @@ -65,10 +114,49 @@ func (_m *ClusterRegisterUtil) IsClusterManaged() (bool, string, error) { return r0, r1, r2 } +// RegisterCluster provides a mock function with given fields: cloudId, k8sServiceId +func (_m *ClusterRegisterUtil) RegisterCluster(cloudId string, k8sServiceId string) (string, string, error) { + ret := _m.Called(cloudId, k8sServiceId) + + if len(ret) == 0 { + panic("no return value specified for RegisterCluster") + } + + var r0 string + var r1 string + var r2 error + if rf, ok := ret.Get(0).(func(string, string) (string, string, error)); ok { + return rf(cloudId, k8sServiceId) + } + if rf, ok := ret.Get(0).(func(string, string) string); ok { + r0 = rf(cloudId, k8sServiceId) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(string, string) string); ok { + r1 = rf(cloudId, k8sServiceId) + } else { + r1 = ret.Get(1).(string) + } + + if rf, ok := ret.Get(2).(func(string, string) error); ok { + r2 = rf(cloudId, k8sServiceId) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + // SetHttpClient provides a mock function with given fields: disableTls, astraHost func (_m *ClusterRegisterUtil) SetHttpClient(disableTls bool, astraHost string) error { ret := _m.Called(disableTls, astraHost) + if len(ret) == 0 { + panic("no return value specified for SetHttpClient") + } + var r0 error if rf, ok := ret.Get(0).(func(bool, string) error); ok { r0 = rf(disableTls, astraHost) @@ -79,13 +167,12 @@ func (_m *ClusterRegisterUtil) SetHttpClient(disableTls bool, astraHost string) return r0 } -type mockConstructorTestingTNewClusterRegisterUtil interface { +// NewClusterRegisterUtil creates a new instance of ClusterRegisterUtil. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewClusterRegisterUtil(t interface { mock.TestingT Cleanup(func()) -} - -// NewClusterRegisterUtil creates a new instance of ClusterRegisterUtil. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewClusterRegisterUtil(t mockConstructorTestingTNewClusterRegisterUtil) *ClusterRegisterUtil { +}) *ClusterRegisterUtil { mock := &ClusterRegisterUtil{} mock.Mock.Test(t) diff --git a/mocks/ClusterTypeCheckerInterface.go b/mocks/ClusterTypeCheckerInterface.go index 3a57cbde..1f9333e5 100644 --- a/mocks/ClusterTypeCheckerInterface.go +++ b/mocks/ClusterTypeCheckerInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.19.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks @@ -13,6 +13,10 @@ type ClusterTypeCheckerInterface struct { func (_m *ClusterTypeCheckerInterface) DetermineClusterType() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for DetermineClusterType") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -23,13 +27,12 @@ func (_m *ClusterTypeCheckerInterface) DetermineClusterType() string { return r0 } -type mockConstructorTestingTNewClusterTypeCheckerInterface interface { +// NewClusterTypeCheckerInterface creates a new instance of ClusterTypeCheckerInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewClusterTypeCheckerInterface(t interface { mock.TestingT Cleanup(func()) -} - -// NewClusterTypeCheckerInterface creates a new instance of ClusterTypeCheckerInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewClusterTypeCheckerInterface(t mockConstructorTestingTNewClusterTypeCheckerInterface) *ClusterTypeCheckerInterface { +}) *ClusterTypeCheckerInterface { mock := &ClusterTypeCheckerInterface{} mock.Mock.Test(t) diff --git a/mocks/Deployer.go b/mocks/Deployer.go index b2f7ae18..abb2e130 100644 --- a/mocks/Deployer.go +++ b/mocks/Deployer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.19.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks @@ -23,7 +23,16 @@ type Deployer struct { func (_m *Deployer) GetClusterRoleBindingObjects(m *v1.AstraConnector, ctx context.Context) ([]client.Object, controllerutil.MutateFn, error) { ret := _m.Called(m, ctx) + if len(ret) == 0 { + panic("no return value specified for GetClusterRoleBindingObjects") + } + var r0 []client.Object + var r1 controllerutil.MutateFn + var r2 error + if rf, ok := ret.Get(0).(func(*v1.AstraConnector, context.Context) ([]client.Object, controllerutil.MutateFn, error)); ok { + return rf(m, ctx) + } if rf, ok := ret.Get(0).(func(*v1.AstraConnector, context.Context) []client.Object); ok { r0 = rf(m, ctx) } else { @@ -32,7 +41,6 @@ func (_m *Deployer) GetClusterRoleBindingObjects(m *v1.AstraConnector, ctx conte } } - var r1 controllerutil.MutateFn if rf, ok := ret.Get(1).(func(*v1.AstraConnector, context.Context) controllerutil.MutateFn); ok { r1 = rf(m, ctx) } else { @@ -41,7 +49,6 @@ func (_m *Deployer) GetClusterRoleBindingObjects(m *v1.AstraConnector, ctx conte } } - var r2 error if rf, ok := ret.Get(2).(func(*v1.AstraConnector, context.Context) error); ok { r2 = rf(m, ctx) } else { @@ -55,7 +62,16 @@ func (_m *Deployer) GetClusterRoleBindingObjects(m *v1.AstraConnector, ctx conte func (_m *Deployer) GetClusterRoleObjects(m *v1.AstraConnector, ctx context.Context) ([]client.Object, controllerutil.MutateFn, error) { ret := _m.Called(m, ctx) + if len(ret) == 0 { + panic("no return value specified for GetClusterRoleObjects") + } + var r0 []client.Object + var r1 controllerutil.MutateFn + var r2 error + if rf, ok := ret.Get(0).(func(*v1.AstraConnector, context.Context) ([]client.Object, controllerutil.MutateFn, error)); ok { + return rf(m, ctx) + } if rf, ok := ret.Get(0).(func(*v1.AstraConnector, context.Context) []client.Object); ok { r0 = rf(m, ctx) } else { @@ -64,7 +80,6 @@ func (_m *Deployer) GetClusterRoleObjects(m *v1.AstraConnector, ctx context.Cont } } - var r1 controllerutil.MutateFn if rf, ok := ret.Get(1).(func(*v1.AstraConnector, context.Context) controllerutil.MutateFn); ok { r1 = rf(m, ctx) } else { @@ -73,7 +88,6 @@ func (_m *Deployer) GetClusterRoleObjects(m *v1.AstraConnector, ctx context.Cont } } - var r2 error if rf, ok := ret.Get(2).(func(*v1.AstraConnector, context.Context) error); ok { r2 = rf(m, ctx) } else { @@ -87,7 +101,16 @@ func (_m *Deployer) GetClusterRoleObjects(m *v1.AstraConnector, ctx context.Cont func (_m *Deployer) GetConfigMapObjects(m *v1.AstraConnector, ctx context.Context) ([]client.Object, controllerutil.MutateFn, error) { ret := _m.Called(m, ctx) + if len(ret) == 0 { + panic("no return value specified for GetConfigMapObjects") + } + var r0 []client.Object + var r1 controllerutil.MutateFn + var r2 error + if rf, ok := ret.Get(0).(func(*v1.AstraConnector, context.Context) ([]client.Object, controllerutil.MutateFn, error)); ok { + return rf(m, ctx) + } if rf, ok := ret.Get(0).(func(*v1.AstraConnector, context.Context) []client.Object); ok { r0 = rf(m, ctx) } else { @@ -96,7 +119,6 @@ func (_m *Deployer) GetConfigMapObjects(m *v1.AstraConnector, ctx context.Contex } } - var r1 controllerutil.MutateFn if rf, ok := ret.Get(1).(func(*v1.AstraConnector, context.Context) controllerutil.MutateFn); ok { r1 = rf(m, ctx) } else { @@ -105,7 +127,6 @@ func (_m *Deployer) GetConfigMapObjects(m *v1.AstraConnector, ctx context.Contex } } - var r2 error if rf, ok := ret.Get(2).(func(*v1.AstraConnector, context.Context) error); ok { r2 = rf(m, ctx) } else { @@ -119,7 +140,16 @@ func (_m *Deployer) GetConfigMapObjects(m *v1.AstraConnector, ctx context.Contex func (_m *Deployer) GetDeploymentObjects(m *v1.AstraConnector, ctx context.Context) ([]client.Object, controllerutil.MutateFn, error) { ret := _m.Called(m, ctx) + if len(ret) == 0 { + panic("no return value specified for GetDeploymentObjects") + } + var r0 []client.Object + var r1 controllerutil.MutateFn + var r2 error + if rf, ok := ret.Get(0).(func(*v1.AstraConnector, context.Context) ([]client.Object, controllerutil.MutateFn, error)); ok { + return rf(m, ctx) + } if rf, ok := ret.Get(0).(func(*v1.AstraConnector, context.Context) []client.Object); ok { r0 = rf(m, ctx) } else { @@ -128,7 +158,6 @@ func (_m *Deployer) GetDeploymentObjects(m *v1.AstraConnector, ctx context.Conte } } - var r1 controllerutil.MutateFn if rf, ok := ret.Get(1).(func(*v1.AstraConnector, context.Context) controllerutil.MutateFn); ok { r1 = rf(m, ctx) } else { @@ -137,7 +166,6 @@ func (_m *Deployer) GetDeploymentObjects(m *v1.AstraConnector, ctx context.Conte } } - var r2 error if rf, ok := ret.Get(2).(func(*v1.AstraConnector, context.Context) error); ok { r2 = rf(m, ctx) } else { @@ -151,7 +179,16 @@ func (_m *Deployer) GetDeploymentObjects(m *v1.AstraConnector, ctx context.Conte func (_m *Deployer) GetRoleBindingObjects(m *v1.AstraConnector, ctx context.Context) ([]client.Object, controllerutil.MutateFn, error) { ret := _m.Called(m, ctx) + if len(ret) == 0 { + panic("no return value specified for GetRoleBindingObjects") + } + var r0 []client.Object + var r1 controllerutil.MutateFn + var r2 error + if rf, ok := ret.Get(0).(func(*v1.AstraConnector, context.Context) ([]client.Object, controllerutil.MutateFn, error)); ok { + return rf(m, ctx) + } if rf, ok := ret.Get(0).(func(*v1.AstraConnector, context.Context) []client.Object); ok { r0 = rf(m, ctx) } else { @@ -160,7 +197,6 @@ func (_m *Deployer) GetRoleBindingObjects(m *v1.AstraConnector, ctx context.Cont } } - var r1 controllerutil.MutateFn if rf, ok := ret.Get(1).(func(*v1.AstraConnector, context.Context) controllerutil.MutateFn); ok { r1 = rf(m, ctx) } else { @@ -169,7 +205,6 @@ func (_m *Deployer) GetRoleBindingObjects(m *v1.AstraConnector, ctx context.Cont } } - var r2 error if rf, ok := ret.Get(2).(func(*v1.AstraConnector, context.Context) error); ok { r2 = rf(m, ctx) } else { @@ -183,7 +218,16 @@ func (_m *Deployer) GetRoleBindingObjects(m *v1.AstraConnector, ctx context.Cont func (_m *Deployer) GetRoleObjects(m *v1.AstraConnector, ctx context.Context) ([]client.Object, controllerutil.MutateFn, error) { ret := _m.Called(m, ctx) + if len(ret) == 0 { + panic("no return value specified for GetRoleObjects") + } + var r0 []client.Object + var r1 controllerutil.MutateFn + var r2 error + if rf, ok := ret.Get(0).(func(*v1.AstraConnector, context.Context) ([]client.Object, controllerutil.MutateFn, error)); ok { + return rf(m, ctx) + } if rf, ok := ret.Get(0).(func(*v1.AstraConnector, context.Context) []client.Object); ok { r0 = rf(m, ctx) } else { @@ -192,7 +236,6 @@ func (_m *Deployer) GetRoleObjects(m *v1.AstraConnector, ctx context.Context) ([ } } - var r1 controllerutil.MutateFn if rf, ok := ret.Get(1).(func(*v1.AstraConnector, context.Context) controllerutil.MutateFn); ok { r1 = rf(m, ctx) } else { @@ -201,7 +244,6 @@ func (_m *Deployer) GetRoleObjects(m *v1.AstraConnector, ctx context.Context) ([ } } - var r2 error if rf, ok := ret.Get(2).(func(*v1.AstraConnector, context.Context) error); ok { r2 = rf(m, ctx) } else { @@ -215,7 +257,16 @@ func (_m *Deployer) GetRoleObjects(m *v1.AstraConnector, ctx context.Context) ([ func (_m *Deployer) GetServiceAccountObjects(m *v1.AstraConnector, ctx context.Context) ([]client.Object, controllerutil.MutateFn, error) { ret := _m.Called(m, ctx) + if len(ret) == 0 { + panic("no return value specified for GetServiceAccountObjects") + } + var r0 []client.Object + var r1 controllerutil.MutateFn + var r2 error + if rf, ok := ret.Get(0).(func(*v1.AstraConnector, context.Context) ([]client.Object, controllerutil.MutateFn, error)); ok { + return rf(m, ctx) + } if rf, ok := ret.Get(0).(func(*v1.AstraConnector, context.Context) []client.Object); ok { r0 = rf(m, ctx) } else { @@ -224,7 +275,6 @@ func (_m *Deployer) GetServiceAccountObjects(m *v1.AstraConnector, ctx context.C } } - var r1 controllerutil.MutateFn if rf, ok := ret.Get(1).(func(*v1.AstraConnector, context.Context) controllerutil.MutateFn); ok { r1 = rf(m, ctx) } else { @@ -233,7 +283,6 @@ func (_m *Deployer) GetServiceAccountObjects(m *v1.AstraConnector, ctx context.C } } - var r2 error if rf, ok := ret.Get(2).(func(*v1.AstraConnector, context.Context) error); ok { r2 = rf(m, ctx) } else { @@ -247,7 +296,16 @@ func (_m *Deployer) GetServiceAccountObjects(m *v1.AstraConnector, ctx context.C func (_m *Deployer) GetServiceObjects(m *v1.AstraConnector, ctx context.Context) ([]client.Object, controllerutil.MutateFn, error) { ret := _m.Called(m, ctx) + if len(ret) == 0 { + panic("no return value specified for GetServiceObjects") + } + var r0 []client.Object + var r1 controllerutil.MutateFn + var r2 error + if rf, ok := ret.Get(0).(func(*v1.AstraConnector, context.Context) ([]client.Object, controllerutil.MutateFn, error)); ok { + return rf(m, ctx) + } if rf, ok := ret.Get(0).(func(*v1.AstraConnector, context.Context) []client.Object); ok { r0 = rf(m, ctx) } else { @@ -256,7 +314,6 @@ func (_m *Deployer) GetServiceObjects(m *v1.AstraConnector, ctx context.Context) } } - var r1 controllerutil.MutateFn if rf, ok := ret.Get(1).(func(*v1.AstraConnector, context.Context) controllerutil.MutateFn); ok { r1 = rf(m, ctx) } else { @@ -265,7 +322,6 @@ func (_m *Deployer) GetServiceObjects(m *v1.AstraConnector, ctx context.Context) } } - var r2 error if rf, ok := ret.Get(2).(func(*v1.AstraConnector, context.Context) error); ok { r2 = rf(m, ctx) } else { @@ -279,7 +335,16 @@ func (_m *Deployer) GetServiceObjects(m *v1.AstraConnector, ctx context.Context) func (_m *Deployer) GetStatefulSetObjects(m *v1.AstraConnector, ctx context.Context) ([]client.Object, controllerutil.MutateFn, error) { ret := _m.Called(m, ctx) + if len(ret) == 0 { + panic("no return value specified for GetStatefulSetObjects") + } + var r0 []client.Object + var r1 controllerutil.MutateFn + var r2 error + if rf, ok := ret.Get(0).(func(*v1.AstraConnector, context.Context) ([]client.Object, controllerutil.MutateFn, error)); ok { + return rf(m, ctx) + } if rf, ok := ret.Get(0).(func(*v1.AstraConnector, context.Context) []client.Object); ok { r0 = rf(m, ctx) } else { @@ -288,7 +353,6 @@ func (_m *Deployer) GetStatefulSetObjects(m *v1.AstraConnector, ctx context.Cont } } - var r1 controllerutil.MutateFn if rf, ok := ret.Get(1).(func(*v1.AstraConnector, context.Context) controllerutil.MutateFn); ok { r1 = rf(m, ctx) } else { @@ -297,7 +361,6 @@ func (_m *Deployer) GetStatefulSetObjects(m *v1.AstraConnector, ctx context.Cont } } - var r2 error if rf, ok := ret.Get(2).(func(*v1.AstraConnector, context.Context) error); ok { r2 = rf(m, ctx) } else { @@ -307,13 +370,12 @@ func (_m *Deployer) GetStatefulSetObjects(m *v1.AstraConnector, ctx context.Cont return r0, r1, r2 } -type mockConstructorTestingTNewDeployer interface { +// NewDeployer creates a new instance of Deployer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewDeployer(t interface { mock.TestingT Cleanup(func()) -} - -// NewDeployer creates a new instance of Deployer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewDeployer(t mockConstructorTestingTNewDeployer) *Deployer { +}) *Deployer { mock := &Deployer{} mock.Mock.Test(t) diff --git a/mocks/HTTPClient.go b/mocks/HTTPClient.go index 08042fc3..4bc69d5e 100644 --- a/mocks/HTTPClient.go +++ b/mocks/HTTPClient.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.19.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks @@ -17,7 +17,15 @@ type HTTPClient struct { func (_m *HTTPClient) Do(req *http.Request) (*http.Response, error) { ret := _m.Called(req) + if len(ret) == 0 { + panic("no return value specified for Do") + } + var r0 *http.Response + var r1 error + if rf, ok := ret.Get(0).(func(*http.Request) (*http.Response, error)); ok { + return rf(req) + } if rf, ok := ret.Get(0).(func(*http.Request) *http.Response); ok { r0 = rf(req) } else { @@ -26,7 +34,6 @@ func (_m *HTTPClient) Do(req *http.Request) (*http.Response, error) { } } - var r1 error if rf, ok := ret.Get(1).(func(*http.Request) error); ok { r1 = rf(req) } else { @@ -36,13 +43,12 @@ func (_m *HTTPClient) Do(req *http.Request) (*http.Response, error) { return r0, r1 } -type mockConstructorTestingTNewHTTPClient interface { +// NewHTTPClient creates a new instance of HTTPClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewHTTPClient(t interface { mock.TestingT Cleanup(func()) -} - -// NewHTTPClient creates a new instance of HTTPClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewHTTPClient(t mockConstructorTestingTNewHTTPClient) *HTTPClient { +}) *HTTPClient { mock := &HTTPClient{} mock.Mock.Test(t) diff --git a/mocks/K8sUtilInterface.go b/mocks/K8sUtilInterface.go index e6385063..f492a4e3 100644 --- a/mocks/K8sUtilInterface.go +++ b/mocks/K8sUtilInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.19.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks @@ -23,14 +23,21 @@ type K8sUtilInterface struct { func (_m *K8sUtilInterface) CreateOrUpdateResource(_a0 context.Context, _a1 client.Object, _a2 client.Object, _a3 controllerutil.MutateFn) (string, error) { ret := _m.Called(_a0, _a1, _a2, _a3) + if len(ret) == 0 { + panic("no return value specified for CreateOrUpdateResource") + } + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, client.Object, client.Object, controllerutil.MutateFn) (string, error)); ok { + return rf(_a0, _a1, _a2, _a3) + } if rf, ok := ret.Get(0).(func(context.Context, client.Object, client.Object, controllerutil.MutateFn) string); ok { r0 = rf(_a0, _a1, _a2, _a3) } else { r0 = ret.Get(0).(string) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, client.Object, client.Object, controllerutil.MutateFn) error); ok { r1 = rf(_a0, _a1, _a2, _a3) } else { @@ -44,6 +51,10 @@ func (_m *K8sUtilInterface) CreateOrUpdateResource(_a0 context.Context, _a1 clie func (_m *K8sUtilInterface) DeleteResource(_a0 context.Context, _a1 client.Object) error { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for DeleteResource") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, client.Object) error); ok { r0 = rf(_a0, _a1) @@ -58,6 +69,10 @@ func (_m *K8sUtilInterface) DeleteResource(_a0 context.Context, _a1 client.Objec func (_m *K8sUtilInterface) IsCRDInstalled(_a0 string) bool { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for IsCRDInstalled") + } + var r0 bool if rf, ok := ret.Get(0).(func(string) bool); ok { r0 = rf(_a0) @@ -72,6 +87,10 @@ func (_m *K8sUtilInterface) IsCRDInstalled(_a0 string) bool { func (_m *K8sUtilInterface) K8sClientset() kubernetes.Interface { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for K8sClientset") + } + var r0 kubernetes.Interface if rf, ok := ret.Get(0).(func() kubernetes.Interface); ok { r0 = rf() @@ -88,7 +107,15 @@ func (_m *K8sUtilInterface) K8sClientset() kubernetes.Interface { func (_m *K8sUtilInterface) RESTGet(_a0 string) ([]byte, error) { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for RESTGet") + } + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(string) ([]byte, error)); ok { + return rf(_a0) + } if rf, ok := ret.Get(0).(func(string) []byte); ok { r0 = rf(_a0) } else { @@ -97,7 +124,6 @@ func (_m *K8sUtilInterface) RESTGet(_a0 string) ([]byte, error) { } } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(_a0) } else { @@ -108,33 +134,46 @@ func (_m *K8sUtilInterface) RESTGet(_a0 string) ([]byte, error) { } // VersionGet provides a mock function with given fields: -func (_m *K8sUtilInterface) VersionGet() (string, error) { +func (_m *K8sUtilInterface) VersionGet() (string, string, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for VersionGet") + } + var r0 string + var r1 string + var r2 error + if rf, ok := ret.Get(0).(func() (string, string, error)); ok { + return rf() + } if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() } else { r0 = ret.Get(0).(string) } - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { + if rf, ok := ret.Get(1).(func() string); ok { r1 = rf() } else { - r1 = ret.Error(1) + r1 = ret.Get(1).(string) } - return r0, r1 -} + if rf, ok := ret.Get(2).(func() error); ok { + r2 = rf() + } else { + r2 = ret.Error(2) + } -type mockConstructorTestingTNewK8sUtilInterface interface { - mock.TestingT - Cleanup(func()) + return r0, r1, r2 } // NewK8sUtilInterface creates a new instance of K8sUtilInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewK8sUtilInterface(t mockConstructorTestingTNewK8sUtilInterface) *K8sUtilInterface { +// The first argument is typically a *testing.T value. +func NewK8sUtilInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *K8sUtilInterface { mock := &K8sUtilInterface{} mock.Mock.Test(t) diff --git a/mocks/SetWarning.go b/mocks/SetWarning.go index 6b1ffe85..9e1ff517 100644 --- a/mocks/SetWarning.go +++ b/mocks/SetWarning.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.19.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks @@ -13,6 +13,10 @@ type SetWarning struct { func (_m *SetWarning) Execute(message string) error { ret := _m.Called(message) + if len(ret) == 0 { + panic("no return value specified for Execute") + } + var r0 error if rf, ok := ret.Get(0).(func(string) error); ok { r0 = rf(message) @@ -23,13 +27,12 @@ func (_m *SetWarning) Execute(message string) error { return r0 } -type mockConstructorTestingTNewSetWarning interface { +// NewSetWarning creates a new instance of SetWarning. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewSetWarning(t interface { mock.TestingT Cleanup(func()) -} - -// NewSetWarning creates a new instance of SetWarning. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewSetWarning(t mockConstructorTestingTNewSetWarning) *SetWarning { +}) *SetWarning { mock := &SetWarning{} mock.Mock.Test(t) diff --git a/mocks/getK8sResources.go b/mocks/getK8sResources.go index 903a6bd2..2fb844b1 100644 --- a/mocks/getK8sResources.go +++ b/mocks/getK8sResources.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.19.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks @@ -25,7 +25,16 @@ type getK8sResources struct { func (_m *getK8sResources) Execute(_a0 model.Deployer, _a1 *v1.AstraConnector, _a2 context.Context) ([]client.Object, controllerutil.MutateFn, error) { ret := _m.Called(_a0, _a1, _a2) + if len(ret) == 0 { + panic("no return value specified for Execute") + } + var r0 []client.Object + var r1 controllerutil.MutateFn + var r2 error + if rf, ok := ret.Get(0).(func(model.Deployer, *v1.AstraConnector, context.Context) ([]client.Object, controllerutil.MutateFn, error)); ok { + return rf(_a0, _a1, _a2) + } if rf, ok := ret.Get(0).(func(model.Deployer, *v1.AstraConnector, context.Context) []client.Object); ok { r0 = rf(_a0, _a1, _a2) } else { @@ -34,7 +43,6 @@ func (_m *getK8sResources) Execute(_a0 model.Deployer, _a1 *v1.AstraConnector, _ } } - var r1 controllerutil.MutateFn if rf, ok := ret.Get(1).(func(model.Deployer, *v1.AstraConnector, context.Context) controllerutil.MutateFn); ok { r1 = rf(_a0, _a1, _a2) } else { @@ -43,7 +51,6 @@ func (_m *getK8sResources) Execute(_a0 model.Deployer, _a1 *v1.AstraConnector, _ } } - var r2 error if rf, ok := ret.Get(2).(func(model.Deployer, *v1.AstraConnector, context.Context) error); ok { r2 = rf(_a0, _a1, _a2) } else { @@ -53,13 +60,12 @@ func (_m *getK8sResources) Execute(_a0 model.Deployer, _a1 *v1.AstraConnector, _ return r0, r1, r2 } -type mockConstructorTestingTnewGetK8sResources interface { +// newGetK8sResources creates a new instance of getK8sResources. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newGetK8sResources(t interface { mock.TestingT Cleanup(func()) -} - -// newGetK8sResources creates a new instance of getK8sResources. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func newGetK8sResources(t mockConstructorTestingTnewGetK8sResources) *getK8sResources { +}) *getK8sResources { mock := &getK8sResources{} mock.Mock.Test(t) From e1673ac9b1579123def3360982a65e4137620fc3 Mon Sep 17 00:00:00 2001 From: jharrod Date: Wed, 12 Jun 2024 11:07:39 -0600 Subject: [PATCH 02/10] create pending cloud and cluster --- app/register/register.go | 11 ++- .../astra.netapp.io_astraconnectors.yaml | 6 +- .../controllers/astraconnector_controller.go | 92 +++++++++---------- details/operator-sdk/controllers/deployer.go | 3 + 4 files changed, 59 insertions(+), 53 deletions(-) diff --git a/app/register/register.go b/app/register/register.go index 0f7512c2..441e01ff 100644 --- a/app/register/register.go +++ b/app/register/register.go @@ -109,7 +109,7 @@ func DoRequest(ctx context.Context, client HTTPClient, method, url string, bodyB type ClusterRegisterUtil interface { GetAPITokenFromSecret(secretName string) (string, string, error) - IsClusterManaged() (bool, string, error) + IsClusterManaged(clusterId string) (bool, string, error) SetHttpClient(disableTls bool, astraHost string) error RegisterCloud() (string, string, error) RegisterCluster(cloudId string, k8sServiceId string) (string, string, error) @@ -222,8 +222,8 @@ func (c clusterRegisterUtil) SetHttpClient(disableTls bool, astraHost string) er return nil } -func (c clusterRegisterUtil) IsClusterManaged() (bool, string, error) { - url := fmt.Sprintf("%s/accounts/%s/topology/v1/managedClusters/%s", c.AstraHostUrl, c.AstraConnector.Spec.Astra.AccountId, c.AstraConnector.Spec.Astra.ClusterId) +func (c clusterRegisterUtil) IsClusterManaged(clusterId string) (bool, string, error) { + url := fmt.Sprintf("%s/accounts/%s/topology/v1/managedClusters/%s", c.AstraHostUrl, c.AstraConnector.Spec.Astra.AccountId, clusterId) c.Log.WithValues("ClusterId", c.AstraConnector.Spec.Astra.ClusterId). Info("Checking if cluster is managed") @@ -521,7 +521,7 @@ func (c clusterRegisterUtil) RegisterCloud() (string, string, error) { } func (c clusterRegisterUtil) ListClusters() ([]Cluster, string, error) { - url := fmt.Sprintf("%s/topology/v1/clusters", c.AstraHostUrl) + url := fmt.Sprintf("%s/accounts/%s/topology/v1/clusters", c.AstraHostUrl, c.AstraConnector.Spec.Astra.AccountId) headerMap := HeaderMap{Authorization: fmt.Sprintf("Bearer %s", c.ApiToken)} response, err, cancel := DoRequest(c.Ctx, c.Client, http.MethodGet, url, nil, headerMap, c.Log) defer cancel() @@ -686,7 +686,7 @@ func (c clusterRegisterUtil) RegisterCluster(cloudId string, k8sApiServiceId str // Check for a duplicate cluster (by ApiServiceID) before continuing c.Log.Info("Checking for duplicate cluster records") - dupeCluster, errMsg, err := c.checkForDuplicateApiServiceIDs(k8sApiServiceId) //todo move this out + dupeCluster, errMsg, err := c.checkForDuplicateApiServiceIDs(k8sApiServiceId) if dupeCluster == nil && err != nil { // Legit error return "", errMsg, fmt.Errorf("error checking for duplicate apiServiceID records: %w", err) @@ -701,6 +701,7 @@ func (c clusterRegisterUtil) RegisterCluster(cloudId string, k8sApiServiceId str c.Log.WithValues("clusterId", dupeCluster.ID).Info("Found duplicate cluster record already registered") return dupeCluster.ID, "", nil } + clusterId = dupeCluster.ID } else { // Cluster not found, go create a new pending cluster record c.Log.WithValues("clusterName", c.AstraConnector.Spec.Astra.ClusterName). diff --git a/details/operator-sdk/config/crd/bases/astra.netapp.io_astraconnectors.yaml b/details/operator-sdk/config/crd/bases/astra.netapp.io_astraconnectors.yaml index 9011d05c..bd9c576d 100644 --- a/details/operator-sdk/config/crd/bases/astra.netapp.io_astraconnectors.yaml +++ b/details/operator-sdk/config/crd/bases/astra.netapp.io_astraconnectors.yaml @@ -64,8 +64,6 @@ spec: type: boolean required: - accountId - - cloudId - - clusterId type: object astraConnect: properties: @@ -248,6 +246,10 @@ spec: status: description: AstraConnectorStatus defines the observed state of AstraConnector properties: + cloudId: + type: string + clusterId: + type: string natsSyncClient: description: NatsSyncClientStatus defines the observed state of NatsSyncClient properties: diff --git a/details/operator-sdk/controllers/astraconnector_controller.go b/details/operator-sdk/controllers/astraconnector_controller.go index ee09ea6c..b0439ded 100644 --- a/details/operator-sdk/controllers/astraconnector_controller.go +++ b/details/operator-sdk/controllers/astraconnector_controller.go @@ -143,8 +143,8 @@ func (r *AstraConnectorController) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, nil } + k8sUtil := k8s.NewK8sUtil(r.Client, r.Clientset, log) if !astraConnector.Spec.SkipPreCheck { - k8sUtil := k8s.NewK8sUtil(r.Client, r.Clientset, log) preCheckClient := precheck.NewPrecheckClient(log, k8sUtil) errList := preCheckClient.Run() @@ -166,6 +166,47 @@ func (r *AstraConnectorController) Reconcile(ctx context.Context, req ctrl.Reque } } + if astraConnector.Spec.Astra.ClusterId == "" && astraConnector.Spec.Astra.ClusterName == "" { + err := fmt.Errorf("clusterID and clusterName both cannot be empty") + log.Error(err, "Bad config") + natsSyncClientStatus.Status = ErrorClusterIdAndNameEmpty + _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) + return ctrl.Result{RequeueAfter: time.Minute * conf.Config.ErrorTimeout()}, err + } + + registerUtil, err := register.NewClusterRegisterUtil(astraConnector, &http.Client{}, r.Client, k8sUtil, log, context.Background()) + if err != nil { + natsSyncClientStatus.Status = ErrorInitiatingRegistration + _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) + return ctrl.Result{RequeueAfter: time.Minute * conf.Config.ErrorTimeout()}, fmt.Errorf("error getting Kubernetes API service ID: %w", err) + } + + cloudID, errMsg, err := registerUtil.RegisterCloud() + if err != nil { + natsSyncClientStatus.Status = ErrorGetK8sServiceId + _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) + return ctrl.Result{RequeueAfter: time.Minute * conf.Config.ErrorTimeout()}, fmt.Errorf("error getting Kubernetes API service ID: %w", err) + } + astraConnector.Status.CloudId = cloudID + _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) + + log.Info("Getting Kubernetes API service ID") + k8sServiceId, err := r.getK8sApiServiceID(ctx) + if err != nil { + natsSyncClientStatus.Status = ErrorGetK8sServiceId + _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) + return ctrl.Result{RequeueAfter: time.Minute * conf.Config.ErrorTimeout()}, fmt.Errorf("error getting Kubernetes API service ID: %w", err) + } + + clusterId, errMsg, err := registerUtil.RegisterCluster(cloudID, k8sServiceId) + if err != nil { + natsSyncClientStatus.Status = errMsg + _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) + return ctrl.Result{RequeueAfter: time.Minute * conf.Config.ErrorTimeout()}, fmt.Errorf("error getting Kubernetes API service ID: %w", err) + } + astraConnector.Status.ClusterId = clusterId + _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) + // deploy Neptune if conf.Config.FeatureFlags().DeployNeptune() { log.Info("Initiating Neptune deployment") @@ -183,47 +224,6 @@ func (r *AstraConnectorController) Reconcile(ctx context.Context, req ctrl.Reque var connectorResults ctrl.Result var deployError error - if astraConnector.Spec.Astra.ClusterId == "" && astraConnector.Spec.Astra.ClusterName == "" { - err := fmt.Errorf("clusterID and clusterName both cannot be empty") - log.Error(err, "Bad config") - natsSyncClientStatus.Status = ErrorClusterIdAndNameEmpty - _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) - return ctrl.Result{RequeueAfter: time.Minute * conf.Config.ErrorTimeout()}, err - } - - registerUtil, err := register.NewClusterRegisterUtil(astraConnector, &http.Client{}, r.Client, nil, log, context.Background()) - if err != nil { - natsSyncClientStatus.Status = ErrorInitiatingRegistration - _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) - return ctrl.Result{RequeueAfter: time.Minute * conf.Config.ErrorTimeout()}, fmt.Errorf("error getting Kubernetes API service ID: %w", err) - } - - cloudID, errMsg, err := registerUtil.RegisterCloud() - if err != nil { - natsSyncClientStatus.Status = ErrorGetK8sServiceId - _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) - return ctrl.Result{RequeueAfter: time.Minute * conf.Config.ErrorTimeout()}, fmt.Errorf("error getting Kubernetes API service ID: %w", err) - } - astraConnector.Status.CloudId = cloudID - _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) - - log.Info("Getting Kubernetes API service ID") - k8sServiceId, err := r.getK8sApiServiceID(ctx) - if err != nil { - natsSyncClientStatus.Status = ErrorGetK8sServiceId - _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) - return ctrl.Result{RequeueAfter: time.Minute * conf.Config.ErrorTimeout()}, fmt.Errorf("error getting Kubernetes API service ID: %w", err) - } - - clusterId, errMsg, err := registerUtil.RegisterCluster(cloudID, k8sServiceId) - if err != nil { - natsSyncClientStatus.Status = errMsg - _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) - return ctrl.Result{RequeueAfter: time.Minute * conf.Config.ErrorTimeout()}, fmt.Errorf("error getting Kubernetes API service ID: %w", err) - } - astraConnector.Status.ClusterId = clusterId - _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) - connectorResults, deployError = r.deployNatlessConnector(ctx, astraConnector, &natsSyncClientStatus) if deployError != nil { // Note: Returning nil in error since we want to wait for the requeue to happen @@ -236,7 +236,7 @@ func (r *AstraConnectorController) Reconcile(ctx context.Context, req ctrl.Reque natsSyncClientStatus.Status = WaitForClusterManagedState _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) - err = waitForManagedCluster(registerUtil, log) + err = waitForManagedCluster(registerUtil, log, clusterId) if err != nil { log.Error(err, "timed out waiting for cluster to become managed, requeueing after delay", "delay", conf.Config.ErrorTimeout()) natsSyncClientStatus.Status = ErrorClusterUnmanaged @@ -371,11 +371,11 @@ func (r *AstraConnectorController) waitForStatusUpdate(astraConnector *v1.AstraC return err } -func waitForManagedCluster(registerUtil register.ClusterRegisterUtil, log logr.Logger) error { - maxRetries := 5 +func waitForManagedCluster(registerUtil register.ClusterRegisterUtil, log logr.Logger, clusterId string) error { + maxRetries := 10 waitTime := 3 * time.Second for i := 1; i <= maxRetries; i++ { - isManaged, _, err := registerUtil.IsClusterManaged() + isManaged, _, err := registerUtil.IsClusterManaged(clusterId) if isManaged { return nil } diff --git a/details/operator-sdk/controllers/deployer.go b/details/operator-sdk/controllers/deployer.go index c0397788..8f55ffc2 100644 --- a/details/operator-sdk/controllers/deployer.go +++ b/details/operator-sdk/controllers/deployer.go @@ -114,6 +114,9 @@ func (r *AstraConnectorController) deleteClusterScopedResources(ctx context.Cont } func (r *AstraConnectorController) waitForResourceReady(ctx context.Context, kubeObject client.Object, astraConnector *installer.AstraConnector) error { + if kubeObject.GetName() == "neptune-controller-manager" { + return nil + } log := ctrllog.FromContext(ctx) timeout := time.After(conf.Config.WaitDurationForResource()) // default is 2 mins ticker := time.NewTicker(3 * time.Second) From 5df52ef6ce9cafb55315fb98a99e70d7713a46c3 Mon Sep 17 00:00:00 2001 From: jharrod Date: Wed, 12 Jun 2024 14:21:26 -0600 Subject: [PATCH 03/10] create pending cloud and cluster --- Makefile | 2 +- app/register/register.go | 7 +++++++ .../operator-sdk/controllers/astraconnector_controller.go | 2 +- details/operator-sdk/controllers/deployer.go | 3 --- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 447de3e4..38ccdf92 100644 --- a/Makefile +++ b/Makefile @@ -148,7 +148,7 @@ envtest: ## Download envtest-setup locally if necessary. MOCKERY = $(shell pwd)/bin/mockery install-mockery: ## Download mockery locally if necessary: https://github.com/vektra/mockery - $(call go-get-tool,$(MOCKERY),github.com/vektra/mockery/v2@v2.43.2) + $(call go-get-tool,$(MOCKERY),github.com/vektra/mockery/v2@v2.19.0) GOLANGCI_LINT = $(shell pwd)/bin/golangci-lint install-golangci-lint: ## Download golangci-lint locally if necessary: https://github.com/golangci/golangci-lint diff --git a/app/register/register.go b/app/register/register.go index 441e01ff..9bd02c13 100644 --- a/app/register/register.go +++ b/app/register/register.go @@ -723,6 +723,13 @@ func (c clusterRegisterUtil) RegisterCluster(cloudId string, k8sApiServiceId str } } else { clusterId = c.AstraConnector.Spec.Astra.ClusterId + // Check that the provided cluster exists + cluster, _, err := c.GetCluster(cloudId, clusterId) + if cluster == nil { + errMsg := fmt.Sprintf("Cluster with ID '%s' not found, please check that the provided ID is correct", clusterId) + return "", errMsg, errors.New(errMsg) + } + c.Log.Info("Checking if Astra Control cluster is already registered") isAlreadyRegistered, err := c.checkClusterRegistrationStatus(cloudId, c.AstraConnector.Spec.Astra.ClusterId, k8sApiServiceId) if err != nil { diff --git a/details/operator-sdk/controllers/astraconnector_controller.go b/details/operator-sdk/controllers/astraconnector_controller.go index b0439ded..b2a86007 100644 --- a/details/operator-sdk/controllers/astraconnector_controller.go +++ b/details/operator-sdk/controllers/astraconnector_controller.go @@ -256,7 +256,7 @@ func (r *AstraConnectorController) Reconcile(ctx context.Context, req ctrl.Reque } natsSyncClientStatus.Registered = "true" - natsSyncClientStatus.AstraClusterId = astraConnector.Spec.Astra.ClusterId + natsSyncClientStatus.AstraClusterId = clusterId natsSyncClientStatus.Status = RegisteredWithAstra _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) } diff --git a/details/operator-sdk/controllers/deployer.go b/details/operator-sdk/controllers/deployer.go index 8f55ffc2..c0397788 100644 --- a/details/operator-sdk/controllers/deployer.go +++ b/details/operator-sdk/controllers/deployer.go @@ -114,9 +114,6 @@ func (r *AstraConnectorController) deleteClusterScopedResources(ctx context.Cont } func (r *AstraConnectorController) waitForResourceReady(ctx context.Context, kubeObject client.Object, astraConnector *installer.AstraConnector) error { - if kubeObject.GetName() == "neptune-controller-manager" { - return nil - } log := ctrllog.FromContext(ctx) timeout := time.After(conf.Config.WaitDurationForResource()) // default is 2 mins ticker := time.NewTicker(3 * time.Second) From feec817350faf51cd349af535a869fa715b067cb Mon Sep 17 00:00:00 2001 From: jharrod Date: Wed, 12 Jun 2024 14:27:44 -0600 Subject: [PATCH 04/10] create pending cloud and cluster --- details/operator-sdk/controllers/status_strings.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/details/operator-sdk/controllers/status_strings.go b/details/operator-sdk/controllers/status_strings.go index 221a8312..a88afbab 100644 --- a/details/operator-sdk/controllers/status_strings.go +++ b/details/operator-sdk/controllers/status_strings.go @@ -27,7 +27,7 @@ const ( ErrorCreateClusterRoles = "Error creating ClusterRoles %s/%s" ErrorClusterUnmanaged = "Timed out waiting for cluster to become managed" ErrorGetK8sServiceId = "Error getting K8s service ID" - ErrorClusterIdAndNameEmpty = "Error: clusterID and clusterName cannot both be empty" + ErrorClusterIdAndNameEmpty = "Error initiating registration, clusterID and clusterName cannot both be empty" ErrorInitiatingRegistration = "Error initiating cluster registration" FailedFinalizerAdd = "Failed to add finalizer" From 336201f9d45dc6667d771d40fe57545b0fe332e7 Mon Sep 17 00:00:00 2001 From: jharrod Date: Wed, 12 Jun 2024 14:52:47 -0600 Subject: [PATCH 05/10] fix lint --- app/register/register.go | 5 +++-- .../operator-sdk/controllers/astraconnector_controller.go | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/register/register.go b/app/register/register.go index 9bd02c13..8d302c93 100644 --- a/app/register/register.go +++ b/app/register/register.go @@ -694,7 +694,7 @@ func (c clusterRegisterUtil) RegisterCluster(cloudId string, k8sApiServiceId str // Found a duplicate cluster, check if the state of the cluster is already managed. registered, err := c.checkRegistrationStatus(dupeCluster, k8sApiServiceId) if err != nil { - errMsg := fmt.Sprintf("unable to check duplicate cluster registration status") + errMsg := "unable to check duplicate cluster registration status" return "", errMsg, fmt.Errorf("%s: %w", errMsg, err) } if registered { @@ -715,7 +715,8 @@ func (c clusterRegisterUtil) RegisterCluster(cloudId string, k8sApiServiceId str pendingCluster.ConnectorInstall = ConnectorInstallStatePending pendingCluster.ApiServiceID = k8sApiServiceId - cluster, errMsg, err := c.CreateCluster(pendingCluster, cloudId) + var cluster *Cluster + cluster, errMsg, err = c.CreateCluster(pendingCluster, cloudId) if err != nil { return "", errMsg, fmt.Errorf("unable to create pending cluster record: %w", err) } diff --git a/details/operator-sdk/controllers/astraconnector_controller.go b/details/operator-sdk/controllers/astraconnector_controller.go index b2a86007..ef129c77 100644 --- a/details/operator-sdk/controllers/astraconnector_controller.go +++ b/details/operator-sdk/controllers/astraconnector_controller.go @@ -181,7 +181,8 @@ func (r *AstraConnectorController) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{RequeueAfter: time.Minute * conf.Config.ErrorTimeout()}, fmt.Errorf("error getting Kubernetes API service ID: %w", err) } - cloudID, errMsg, err := registerUtil.RegisterCloud() + var cloudID, errMsg string + cloudID, errMsg, err = registerUtil.RegisterCloud() if err != nil { natsSyncClientStatus.Status = ErrorGetK8sServiceId _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) From 9d179e07d012eab8c755e040e5ed7fdcffb50ed1 Mon Sep 17 00:00:00 2001 From: jharrod Date: Wed, 12 Jun 2024 14:59:42 -0600 Subject: [PATCH 06/10] fix lint --- app/register/register.go | 4 ++++ .../operator-sdk/controllers/astraconnector_controller.go | 7 +++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/register/register.go b/app/register/register.go index 8d302c93..d7e9130b 100644 --- a/app/register/register.go +++ b/app/register/register.go @@ -726,6 +726,10 @@ func (c clusterRegisterUtil) RegisterCluster(cloudId string, k8sApiServiceId str clusterId = c.AstraConnector.Spec.Astra.ClusterId // Check that the provided cluster exists cluster, _, err := c.GetCluster(cloudId, clusterId) + if err != nil { + errMsg := fmt.Sprintf("Error checking cluster '%s' exists: %s", clusterId, err) + return "", errMsg, errors.New(errMsg) + } if cluster == nil { errMsg := fmt.Sprintf("Cluster with ID '%s' not found, please check that the provided ID is correct", clusterId) return "", errMsg, errors.New(errMsg) diff --git a/details/operator-sdk/controllers/astraconnector_controller.go b/details/operator-sdk/controllers/astraconnector_controller.go index ef129c77..0a9f8014 100644 --- a/details/operator-sdk/controllers/astraconnector_controller.go +++ b/details/operator-sdk/controllers/astraconnector_controller.go @@ -181,12 +181,11 @@ func (r *AstraConnectorController) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{RequeueAfter: time.Minute * conf.Config.ErrorTimeout()}, fmt.Errorf("error getting Kubernetes API service ID: %w", err) } - var cloudID, errMsg string - cloudID, errMsg, err = registerUtil.RegisterCloud() + cloudID, errMsg, err := registerUtil.RegisterCloud() if err != nil { - natsSyncClientStatus.Status = ErrorGetK8sServiceId + natsSyncClientStatus.Status = errMsg _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) - return ctrl.Result{RequeueAfter: time.Minute * conf.Config.ErrorTimeout()}, fmt.Errorf("error getting Kubernetes API service ID: %w", err) + return ctrl.Result{RequeueAfter: time.Minute * conf.Config.ErrorTimeout()}, fmt.Errorf("error registering cloud: %w", err) } astraConnector.Status.CloudId = cloudID _ = r.updateAstraConnectorStatus(ctx, astraConnector, natsSyncClientStatus) From 9d8c2a3d656d3f0677230f6ef4ff2fc625dee545 Mon Sep 17 00:00:00 2001 From: jharrod Date: Wed, 12 Jun 2024 16:44:00 -0600 Subject: [PATCH 07/10] fix unit test --- app/deployer/connector/astra_connect_test.go | 6 ++-- app/register/register_test.go | 30 +++++++++++--------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/app/deployer/connector/astra_connect_test.go b/app/deployer/connector/astra_connect_test.go index ed7f0dd8..52289f6a 100644 --- a/app/deployer/connector/astra_connect_test.go +++ b/app/deployer/connector/astra_connect_test.go @@ -99,9 +99,9 @@ func TestAstraConnect_ClusterIDAndNameEmpty(t *testing.T) { } objects, f, err := deployer.GetDeploymentObjects(astraConnector, ctx) - assert.Error(t, err) - assert.Nil(t, objects) - assert.Nil(t, f) + assert.NoError(t, err) + assert.NotNil(t, objects) + assert.NotNil(t, f) } func DummyAstraConnector() v1.AstraConnector { diff --git a/app/register/register_test.go b/app/register/register_test.go index b5015dda..ed4d392b 100644 --- a/app/register/register_test.go +++ b/app/register/register_test.go @@ -6,6 +6,7 @@ package register_test import ( "context" "errors" + "fmt" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -42,7 +43,8 @@ func setupTokenSecret(secretName string, k8sClient client.Client) { }, } - _ = k8sClient.Create(ctx, secretObj) + err := k8sClient.Create(ctx, secretObj) + fmt.Println(err) } type AstraConnectorInput struct { @@ -50,6 +52,7 @@ type AstraConnectorInput struct { cloudId bool clusterId bool invalidHostDetails bool + mockSecret *coreV1.Secret } func createClusterRegister(astraConnectorInput AstraConnectorInput) (register.ClusterRegisterUtil, *mocks.HTTPClient, string, client.Client, error) { @@ -92,6 +95,14 @@ func createClusterRegister(astraConnectorInput AstraConnectorInput) (register.Cl astraConnector.Spec.Astra.TokenRef = apiTokenSecret } + if astraConnectorInput.mockSecret != nil { + err := fakeClient.Create(context.Background(), astraConnectorInput.mockSecret) + if err != nil { + return nil, mockHttpClient, apiTokenSecret, fakeClient, err + } + astraConnector.Spec.Astra.TokenRef = astraConnectorInput.mockSecret.Name + } + if astraConnectorInput.cloudId { astraConnector.Spec.Astra.CloudId = testCloudId } @@ -116,7 +127,7 @@ func createClusterRegister(astraConnectorInput AstraConnectorInput) (register.Cl func TestGetAPITokenFromSecret(t *testing.T) { t.Run("GetAPITokenFromSecret__SecretNotPresentReturnsError", func(t *testing.T) { - clusterRegisterUtil, _, _, _, err := createClusterRegister(AstraConnectorInput{}) + clusterRegisterUtil, _, _, _, err := createClusterRegister(AstraConnectorInput{createTokenSecret: true}) assert.NoError(t, err) apiToken, errorReason, err := clusterRegisterUtil.GetAPITokenFromSecret("astra-token") @@ -126,27 +137,18 @@ func TestGetAPITokenFromSecret(t *testing.T) { }) t.Run("GetAPITokenFromSecret__SecretInvalidReturnsError", func(t *testing.T) { - clusterRegisterUtil, _, apiTokenSecret, fakeClient, err := createClusterRegister(AstraConnectorInput{}) - assert.NoError(t, err) - secret := &coreV1.Secret{ ObjectMeta: metaV1.ObjectMeta{ - Name: apiTokenSecret, + Name: "astra-token", Namespace: testNamespace, }, Data: map[string][]byte{ "api-token": []byte("auth-token"), }, } + _, _, _, _, err := createClusterRegister(AstraConnectorInput{mockSecret: secret}) + assert.Error(t, err) - // creating secret - err = fakeClient.Create(ctx, secret) - assert.NoError(t, err) - - apiToken, errorReason, err := clusterRegisterUtil.GetAPITokenFromSecret(apiTokenSecret) - - assert.Equal(t, apiToken, "") - assert.Equal(t, "Failed to extract 'apiToken' key from secret astra-token", errorReason) assert.EqualError(t, err, "failed to extract apiToken key from secret") }) From 16a7d034ed6921e52dea44dbd05b0f175a9d7b00 Mon Sep 17 00:00:00 2001 From: jharrod Date: Wed, 12 Jun 2024 16:52:21 -0600 Subject: [PATCH 08/10] fix unit test --- details/k8s/cluster_type_checker_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/details/k8s/cluster_type_checker_test.go b/details/k8s/cluster_type_checker_test.go index 7576d102..b6d4918f 100644 --- a/details/k8s/cluster_type_checker_test.go +++ b/details/k8s/cluster_type_checker_test.go @@ -29,7 +29,7 @@ func TestDetermineClusterType(t *testing.T) { t.Run("AKS", func(t *testing.T) { clusterTypeChecker, k8sUtil, k8sInterface := createHandler(t) k8sUtil.On("RESTGet", mock.Anything).Return(nil, errors.New("testing")) - k8sUtil.On("VersionGet").Return("1.1", nil) + k8sUtil.On("VersionGet").Return("1.1", "1.1", nil) clusterRole := &v1.ClusterRole{ ObjectMeta: metav1.ObjectMeta{ Name: "aks-service", @@ -45,7 +45,7 @@ func TestDetermineClusterType(t *testing.T) { t.Run("Anthos", func(t *testing.T) { clusterTypeChecker, k8sUtil, _ := createHandler(t) k8sUtil.On("RESTGet", "apis/anthos.gke.io").Return(make([]byte, 0), nil) - k8sUtil.On("VersionGet").Return("1.1", nil) + k8sUtil.On("VersionGet").Return("1.1", "1.1", nil) assert.Equal(t, k8s.FlavorAnthos, clusterTypeChecker.DetermineClusterType()) }) @@ -54,7 +54,7 @@ func TestDetermineClusterType(t *testing.T) { clusterTypeChecker, k8sUtil, _ := createHandler(t) k8sUtil.On("RESTGet", "apis/management.cattle.io").Return(make([]byte, 0), nil) k8sUtil.On("RESTGet", mock.Anything).Return(nil, errors.New("testing")) - k8sUtil.On("VersionGet").Return("1.1", nil) + k8sUtil.On("VersionGet").Return("1.1", "1.1", nil) assert.Equal(t, k8s.FlavorRKE, clusterTypeChecker.DetermineClusterType()) }) @@ -63,7 +63,7 @@ func TestDetermineClusterType(t *testing.T) { clusterTypeChecker, k8sUtil, _ := createHandler(t) k8sUtil.On("RESTGet", "apis/k3s.cattle.io").Return(make([]byte, 0), nil) k8sUtil.On("RESTGet", mock.Anything).Return(nil, errors.New("testing")) - k8sUtil.On("VersionGet").Return("1.1", nil) + k8sUtil.On("VersionGet").Return("1.1", "1.1", nil) assert.Equal(t, k8s.FlavorRKE2, clusterTypeChecker.DetermineClusterType()) }) @@ -72,7 +72,7 @@ func TestDetermineClusterType(t *testing.T) { clusterTypeChecker, k8sUtil, _ := createHandler(t) k8sUtil.On("RESTGet", "apis/core.antrea.tanzu.vmware.com").Return(make([]byte, 0), nil) k8sUtil.On("RESTGet", mock.Anything).Return(nil, errors.New("testing")) - k8sUtil.On("VersionGet").Return("1.1", nil) + k8sUtil.On("VersionGet").Return("1.1", "1.1", nil) assert.Equal(t, k8s.FlavorTanzu, clusterTypeChecker.DetermineClusterType()) }) @@ -80,7 +80,7 @@ func TestDetermineClusterType(t *testing.T) { t.Run("GKE", func(t *testing.T) { clusterTypeChecker, k8sUtil, _ := createHandler(t) k8sUtil.On("RESTGet", mock.Anything).Return(nil, errors.New("testing")) - k8sUtil.On("VersionGet").Return("1.1-gke", nil) + k8sUtil.On("VersionGet").Return("1.1-gke", "1.1", nil) assert.Equal(t, k8s.FlavorGKE, clusterTypeChecker.DetermineClusterType()) }) @@ -88,7 +88,7 @@ func TestDetermineClusterType(t *testing.T) { t.Run("EKS", func(t *testing.T) { clusterTypeChecker, k8sUtil, _ := createHandler(t) k8sUtil.On("RESTGet", mock.Anything).Return(nil, errors.New("testing")) - k8sUtil.On("VersionGet").Return("v1.28.5-eks-5e0fdde", nil) + k8sUtil.On("VersionGet").Return("v1.28.5-eks-5e0fdde", "1.1", nil) assert.Equal(t, k8s.FlavorEKS, clusterTypeChecker.DetermineClusterType()) }) @@ -99,7 +99,7 @@ func TestDetermineClusterType(t *testing.T) { k8sUtil.On("RESTGet", "apis/config.openshift.io/v1/clusteroperators/openshift-apiserver").Return(versionBytes, nil) k8sUtil.On("RESTGet", mock.Anything).Return(nil, errors.New("testing")) - k8sUtil.On("VersionGet").Return("1.1", nil) + k8sUtil.On("VersionGet").Return("1.1", "1.1", nil) assert.Equal(t, k8s.FlavorOpenShift, clusterTypeChecker.DetermineClusterType()) }) From 2162d2e0cfb18a59b72d5643257496cf6e1496ca Mon Sep 17 00:00:00 2001 From: jharrod Date: Wed, 12 Jun 2024 16:56:50 -0600 Subject: [PATCH 09/10] fix unit test --- details/k8s/precheck/k8s_version_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/details/k8s/precheck/k8s_version_test.go b/details/k8s/precheck/k8s_version_test.go index 1a76af3b..7bec8272 100644 --- a/details/k8s/precheck/k8s_version_test.go +++ b/details/k8s/precheck/k8s_version_test.go @@ -48,7 +48,7 @@ func TestIsSupported(t *testing.T) { mockK8sUtil := mocks.NewK8sUtilInterface(t) precheckClient := precheck.NewPrecheckClient(log, mockK8sUtil) - mockK8sUtil.On("VersionGet").Return(tc.k8sVersion, nil) + mockK8sUtil.On("VersionGet").Return(tc.k8sVersion, tc.k8sVersion, nil) err := precheckClient.RunK8sVersionCheck() if tc.expectedValid { From fa0c7f0a93ffa1b202ecf6f30c60ec84f31a9f84 Mon Sep 17 00:00:00 2001 From: jharrod Date: Wed, 12 Jun 2024 17:04:01 -0600 Subject: [PATCH 10/10] update mocks --- mocks/ClusterRegisterUtil.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/mocks/ClusterRegisterUtil.go b/mocks/ClusterRegisterUtil.go index 0acea981..08d0e583 100644 --- a/mocks/ClusterRegisterUtil.go +++ b/mocks/ClusterRegisterUtil.go @@ -44,9 +44,9 @@ func (_m *ClusterRegisterUtil) GetAPITokenFromSecret(secretName string) (string, return r0, r1, r2 } -// IsClusterManaged provides a mock function with given fields: -func (_m *ClusterRegisterUtil) IsClusterManaged() (bool, string, error) { - ret := _m.Called() +// IsClusterManaged provides a mock function with given fields: clusterId +func (_m *ClusterRegisterUtil) IsClusterManaged(clusterId string) (bool, string, error) { + ret := _m.Called(clusterId) if len(ret) == 0 { panic("no return value specified for IsClusterManaged") @@ -55,23 +55,23 @@ func (_m *ClusterRegisterUtil) IsClusterManaged() (bool, string, error) { var r0 bool var r1 string var r2 error - if rf, ok := ret.Get(0).(func() (bool, string, error)); ok { - return rf() + if rf, ok := ret.Get(0).(func(string) (bool, string, error)); ok { + return rf(clusterId) } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() + if rf, ok := ret.Get(0).(func(string) bool); ok { + r0 = rf(clusterId) } else { r0 = ret.Get(0).(bool) } - if rf, ok := ret.Get(1).(func() string); ok { - r1 = rf() + if rf, ok := ret.Get(1).(func(string) string); ok { + r1 = rf(clusterId) } else { r1 = ret.Get(1).(string) } - if rf, ok := ret.Get(2).(func() error); ok { - r2 = rf() + if rf, ok := ret.Get(2).(func(string) error); ok { + r2 = rf(clusterId) } else { r2 = ret.Error(2) }