From 05b8c1a06819ea06944a6afb34b4c6c668e97062 Mon Sep 17 00:00:00 2001 From: Dimitri Koshkin Date: Wed, 10 Apr 2024 09:53:51 -0700 Subject: [PATCH] feat: deploy Nutanix CCM as an Addon (#38) * build: remove nutanix CCM from examples * build: add scripts to sync Nutanix CCM manifests * build: add CCM addon var to Nutanix examples * feat: deploy Nutanix CCM addon Aligns the method of deploying the CCM with all other addons. * fixup! build: add scripts to sync Nutanix CCM manifests * fixup! feat: deploy Nutanix CCM addon * build: remove unused CRS tooling for Nutanix CCM The CRS strategy is not supported for the Nutanix CCM, removing it until we actually need it. --- api/v1alpha1/addon_types.go | 24 +- api/v1alpha1/clusterconfig_types.go | 3 +- api/v1alpha1/nutanix_clusterconfig_types.go | 27 ++ api/v1alpha1/zz_generated.deepcopy.go | 7 +- .../README.md | 2 + .../manifests/helm-addon-installation.yaml | 20 ++ .../templates/helm-config.yaml | 4 + .../values.yaml | 6 + .../nutanix-cluster-calico-crs.yaml | 272 +----------------- .../nutanix-cluster-calico-helm-addon.yaml | 272 +----------------- .../nutanix-cluster-cilium-crs.yaml | 272 +----------------- .../nutanix-cluster-cilium-helm-addon.yaml | 272 +----------------- .../nutanix-ccm/kustomization.yaml.tmpl | 18 ++ .../nutanix/cluster/kustomization.yaml.tmpl | 20 ++ hack/examples/patches/nutanix/ccm.yaml | 8 + .../nutanix/remove-ccm/cluster-label.yaml | 5 + .../patches/nutanix/remove-ccm/crs-cm.yaml | 8 + .../patches/nutanix/remove-ccm/crs.yaml | 8 + .../patches/nutanix/remove-ccm/secret.yaml | 8 + make/addons.mk | 2 + .../generic/lifecycle/ccm/aws/handler.go | 2 + pkg/handlers/generic/lifecycle/ccm/handler.go | 28 +- .../generic/lifecycle/ccm/nutanix/handler.go | 201 +++++++++++++ .../lifecycle/ccm/nutanix/handler_test.go | 121 ++++++++ .../clusterautoscaler/strategy_helmaddon.go | 3 +- pkg/handlers/generic/lifecycle/config/cm.go | 1 + pkg/handlers/generic/lifecycle/handlers.go | 7 +- .../mutation/prismcentralendpoint/inject.go | 27 +- 28 files changed, 538 insertions(+), 1110 deletions(-) create mode 100644 charts/cluster-api-runtime-extensions-nutanix/templates/ccm/nutanix/manifests/helm-addon-installation.yaml create mode 100644 hack/addons/kustomize/nutanix-ccm/kustomization.yaml.tmpl create mode 100644 hack/examples/patches/nutanix/ccm.yaml create mode 100644 hack/examples/patches/nutanix/remove-ccm/cluster-label.yaml create mode 100644 hack/examples/patches/nutanix/remove-ccm/crs-cm.yaml create mode 100644 hack/examples/patches/nutanix/remove-ccm/crs.yaml create mode 100644 hack/examples/patches/nutanix/remove-ccm/secret.yaml create mode 100644 pkg/handlers/generic/lifecycle/ccm/nutanix/handler.go create mode 100644 pkg/handlers/generic/lifecycle/ccm/nutanix/handler_test.go diff --git a/api/v1alpha1/addon_types.go b/api/v1alpha1/addon_types.go index 05c0d9145..6cafdfdb1 100644 --- a/api/v1alpha1/addon_types.go +++ b/api/v1alpha1/addon_types.go @@ -308,12 +308,34 @@ func (CSI) VariableSchema() clusterv1.VariableSchema { } // CCM tells us to enable or disable the cloud provider interface. -type CCM struct{} +type CCM struct { + // A reference to the Secret for credential information for the target Prism Central instance + // +optional + Credentials *corev1.LocalObjectReference `json:"credentials"` +} func (CCM) VariableSchema() clusterv1.VariableSchema { + // TODO Validate credentials is set. + // This CCM is shared across all providers. + // Some of these providers may require credentials to be set, but we don't want to require it for all providers. + // The Nutanix CCM handler will fail in at runtime if credentials are not set. return clusterv1.VariableSchema{ OpenAPIV3Schema: clusterv1.JSONSchemaProps{ Type: "object", + Properties: map[string]clusterv1.JSONSchemaProps{ + "credentials": { + Description: "A reference to the Secret for credential information" + + "for the target Prism Central instance", + Type: "object", + Properties: map[string]clusterv1.JSONSchemaProps{ + "name": { + Description: "The name of the Secret", + Type: "string", + }, + }, + Required: []string{"name"}, + }, + }, }, } } diff --git a/api/v1alpha1/clusterconfig_types.go b/api/v1alpha1/clusterconfig_types.go index ba7e012b0..5cee9a2fb 100644 --- a/api/v1alpha1/clusterconfig_types.go +++ b/api/v1alpha1/clusterconfig_types.go @@ -25,7 +25,8 @@ const ( CSIProviderAWSEBS = "aws-ebs" CSIProviderNutanix = "nutanix" - CCMProviderAWS = "aws" + CCMProviderAWS = "aws" + CCMProviderNutanix = "nutanix" ) // +kubebuilder:object:root=true diff --git a/api/v1alpha1/nutanix_clusterconfig_types.go b/api/v1alpha1/nutanix_clusterconfig_types.go index 9e1e975ca..5ca7fe760 100644 --- a/api/v1alpha1/nutanix_clusterconfig_types.go +++ b/api/v1alpha1/nutanix_clusterconfig_types.go @@ -4,6 +4,10 @@ package v1alpha1 import ( + "fmt" + "net/url" + "strconv" + corev1 "k8s.io/api/core/v1" "k8s.io/utils/ptr" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" @@ -99,3 +103,26 @@ func (NutanixPrismCentralEndpointSpec) VariableSchema() clusterv1.VariableSchema }, } } + +//nolint:gocritic // no need for named return values +func (s NutanixPrismCentralEndpointSpec) ParseURL() (string, int32, error) { + var prismCentralURL *url.URL + prismCentralURL, err := url.Parse(s.URL) + if err != nil { + return "", -1, fmt.Errorf("error parsing Prism Central URL: %w", err) + } + + hostname := prismCentralURL.Hostname() + + // return early with the default port if no port is specified + if prismCentralURL.Port() == "" { + return hostname, DefaultPrismCentralPort, nil + } + + port, err := strconv.ParseInt(prismCentralURL.Port(), 10, 32) + if err != nil { + return "", -1, fmt.Errorf("error converting port to int: %w", err) + } + + return hostname, int32(port), nil +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index fe954a016..3bd4d46f9 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -202,7 +202,7 @@ func (in *Addons) DeepCopyInto(out *Addons) { if in.CCM != nil { in, out := &in.CCM, &out.CCM *out = new(CCM) - **out = **in + (*in).DeepCopyInto(*out) } if in.CSIProviders != nil { in, out := &in.CSIProviders, &out.CSIProviders @@ -224,6 +224,11 @@ func (in *Addons) DeepCopy() *Addons { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CCM) DeepCopyInto(out *CCM) { *out = *in + if in.Credentials != nil { + in, out := &in.Credentials, &out.Credentials + *out = new(v1.LocalObjectReference) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CCM. diff --git a/charts/cluster-api-runtime-extensions-nutanix/README.md b/charts/cluster-api-runtime-extensions-nutanix/README.md index 46601fdf4..80c72c84b 100644 --- a/charts/cluster-api-runtime-extensions-nutanix/README.md +++ b/charts/cluster-api-runtime-extensions-nutanix/README.md @@ -32,6 +32,8 @@ A Helm chart for cluster-api-runtime-extensions-nutanix | deployment.replicas | int | `1` | | | env | object | `{}` | | | helmAddonsConfigMap | string | `"default-helm-addons-config"` | | +| hooks.ccm.nutanix.helmAddonStrategy.defaultValueTemplateConfigMap.create | bool | `true` | | +| hooks.ccm.nutanix.helmAddonStrategy.defaultValueTemplateConfigMap.name | string | `"default-nutanix-ccm-helm-values-template"` | | | hooks.clusterAutoscaler.crsStrategy.defaultInstallationConfigMap.name | string | `"cluster-autoscaler"` | | | hooks.clusterAutoscaler.helmAddonStrategy.defaultValueTemplateConfigMap.create | bool | `true` | | | hooks.clusterAutoscaler.helmAddonStrategy.defaultValueTemplateConfigMap.name | string | `"default-cluster-autoscaler-helm-values-template"` | | diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/ccm/nutanix/manifests/helm-addon-installation.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/ccm/nutanix/manifests/helm-addon-installation.yaml new file mode 100644 index 000000000..72c63a63d --- /dev/null +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/ccm/nutanix/manifests/helm-addon-installation.yaml @@ -0,0 +1,20 @@ +# Copyright 2024 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +{{- if .Values.hooks.ccm.nutanix.helmAddonStrategy.defaultValueTemplateConfigMap.create }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: '{{ .Values.hooks.ccm.nutanix.helmAddonStrategy.defaultValueTemplateConfigMap.name }}' +data: + values.yaml: |- + --- + prismCentralEndPoint: {{ `{{ .PrismCentralHost }}` }} + prismCentralPort: {{ `{{ .PrismCentralPort }}` }} + prismCentralInsecure: {{ `{{ .PrismCentralInsecure }}` }} + prismCentralAdditionalTrustBundle: {{ `"{{ or .PrismCentralAdditionalTrustBundle "" }}"` }} + + # The Secret containing the credentials will be created by the handler. + createSecret: false + secretName: nutanix-ccm-credentials +{{- end -}} diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/helm-config.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/helm-config.yaml index 255e8d69f..4dc513cd4 100644 --- a/charts/cluster-api-runtime-extensions-nutanix/templates/helm-config.yaml +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/helm-config.yaml @@ -19,6 +19,10 @@ data: ChartName: node-feature-discovery ChartVersion: 0.15.2 RepositoryURL: https://kubernetes-sigs.github.io/node-feature-discovery/charts + nutanix-ccm: | + ChartName: nutanix-cloud-provider + ChartVersion: 0.3.3 + RepositoryURL: https://nutanix.github.io/helm/ nutanix-snapshot-csi: | ChartName: nutanix-csi-snapshot ChartVersion: v6.3.2 diff --git a/charts/cluster-api-runtime-extensions-nutanix/values.yaml b/charts/cluster-api-runtime-extensions-nutanix/values.yaml index 88ac607ea..eee0993af 100644 --- a/charts/cluster-api-runtime-extensions-nutanix/values.yaml +++ b/charts/cluster-api-runtime-extensions-nutanix/values.yaml @@ -49,6 +49,12 @@ hooks: defaultValueTemplateConfigMap: create: true name: default-nutanix-csi-helm-values-template + ccm: + nutanix: + helmAddonStrategy: + defaultValueTemplateConfigMap: + create: true + name: default-nutanix-ccm-helm-values-template nfd: crsStrategy: defaultInstallationConfigMap: diff --git a/examples/capi-quick-start/nutanix-cluster-calico-crs.yaml b/examples/capi-quick-start/nutanix-cluster-calico-crs.yaml index 6f0758103..19356a534 100644 --- a/examples/capi-quick-start/nutanix-cluster-calico-crs.yaml +++ b/examples/capi-quick-start/nutanix-cluster-calico-crs.yaml @@ -8,226 +8,6 @@ stringData: key: ${NUTANIX_PRISM_ELEMENT_ENDPOINT}:${NUTANIX_PORT}:${NUTANIX_USER}:${NUTANIX_PASSWORD} --- apiVersion: v1 -data: - nutanix-ccm.yaml: | - --- - apiVersion: v1 - kind: ConfigMap - metadata: - name: nutanix-ccm-pc-trusted-ca-bundle - namespace: kube-system - binaryData: - ca.crt: ${NUTANIX_ADDITIONAL_TRUST_BUNDLE=""} - --- - # Source: nutanix-cloud-provider/templates/rbac.yaml - apiVersion: v1 - kind: ServiceAccount - metadata: - name: cloud-controller-manager - namespace: kube-system - --- - # Source: nutanix-cloud-provider/templates/cm.yaml - kind: ConfigMap - apiVersion: v1 - metadata: - name: nutanix-config - namespace: kube-system - data: - nutanix_config.json: |- - { - "prismCentral": { - "address": "${NUTANIX_ENDPOINT}", - "port": ${NUTANIX_PORT=9440}, - "insecure": ${NUTANIX_INSECURE=false}, - "credentialRef": { - "kind": "secret", - "name": "nutanix-creds", - "namespace": "kube-system" - }, - "additionalTrustBundle": { - "kind": "ConfigMap", - "name": "nutanix-ccm-pc-trusted-ca-bundle", - "namespace": "kube-system" - } - }, - "enableCustomLabeling": ${CCM_CUSTOM_LABEL=false}, - "topologyDiscovery": { - "type": "Prism" - } - } - --- - # Source: nutanix-cloud-provider/templates/rbac.yaml - apiVersion: rbac.authorization.k8s.io/v1 - kind: ClusterRole - metadata: - annotations: - rbac.authorization.kubernetes.io/autoupdate: "true" - name: system:cloud-controller-manager - rules: - - apiGroups: - - "" - resources: - - secrets - verbs: - - get - - list - - watch - - apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - apiGroups: - - "" - resources: - - events - verbs: - - create - - patch - - update - - apiGroups: - - "" - resources: - - nodes - verbs: - - "*" - - apiGroups: - - "" - resources: - - nodes/status - verbs: - - patch - - apiGroups: - - "" - resources: - - serviceaccounts - verbs: - - create - - apiGroups: - - "" - resources: - - endpoints - verbs: - - create - - get - - list - - watch - - update - - apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete - --- - # Source: nutanix-cloud-provider/templates/rbac.yaml - kind: ClusterRoleBinding - apiVersion: rbac.authorization.k8s.io/v1 - metadata: - name: system:cloud-controller-manager - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: system:cloud-controller-manager - subjects: - - kind: ServiceAccount - name: cloud-controller-manager - namespace: kube-system - --- - # Source: nutanix-cloud-provider/templates/cloud-provider-nutanix-deployment.yaml - apiVersion: apps/v1 - kind: Deployment - metadata: - labels: - k8s-app: nutanix-cloud-controller-manager - name: nutanix-cloud-controller-manager - namespace: kube-system - spec: - replicas: 1 - selector: - matchLabels: - k8s-app: nutanix-cloud-controller-manager - strategy: - type: Recreate - template: - metadata: - labels: - k8s-app: nutanix-cloud-controller-manager - spec: - hostNetwork: true - priorityClassName: system-cluster-critical - nodeSelector: - node-role.kubernetes.io/control-plane: "" - serviceAccountName: cloud-controller-manager - affinity: - podAntiAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - - labelSelector: - matchLabels: - k8s-app: nutanix-cloud-controller-manager - topologyKey: kubernetes.io/hostname - dnsPolicy: Default - tolerations: - - effect: NoSchedule - key: node-role.kubernetes.io/master - operator: Exists - - effect: NoSchedule - key: node-role.kubernetes.io/control-plane - operator: Exists - - effect: NoExecute - key: node.kubernetes.io/unreachable - operator: Exists - tolerationSeconds: 120 - - effect: NoExecute - key: node.kubernetes.io/not-ready - operator: Exists - tolerationSeconds: 120 - - effect: NoSchedule - key: node.cloudprovider.kubernetes.io/uninitialized - operator: Exists - - effect: NoSchedule - key: node.kubernetes.io/not-ready - operator: Exists - containers: - - image: "${CCM_REPO=ghcr.io/nutanix-cloud-native/cloud-provider-nutanix/controller}:${CCM_TAG=v0.3.2}" - imagePullPolicy: IfNotPresent - name: nutanix-cloud-controller-manager - env: - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - args: - - "--leader-elect=true" - - "--cloud-config=/etc/cloud/nutanix_config.json" - resources: - requests: - cpu: 100m - memory: 50Mi - volumeMounts: - - mountPath: /etc/cloud - name: nutanix-config-volume - readOnly: true - volumes: - - name: nutanix-config-volume - configMap: - name: nutanix-config -kind: ConfigMap -metadata: - labels: - cluster.x-k8s.io/provider: nutanix - name: nutanix-ccm ---- -apiVersion: v1 kind: Secret metadata: labels: @@ -247,59 +27,10 @@ stringData: } ] --- -apiVersion: v1 -kind: Secret -metadata: - labels: - cluster.x-k8s.io/provider: nutanix - name: nutanix-ccm-secret -stringData: - nutanix-ccm-secret.yaml: | - apiVersion: v1 - kind: Secret - metadata: - name: nutanix-creds - namespace: kube-system - stringData: - credentials: | - [ - { - "type": "basic_auth", - "data": { - "prismCentral":{ - "username": "${NUTANIX_USER}", - "password": "${NUTANIX_PASSWORD}" - }, - "prismElements": null - } - } - ] -type: addons.cluster.x-k8s.io/resource-set ---- -apiVersion: addons.cluster.x-k8s.io/v1beta1 -kind: ClusterResourceSet -metadata: - labels: - cluster.x-k8s.io/provider: nutanix - name: nutanix-ccm-crs -spec: - clusterSelector: - matchLabels: - ccm: nutanix - resources: - - kind: ConfigMap - name: nutanix-ccm - - kind: Secret - name: nutanix-ccm-secret - - kind: ConfigMap - name: nutanix-ccm-pc-trusted-ca-bundle - strategy: ApplyOnce ---- apiVersion: cluster.x-k8s.io/v1beta1 kind: Cluster metadata: labels: - ccm: nutanix cluster.x-k8s.io/cluster-name: ${CLUSTER_NAME} cluster.x-k8s.io/provider: nutanix name: ${CLUSTER_NAME} @@ -321,6 +52,9 @@ spec: - name: clusterConfig value: addons: + ccm: + credentials: + name: ${CLUSTER_NAME}-pc-creds clusterAutoscaler: strategy: ClusterResourceSet cni: diff --git a/examples/capi-quick-start/nutanix-cluster-calico-helm-addon.yaml b/examples/capi-quick-start/nutanix-cluster-calico-helm-addon.yaml index e6c77a185..73c60a3c6 100644 --- a/examples/capi-quick-start/nutanix-cluster-calico-helm-addon.yaml +++ b/examples/capi-quick-start/nutanix-cluster-calico-helm-addon.yaml @@ -8,226 +8,6 @@ stringData: key: ${NUTANIX_PRISM_ELEMENT_ENDPOINT}:${NUTANIX_PORT}:${NUTANIX_USER}:${NUTANIX_PASSWORD} --- apiVersion: v1 -data: - nutanix-ccm.yaml: | - --- - apiVersion: v1 - kind: ConfigMap - metadata: - name: nutanix-ccm-pc-trusted-ca-bundle - namespace: kube-system - binaryData: - ca.crt: ${NUTANIX_ADDITIONAL_TRUST_BUNDLE=""} - --- - # Source: nutanix-cloud-provider/templates/rbac.yaml - apiVersion: v1 - kind: ServiceAccount - metadata: - name: cloud-controller-manager - namespace: kube-system - --- - # Source: nutanix-cloud-provider/templates/cm.yaml - kind: ConfigMap - apiVersion: v1 - metadata: - name: nutanix-config - namespace: kube-system - data: - nutanix_config.json: |- - { - "prismCentral": { - "address": "${NUTANIX_ENDPOINT}", - "port": ${NUTANIX_PORT=9440}, - "insecure": ${NUTANIX_INSECURE=false}, - "credentialRef": { - "kind": "secret", - "name": "nutanix-creds", - "namespace": "kube-system" - }, - "additionalTrustBundle": { - "kind": "ConfigMap", - "name": "nutanix-ccm-pc-trusted-ca-bundle", - "namespace": "kube-system" - } - }, - "enableCustomLabeling": ${CCM_CUSTOM_LABEL=false}, - "topologyDiscovery": { - "type": "Prism" - } - } - --- - # Source: nutanix-cloud-provider/templates/rbac.yaml - apiVersion: rbac.authorization.k8s.io/v1 - kind: ClusterRole - metadata: - annotations: - rbac.authorization.kubernetes.io/autoupdate: "true" - name: system:cloud-controller-manager - rules: - - apiGroups: - - "" - resources: - - secrets - verbs: - - get - - list - - watch - - apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - apiGroups: - - "" - resources: - - events - verbs: - - create - - patch - - update - - apiGroups: - - "" - resources: - - nodes - verbs: - - "*" - - apiGroups: - - "" - resources: - - nodes/status - verbs: - - patch - - apiGroups: - - "" - resources: - - serviceaccounts - verbs: - - create - - apiGroups: - - "" - resources: - - endpoints - verbs: - - create - - get - - list - - watch - - update - - apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete - --- - # Source: nutanix-cloud-provider/templates/rbac.yaml - kind: ClusterRoleBinding - apiVersion: rbac.authorization.k8s.io/v1 - metadata: - name: system:cloud-controller-manager - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: system:cloud-controller-manager - subjects: - - kind: ServiceAccount - name: cloud-controller-manager - namespace: kube-system - --- - # Source: nutanix-cloud-provider/templates/cloud-provider-nutanix-deployment.yaml - apiVersion: apps/v1 - kind: Deployment - metadata: - labels: - k8s-app: nutanix-cloud-controller-manager - name: nutanix-cloud-controller-manager - namespace: kube-system - spec: - replicas: 1 - selector: - matchLabels: - k8s-app: nutanix-cloud-controller-manager - strategy: - type: Recreate - template: - metadata: - labels: - k8s-app: nutanix-cloud-controller-manager - spec: - hostNetwork: true - priorityClassName: system-cluster-critical - nodeSelector: - node-role.kubernetes.io/control-plane: "" - serviceAccountName: cloud-controller-manager - affinity: - podAntiAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - - labelSelector: - matchLabels: - k8s-app: nutanix-cloud-controller-manager - topologyKey: kubernetes.io/hostname - dnsPolicy: Default - tolerations: - - effect: NoSchedule - key: node-role.kubernetes.io/master - operator: Exists - - effect: NoSchedule - key: node-role.kubernetes.io/control-plane - operator: Exists - - effect: NoExecute - key: node.kubernetes.io/unreachable - operator: Exists - tolerationSeconds: 120 - - effect: NoExecute - key: node.kubernetes.io/not-ready - operator: Exists - tolerationSeconds: 120 - - effect: NoSchedule - key: node.cloudprovider.kubernetes.io/uninitialized - operator: Exists - - effect: NoSchedule - key: node.kubernetes.io/not-ready - operator: Exists - containers: - - image: "${CCM_REPO=ghcr.io/nutanix-cloud-native/cloud-provider-nutanix/controller}:${CCM_TAG=v0.3.2}" - imagePullPolicy: IfNotPresent - name: nutanix-cloud-controller-manager - env: - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - args: - - "--leader-elect=true" - - "--cloud-config=/etc/cloud/nutanix_config.json" - resources: - requests: - cpu: 100m - memory: 50Mi - volumeMounts: - - mountPath: /etc/cloud - name: nutanix-config-volume - readOnly: true - volumes: - - name: nutanix-config-volume - configMap: - name: nutanix-config -kind: ConfigMap -metadata: - labels: - cluster.x-k8s.io/provider: nutanix - name: nutanix-ccm ---- -apiVersion: v1 kind: Secret metadata: labels: @@ -247,59 +27,10 @@ stringData: } ] --- -apiVersion: v1 -kind: Secret -metadata: - labels: - cluster.x-k8s.io/provider: nutanix - name: nutanix-ccm-secret -stringData: - nutanix-ccm-secret.yaml: | - apiVersion: v1 - kind: Secret - metadata: - name: nutanix-creds - namespace: kube-system - stringData: - credentials: | - [ - { - "type": "basic_auth", - "data": { - "prismCentral":{ - "username": "${NUTANIX_USER}", - "password": "${NUTANIX_PASSWORD}" - }, - "prismElements": null - } - } - ] -type: addons.cluster.x-k8s.io/resource-set ---- -apiVersion: addons.cluster.x-k8s.io/v1beta1 -kind: ClusterResourceSet -metadata: - labels: - cluster.x-k8s.io/provider: nutanix - name: nutanix-ccm-crs -spec: - clusterSelector: - matchLabels: - ccm: nutanix - resources: - - kind: ConfigMap - name: nutanix-ccm - - kind: Secret - name: nutanix-ccm-secret - - kind: ConfigMap - name: nutanix-ccm-pc-trusted-ca-bundle - strategy: ApplyOnce ---- apiVersion: cluster.x-k8s.io/v1beta1 kind: Cluster metadata: labels: - ccm: nutanix cluster.x-k8s.io/cluster-name: ${CLUSTER_NAME} cluster.x-k8s.io/provider: nutanix name: ${CLUSTER_NAME} @@ -321,6 +52,9 @@ spec: - name: clusterConfig value: addons: + ccm: + credentials: + name: ${CLUSTER_NAME}-pc-creds clusterAutoscaler: strategy: HelmAddon cni: diff --git a/examples/capi-quick-start/nutanix-cluster-cilium-crs.yaml b/examples/capi-quick-start/nutanix-cluster-cilium-crs.yaml index 6b34119d0..ebb5825cc 100644 --- a/examples/capi-quick-start/nutanix-cluster-cilium-crs.yaml +++ b/examples/capi-quick-start/nutanix-cluster-cilium-crs.yaml @@ -8,226 +8,6 @@ stringData: key: ${NUTANIX_PRISM_ELEMENT_ENDPOINT}:${NUTANIX_PORT}:${NUTANIX_USER}:${NUTANIX_PASSWORD} --- apiVersion: v1 -data: - nutanix-ccm.yaml: | - --- - apiVersion: v1 - kind: ConfigMap - metadata: - name: nutanix-ccm-pc-trusted-ca-bundle - namespace: kube-system - binaryData: - ca.crt: ${NUTANIX_ADDITIONAL_TRUST_BUNDLE=""} - --- - # Source: nutanix-cloud-provider/templates/rbac.yaml - apiVersion: v1 - kind: ServiceAccount - metadata: - name: cloud-controller-manager - namespace: kube-system - --- - # Source: nutanix-cloud-provider/templates/cm.yaml - kind: ConfigMap - apiVersion: v1 - metadata: - name: nutanix-config - namespace: kube-system - data: - nutanix_config.json: |- - { - "prismCentral": { - "address": "${NUTANIX_ENDPOINT}", - "port": ${NUTANIX_PORT=9440}, - "insecure": ${NUTANIX_INSECURE=false}, - "credentialRef": { - "kind": "secret", - "name": "nutanix-creds", - "namespace": "kube-system" - }, - "additionalTrustBundle": { - "kind": "ConfigMap", - "name": "nutanix-ccm-pc-trusted-ca-bundle", - "namespace": "kube-system" - } - }, - "enableCustomLabeling": ${CCM_CUSTOM_LABEL=false}, - "topologyDiscovery": { - "type": "Prism" - } - } - --- - # Source: nutanix-cloud-provider/templates/rbac.yaml - apiVersion: rbac.authorization.k8s.io/v1 - kind: ClusterRole - metadata: - annotations: - rbac.authorization.kubernetes.io/autoupdate: "true" - name: system:cloud-controller-manager - rules: - - apiGroups: - - "" - resources: - - secrets - verbs: - - get - - list - - watch - - apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - apiGroups: - - "" - resources: - - events - verbs: - - create - - patch - - update - - apiGroups: - - "" - resources: - - nodes - verbs: - - "*" - - apiGroups: - - "" - resources: - - nodes/status - verbs: - - patch - - apiGroups: - - "" - resources: - - serviceaccounts - verbs: - - create - - apiGroups: - - "" - resources: - - endpoints - verbs: - - create - - get - - list - - watch - - update - - apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete - --- - # Source: nutanix-cloud-provider/templates/rbac.yaml - kind: ClusterRoleBinding - apiVersion: rbac.authorization.k8s.io/v1 - metadata: - name: system:cloud-controller-manager - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: system:cloud-controller-manager - subjects: - - kind: ServiceAccount - name: cloud-controller-manager - namespace: kube-system - --- - # Source: nutanix-cloud-provider/templates/cloud-provider-nutanix-deployment.yaml - apiVersion: apps/v1 - kind: Deployment - metadata: - labels: - k8s-app: nutanix-cloud-controller-manager - name: nutanix-cloud-controller-manager - namespace: kube-system - spec: - replicas: 1 - selector: - matchLabels: - k8s-app: nutanix-cloud-controller-manager - strategy: - type: Recreate - template: - metadata: - labels: - k8s-app: nutanix-cloud-controller-manager - spec: - hostNetwork: true - priorityClassName: system-cluster-critical - nodeSelector: - node-role.kubernetes.io/control-plane: "" - serviceAccountName: cloud-controller-manager - affinity: - podAntiAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - - labelSelector: - matchLabels: - k8s-app: nutanix-cloud-controller-manager - topologyKey: kubernetes.io/hostname - dnsPolicy: Default - tolerations: - - effect: NoSchedule - key: node-role.kubernetes.io/master - operator: Exists - - effect: NoSchedule - key: node-role.kubernetes.io/control-plane - operator: Exists - - effect: NoExecute - key: node.kubernetes.io/unreachable - operator: Exists - tolerationSeconds: 120 - - effect: NoExecute - key: node.kubernetes.io/not-ready - operator: Exists - tolerationSeconds: 120 - - effect: NoSchedule - key: node.cloudprovider.kubernetes.io/uninitialized - operator: Exists - - effect: NoSchedule - key: node.kubernetes.io/not-ready - operator: Exists - containers: - - image: "${CCM_REPO=ghcr.io/nutanix-cloud-native/cloud-provider-nutanix/controller}:${CCM_TAG=v0.3.2}" - imagePullPolicy: IfNotPresent - name: nutanix-cloud-controller-manager - env: - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - args: - - "--leader-elect=true" - - "--cloud-config=/etc/cloud/nutanix_config.json" - resources: - requests: - cpu: 100m - memory: 50Mi - volumeMounts: - - mountPath: /etc/cloud - name: nutanix-config-volume - readOnly: true - volumes: - - name: nutanix-config-volume - configMap: - name: nutanix-config -kind: ConfigMap -metadata: - labels: - cluster.x-k8s.io/provider: nutanix - name: nutanix-ccm ---- -apiVersion: v1 kind: Secret metadata: labels: @@ -247,59 +27,10 @@ stringData: } ] --- -apiVersion: v1 -kind: Secret -metadata: - labels: - cluster.x-k8s.io/provider: nutanix - name: nutanix-ccm-secret -stringData: - nutanix-ccm-secret.yaml: | - apiVersion: v1 - kind: Secret - metadata: - name: nutanix-creds - namespace: kube-system - stringData: - credentials: | - [ - { - "type": "basic_auth", - "data": { - "prismCentral":{ - "username": "${NUTANIX_USER}", - "password": "${NUTANIX_PASSWORD}" - }, - "prismElements": null - } - } - ] -type: addons.cluster.x-k8s.io/resource-set ---- -apiVersion: addons.cluster.x-k8s.io/v1beta1 -kind: ClusterResourceSet -metadata: - labels: - cluster.x-k8s.io/provider: nutanix - name: nutanix-ccm-crs -spec: - clusterSelector: - matchLabels: - ccm: nutanix - resources: - - kind: ConfigMap - name: nutanix-ccm - - kind: Secret - name: nutanix-ccm-secret - - kind: ConfigMap - name: nutanix-ccm-pc-trusted-ca-bundle - strategy: ApplyOnce ---- apiVersion: cluster.x-k8s.io/v1beta1 kind: Cluster metadata: labels: - ccm: nutanix cluster.x-k8s.io/cluster-name: ${CLUSTER_NAME} cluster.x-k8s.io/provider: nutanix name: ${CLUSTER_NAME} @@ -321,6 +52,9 @@ spec: - name: clusterConfig value: addons: + ccm: + credentials: + name: ${CLUSTER_NAME}-pc-creds clusterAutoscaler: strategy: ClusterResourceSet cni: diff --git a/examples/capi-quick-start/nutanix-cluster-cilium-helm-addon.yaml b/examples/capi-quick-start/nutanix-cluster-cilium-helm-addon.yaml index 5552bcb62..cedeee8ea 100644 --- a/examples/capi-quick-start/nutanix-cluster-cilium-helm-addon.yaml +++ b/examples/capi-quick-start/nutanix-cluster-cilium-helm-addon.yaml @@ -8,226 +8,6 @@ stringData: key: ${NUTANIX_PRISM_ELEMENT_ENDPOINT}:${NUTANIX_PORT}:${NUTANIX_USER}:${NUTANIX_PASSWORD} --- apiVersion: v1 -data: - nutanix-ccm.yaml: | - --- - apiVersion: v1 - kind: ConfigMap - metadata: - name: nutanix-ccm-pc-trusted-ca-bundle - namespace: kube-system - binaryData: - ca.crt: ${NUTANIX_ADDITIONAL_TRUST_BUNDLE=""} - --- - # Source: nutanix-cloud-provider/templates/rbac.yaml - apiVersion: v1 - kind: ServiceAccount - metadata: - name: cloud-controller-manager - namespace: kube-system - --- - # Source: nutanix-cloud-provider/templates/cm.yaml - kind: ConfigMap - apiVersion: v1 - metadata: - name: nutanix-config - namespace: kube-system - data: - nutanix_config.json: |- - { - "prismCentral": { - "address": "${NUTANIX_ENDPOINT}", - "port": ${NUTANIX_PORT=9440}, - "insecure": ${NUTANIX_INSECURE=false}, - "credentialRef": { - "kind": "secret", - "name": "nutanix-creds", - "namespace": "kube-system" - }, - "additionalTrustBundle": { - "kind": "ConfigMap", - "name": "nutanix-ccm-pc-trusted-ca-bundle", - "namespace": "kube-system" - } - }, - "enableCustomLabeling": ${CCM_CUSTOM_LABEL=false}, - "topologyDiscovery": { - "type": "Prism" - } - } - --- - # Source: nutanix-cloud-provider/templates/rbac.yaml - apiVersion: rbac.authorization.k8s.io/v1 - kind: ClusterRole - metadata: - annotations: - rbac.authorization.kubernetes.io/autoupdate: "true" - name: system:cloud-controller-manager - rules: - - apiGroups: - - "" - resources: - - secrets - verbs: - - get - - list - - watch - - apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - apiGroups: - - "" - resources: - - events - verbs: - - create - - patch - - update - - apiGroups: - - "" - resources: - - nodes - verbs: - - "*" - - apiGroups: - - "" - resources: - - nodes/status - verbs: - - patch - - apiGroups: - - "" - resources: - - serviceaccounts - verbs: - - create - - apiGroups: - - "" - resources: - - endpoints - verbs: - - create - - get - - list - - watch - - update - - apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete - --- - # Source: nutanix-cloud-provider/templates/rbac.yaml - kind: ClusterRoleBinding - apiVersion: rbac.authorization.k8s.io/v1 - metadata: - name: system:cloud-controller-manager - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: system:cloud-controller-manager - subjects: - - kind: ServiceAccount - name: cloud-controller-manager - namespace: kube-system - --- - # Source: nutanix-cloud-provider/templates/cloud-provider-nutanix-deployment.yaml - apiVersion: apps/v1 - kind: Deployment - metadata: - labels: - k8s-app: nutanix-cloud-controller-manager - name: nutanix-cloud-controller-manager - namespace: kube-system - spec: - replicas: 1 - selector: - matchLabels: - k8s-app: nutanix-cloud-controller-manager - strategy: - type: Recreate - template: - metadata: - labels: - k8s-app: nutanix-cloud-controller-manager - spec: - hostNetwork: true - priorityClassName: system-cluster-critical - nodeSelector: - node-role.kubernetes.io/control-plane: "" - serviceAccountName: cloud-controller-manager - affinity: - podAntiAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - - labelSelector: - matchLabels: - k8s-app: nutanix-cloud-controller-manager - topologyKey: kubernetes.io/hostname - dnsPolicy: Default - tolerations: - - effect: NoSchedule - key: node-role.kubernetes.io/master - operator: Exists - - effect: NoSchedule - key: node-role.kubernetes.io/control-plane - operator: Exists - - effect: NoExecute - key: node.kubernetes.io/unreachable - operator: Exists - tolerationSeconds: 120 - - effect: NoExecute - key: node.kubernetes.io/not-ready - operator: Exists - tolerationSeconds: 120 - - effect: NoSchedule - key: node.cloudprovider.kubernetes.io/uninitialized - operator: Exists - - effect: NoSchedule - key: node.kubernetes.io/not-ready - operator: Exists - containers: - - image: "${CCM_REPO=ghcr.io/nutanix-cloud-native/cloud-provider-nutanix/controller}:${CCM_TAG=v0.3.2}" - imagePullPolicy: IfNotPresent - name: nutanix-cloud-controller-manager - env: - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - args: - - "--leader-elect=true" - - "--cloud-config=/etc/cloud/nutanix_config.json" - resources: - requests: - cpu: 100m - memory: 50Mi - volumeMounts: - - mountPath: /etc/cloud - name: nutanix-config-volume - readOnly: true - volumes: - - name: nutanix-config-volume - configMap: - name: nutanix-config -kind: ConfigMap -metadata: - labels: - cluster.x-k8s.io/provider: nutanix - name: nutanix-ccm ---- -apiVersion: v1 kind: Secret metadata: labels: @@ -247,59 +27,10 @@ stringData: } ] --- -apiVersion: v1 -kind: Secret -metadata: - labels: - cluster.x-k8s.io/provider: nutanix - name: nutanix-ccm-secret -stringData: - nutanix-ccm-secret.yaml: | - apiVersion: v1 - kind: Secret - metadata: - name: nutanix-creds - namespace: kube-system - stringData: - credentials: | - [ - { - "type": "basic_auth", - "data": { - "prismCentral":{ - "username": "${NUTANIX_USER}", - "password": "${NUTANIX_PASSWORD}" - }, - "prismElements": null - } - } - ] -type: addons.cluster.x-k8s.io/resource-set ---- -apiVersion: addons.cluster.x-k8s.io/v1beta1 -kind: ClusterResourceSet -metadata: - labels: - cluster.x-k8s.io/provider: nutanix - name: nutanix-ccm-crs -spec: - clusterSelector: - matchLabels: - ccm: nutanix - resources: - - kind: ConfigMap - name: nutanix-ccm - - kind: Secret - name: nutanix-ccm-secret - - kind: ConfigMap - name: nutanix-ccm-pc-trusted-ca-bundle - strategy: ApplyOnce ---- apiVersion: cluster.x-k8s.io/v1beta1 kind: Cluster metadata: labels: - ccm: nutanix cluster.x-k8s.io/cluster-name: ${CLUSTER_NAME} cluster.x-k8s.io/provider: nutanix name: ${CLUSTER_NAME} @@ -321,6 +52,9 @@ spec: - name: clusterConfig value: addons: + ccm: + credentials: + name: ${CLUSTER_NAME}-pc-creds clusterAutoscaler: strategy: HelmAddon cni: diff --git a/hack/addons/kustomize/nutanix-ccm/kustomization.yaml.tmpl b/hack/addons/kustomize/nutanix-ccm/kustomization.yaml.tmpl new file mode 100644 index 000000000..2758a25c6 --- /dev/null +++ b/hack/addons/kustomize/nutanix-ccm/kustomization.yaml.tmpl @@ -0,0 +1,18 @@ +# Copyright 2023 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +metadata: + name: nutanix-ccm-kustomize + +helmCharts: +- name: nutanix-cloud-provider + namespace: kube-system + repo: https://nutanix.github.io/helm/ + releaseName: nutanix-ccm + version: ${NUTANIX_CCM_CHART_VERSION} + valuesFile: helm-values.yaml + includeCRDs: true + skipTests: true diff --git a/hack/examples/bases/nutanix/cluster/kustomization.yaml.tmpl b/hack/examples/bases/nutanix/cluster/kustomization.yaml.tmpl index 73844b6ac..4db679840 100644 --- a/hack/examples/bases/nutanix/cluster/kustomization.yaml.tmpl +++ b/hack/examples/bases/nutanix/cluster/kustomization.yaml.tmpl @@ -29,6 +29,9 @@ patches: - target: kind: Cluster path: ../../../patches/nutanix/csi.yaml +- target: + kind: Cluster + path: ../../../patches/nutanix/ccm.yaml - target: kind: Cluster path: ../../../patches/nutanix/initialize-variables.yaml @@ -38,3 +41,20 @@ patches: kind: ConfigMap name: ".*-pc-trusted-ca-bundle" path: ../../../patches/nutanix/remove-additional-trust-bundle/cm.yaml + +# Remove CCM CRS +- target: + kind: Secret + name: nutanix-ccm-secret + path: ../../../patches/nutanix/remove-ccm/secret.yaml +- target: + kind: ClusterResourceSet + name: nutanix-ccm-crs + path: ../../../patches/nutanix/remove-ccm/crs.yaml +- target: + kind: ConfigMap + name: nutanix-ccm + path: ../../../patches/nutanix/remove-ccm/crs-cm.yaml +- target: + kind: Cluster + path: ../../../patches/nutanix/remove-ccm/cluster-label.yaml diff --git a/hack/examples/patches/nutanix/ccm.yaml b/hack/examples/patches/nutanix/ccm.yaml new file mode 100644 index 000000000..3518d101f --- /dev/null +++ b/hack/examples/patches/nutanix/ccm.yaml @@ -0,0 +1,8 @@ +# Copyright 2024 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +- op: "add" + path: "/spec/topology/variables/0/value/addons/ccm" + value: + credentials: + name: ${CLUSTER_NAME}-pc-creds diff --git a/hack/examples/patches/nutanix/remove-ccm/cluster-label.yaml b/hack/examples/patches/nutanix/remove-ccm/cluster-label.yaml new file mode 100644 index 000000000..ce92832f7 --- /dev/null +++ b/hack/examples/patches/nutanix/remove-ccm/cluster-label.yaml @@ -0,0 +1,5 @@ +# Copyright 2024 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +- op: "remove" + path: "/metadata/labels/ccm" diff --git a/hack/examples/patches/nutanix/remove-ccm/crs-cm.yaml b/hack/examples/patches/nutanix/remove-ccm/crs-cm.yaml new file mode 100644 index 000000000..0721f0c2a --- /dev/null +++ b/hack/examples/patches/nutanix/remove-ccm/crs-cm.yaml @@ -0,0 +1,8 @@ +# Copyright 2024 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +$patch: delete +apiVersion: v1 +kind: ConfigMap +metadata: + name: nutanix-ccm diff --git a/hack/examples/patches/nutanix/remove-ccm/crs.yaml b/hack/examples/patches/nutanix/remove-ccm/crs.yaml new file mode 100644 index 000000000..30b6604af --- /dev/null +++ b/hack/examples/patches/nutanix/remove-ccm/crs.yaml @@ -0,0 +1,8 @@ +# Copyright 2024 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +$patch: delete +apiVersion: addons.cluster.x-k8s.io/v1beta1 +kind: ClusterResourceSet +metadata: + name: nutanix-ccm-crs diff --git a/hack/examples/patches/nutanix/remove-ccm/secret.yaml b/hack/examples/patches/nutanix/remove-ccm/secret.yaml new file mode 100644 index 000000000..c8c852d3c --- /dev/null +++ b/hack/examples/patches/nutanix/remove-ccm/secret.yaml @@ -0,0 +1,8 @@ +# Copyright 2024 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +$patch: delete +apiVersion: v1 +kind: Secret +metadata: + name: nutanix-ccm-secret diff --git a/make/addons.mk b/make/addons.mk index 648fc47d9..36ae1b33e 100644 --- a/make/addons.mk +++ b/make/addons.mk @@ -15,6 +15,8 @@ export AWS_CCM_CHART_VERSION_127 := 0.0.8 export AWS_CCM_VERSION_128 := v1.28.1 export AWS_CCM_CHART_VERSION_128 := 0.0.8 +export NUTANIX_CCM_CHART_VERSION := 0.3.3 + .PHONY: addons.sync addons.sync: $(addprefix update-addon.,calico cilium nfd cluster-autoscaler aws-ebs-csi aws-ccm.127 nutanix-storage-csi aws-ccm.128) diff --git a/pkg/handlers/generic/lifecycle/ccm/aws/handler.go b/pkg/handlers/generic/lifecycle/ccm/aws/handler.go index fa37fd1db..626de46fb 100644 --- a/pkg/handlers/generic/lifecycle/ccm/aws/handler.go +++ b/pkg/handlers/generic/lifecycle/ccm/aws/handler.go @@ -15,6 +15,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/k8s/client" lifecycleutils "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/utils" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/options" @@ -56,6 +57,7 @@ func New( func (a *AWSCCM) Apply( ctx context.Context, cluster *clusterv1.Cluster, + _ *v1alpha1.ClusterConfigSpec, ) error { log := ctrl.LoggerFrom(ctx).WithValues( "cluster", diff --git a/pkg/handlers/generic/lifecycle/ccm/handler.go b/pkg/handlers/generic/lifecycle/ccm/handler.go index 08d8372f0..29a22eb5d 100644 --- a/pkg/handlers/generic/lifecycle/ccm/handler.go +++ b/pkg/handlers/generic/lifecycle/ccm/handler.go @@ -25,7 +25,7 @@ const ( ) type CCMProvider interface { - Apply(context.Context, *clusterv1.Cluster) error + Apply(context.Context, *clusterv1.Cluster, *v1alpha1.ClusterConfigSpec) error } type CCMHandler struct { @@ -78,7 +78,7 @@ func (c *CCMHandler) AfterControlPlaneInitialized( ) resp.SetStatus(runtimehooksv1.ResponseStatusFailure) resp.SetMessage( - fmt.Sprintf("failed to read CCM provider from cluster definition: %v", + fmt.Sprintf("failed to read CCM from cluster definition: %v", err, ), ) @@ -88,17 +88,39 @@ func (c *CCMHandler) AfterControlPlaneInitialized( log.V(4).Info("Skipping CCM handler.") return } + + clusterConfigVar, _, err := variables.Get[v1alpha1.ClusterConfigSpec]( + varMap, + clusterconfig.MetaVariableName, + ) + if err != nil { + log.Error( + err, + "failed to read clusterConfig variable from cluster definition", + ) + resp.SetStatus(runtimehooksv1.ResponseStatusFailure) + resp.SetMessage( + fmt.Sprintf("failed to read clusterConfig variable from cluster definition: %v", + err, + ), + ) + return + } + infraKind := req.Cluster.Spec.InfrastructureRef.Kind log.Info(fmt.Sprintf("finding CCM handler for %s", infraKind)) var handler CCMProvider switch { case strings.Contains(strings.ToLower(infraKind), v1alpha1.CCMProviderAWS): handler = c.ProviderHandler[v1alpha1.CCMProviderAWS] + case strings.Contains(strings.ToLower(infraKind), v1alpha1.CCMProviderNutanix): + handler = c.ProviderHandler[v1alpha1.CCMProviderNutanix] default: log.Info(fmt.Sprintf("No CCM handler provided for infra kind %s", infraKind)) return } - err = handler.Apply(ctx, &req.Cluster) + + err = handler.Apply(ctx, &req.Cluster, &clusterConfigVar) if err != nil { log.Error( err, diff --git a/pkg/handlers/generic/lifecycle/ccm/nutanix/handler.go b/pkg/handlers/generic/lifecycle/ccm/nutanix/handler.go new file mode 100644 index 000000000..93c325d88 --- /dev/null +++ b/pkg/handlers/generic/lifecycle/ccm/nutanix/handler.go @@ -0,0 +1,201 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package nutanix + +import ( + "bytes" + "context" + "errors" + "fmt" + "text/template" + + "github.com/spf13/pflag" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + ctrl "sigs.k8s.io/controller-runtime" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + caaphv1 "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/external/sigs.k8s.io/cluster-api-addon-provider-helm/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/k8s/client" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/config" + lifecycleutils "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/utils" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/options" +) + +const ( + defaultHelmReleaseName = "nutanix-ccm" + defaultHelmReleaseNamespace = "kube-system" + + // This is the name of the Secret on the remote cluster that should match what is defined in Helm values. + //nolint:gosec // Does not contain hard coded credentials. + defaultCredentialsSecretName = "nutanix-ccm-credentials" +) + +var ErrMissingCredentials = errors.New("name of the Secret containing PC credentials must be set") + +type Config struct { + *options.GlobalOptions + + defaultValuesTemplateConfigMapName string +} + +func (c *Config) AddFlags(prefix string, flags *pflag.FlagSet) { + flags.StringVar( + &c.defaultValuesTemplateConfigMapName, + prefix+".default-values-template-configmap-name", + "default-nutanix-ccm-helm-values-template", + "default values ConfigMap name", + ) +} + +type provider struct { + client ctrlclient.Client + config *Config + helmChartInfoGetter *config.HelmChartGetter +} + +func New( + c ctrlclient.Client, + cfg *Config, + helmChartInfoGetter *config.HelmChartGetter, +) *provider { + return &provider{ + client: c, + config: cfg, + helmChartInfoGetter: helmChartInfoGetter, + } +} + +func (p *provider) Apply( + ctx context.Context, + cluster *clusterv1.Cluster, + clusterConfig *v1alpha1.ClusterConfigSpec, +) error { + // No need to check for nil values in the struct, this function will only be called if CCM is not nil + if clusterConfig.Addons.CCM.Credentials == nil { + return ErrMissingCredentials + } + + valuesTemplateConfigMap, err := lifecycleutils.RetrieveValuesTemplateConfigMap( + ctx, + p.client, + p.config.defaultValuesTemplateConfigMapName, + p.config.DefaultsNamespace(), + ) + if err != nil { + return fmt.Errorf( + "failed to retrieve Nutanix CCM installation values template ConfigMap for cluster: %w", + err, + ) + } + + // It's possible to have the credentials Secret be created by the Helm chart. + // However, that would leave the credentials visible in the HelmChartProxy. + // Instead, we'll create the Secret on the remote cluster and reference it in the Helm values. + if clusterConfig.Addons.CCM.Credentials != nil { + key := ctrlclient.ObjectKey{ + Name: defaultCredentialsSecretName, + Namespace: defaultHelmReleaseNamespace, + } + err = lifecycleutils.CopySecretToRemoteCluster( + ctx, + p.client, + clusterConfig.Addons.CCM.Credentials.Name, + key, + cluster, + ) + if err != nil { + return fmt.Errorf( + "error creating Nutanix CCM Credentials Secret on the remote cluster: %w", + err, + ) + } + } + + log := ctrl.LoggerFrom(ctx).WithValues( + "cluster", + ctrlclient.ObjectKeyFromObject(cluster), + ) + helmChart, err := p.helmChartInfoGetter.For(ctx, log, config.NutanixCCM) + if err != nil { + return fmt.Errorf("failed to get values for nutanix-ccm-config %w", err) + } + + values := valuesTemplateConfigMap.Data["values.yaml"] + // The configMap will contain the Helm values, but templated with fields that need to be filled in. + values, err = templateValues(clusterConfig, values) + if err != nil { + return fmt.Errorf("failed to template Helm values read from ConfigMap: %w", err) + } + + hcp := &caaphv1.HelmChartProxy{ + TypeMeta: metav1.TypeMeta{ + APIVersion: caaphv1.GroupVersion.String(), + Kind: "HelmChartProxy", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: cluster.Namespace, + Name: "nutanix-ccm-" + cluster.Name, + }, + Spec: caaphv1.HelmChartProxySpec{ + RepoURL: helmChart.Repository, + ChartName: helmChart.Name, + ClusterSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{clusterv1.ClusterNameLabel: cluster.Name}, + }, + ReleaseNamespace: defaultHelmReleaseNamespace, + ReleaseName: defaultHelmReleaseName, + Version: helmChart.Version, + ValuesTemplate: values, + }, + } + + if err = controllerutil.SetOwnerReference(cluster, hcp, p.client.Scheme()); err != nil { + return fmt.Errorf( + "failed to set owner reference on nutanix-ccm installation HelmChartProxy: %w", + err, + ) + } + + if err = client.ServerSideApply(ctx, p.client, hcp); err != nil { + return fmt.Errorf("failed to apply nutanix-ccm installation HelmChartProxy: %w", err) + } + + return nil +} + +func templateValues(clusterConfig *v1alpha1.ClusterConfigSpec, text string) (string, error) { + helmValuesTemplate, err := template.New("").Parse(text) + if err != nil { + return "", fmt.Errorf("failed to parse Helm values template: %w", err) + } + + type input struct { + PrismCentralHost string + PrismCentralPort int32 + PrismCentralInsecure bool + PrismCentralAdditionalTrustBundle *string + } + + address, port, err := clusterConfig.Nutanix.PrismCentralEndpoint.ParseURL() + if err != nil { + return "", err + } + templateInput := input{ + PrismCentralHost: address, + PrismCentralPort: port, + PrismCentralInsecure: clusterConfig.Nutanix.PrismCentralEndpoint.Insecure, + PrismCentralAdditionalTrustBundle: clusterConfig.Nutanix.PrismCentralEndpoint.AdditionalTrustBundle, + } + + var b bytes.Buffer + err = helmValuesTemplate.Execute(&b, templateInput) + if err != nil { + return "", fmt.Errorf("failed setting PrismCentral configuration in template: %w", err) + } + + return b.String(), nil +} diff --git a/pkg/handlers/generic/lifecycle/ccm/nutanix/handler_test.go b/pkg/handlers/generic/lifecycle/ccm/nutanix/handler_test.go new file mode 100644 index 000000000..5e8869c9a --- /dev/null +++ b/pkg/handlers/generic/lifecycle/ccm/nutanix/handler_test.go @@ -0,0 +1,121 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package nutanix + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/ptr" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" +) + +const ( + in = `--- +prismCentralEndPoint: {{ .PrismCentralHost }} +prismCentralPort: {{ .PrismCentralPort }} +prismCentralInsecure: {{ .PrismCentralInsecure }} +prismCentralAdditionalTrustBundle: "{{ or .PrismCentralAdditionalTrustBundle "" }}" + +# The Secret containing the credentials will be created by the handler. +createSecret: false +secretName: nutanix-ccm-credentials +` + //nolint:lll // just a long string + testCertBundle = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVjekNDQTF1Z0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRUUZBRC4uQWtHQTFVRUJoTUNSMEl4CkV6QVJCZ05WQkFnVENsTnZiV1V0VTNSaGRHVXhGREFTQmdOVkJBb1RDMC4uMEVnVEhSa01UY3dOUVlEClZRUUxFeTVEYkdGemN5QXhJRkIxWW14cFl5QlFjbWx0WVhKNUlFTmxjbi4uWFJwYjI0Z1FYVjBhRzl5CmFYUjVNUlF3RWdZRFZRUURFd3RDWlhOMElFTkJJRXgwWkRBZUZ3MHdNRC4uVFV3TVRaYUZ3MHdNVEF5Ck1EUXhPVFV3TVRaYU1JR0hNUXN3Q1FZRFZRUUdFd0pIUWpFVE1CRUdBMS4uMjl0WlMxVGRHRjBaVEVVCk1CSUdBMVVFQ2hNTFFtVnpkQ0JEUVNCTWRHUXhOekExQmdOVkJBc1RMay4uREVnVUhWaWJHbGpJRkJ5CmFXMWhjbmtnUTJWeWRHbG1hV05oZEdsdmJpQkJkWFJvYjNKcGRIa3hGRC4uQU1UQzBKbGMzUWdRMEVnClRIUmtNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZy4uVHoybXI3U1ppQU1mUXl1CnZCak05T2lKalJhelhCWjFCalA1Q0UvV20vUnI1MDBQUksrTGg5eDVlSi4uL0FOQkUwc1RLMFpzREdNCmFrMm0xZzdvcnVJM2RZM1ZIcUl4RlR6MFRhMWQrTkFqd25MZTRuT2I3Ly4uazA1U2hoQnJKR0JLS3hiCjhuMTA0by81cDhIQXNaUGR6YkZNSXlOakp6Qk0ybzV5NUExM3dpTGl0RS4uZnlZa1F6YXhDdzBBd3psCmtWSGlJeUN1YUY0d2o1NzFwU3prdjZzdis0SURNYlQvWHBDbzhMNndUYS4uc2grZXRMRDZGdFRqWWJiCnJ2WjhSUU0xdGxLZG9NSGcycXhyYUFWKytITkJZbU5XczBkdUVkalViSi4uWEk5VHRuUzRvMUNrajdQCk9mbGppUUlEQVFBQm80SG5NSUhrTUIwR0ExVWREZ1FXQkJROHVyTUNSTC4uNUFrSXA5TkpISnc1VENCCnRBWURWUjBqQklHc01JR3BnQlE4dXJNQ1JMWVlNSFVLVTVBa0lwOU5KSC4uYVNCaWpDQmh6RUxNQWtHCkExVUVCaE1DUjBJeEV6QVJCZ05WQkFnVENsTnZiV1V0VTNSaGRHVXhGRC4uQW9UQzBKbGMzUWdRMEVnClRIUmtNVGN3TlFZRFZRUUxFeTVEYkdGemN5QXhJRkIxWW14cFl5QlFjbS4uRU5sY25ScFptbGpZWFJwCmIyNGdRWFYwYUc5eWFYUjVNUlF3RWdZRFZRUURFd3RDWlhOMElFTkJJRS4uREFNQmdOVkhSTUVCVEFECkFRSC9NQTBHQ1NxR1NJYjNEUUVCQkFVQUE0SUJBUUMxdVlCY3NTbmN3QS4uRENzUWVyNzcyQzJ1Y3BYCnhRVUUvQzBwV1dtNmdEa3dkNUQwRFNNREpScVYvd2VvWjR3QzZCNzNmNS4uYkxoR1lIYVhKZVNENktyClhjb093TGRTYUdtSllzbExLWkIzWklERXAwd1lUR2hndGViNkpGaVR0bi4uc2YyeGRyWWZQQ2lJQjdnCkJNQVY3R3pkYzRWc3BTNmxqckFoYmlpYXdkQmlRbFFtc0JlRno5SmtGNC4uYjNsOEJvR04rcU1hNTZZCkl0OHVuYTJnWTRsMk8vL29uODhyNUlXSmxtMUwwb0E4ZTRmUjJ5ckJIWC4uYWRzR2VGS2t5TnJ3R2kvCjd2UU1mWGRHc1JyWE5HUkduWCt2V0RaMy96V0kwam9EdENrTm5xRXBWbi4uSG9YCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=" +) + +const ( + //nolint:lll // just a long string + expectedWithAdditionalTrustBundle = `--- +prismCentralEndPoint: prism-central.nutanix.com +prismCentralPort: 9440 +prismCentralInsecure: false +prismCentralAdditionalTrustBundle: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVjekNDQTF1Z0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRUUZBRC4uQWtHQTFVRUJoTUNSMEl4CkV6QVJCZ05WQkFnVENsTnZiV1V0VTNSaGRHVXhGREFTQmdOVkJBb1RDMC4uMEVnVEhSa01UY3dOUVlEClZRUUxFeTVEYkdGemN5QXhJRkIxWW14cFl5QlFjbWx0WVhKNUlFTmxjbi4uWFJwYjI0Z1FYVjBhRzl5CmFYUjVNUlF3RWdZRFZRUURFd3RDWlhOMElFTkJJRXgwWkRBZUZ3MHdNRC4uVFV3TVRaYUZ3MHdNVEF5Ck1EUXhPVFV3TVRaYU1JR0hNUXN3Q1FZRFZRUUdFd0pIUWpFVE1CRUdBMS4uMjl0WlMxVGRHRjBaVEVVCk1CSUdBMVVFQ2hNTFFtVnpkQ0JEUVNCTWRHUXhOekExQmdOVkJBc1RMay4uREVnVUhWaWJHbGpJRkJ5CmFXMWhjbmtnUTJWeWRHbG1hV05oZEdsdmJpQkJkWFJvYjNKcGRIa3hGRC4uQU1UQzBKbGMzUWdRMEVnClRIUmtNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZy4uVHoybXI3U1ppQU1mUXl1CnZCak05T2lKalJhelhCWjFCalA1Q0UvV20vUnI1MDBQUksrTGg5eDVlSi4uL0FOQkUwc1RLMFpzREdNCmFrMm0xZzdvcnVJM2RZM1ZIcUl4RlR6MFRhMWQrTkFqd25MZTRuT2I3Ly4uazA1U2hoQnJKR0JLS3hiCjhuMTA0by81cDhIQXNaUGR6YkZNSXlOakp6Qk0ybzV5NUExM3dpTGl0RS4uZnlZa1F6YXhDdzBBd3psCmtWSGlJeUN1YUY0d2o1NzFwU3prdjZzdis0SURNYlQvWHBDbzhMNndUYS4uc2grZXRMRDZGdFRqWWJiCnJ2WjhSUU0xdGxLZG9NSGcycXhyYUFWKytITkJZbU5XczBkdUVkalViSi4uWEk5VHRuUzRvMUNrajdQCk9mbGppUUlEQVFBQm80SG5NSUhrTUIwR0ExVWREZ1FXQkJROHVyTUNSTC4uNUFrSXA5TkpISnc1VENCCnRBWURWUjBqQklHc01JR3BnQlE4dXJNQ1JMWVlNSFVLVTVBa0lwOU5KSC4uYVNCaWpDQmh6RUxNQWtHCkExVUVCaE1DUjBJeEV6QVJCZ05WQkFnVENsTnZiV1V0VTNSaGRHVXhGRC4uQW9UQzBKbGMzUWdRMEVnClRIUmtNVGN3TlFZRFZRUUxFeTVEYkdGemN5QXhJRkIxWW14cFl5QlFjbS4uRU5sY25ScFptbGpZWFJwCmIyNGdRWFYwYUc5eWFYUjVNUlF3RWdZRFZRUURFd3RDWlhOMElFTkJJRS4uREFNQmdOVkhSTUVCVEFECkFRSC9NQTBHQ1NxR1NJYjNEUUVCQkFVQUE0SUJBUUMxdVlCY3NTbmN3QS4uRENzUWVyNzcyQzJ1Y3BYCnhRVUUvQzBwV1dtNmdEa3dkNUQwRFNNREpScVYvd2VvWjR3QzZCNzNmNS4uYkxoR1lIYVhKZVNENktyClhjb093TGRTYUdtSllzbExLWkIzWklERXAwd1lUR2hndGViNkpGaVR0bi4uc2YyeGRyWWZQQ2lJQjdnCkJNQVY3R3pkYzRWc3BTNmxqckFoYmlpYXdkQmlRbFFtc0JlRno5SmtGNC4uYjNsOEJvR04rcU1hNTZZCkl0OHVuYTJnWTRsMk8vL29uODhyNUlXSmxtMUwwb0E4ZTRmUjJ5ckJIWC4uYWRzR2VGS2t5TnJ3R2kvCjd2UU1mWGRHc1JyWE5HUkduWCt2V0RaMy96V0kwam9EdENrTm5xRXBWbi4uSG9YCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=" + +# The Secret containing the credentials will be created by the handler. +createSecret: false +secretName: nutanix-ccm-credentials +` + + expectedWithoutAdditionalTrustBundle = `--- +prismCentralEndPoint: prism-central.nutanix.com +prismCentralPort: 9440 +prismCentralInsecure: true +prismCentralAdditionalTrustBundle: "" + +# The Secret containing the credentials will be created by the handler. +createSecret: false +secretName: nutanix-ccm-credentials +` +) + +func Test_templateValues(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + clusterConfig *v1alpha1.ClusterConfigSpec + in string + expected string + }{ + { + name: "With AdditionalTrustBundle set", + clusterConfig: &v1alpha1.ClusterConfigSpec{ + GenericClusterConfig: v1alpha1.GenericClusterConfig{ + Addons: &v1alpha1.Addons{ + CCM: &v1alpha1.CCM{ + Credentials: &corev1.LocalObjectReference{ + Name: "creds", + }, + }, + }, + }, + Nutanix: &v1alpha1.NutanixSpec{ + PrismCentralEndpoint: v1alpha1.NutanixPrismCentralEndpointSpec{ + URL: fmt.Sprintf("https://prism-central.nutanix.com:%d", v1alpha1.DefaultPrismCentralPort), + AdditionalTrustBundle: ptr.To(testCertBundle), + }, + }, + }, + in: in, + expected: expectedWithAdditionalTrustBundle, + }, + { + name: "Without an AdditionalTrustBundle set", + clusterConfig: &v1alpha1.ClusterConfigSpec{ + GenericClusterConfig: v1alpha1.GenericClusterConfig{ + Addons: &v1alpha1.Addons{ + CCM: &v1alpha1.CCM{ + Credentials: &corev1.LocalObjectReference{ + Name: "creds", + }, + }, + }, + }, + Nutanix: &v1alpha1.NutanixSpec{ + PrismCentralEndpoint: v1alpha1.NutanixPrismCentralEndpointSpec{ + URL: fmt.Sprintf("https://prism-central.nutanix.com:%d", v1alpha1.DefaultPrismCentralPort), + Insecure: true, + }, + }, + }, + in: in, + expected: expectedWithoutAdditionalTrustBundle, + }, + } + for idx := range tests { + tt := tests[idx] + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + out, err := templateValues(tt.clusterConfig, tt.in) + require.NoError(t, err) + assert.Equal(t, tt.expected, out) + }) + } +} diff --git a/pkg/handlers/generic/lifecycle/clusterautoscaler/strategy_helmaddon.go b/pkg/handlers/generic/lifecycle/clusterautoscaler/strategy_helmaddon.go index 7f1ba5469..246c52992 100644 --- a/pkg/handlers/generic/lifecycle/clusterautoscaler/strategy_helmaddon.go +++ b/pkg/handlers/generic/lifecycle/clusterautoscaler/strategy_helmaddon.go @@ -56,7 +56,8 @@ func (s helmAddonStrategy) apply( ctx, s.client, s.config.defaultValuesTemplateConfigMapName, - defaultsNamespace) + defaultsNamespace, + ) if err != nil { return fmt.Errorf( "failed to retrieve cluster-autoscaler installation values template ConfigMap for cluster: %w", diff --git a/pkg/handlers/generic/lifecycle/config/cm.go b/pkg/handlers/generic/lifecycle/config/cm.go index 5bb58caa8..89b44fa20 100644 --- a/pkg/handlers/generic/lifecycle/config/cm.go +++ b/pkg/handlers/generic/lifecycle/config/cm.go @@ -23,6 +23,7 @@ const ( NFD Component = "nfd" NutanixStorageCSI Component = "nutanix-storage-csi" NutanixSnapshotCSI Component = "nutanix-snapshot-csi" + NutanixCCM Component = "nutanix-ccm" ) type HelmChartGetter struct { diff --git a/pkg/handlers/generic/lifecycle/handlers.go b/pkg/handlers/generic/lifecycle/handlers.go index 77a2e7a6e..0452ec4fc 100644 --- a/pkg/handlers/generic/lifecycle/handlers.go +++ b/pkg/handlers/generic/lifecycle/handlers.go @@ -11,6 +11,7 @@ import ( "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/ccm" awsccm "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/ccm/aws" + nutanixccm "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/ccm/nutanix" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/clusterautoscaler" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/cni/calico" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/cni/cilium" @@ -32,6 +33,7 @@ type Handlers struct { ebsConfig *awsebs.AWSEBSConfig nutnaixCSIConfig *nutanixcsi.NutanixCSIConfig awsccmConfig *awsccm.AWSCCMConfig + nutanixCCMConfig *nutanixccm.Config } func New( @@ -48,6 +50,7 @@ func New( ebsConfig: &awsebs.AWSEBSConfig{GlobalOptions: globalOptions}, awsccmConfig: &awsccm.AWSCCMConfig{GlobalOptions: globalOptions}, nutnaixCSIConfig: &nutanixcsi.NutanixCSIConfig{GlobalOptions: globalOptions}, + nutanixCCMConfig: &nutanixccm.Config{GlobalOptions: globalOptions}, } } @@ -66,7 +69,8 @@ func (h *Handlers) AllHandlers(mgr manager.Manager) []handlers.Named { ), } ccmHandlers := map[string]ccm.CCMProvider{ - v1alpha1.CCMProviderAWS: awsccm.New(mgr.GetClient(), h.awsccmConfig), + v1alpha1.CCMProviderAWS: awsccm.New(mgr.GetClient(), h.awsccmConfig), + v1alpha1.CCMProviderNutanix: nutanixccm.New(mgr.GetClient(), h.nutanixCCMConfig, helmChartInfoGetter), } return []handlers.Named{ calico.New(mgr.GetClient(), h.calicoCNIConfig, helmChartInfoGetter), @@ -87,4 +91,5 @@ func (h *Handlers) AddFlags(flagSet *pflag.FlagSet) { h.ebsConfig.AddFlags("awsebs", pflag.CommandLine) h.awsccmConfig.AddFlags("awsccm", pflag.CommandLine) h.nutnaixCSIConfig.AddFlags("nutanixcsi", flagSet) + h.nutanixCCMConfig.AddFlags("nutanixccm", flagSet) } diff --git a/pkg/handlers/nutanix/mutation/prismcentralendpoint/inject.go b/pkg/handlers/nutanix/mutation/prismcentralendpoint/inject.go index 40a5ab537..0e9350477 100644 --- a/pkg/handlers/nutanix/mutation/prismcentralendpoint/inject.go +++ b/pkg/handlers/nutanix/mutation/prismcentralendpoint/inject.go @@ -7,8 +7,6 @@ import ( "context" "encoding/base64" "fmt" - "net/url" - "strconv" "github.com/nutanix-cloud-native/prism-go-client/environment/credentials" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" @@ -101,7 +99,7 @@ func (h *nutanixPrismCentralEndpoint) Mutate( var address string var port int32 - address, port, err = parsePrismCentralURL(prismCentralEndpointVar.URL) + address, port, err = prismCentralEndpointVar.ParseURL() if err != nil { return err } @@ -138,26 +136,3 @@ func (h *nutanixPrismCentralEndpoint) Mutate( }, ) } - -//nolint:gocritic // no need for named return values -func parsePrismCentralURL(in string) (string, int32, error) { - var prismCentralURL *url.URL - prismCentralURL, err := url.Parse(in) - if err != nil { - return "", -1, fmt.Errorf("error parsing Prism Central URL: %w", err) - } - - hostname := prismCentralURL.Hostname() - - // return early with the default port if no port is specified - if prismCentralURL.Port() == "" { - return hostname, v1alpha1.DefaultPrismCentralPort, nil - } - - port, err := strconv.ParseInt(prismCentralURL.Port(), 10, 32) - if err != nil { - return "", -1, fmt.Errorf("error converting port to int: %w", err) - } - - return hostname, int32(port), nil -}