diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index cf0fe6a74..56af76939 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -31,7 +31,7 @@ jobs: uses: actions/checkout@v4 - name: Install devbox - uses: jetpack-io/devbox-install-action@v0.8.0 + uses: jetify-com/devbox-install-action@v0.9.0 with: enable-cache: true @@ -59,6 +59,7 @@ jobs: matrix: provider: - Docker + fail-fast: false uses: ./.github/workflows/e2e.yml with: provider: ${{ matrix.provider }} @@ -72,12 +73,13 @@ jobs: strategy: matrix: module: [api, common, .] + fail-fast: false steps: - name: Check out code uses: actions/checkout@v4 - name: Install devbox - uses: jetpack-io/devbox-install-action@v0.8.0 + uses: jetify-com/devbox-install-action@v0.9.0 with: enable-cache: true @@ -118,7 +120,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - name: Install devbox - uses: jetpack-io/devbox-install-action@v0.8.0 + uses: jetify-com/devbox-install-action@v0.9.0 with: enable-cache: true @@ -152,7 +154,7 @@ jobs: fetch-depth: 0 - name: Install devbox - uses: jetpack-io/devbox-install-action@v0.8.0 + uses: jetify-com/devbox-install-action@v0.9.0 with: enable-cache: true @@ -225,12 +227,17 @@ jobs: strategy: matrix: module: [api, common, .] + fail-fast: false steps: - - name: Check out code + - name: Checkout uses: actions/checkout@v4 + with: + fetch-depth: 0 - - id: govulncheck - uses: golang/govulncheck-action@v1 + - name: Install devbox + uses: jetify-com/devbox-install-action@v0.9.0 with: - work-dir: ${{ matrix.module }} - go-version-file: go.mod + enable-cache: true + + - id: govulncheck + run: devbox run -- make govulncheck.${{ matrix.module }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 91260ca38..23eb02b0b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,7 +35,7 @@ jobs: languages: ${{ matrix.language }} - name: Install devbox - uses: jetpack-io/devbox-install-action@v0.8.0 + uses: jetify-com/devbox-install-action@v0.9.0 with: enable-cache: true diff --git a/.github/workflows/devbox-update.yml b/.github/workflows/devbox-update.yml index 8eec42817..a24925b38 100644 --- a/.github/workflows/devbox-update.yml +++ b/.github/workflows/devbox-update.yml @@ -27,7 +27,7 @@ jobs: uses: actions/checkout@v4 - name: Install devbox - uses: jetpack-io/devbox-install-action@v0.8.0 + uses: jetify-com/devbox-install-action@v0.9.0 with: enable-cache: true diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 16b4fda00..cf6cb77e0 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -22,7 +22,7 @@ jobs: fetch-depth: 0 - name: Install devbox - uses: jetpack-io/devbox-install-action@v0.8.0 + uses: jetify-com/devbox-install-action@v0.9.0 with: enable-cache: true diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index 26a262599..4c384f963 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -39,7 +39,7 @@ jobs: uses: actions/checkout@v4 - name: Install devbox - uses: jetpack-io/devbox-install-action@v0.8.0 + uses: jetify-com/devbox-install-action@v0.9.0 with: enable-cache: true diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index 128142b58..4f559cab9 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -28,7 +28,7 @@ jobs: fetch-depth: 0 - name: Install devbox - uses: jetpack-io/devbox-install-action@v0.8.0 + uses: jetify-com/devbox-install-action@v0.9.0 with: enable-cache: true diff --git a/.gitignore b/.gitignore index 05f2f7183..a50624695 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,4 @@ node_modules/ /cluster.yaml /runtime-extension-components.yaml /_artifacts/ -test/e2e/config/cre-envsubst.yaml +test/e2e/config/caren-envsubst.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e9fc7bedb..643fdf161 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,6 +34,12 @@ repos: language: system files: "^(hack/addons/|charts/cluster-api-runtime-extensions-nutanix/templates/.+/manifests/|make/addons.mk$)" pass_filenames: false + - id: addons-configmap + name: addons-configmap + entry: make generate-helm-configmap + language: system + files: "^(hack/addons/|charts/cluster-api-runtime-extensions-nutanix/templates/.+|make/addons.mk$)" + pass_filenames: false - repo: https://github.com/tekwizely/pre-commit-golang rev: v1.0.0-rc.1 hooks: @@ -117,7 +123,7 @@ repos: name: License headers - YAML and Makefiles stages: [commit] files: (^Makefile|\.(ya?ml|mk))$ - exclude: ^(pkg/handlers/.+/embedded|examples|charts/cluster-api-runtime-extensions-nutanix/defaultclusterclasses)/.+\.ya?ml|docs/static/helm/index\.yaml$ + exclude: ^(pkg/handlers/.+/embedded|examples|charts/cluster-api-runtime-extensions-nutanix/defaultclusterclasses)/.+\.ya?ml|docs/static/helm/index\.yaml|charts/cluster-api-runtime-extensions-nutanix/templates/helm-config.yaml$ args: - --license-filepath - hack/license-header.txt diff --git a/README.md b/README.md index e391b3025..6d20ce2a3 100644 --- a/README.md +++ b/README.md @@ -44,12 +44,13 @@ For example, the following command will create a Docker cluster with Cilium CNI ```shell export CLUSTER_NAME=docker-cluster-cilium-helm-addon export CLUSTER_FILE=examples/capi-quick-start/docker-cluster-cilium-helm-addon.yaml +export KUBERNETES_VERSION=v1.28.7 ``` ```shell clusterctl generate cluster ${CLUSTER_NAME} \ --from ${CLUSTER_FILE} \ - --kubernetes-version v1.29.1 \ + --kubernetes-version ${KUBERNETES_VERSION} \ --worker-machine-count 1 | \ kubectl apply --server-side -f - ``` diff --git a/api/go.mod b/api/go.mod index d41dcf73a..bc15c10d6 100644 --- a/api/go.mod +++ b/api/go.mod @@ -34,7 +34,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pkg/errors v0.9.1 // indirect - golang.org/x/net v0.19.0 // indirect + golang.org/x/net v0.24.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/api/go.sum b/api/go.sum index 1aa5ecc1d..0974afbd1 100644 --- a/api/go.sum +++ b/api/go.sum @@ -501,8 +501,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -583,8 +583,8 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/api/openapi/patterns/anchored.go b/api/openapi/patterns/anchored.go index 201dba17c..8f4a36dfe 100644 --- a/api/openapi/patterns/anchored.go +++ b/api/openapi/patterns/anchored.go @@ -6,3 +6,7 @@ package patterns func Anchored(pattern string) string { return "^" + pattern + "$" } + +func HTTPSURL() string { + return `^https://` +} diff --git a/api/v1alpha1/addon_types.go b/api/v1alpha1/addon_types.go index 7a0863bbf..6cafdfdb1 100644 --- a/api/v1alpha1/addon_types.go +++ b/api/v1alpha1/addon_types.go @@ -4,11 +4,25 @@ package v1alpha1 import ( + corev1 "k8s.io/api/core/v1" + storagev1 "k8s.io/api/storage/v1" + "k8s.io/utils/ptr" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/variables" ) +const ( + AddonStrategyClusterResourceSet AddonStrategy = "ClusterResourceSet" + AddonStrategyHelmAddon AddonStrategy = "HelmAddon" + VolumeBindingImmediate = storagev1.VolumeBindingImmediate + VolumeBindingWaitForFirstConsumer = storagev1.VolumeBindingWaitForFirstConsumer + + VolumeReclaimRecycle = corev1.PersistentVolumeReclaimRecycle + VolumeReclaimDelete = corev1.PersistentVolumeReclaimDelete + VolumeReclaimRetain = corev1.PersistentVolumeReclaimRetain +) + type Addons struct { // +optional CNI *CNI `json:"cni,omitempty"` @@ -20,10 +34,10 @@ type Addons struct { ClusterAutoscaler *ClusterAutoscaler `json:"clusterAutoscaler,omitempty"` // +optional - CPI *CPI `json:"cpi,omitempty"` + CCM *CCM `json:"ccm,omitempty"` // +optional - CSIProviders *CSIProviders `json:"csi,omitempty"` + CSIProviders *CSI `json:"csi,omitempty"` } func (Addons) VariableSchema() clusterv1.VariableSchema { @@ -35,8 +49,8 @@ func (Addons) VariableSchema() clusterv1.VariableSchema { "cni": CNI{}.VariableSchema().OpenAPIV3Schema, "nfd": NFD{}.VariableSchema().OpenAPIV3Schema, "clusterAutoscaler": ClusterAutoscaler{}.VariableSchema().OpenAPIV3Schema, - "csi": CSIProviders{}.VariableSchema().OpenAPIV3Schema, - "cpi": CPI{}.VariableSchema().OpenAPIV3Schema, + "csi": CSI{}.VariableSchema().OpenAPIV3Schema, + "ccm": CCM{}.VariableSchema().OpenAPIV3Schema, }, }, } @@ -44,11 +58,6 @@ func (Addons) VariableSchema() clusterv1.VariableSchema { type AddonStrategy string -const ( - AddonStrategyClusterResourceSet AddonStrategy = "ClusterResourceSet" - AddonStrategyHelmAddon AddonStrategy = "HelmAddon" -) - // CNI required for providing CNI configuration. type CNI struct { // +optional @@ -134,52 +143,199 @@ func (ClusterAutoscaler) VariableSchema() clusterv1.VariableSchema { } } -type CSIProviders struct { +type DefaultStorage struct { + ProviderName string `json:"providerName"` + StorageClassConfigName string `json:"storageClassConfigName"` +} + +type CSI struct { // +optional Providers []CSIProvider `json:"providers,omitempty"` // +optional - DefaultClassName string `json:"defaultClassName,omitempty"` + DefaultStorage *DefaultStorage `json:"defaultStorage,omitempty"` } type CSIProvider struct { - Name string `json:"name,omitempty"` + Name string `json:"name"` + + // +optional + StorageClassConfig []StorageClassConfig `json:"storageClassConfig,omitempty"` + + Strategy AddonStrategy `json:"strategy"` + + // +optional + Credentials *corev1.LocalObjectReference `json:"credentials,omitempty"` +} + +type StorageClassConfig struct { + Name string `json:"name"` + + // +optional + Parameters map[string]string `json:"parameters,omitempty"` + + // +optional + ReclaimPolicy corev1.PersistentVolumeReclaimPolicy `json:"reclaimPolicy,omitempty"` + + // +optional + VolumeBindingMode storagev1.VolumeBindingMode `json:"volumeBindingMode,omitempty"` + + // +optional + AllowExpansion bool `json:"allowExpansion,omitempty"` } -func (CSIProviders) VariableSchema() clusterv1.VariableSchema { - supportedCSIProviders := []string{CSIProviderAWSEBS} +func (StorageClassConfig) VariableSchema() clusterv1.VariableSchema { + supportedReclaimPolicies := []string{ + string(VolumeReclaimRecycle), + string(VolumeReclaimDelete), + string(VolumeReclaimRetain), + } + supportedBindingModes := []string{ + string(VolumeBindingImmediate), + string(VolumeBindingWaitForFirstConsumer), + } return clusterv1.VariableSchema{ OpenAPIV3Schema: clusterv1.JSONSchemaProps{ - Type: "object", + Type: "object", + Required: []string{"name"}, Properties: map[string]clusterv1.JSONSchemaProps{ - "providers": { - Type: "array", - Items: &clusterv1.JSONSchemaProps{ - Type: "object", - Properties: map[string]clusterv1.JSONSchemaProps{ - "name": { - Type: "string", - Enum: variables.MustMarshalValuesToEnumJSON( - supportedCSIProviders...), - }, + "name": { + Type: "string", + Description: "Name of storage class config.", + }, + "parameters": { + Type: "object", + Description: "Parameters passed into the storage class object.", + AdditionalProperties: &clusterv1.JSONSchemaProps{ + Type: "string", + }, + }, + "reclaimPolicy": { + Type: "string", + Enum: variables.MustMarshalValuesToEnumJSON(supportedReclaimPolicies...), + Default: variables.MustMarshal(VolumeReclaimDelete), + }, + "volumeBindingMode": { + Type: "string", + Enum: variables.MustMarshalValuesToEnumJSON(supportedBindingModes...), + Default: variables.MustMarshal(VolumeBindingWaitForFirstConsumer), + }, + "allowExpansion": { + Type: "boolean", + Default: variables.MustMarshal(false), + Description: "If the storage class should allow volume expanding", + }, + }, + }, + } +} + +func (CSIProvider) VariableSchema() clusterv1.VariableSchema { + supportedCSIProviders := []string{CSIProviderAWSEBS, CSIProviderNutanix} + return clusterv1.VariableSchema{ + OpenAPIV3Schema: clusterv1.JSONSchemaProps{ + Type: "object", + Required: []string{"name", "strategy"}, + Properties: map[string]clusterv1.JSONSchemaProps{ + "name": { + Description: "Name of the CSI Provider", + Type: "string", + Enum: variables.MustMarshalValuesToEnumJSON( + supportedCSIProviders...), + }, + "strategy": { + Description: "Addon strategy used to deploy the CSI provider to the workload cluster", + Type: "string", + Enum: variables.MustMarshalValuesToEnumJSON( + AddonStrategyClusterResourceSet, + AddonStrategyHelmAddon, + ), + }, + "credentials": { + Type: "object", + Description: "The reference to any secret used by the CSI Provider.", + Properties: map[string]clusterv1.JSONSchemaProps{ + "name": { + Type: "string", }, }, }, - "defaultClassName": { - Type: "string", - Enum: variables.MustMarshalValuesToEnumJSON(supportedCSIProviders...), + "storageClassConfig": { + Type: "array", + Items: ptr.To(StorageClassConfig{}.VariableSchema().OpenAPIV3Schema), }, }, }, } } -// CPI tells us to enable or disable the cloud provider interface. -type CPI struct{} +func (DefaultStorage) VariableSchema() clusterv1.VariableSchema { + supportedCSIProviders := []string{CSIProviderAWSEBS, CSIProviderNutanix} + return clusterv1.VariableSchema{ + OpenAPIV3Schema: clusterv1.JSONSchemaProps{ + Type: "object", + Description: "A tuple of provider name and storage class ", + Required: []string{"providerName", "storageClassConfigName"}, + Properties: map[string]clusterv1.JSONSchemaProps{ + "providerName": { + Type: "string", + Description: "Name of the CSI Provider for the default storage class", + Enum: variables.MustMarshalValuesToEnumJSON( + supportedCSIProviders..., + ), + }, + "storageClassConfigName": { + Type: "string", + Description: "Name of storage class config in any of the provider objects", + }, + }, + }, + } +} -func (CPI) VariableSchema() clusterv1.VariableSchema { +func (CSI) VariableSchema() clusterv1.VariableSchema { return clusterv1.VariableSchema{ OpenAPIV3Schema: clusterv1.JSONSchemaProps{ Type: "object", + Properties: map[string]clusterv1.JSONSchemaProps{ + "providers": { + Type: "array", + Items: ptr.To(CSIProvider{}.VariableSchema().OpenAPIV3Schema), + }, + "defaultStorage": DefaultStorage{}.VariableSchema().OpenAPIV3Schema, + }, + }, + } +} + +// CCM tells us to enable or disable the cloud provider interface. +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 17e5b119e..f19b35ea8 100644 --- a/api/v1alpha1/clusterconfig_types.go +++ b/api/v1alpha1/clusterconfig_types.go @@ -12,25 +12,32 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/openapi/patterns" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/variables" ) +type StorageProvisioner string + const ( - CNIProviderCalico = "Calico" - CNIProviderCilium = "Cilium" + CNIProviderCalico = "Calico" + CNIProviderCilium = "Cilium" + AWSEBSProvisioner StorageProvisioner = "ebs.csi.aws.com" + NutanixProvisioner StorageProvisioner = "csi.nutanix.com" - CSIProviderAWSEBS = "aws-ebs" + CSIProviderAWSEBS = "aws-ebs" + CSIProviderNutanix = "nutanix" - CPIProviderAWS = "aws" + CCMProviderAWS = "aws" + CCMProviderNutanix = "nutanix" ) -//+kubebuilder:object:root=true +// +kubebuilder:object:root=true // ClusterConfig is the Schema for the clusterconfigs API. type ClusterConfig struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - //+optional + // +optional Spec ClusterConfigSpec `json:"spec,omitempty"` } @@ -40,10 +47,12 @@ type ClusterConfigSpec struct { AWS *AWSSpec `json:"aws,omitempty"` // +optional Docker *DockerSpec `json:"docker,omitempty"` + // +optional + Nutanix *NutanixSpec `json:"nutanix,omitempty"` GenericClusterConfig `json:",inline"` - //+optional + // +optional ControlPlane *NodeConfigSpec `json:"controlPlane,omitempty"` } @@ -71,11 +80,29 @@ func (s ClusterConfigSpec) VariableSchema() clusterv1.VariableSchema { //nolint: }.VariableSchema().OpenAPIV3Schema, }, ) + case s.Nutanix != nil: + maps.Copy( + clusterConfigProps.OpenAPIV3Schema.Properties, + map[string]clusterv1.JSONSchemaProps{ + NutanixVariableName: NutanixSpec{}.VariableSchema().OpenAPIV3Schema, + "controlPlane": NodeConfigSpec{ + Nutanix: &NutanixNodeSpec{}, + }.VariableSchema().OpenAPIV3Schema, + }, + ) } return clusterConfigProps } +func (s *ClusterConfigSpec) ToClusterVariable(name string) (*clusterv1.ClusterVariable, error) { + return variables.MarshalToClusterVariable(name, s) +} + +func (s *ClusterConfigSpec) FromClusterVariable(clusterVariable *clusterv1.ClusterVariable) error { + return variables.UnmarshalClusterVariable(clusterVariable, s) +} + // GenericClusterConfig defines the generic cluster configdesired. type GenericClusterConfig struct { // +optional @@ -98,6 +125,9 @@ type GenericClusterConfig struct { // +optional Addons *Addons `json:"addons,omitempty"` + + // +optional + Users Users `json:"users,omitempty"` } func (s GenericClusterConfig) VariableSchema() clusterv1.VariableSchema { //nolint:gocritic,lll // Passed by value for no potential side-effect. @@ -116,6 +146,7 @@ func (s GenericClusterConfig) VariableSchema() clusterv1.VariableSchema { //noli OpenAPIV3Schema, "imageRegistries": ImageRegistries{}.VariableSchema().OpenAPIV3Schema, "globalImageRegistryMirror": GlobalImageRegistryMirror{}.VariableSchema().OpenAPIV3Schema, + "users": Users{}.VariableSchema().OpenAPIV3Schema, }, }, } @@ -140,11 +171,11 @@ func (v KubernetesImageRepository) String() string { type Image struct { // Repository is used to override the image repository to pull from. - //+optional + // +optional Repository string `json:"repository,omitempty"` // Tag is used to override the default image tag. - //+optional + // +optional Tag string `json:"tag,omitempty"` } @@ -170,7 +201,7 @@ func (Image) VariableSchema() clusterv1.VariableSchema { type Etcd struct { // Image required for overriding etcd image details. - //+optional + // +optional Image *Image `json:"image,omitempty"` } @@ -344,6 +375,83 @@ func (ImageRegistries) VariableSchema() clusterv1.VariableSchema { } } +type Users []User + +func (Users) VariableSchema() clusterv1.VariableSchema { + return clusterv1.VariableSchema{ + OpenAPIV3Schema: clusterv1.JSONSchemaProps{ + Description: "Users to add to the machine", + Type: "array", + Items: ptr.To(User{}.VariableSchema().OpenAPIV3Schema), + }, + } +} + +// User defines the input for a generated user in cloud-init. +type User struct { + // Name specifies the user name. + Name string `json:"name"` + + // HashedPassword is a hashed password for the user, formatted as described + // by the crypt(5) man page. See your distribution's documentation for + // instructions to create a hashed password. + // An empty string is not marshalled, because it is not a valid value. + // +optional + HashedPassword string `json:"hashedPassword,omitempty"` + + // SSHAuthorizedKeys is a list of public SSH keys to write to the + // machine. Use the corresponding private SSH keys to authenticate. See SSH + // documentation for instructions to create a key pair. + // +optional + SSHAuthorizedKeys []string `json:"sshAuthorizedKeys,omitempty"` + + // Sudo is a sudo user specification, formatted as described in the sudo + // documentation. + // An empty string is not marshalled, because it is not a valid value. + // +optional + Sudo string `json:"sudo,omitempty"` +} + +func (User) VariableSchema() clusterv1.VariableSchema { + return clusterv1.VariableSchema{ + OpenAPIV3Schema: clusterv1.JSONSchemaProps{ + Type: "object", + Required: []string{"name"}, + Properties: map[string]clusterv1.JSONSchemaProps{ + "name": { + Description: "The username", + Type: "string", + }, + "hashedPassword": { + Description: "The hashed password for the user. Must be in the format of some hash function supported by the OS.", + Type: "string", + // The crypt (5) man page lists regexes for supported hash + // functions. We could validate input against a set of + // regexes, but because the set may be different from the + // set supported by the chosen OS, we might return a false + // negative or positive. For this reason, we do not validate + // the input. + }, + "sshAuthorizedKeys": { + Description: "A list of SSH authorized keys for this user", + Type: "array", + Items: &clusterv1.JSONSchemaProps{ + // No description, because the one for the parent array is enough. + Type: "string", + }, + }, + "sudo": { + Description: "The sudo rule that applies to this user", + Type: "string", + // A sudo rule is defined using an EBNF grammar, and must be + // parsed to be validated. We have decided to not integrate + // a sudo rule parser, so we do not validate the input. + }, + }, + }, + } +} + func init() { SchemeBuilder.Register(&ClusterConfig{}) } diff --git a/api/v1alpha1/common_types.go b/api/v1alpha1/common_types.go index fab81e9f8..d2e3244a3 100644 --- a/api/v1alpha1/common_types.go +++ b/api/v1alpha1/common_types.go @@ -3,6 +3,17 @@ package v1alpha1 +import ( + "k8s.io/utils/ptr" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/variables" +) + +const ( + APIServerPort = 6443 +) + // ObjectMeta is metadata that all persisted resources must have, which includes all objects // users must create. This is a copy of customizable fields from metav1.ObjectMeta. // @@ -23,3 +34,29 @@ type ObjectMeta struct { // +optional Annotations map[string]string `json:"annotations,omitempty"` } + +type ControlPlaneEndpointSpec clusterv1.APIEndpoint + +func (ControlPlaneEndpointSpec) VariableSchema() clusterv1.VariableSchema { + return clusterv1.VariableSchema{ + OpenAPIV3Schema: clusterv1.JSONSchemaProps{ + Description: "Kubernetes control-plane endpoint configuration", + Type: "object", + Properties: map[string]clusterv1.JSONSchemaProps{ + "host": { + Description: "host ip/fqdn for control plane API Server", + Type: "string", + MinLength: ptr.To[int64](1), + }, + "port": { + Description: "port for control plane API Server", + Type: "integer", + Default: variables.MustMarshal(APIServerPort), + Minimum: ptr.To[int64](1), + Maximum: ptr.To[int64](65535), + }, + }, + Required: []string{"host", "port"}, + }, + } +} diff --git a/api/v1alpha1/constants.go b/api/v1alpha1/constants.go index 8dd88b4a8..de4b8b1f6 100644 --- a/api/v1alpha1/constants.go +++ b/api/v1alpha1/constants.go @@ -12,4 +12,6 @@ const ( ClusterAutoscalerVariableName = "clusterAutoscaler" // AWSVariableName is the AWS config patch variable name. AWSVariableName = "aws" + // NutanixVariableName is the Nutanix config patch variable name. + NutanixVariableName = "nutanix" ) diff --git a/api/v1alpha1/node_types.go b/api/v1alpha1/node_types.go index b9b678866..f1046445e 100644 --- a/api/v1alpha1/node_types.go +++ b/api/v1alpha1/node_types.go @@ -8,16 +8,18 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/variables" ) -//+kubebuilder:object:root=true +// +kubebuilder:object:root=true // NodeConfig is the Schema for the workerconfigs API. type NodeConfig struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - //+optional + // +optional Spec NodeConfigSpec `json:"spec,omitempty"` } @@ -29,6 +31,8 @@ type NodeConfigSpec struct { AWS *AWSNodeSpec `json:"aws,omitempty"` // +optional Docker *DockerNodeSpec `json:"docker,omitempty"` + // +optional + Nutanix *NutanixNodeSpec `json:"nutanix,omitempty"` } func (s NodeConfigSpec) VariableSchema() clusterv1.VariableSchema { @@ -49,11 +53,26 @@ func (s NodeConfigSpec) VariableSchema() clusterv1.VariableSchema { "docker": DockerNodeSpec{}.VariableSchema().OpenAPIV3Schema, }, ) + case s.Nutanix != nil: + maps.Copy( + nodeConfigProps.OpenAPIV3Schema.Properties, + map[string]clusterv1.JSONSchemaProps{ + "nutanix": NutanixNodeSpec{}.VariableSchema().OpenAPIV3Schema, + }, + ) } return nodeConfigProps } +func (s *NodeConfigSpec) ToClusterVariable(name string) (*clusterv1.ClusterVariable, error) { + return variables.MarshalToClusterVariable(name, s) +} + +func (s *NodeConfigSpec) FromClusterVariable(clusterVariable *clusterv1.ClusterVariable) error { + return variables.UnmarshalClusterVariable(clusterVariable, s) +} + type GenericNodeConfig struct{} func (GenericNodeConfig) VariableSchema() clusterv1.VariableSchema { diff --git a/api/v1alpha1/nutanix_clusterconfig_types.go b/api/v1alpha1/nutanix_clusterconfig_types.go new file mode 100644 index 000000000..5ca7fe760 --- /dev/null +++ b/api/v1alpha1/nutanix_clusterconfig_types.go @@ -0,0 +1,128 @@ +// Copyright 2024 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +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" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/openapi/patterns" +) + +const ( + DefaultPrismCentralPort = 9440 +) + +// NutanixSpec defines the desired state of NutanixCluster. +type NutanixSpec struct { + // ControlPlaneEndpoint represents the endpoint used to communicate with the control plane. + // host can be either DNS name or ip address + ControlPlaneEndpoint clusterv1.APIEndpoint `json:"controlPlaneEndpoint"` + + // Nutanix Prism Central endpoint configuration. + PrismCentralEndpoint NutanixPrismCentralEndpointSpec `json:"prismCentralEndpoint"` +} + +func (NutanixSpec) VariableSchema() clusterv1.VariableSchema { + return clusterv1.VariableSchema{ + OpenAPIV3Schema: clusterv1.JSONSchemaProps{ + Description: "Nutanix cluster configuration", + Type: "object", + Properties: map[string]clusterv1.JSONSchemaProps{ + "controlPlaneEndpoint": ControlPlaneEndpointSpec{}.VariableSchema().OpenAPIV3Schema, + "prismCentralEndpoint": NutanixPrismCentralEndpointSpec{}.VariableSchema().OpenAPIV3Schema, + }, + }, + } +} + +type NutanixPrismCentralEndpointSpec struct { + // The URL of Nutanix Prism Central, can be DNS name or an IP address + URL string `json:"url"` + + // use insecure connection to Prism Central endpoint + // +optional + Insecure bool `json:"insecure"` + + // A base64 PEM encoded x509 cert for the RootCA that was used to create + // the certificate for a Prism Central that uses certificates that were issued by a non-publicly trusted RootCA. + // The trust bundle is added to the cert pool used to authenticate the TLS connection to the Prism Central. + // +optional + AdditionalTrustBundle *string `json:"additionalTrustBundle,omitempty"` + + // A reference to the Secret for credential information for the target Prism Central instance + Credentials corev1.LocalObjectReference `json:"credentials"` +} + +func (NutanixPrismCentralEndpointSpec) VariableSchema() clusterv1.VariableSchema { + return clusterv1.VariableSchema{ + OpenAPIV3Schema: clusterv1.JSONSchemaProps{ + Description: "Nutanix Prism Central endpoint configuration", + Type: "object", + Properties: map[string]clusterv1.JSONSchemaProps{ + "url": { + Description: "The URL of Nutanix Prism Central, can be DNS name or an IP address", + Type: "string", + MinLength: ptr.To[int64](1), + Format: "uri", + Pattern: patterns.HTTPSURL(), + }, + "insecure": { + Description: "Use insecure connection to Prism Central endpoint", + Type: "boolean", + }, + "additionalTrustBundle": { + Description: "A base64 PEM encoded x509 cert for the RootCA " + + "that was used to create the certificate for a Prism Central that uses certificates " + + "that were issued by a non-publicly trusted RootCA." + + "The trust bundle is added to the cert pool used to authenticate the TLS connection " + + "to the Prism Central.", + Type: "string", + Format: "byte", + }, + "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"}, + }, + }, + Required: []string{"url", "credentials"}, + }, + } +} + +//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/nutanix_node_types.go b/api/v1alpha1/nutanix_node_types.go new file mode 100644 index 000000000..8190c395a --- /dev/null +++ b/api/v1alpha1/nutanix_node_types.go @@ -0,0 +1,177 @@ +// Copyright 2024 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/api/resource" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + + capxv1 "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/external/github.com/nutanix-cloud-native/cluster-api-provider-nutanix/api/v1beta1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/variables" +) + +type NutanixNodeSpec struct { + MachineDetails NutanixMachineDetails `json:"machineDetails"` +} + +func (NutanixNodeSpec) VariableSchema() clusterv1.VariableSchema { + return clusterv1.VariableSchema{ + OpenAPIV3Schema: clusterv1.JSONSchemaProps{ + Description: "Nutanix Node configuration", + Type: "object", + Properties: map[string]clusterv1.JSONSchemaProps{ + "machineDetails": NutanixMachineDetails{}.VariableSchema().OpenAPIV3Schema, + }, + Required: []string{"machineDetails"}, + }, + } +} + +type NutanixMachineDetails struct { + // vcpusPerSocket is the number of vCPUs per socket of the VM + VCPUsPerSocket int32 `json:"vcpusPerSocket"` + + // vcpuSockets is the number of vCPU sockets of the VM + VCPUSockets int32 `json:"vcpuSockets"` + + // memorySize is the memory size (in Quantity format) of the VM + MemorySize resource.Quantity `json:"memorySize"` + + // image is to identify the rhcos image uploaded to the Prism Central (PC) + // The image identifier (uuid or name) can be obtained from the Prism Central console + // or using the prism_central API. + Image NutanixResourceIdentifier `json:"image"` + + // cluster is to identify the cluster (the Prism Element under management + // of the Prism Central), in which the Machine's VM will be created. + // The cluster identifier (uuid or name) can be obtained from the Prism Central console + // or using the prism_central API. + Cluster NutanixResourceIdentifier `json:"cluster"` + + // subnet is to identify the cluster's network subnet to use for the Machine's VM + // The cluster identifier (uuid or name) can be obtained from the Prism Central console + // or using the prism_central API. + Subnets NutanixResourceIdentifiers `json:"subnets"` + + // Defines the boot type of the virtual machine. Only supports UEFI and Legacy + BootType NutanixBootType `json:"bootType,omitempty"` + + // systemDiskSize is size (in Quantity format) of the system disk of the VM + // The minimum systemDiskSize is 20Gi bytes + SystemDiskSize resource.Quantity `json:"systemDiskSize"` +} + +func (NutanixMachineDetails) VariableSchema() clusterv1.VariableSchema { + return clusterv1.VariableSchema{ + OpenAPIV3Schema: clusterv1.JSONSchemaProps{ + Description: "Nutanix Machine configuration", + Type: "object", + Properties: map[string]clusterv1.JSONSchemaProps{ + "vcpusPerSocket": { + Description: "vcpusPerSocket is the number of vCPUs per socket of the VM", + Type: "integer", + }, + "vcpuSockets": { + Description: "vcpuSockets is the number of vCPU sockets of the VM", + Type: "integer", + }, + "memorySize": { + Description: "memorySize is the memory size (in Quantity format) of the VM eg. 4Gi", + Type: "string", + }, + "image": NutanixResourceIdentifier{}.VariableSchema().OpenAPIV3Schema, + "cluster": NutanixResourceIdentifier{}.VariableSchema().OpenAPIV3Schema, + "subnets": NutanixResourceIdentifiers{}.VariableSchema().OpenAPIV3Schema, + "bootType": NutanixBootType( + capxv1.NutanixBootTypeLegacy, + ).VariableSchema(). + OpenAPIV3Schema, + "systemDiskSize": { + Description: "systemDiskSize is size (in Quantity format) of the system disk of the VM eg. 20Gi", + Type: "string", + }, + }, + Required: []string{ + "vcpusPerSocket", + "vcpuSockets", + "memorySize", + "image", + "cluster", + "subnets", + "systemDiskSize", + }, + }, + } +} + +// NutanixIdentifierType is an enumeration of different resource identifier types. +type NutanixIdentifierType capxv1.NutanixIdentifierType + +func (NutanixIdentifierType) VariableSchema() clusterv1.VariableSchema { + return clusterv1.VariableSchema{ + OpenAPIV3Schema: clusterv1.JSONSchemaProps{ + Type: "string", + Description: "NutanixIdentifierType is an enumeration of different resource identifier types", + Enum: variables.MustMarshalValuesToEnumJSON( + capxv1.NutanixIdentifierUUID, + capxv1.NutanixIdentifierName, + ), + }, + } +} + +// NutanixBootType is an enumeration of different boot types. +type NutanixBootType capxv1.NutanixBootType + +func (NutanixBootType) VariableSchema() clusterv1.VariableSchema { + return clusterv1.VariableSchema{ + OpenAPIV3Schema: clusterv1.JSONSchemaProps{ + Type: "string", + Description: "NutanixBootType is an enumeration of different boot types.", + Enum: variables.MustMarshalValuesToEnumJSON( + capxv1.NutanixBootTypeLegacy, + capxv1.NutanixBootTypeUEFI, + ), + }, + } +} + +type NutanixResourceIdentifier capxv1.NutanixResourceIdentifier + +func (NutanixResourceIdentifier) VariableSchema() clusterv1.VariableSchema { + return clusterv1.VariableSchema{ + OpenAPIV3Schema: clusterv1.JSONSchemaProps{ + Description: "Nutanix Resource Identifier", + Type: "object", + Properties: map[string]clusterv1.JSONSchemaProps{ + "type": NutanixIdentifierType( + capxv1.NutanixIdentifierName, + ).VariableSchema(). + OpenAPIV3Schema, + "uuid": { + Type: "string", + Description: "uuid is the UUID of the resource in the PC.", + }, + "name": { + Type: "string", + Description: "name is the resource name in the PC.", + }, + }, + }, + } +} + +type NutanixResourceIdentifiers []NutanixResourceIdentifier + +func (NutanixResourceIdentifiers) VariableSchema() clusterv1.VariableSchema { + resourceSchema := NutanixResourceIdentifier{}.VariableSchema().OpenAPIV3Schema + + return clusterv1.VariableSchema{ + OpenAPIV3Schema: clusterv1.JSONSchemaProps{ + Description: "Nutanix resource identifier", + Type: "array", + Items: &resourceSchema, + }, + } +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 5af92e536..3bd4d46f9 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -199,14 +199,14 @@ func (in *Addons) DeepCopyInto(out *Addons) { *out = new(ClusterAutoscaler) **out = **in } - if in.CPI != nil { - in, out := &in.CPI, &out.CPI - *out = new(CPI) - **out = **in + if in.CCM != nil { + in, out := &in.CCM, &out.CCM + *out = new(CCM) + (*in).DeepCopyInto(*out) } if in.CSIProviders != nil { in, out := &in.CSIProviders, &out.CSIProviders - *out = new(CSIProviders) + *out = new(CSI) (*in).DeepCopyInto(*out) } } @@ -222,66 +222,90 @@ func (in *Addons) DeepCopy() *Addons { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CNI) DeepCopyInto(out *CNI) { +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 CNI. -func (in *CNI) DeepCopy() *CNI { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CCM. +func (in *CCM) DeepCopy() *CCM { if in == nil { return nil } - out := new(CNI) + out := new(CCM) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CPI) DeepCopyInto(out *CPI) { +func (in *CNI) DeepCopyInto(out *CNI) { *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CPI. -func (in *CPI) DeepCopy() *CPI { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CNI. +func (in *CNI) DeepCopy() *CNI { if in == nil { return nil } - out := new(CPI) + out := new(CNI) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CSIProvider) DeepCopyInto(out *CSIProvider) { +func (in *CSI) DeepCopyInto(out *CSI) { *out = *in + if in.Providers != nil { + in, out := &in.Providers, &out.Providers + *out = make([]CSIProvider, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.DefaultStorage != nil { + in, out := &in.DefaultStorage, &out.DefaultStorage + *out = new(DefaultStorage) + **out = **in + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CSIProvider. -func (in *CSIProvider) DeepCopy() *CSIProvider { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CSI. +func (in *CSI) DeepCopy() *CSI { if in == nil { return nil } - out := new(CSIProvider) + out := new(CSI) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CSIProviders) DeepCopyInto(out *CSIProviders) { +func (in *CSIProvider) DeepCopyInto(out *CSIProvider) { *out = *in - if in.Providers != nil { - in, out := &in.Providers, &out.Providers - *out = make([]CSIProvider, len(*in)) - copy(*out, *in) + if in.StorageClassConfig != nil { + in, out := &in.StorageClassConfig, &out.StorageClassConfig + *out = make([]StorageClassConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + 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 CSIProviders. -func (in *CSIProviders) DeepCopy() *CSIProviders { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CSIProvider. +func (in *CSIProvider) DeepCopy() *CSIProvider { if in == nil { return nil } - out := new(CSIProviders) + out := new(CSIProvider) in.DeepCopyInto(out) return out } @@ -340,6 +364,11 @@ func (in *ClusterConfigSpec) DeepCopyInto(out *ClusterConfigSpec) { *out = new(DockerSpec) **out = **in } + if in.Nutanix != nil { + in, out := &in.Nutanix, &out.Nutanix + *out = new(NutanixSpec) + (*in).DeepCopyInto(*out) + } in.GenericClusterConfig.DeepCopyInto(&out.GenericClusterConfig) if in.ControlPlane != nil { in, out := &in.ControlPlane, &out.ControlPlane @@ -358,6 +387,36 @@ func (in *ClusterConfigSpec) DeepCopy() *ClusterConfigSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ControlPlaneEndpointSpec) DeepCopyInto(out *ControlPlaneEndpointSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControlPlaneEndpointSpec. +func (in *ControlPlaneEndpointSpec) DeepCopy() *ControlPlaneEndpointSpec { + if in == nil { + return nil + } + out := new(ControlPlaneEndpointSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DefaultStorage) DeepCopyInto(out *DefaultStorage) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DefaultStorage. +func (in *DefaultStorage) DeepCopy() *DefaultStorage { + if in == nil { + return nil + } + out := new(DefaultStorage) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DockerNodeSpec) DeepCopyInto(out *DockerNodeSpec) { *out = *in @@ -472,6 +531,13 @@ func (in *GenericClusterConfig) DeepCopyInto(out *GenericClusterConfig) { *out = new(Addons) (*in).DeepCopyInto(*out) } + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make(Users, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericClusterConfig. @@ -649,6 +715,11 @@ func (in *NodeConfigSpec) DeepCopyInto(out *NodeConfigSpec) { *out = new(DockerNodeSpec) (*in).DeepCopyInto(*out) } + if in.Nutanix != nil { + in, out := &in.Nutanix, &out.Nutanix + *out = new(NutanixNodeSpec) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeConfigSpec. @@ -661,6 +732,132 @@ func (in *NodeConfigSpec) DeepCopy() *NodeConfigSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NutanixMachineDetails) DeepCopyInto(out *NutanixMachineDetails) { + *out = *in + out.MemorySize = in.MemorySize.DeepCopy() + in.Image.DeepCopyInto(&out.Image) + in.Cluster.DeepCopyInto(&out.Cluster) + if in.Subnets != nil { + in, out := &in.Subnets, &out.Subnets + *out = make(NutanixResourceIdentifiers, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + out.SystemDiskSize = in.SystemDiskSize.DeepCopy() +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NutanixMachineDetails. +func (in *NutanixMachineDetails) DeepCopy() *NutanixMachineDetails { + if in == nil { + return nil + } + out := new(NutanixMachineDetails) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NutanixNodeSpec) DeepCopyInto(out *NutanixNodeSpec) { + *out = *in + in.MachineDetails.DeepCopyInto(&out.MachineDetails) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NutanixNodeSpec. +func (in *NutanixNodeSpec) DeepCopy() *NutanixNodeSpec { + if in == nil { + return nil + } + out := new(NutanixNodeSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NutanixPrismCentralEndpointSpec) DeepCopyInto(out *NutanixPrismCentralEndpointSpec) { + *out = *in + if in.AdditionalTrustBundle != nil { + in, out := &in.AdditionalTrustBundle, &out.AdditionalTrustBundle + *out = new(string) + **out = **in + } + out.Credentials = in.Credentials +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NutanixPrismCentralEndpointSpec. +func (in *NutanixPrismCentralEndpointSpec) DeepCopy() *NutanixPrismCentralEndpointSpec { + if in == nil { + return nil + } + out := new(NutanixPrismCentralEndpointSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NutanixResourceIdentifier) DeepCopyInto(out *NutanixResourceIdentifier) { + *out = *in + if in.UUID != nil { + in, out := &in.UUID, &out.UUID + *out = new(string) + **out = **in + } + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NutanixResourceIdentifier. +func (in *NutanixResourceIdentifier) DeepCopy() *NutanixResourceIdentifier { + if in == nil { + return nil + } + out := new(NutanixResourceIdentifier) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in NutanixResourceIdentifiers) DeepCopyInto(out *NutanixResourceIdentifiers) { + { + in := &in + *out = make(NutanixResourceIdentifiers, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NutanixResourceIdentifiers. +func (in NutanixResourceIdentifiers) DeepCopy() NutanixResourceIdentifiers { + if in == nil { + return nil + } + out := new(NutanixResourceIdentifiers) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NutanixSpec) DeepCopyInto(out *NutanixSpec) { + *out = *in + out.ControlPlaneEndpoint = in.ControlPlaneEndpoint + in.PrismCentralEndpoint.DeepCopyInto(&out.PrismCentralEndpoint) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NutanixSpec. +func (in *NutanixSpec) DeepCopy() *NutanixSpec { + if in == nil { + return nil + } + out := new(NutanixSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ObjectMeta) DeepCopyInto(out *ObjectMeta) { *out = *in @@ -730,6 +927,28 @@ func (in *SecurityGroup) DeepCopy() *SecurityGroup { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StorageClassConfig) DeepCopyInto(out *StorageClassConfig) { + *out = *in + if in.Parameters != nil { + in, out := &in.Parameters, &out.Parameters + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageClassConfig. +func (in *StorageClassConfig) DeepCopy() *StorageClassConfig { + if in == nil { + return nil + } + out := new(StorageClassConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SubnetSpec) DeepCopyInto(out *SubnetSpec) { *out = *in @@ -764,6 +983,47 @@ func (in Subnets) DeepCopy() Subnets { return *out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *User) DeepCopyInto(out *User) { + *out = *in + if in.SSHAuthorizedKeys != nil { + in, out := &in.SSHAuthorizedKeys, &out.SSHAuthorizedKeys + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new User. +func (in *User) DeepCopy() *User { + if in == nil { + return nil + } + out := new(User) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in Users) DeepCopyInto(out *Users) { + { + in := &in + *out = make(Users, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Users. +func (in Users) DeepCopy() Users { + if in == nil { + return nil + } + out := new(Users) + in.DeepCopyInto(out) + return *out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VPC) DeepCopyInto(out *VPC) { *out = *in diff --git a/api/variables/variables.go b/api/variables/variables.go new file mode 100644 index 000000000..a570410f5 --- /dev/null +++ b/api/variables/variables.go @@ -0,0 +1,59 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package variables + +import ( + "encoding/json" + "fmt" + + v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" +) + +func MarshalToClusterVariable[T any](name string, obj T) (*clusterv1.ClusterVariable, error) { + marshaled, err := json.Marshal(obj) + if err != nil { + return nil, fmt.Errorf("failed to marshal variable value %q: %w", name, err) + } + return &clusterv1.ClusterVariable{ + Name: name, + Value: v1.JSON{Raw: marshaled}, + }, nil +} + +func UnmarshalClusterVariable[T any](clusterVariable *clusterv1.ClusterVariable, obj *T) error { + err := json.Unmarshal(clusterVariable.Value.Raw, obj) + if err != nil { + return fmt.Errorf("error unmarshalling variable: %w", err) + } + + return nil +} + +func GetClusterVariableByName( + name string, + clusterVariables []clusterv1.ClusterVariable, +) (*clusterv1.ClusterVariable, int) { + for i, clusterVar := range clusterVariables { + if clusterVar.Name == name { + return &clusterVar, i + } + } + return nil, -1 +} + +func GetMachineDeploymentVariableByName( + name string, + machineDeploymentVariables *clusterv1.MachineDeploymentVariables, +) (*clusterv1.ClusterVariable, int) { + if machineDeploymentVariables == nil { + return nil, -1 + } + for i, mdVar := range machineDeploymentVariables.Overrides { + if mdVar.Name == name { + return &mdVar, i + } + } + return nil, -1 +} diff --git a/charts/cluster-api-runtime-extensions-nutanix/README.md b/charts/cluster-api-runtime-extensions-nutanix/README.md index a90733b90..5bbea1aec 100644 --- a/charts/cluster-api-runtime-extensions-nutanix/README.md +++ b/charts/cluster-api-runtime-extensions-nutanix/README.md @@ -31,6 +31,9 @@ A Helm chart for cluster-api-runtime-extensions-nutanix | deployDefaultClusterClasses | bool | `true` | | | 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"` | | @@ -40,15 +43,22 @@ A Helm chart for cluster-api-runtime-extensions-nutanix | hooks.cni.calico.crsStrategy.defaultInstallationConfigMaps.DockerCluster.configMap.content | string | `""` | | | hooks.cni.calico.crsStrategy.defaultInstallationConfigMaps.DockerCluster.configMap.name | string | `"calico-cni-crs-installation-dockercluster"` | | | hooks.cni.calico.crsStrategy.defaultInstallationConfigMaps.DockerCluster.create | bool | `true` | | +| hooks.cni.calico.crsStrategy.defaultInstallationConfigMaps.NutanixCluster.configMap.content | string | `""` | | +| hooks.cni.calico.crsStrategy.defaultInstallationConfigMaps.NutanixCluster.configMap.name | string | `"calico-cni-crs-installation-nutanixcluster"` | | +| hooks.cni.calico.crsStrategy.defaultInstallationConfigMaps.NutanixCluster.create | bool | `true` | | | hooks.cni.calico.crsStrategy.defaultTigeraOperatorConfigMap.name | string | `"tigera-operator"` | | | hooks.cni.calico.defaultPodSubnet | string | `"192.168.0.0/16"` | | | hooks.cni.calico.helmAddonStrategy.defaultValueTemplatesConfigMaps.AWSCluster.create | bool | `true` | | | hooks.cni.calico.helmAddonStrategy.defaultValueTemplatesConfigMaps.AWSCluster.name | string | `"calico-cni-helm-values-template-awscluster"` | | | hooks.cni.calico.helmAddonStrategy.defaultValueTemplatesConfigMaps.DockerCluster.create | bool | `true` | | | hooks.cni.calico.helmAddonStrategy.defaultValueTemplatesConfigMaps.DockerCluster.name | string | `"calico-cni-helm-values-template-dockercluster"` | | +| hooks.cni.calico.helmAddonStrategy.defaultValueTemplatesConfigMaps.NutanixCluster.create | bool | `true` | | +| hooks.cni.calico.helmAddonStrategy.defaultValueTemplatesConfigMaps.NutanixCluster.name | string | `"calico-cni-helm-values-template-nutanixcluster"` | | | hooks.cni.cilium.crsStrategy.defaultCiliumConfigMap.name | string | `"cilium"` | | | hooks.cni.cilium.helmAddonStrategy.defaultValueTemplateConfigMap.create | bool | `true` | | | hooks.cni.cilium.helmAddonStrategy.defaultValueTemplateConfigMap.name | string | `"default-cilium-cni-helm-values-template"` | | +| hooks.csi.nutanix.helmAddonStrategy.defaultValueTemplateConfigMap.create | bool | `true` | | +| hooks.csi.nutanix.helmAddonStrategy.defaultValueTemplateConfigMap.name | string | `"default-nutanix-csi-helm-values-template"` | | | hooks.nfd.crsStrategy.defaultInstallationConfigMap.name | string | `"node-feature-discovery"` | | | hooks.nfd.helmAddonStrategy.defaultValueTemplateConfigMap.create | bool | `true` | | | hooks.nfd.helmAddonStrategy.defaultValueTemplateConfigMap.name | string | `"default-nfd-helm-values-template"` | | @@ -67,3 +77,6 @@ A Helm chart for cluster-api-runtime-extensions-nutanix | service.port | int | `443` | | | service.type | string | `"ClusterIP"` | | | tolerations | list | `[{"effect":"NoSchedule","key":"node-role.kubernetes.io/master","operator":"Equal"},{"effect":"NoSchedule","key":"node-role.kubernetes.io/control-plane","operator":"Equal"}]` | Kubernetes pod tolerations | +| webhooks.service.annotations | object | `{}` | | +| webhooks.service.port | int | `443` | | +| webhooks.service.type | string | `"ClusterIP"` | | diff --git a/charts/cluster-api-runtime-extensions-nutanix/defaultclusterclasses/nutanix-cluster-class.yaml b/charts/cluster-api-runtime-extensions-nutanix/defaultclusterclasses/nutanix-cluster-class.yaml new file mode 100644 index 000000000..61c04f53d --- /dev/null +++ b/charts/cluster-api-runtime-extensions-nutanix/defaultclusterclasses/nutanix-cluster-class.yaml @@ -0,0 +1,315 @@ +apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 +kind: KubeadmConfigTemplate +metadata: + labels: + cluster.x-k8s.io/provider: nutanix + name: nutanix-quick-start-kcfg-0 +spec: + template: + spec: + joinConfiguration: + nodeRegistration: + kubeletExtraArgs: + cloud-provider: external + eviction-hard: nodefs.available<10%,nodefs.inodesFree<5%,imagefs.available<15%,memory.available<100Mi,imagefs.inodesFree<10% + tls-cipher-suites: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + postKubeadmCommands: + - echo "after kubeadm call" > /var/log/postkubeadm.log + preKubeadmCommands: + - echo "before kubeadm call" > /var/log/prekubeadm.log + - hostnamectl set-hostname "{{ ds.meta_data.hostname }}" + verbosity: 10 +--- +apiVersion: cluster.x-k8s.io/v1beta1 +kind: ClusterClass +metadata: + labels: + cluster.x-k8s.io/provider: nutanix + name: nutanix-quick-start +spec: + controlPlane: + machineHealthCheck: + maxUnhealthy: 40% + nodeStartupTimeout: 10m + unhealthyConditions: + - status: "False" + timeout: 300s + type: Ready + - status: Unknown + timeout: 300s + type: Ready + - status: "True" + timeout: 300s + type: MemoryPressure + - status: "True" + timeout: 300s + type: DiskPressure + - status: "True" + timeout: 300s + type: PIDPressure + - status: "True" + timeout: 300s + type: NetworkUnavailable + machineInfrastructure: + ref: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: NutanixMachineTemplate + name: nutanix-quick-start-cp-nmt + ref: + apiVersion: controlplane.cluster.x-k8s.io/v1beta1 + kind: KubeadmControlPlaneTemplate + name: nutanix-quick-start-kcpt + infrastructure: + ref: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: NutanixClusterTemplate + name: nutanix-quick-start-nct + patches: + - external: + discoverVariablesExtension: nutanixclusterconfigvars.cluster-api-runtime-extensions-nutanix + generateExtension: nutanixclusterconfigpatch.cluster-api-runtime-extensions-nutanix + name: cluster-config + - external: + discoverVariablesExtension: nutanixworkerconfigvars.cluster-api-runtime-extensions-nutanix + generateExtension: nutanixworkerconfigpatch.cluster-api-runtime-extensions-nutanix + name: worker-config + workers: + machineDeployments: + - class: nutanix-quick-start-worker + machineHealthCheck: + maxUnhealthy: 40% + nodeStartupTimeout: 10m + unhealthyConditions: + - status: "False" + timeout: 300s + type: Ready + - status: Unknown + timeout: 300s + type: Ready + - status: "True" + timeout: 300s + type: MemoryPressure + - status: "True" + timeout: 300s + type: DiskPressure + - status: "True" + timeout: 300s + type: PIDPressure + - status: "True" + timeout: 300s + type: NetworkUnavailable + template: + bootstrap: + ref: + apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 + kind: KubeadmConfigTemplate + name: nutanix-quick-start-kcfg-0 + infrastructure: + ref: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: NutanixMachineTemplate + name: nutanix-quick-start-md-nmt +--- +apiVersion: controlplane.cluster.x-k8s.io/v1beta1 +kind: KubeadmControlPlaneTemplate +metadata: + labels: + cluster.x-k8s.io/provider: nutanix + name: nutanix-quick-start-kcpt +spec: + template: + spec: + kubeadmConfigSpec: + clusterConfiguration: + apiServer: + certSANs: + - localhost + - 127.0.0.1 + - 0.0.0.0 + extraArgs: + cloud-provider: external + tls-cipher-suites: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + controllerManager: + extraArgs: + cloud-provider: external + tls-cipher-suites: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + scheduler: + extraArgs: + tls-cipher-suites: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + files: + - content: | + apiVersion: v1 + kind: Pod + metadata: + name: kube-vip + namespace: kube-system + spec: + containers: + - name: kube-vip + image: ghcr.io/kube-vip/kube-vip:v0.6.4 + imagePullPolicy: IfNotPresent + args: + - manager + env: + - name: vip_arp + value: "true" + - name: address + value: "control_plane_endpoint_ip" + - name: port + value: "control_plane_endpoint_port" + - name: vip_cidr + value: "32" + - name: cp_enable + value: "true" + - name: cp_namespace + value: kube-system + - name: vip_ddns + value: "false" + - name: vip_leaderelection + value: "true" + - name: vip_leaseduration + value: "15" + - name: vip_renewdeadline + value: "10" + - name: vip_retryperiod + value: "2" + - name: svc_enable + value: "false" + - name: lb_enable + value: "false" + - name: enableServicesElection + value: "false" + securityContext: + capabilities: + add: + - NET_ADMIN + - SYS_TIME + - NET_RAW + volumeMounts: + - mountPath: /etc/kubernetes/admin.conf + name: kubeconfig + resources: {} + hostNetwork: true + hostAliases: + - hostnames: + - kubernetes + ip: 127.0.0.1 + volumes: + - name: kubeconfig + hostPath: + type: FileOrCreate + path: /etc/kubernetes/admin.conf + status: {} + owner: root:root + path: /etc/kubernetes/manifests/kube-vip.yaml + initConfiguration: + nodeRegistration: + kubeletExtraArgs: + cloud-provider: external + eviction-hard: nodefs.available<10%,nodefs.inodesFree<5%,imagefs.available<15%,memory.available<100Mi,imagefs.inodesFree<10% + tls-cipher-suites: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + joinConfiguration: + nodeRegistration: + kubeletExtraArgs: + cloud-provider: external + eviction-hard: nodefs.available<10%,nodefs.inodesFree<5%,imagefs.available<15%,memory.available<100Mi,imagefs.inodesFree<10% + tls-cipher-suites: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + postKubeadmCommands: + - echo export KUBECONFIG=/etc/kubernetes/admin.conf >> /root/.bashrc + - | + KUBERNETES_VERSION_NO_V=${KUBERNETES_VERSION#v} + VERSION_TO_COMPARE=1.29.0 + if [ "$(printf '%s\n' "$KUBERNETES_VERSION_NO_V" "$VERSION_TO_COMPARE" | sort -V | head -n1)" != "$KUBERNETES_VERSION_NO_V" ]; then + if [ -f /run/kubeadm/kubeadm.yaml ]; then + sed -i 's#path: /etc/kubernetes/super-admin.conf#path: /etc/kubernetes/admin.conf#' /etc/kubernetes/manifests/kube-vip.yaml; + fi + fi + - echo "after kubeadm call" > /var/log/postkubeadm.log + preKubeadmCommands: + - echo "before kubeadm call" > /var/log/prekubeadm.log + - hostnamectl set-hostname "{{ ds.meta_data.hostname }}" + - echo "::1 ipv6-localhost ipv6-loopback" >/etc/hosts + - echo "127.0.0.1 localhost" >>/etc/hosts + - echo "127.0.0.1 kubernetes" >>/etc/hosts + - echo "127.0.0.1 {{ ds.meta_data.hostname }}" >> /etc/hosts + - | + KUBERNETES_VERSION_NO_V=${KUBERNETES_VERSION#v} + VERSION_TO_COMPARE=1.29.0 + if [ "$(printf '%s\n' "$KUBERNETES_VERSION_NO_V" "$VERSION_TO_COMPARE" | sort -V | head -n1)" != "$KUBERNETES_VERSION_NO_V" ]; then + if [ -f /run/kubeadm/kubeadm.yaml ]; then + sed -i 's#path: /etc/kubernetes/admin.conf#path: /etc/kubernetes/super-admin.conf#' /etc/kubernetes/manifests/kube-vip.yaml; + fi + fi + useExperimentalRetryJoin: true + verbosity: 10 +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: NutanixClusterTemplate +metadata: + labels: + cluster.x-k8s.io/provider: nutanix + name: nutanix-quick-start-nct +spec: + template: + spec: + controlPlaneEndpoint: + host: PLACEHOLDER + port: 6443 + failureDomains: [] + prismCentral: + address: PLACEHOLDER + credentialRef: + kind: Secret + name: PLACEHOLDER + namespace: default + port: 9440 +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: NutanixMachineTemplate +metadata: + labels: + cluster.x-k8s.io/provider: nutanix + name: nutanix-quick-start-cp-nmt +spec: + template: + spec: + bootType: legacy + cluster: + name: "" + type: name + image: + name: "" + type: name + memorySize: 4Gi + providerID: nutanix://vm-uuid + subnet: + - name: "" + type: name + systemDiskSize: 40Gi + vcpuSockets: 2 + vcpusPerSocket: 1 +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: NutanixMachineTemplate +metadata: + labels: + cluster.x-k8s.io/provider: nutanix + name: nutanix-quick-start-md-nmt +spec: + template: + spec: + bootType: legacy + cluster: + name: "" + type: name + image: + name: "" + type: name + memorySize: 4Gi + providerID: nutanix://vm-uuid + subnet: + - name: "" + type: name + systemDiskSize: 40Gi + vcpuSockets: 2 + vcpusPerSocket: 1 diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/cpi/aws/manifests/aws-cpi-v1.27.1-configmap.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/ccm/aws/manifests/aws-ccm-v1.27.1-configmap.yaml similarity index 97% rename from charts/cluster-api-runtime-extensions-nutanix/templates/cpi/aws/manifests/aws-cpi-v1.27.1-configmap.yaml rename to charts/cluster-api-runtime-extensions-nutanix/templates/ccm/aws/manifests/aws-ccm-v1.27.1-configmap.yaml index 7d34b818e..94f9739ce 100644 --- a/charts/cluster-api-runtime-extensions-nutanix/templates/cpi/aws/manifests/aws-cpi-v1.27.1-configmap.yaml +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/ccm/aws/manifests/aws-ccm-v1.27.1-configmap.yaml @@ -3,11 +3,11 @@ #================================================================= # DO NOT EDIT THIS FILE -# IT HAS BEEN GENERATED BY /hack/addons/update-aws-cpi.sh +# IT HAS BEEN GENERATED BY /hack/addons/update-aws-ccm.sh #================================================================= apiVersion: v1 data: - aws-cpi-v1.27.1.yaml: | + aws-ccm-v1.27.1.yaml: | apiVersion: v1 kind: ServiceAccount metadata: @@ -185,4 +185,4 @@ data: kind: ConfigMap metadata: creationTimestamp: null - name: aws-cpi-v1.27.1 + name: aws-ccm-v1.27.1 diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/cpi/aws/manifests/aws-cpi-v1.28.1-configmap.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/ccm/aws/manifests/aws-ccm-v1.28.1-configmap.yaml similarity index 97% rename from charts/cluster-api-runtime-extensions-nutanix/templates/cpi/aws/manifests/aws-cpi-v1.28.1-configmap.yaml rename to charts/cluster-api-runtime-extensions-nutanix/templates/ccm/aws/manifests/aws-ccm-v1.28.1-configmap.yaml index 6a6b0d0f2..593b24c98 100644 --- a/charts/cluster-api-runtime-extensions-nutanix/templates/cpi/aws/manifests/aws-cpi-v1.28.1-configmap.yaml +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/ccm/aws/manifests/aws-ccm-v1.28.1-configmap.yaml @@ -3,11 +3,11 @@ #================================================================= # DO NOT EDIT THIS FILE -# IT HAS BEEN GENERATED BY /hack/addons/update-aws-cpi.sh +# IT HAS BEEN GENERATED BY /hack/addons/update-aws-ccm.sh #================================================================= apiVersion: v1 data: - aws-cpi-v1.28.1.yaml: | + aws-ccm-v1.28.1.yaml: | apiVersion: v1 kind: ServiceAccount metadata: @@ -185,4 +185,4 @@ data: kind: ConfigMap metadata: creationTimestamp: null - name: aws-cpi-v1.28.1 + name: aws-ccm-v1.28.1 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/certificates.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/certificates.yaml index e07fcc3dc..aa8891313 100644 --- a/charts/cluster-api-runtime-extensions-nutanix/templates/certificates.yaml +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/certificates.yaml @@ -4,7 +4,7 @@ apiVersion: cert-manager.io/v1 kind: Certificate metadata: - name: {{ template "chart.name" . }}-runtimehooks-tls + name: {{ template "chart.name" . }}-service-cert namespace: {{ .Release.Namespace }} labels: {{- include "chart.labels" . | nindent 4 }} @@ -12,7 +12,9 @@ spec: dnsNames: - {{ template "chart.name" . }}-runtimehooks.{{ .Release.Namespace }}.svc - {{ template "chart.name" . }}-runtimehooks.{{ .Release.Namespace }}.svc.cluster.local + - {{ template "chart.name" . }}-webhook-service.{{ .Release.Namespace }}.svc + - {{ template "chart.name" . }}-webhook-service.{{ .Release.Namespace }}.svc.cluster.local issuerRef: kind: {{ .Values.certificates.issuer.kind }} name: {{ template "chart.issuerName" . }} - secretName: {{ template "chart.name" . }}-runtimehooks-tls + secretName: {{ template "chart.name" . }}-service-cert diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/clusterclass_nutanix.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/clusterclass_nutanix.yaml new file mode 100644 index 000000000..42ee09357 --- /dev/null +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/clusterclass_nutanix.yaml @@ -0,0 +1,6 @@ +# Copyright 2023 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +{{- if and .Values.deployDefaultClusterClasses (.Capabilities.APIVersions.Has "infrastructure.cluster.x-k8s.io/v1beta1/NutanixClusterTemplate") }} +{{ .Files.Get "defaultclusterclasses/nutanix-cluster-class.yaml" }} +{{- end}} diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/cni/calico/manifests/nutanix/crs-installation.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/cni/calico/manifests/nutanix/crs-installation.yaml new file mode 100644 index 000000000..69798aed9 --- /dev/null +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/cni/calico/manifests/nutanix/crs-installation.yaml @@ -0,0 +1,36 @@ +# Copyright 2023 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +{{- if .Values.hooks.cni.calico.crsStrategy.defaultInstallationConfigMaps.NutanixCluster.create }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: '{{ .Values.hooks.cni.calico.crsStrategy.defaultInstallationConfigMaps.NutanixCluster.configMap.name }}' +data: + calico-installation: | +{{- if .Values.hooks.cni.calico.crsStrategy.defaultInstallationConfigMaps.NutanixCluster.configMap.content -}} + {{ .Values.hooks.cni.calico.crsStrategy.defaultInstallationConfigMaps.NutanixCluster.configMap.content | nindent 4 }} +{{- else -}} + # This section includes base Calico installation configuration. + # For more information, see: https://docs.projectcalico.org/reference/installation/api + apiVersion: operator.tigera.io/v1 + kind: Installation + metadata: + name: default + spec: + cni: + type: Calico + # Configures Calico networking. + calicoNetwork: + # Note: The ipPools section cannot be modified post-install. + ipPools: + - blockSize: 26 + cidr: {{ .Values.hooks.cni.calico.defaultPodSubnet }} + encapsulation: IPIP + natOutgoing: Enabled + nodeSelector: all() + bgp: Enabled + nodeMetricsPort: 9091 + typhaMetricsPort: 9093 +{{- end -}} +{{- end -}} diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/cni/calico/manifests/nutanix/helm-addon-installation.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/cni/calico/manifests/nutanix/helm-addon-installation.yaml new file mode 100644 index 000000000..796dce4db --- /dev/null +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/cni/calico/manifests/nutanix/helm-addon-installation.yaml @@ -0,0 +1,23 @@ +# Copyright 2023 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +{{- if .Values.hooks.cni.calico.helmAddonStrategy.defaultValueTemplatesConfigMaps.NutanixCluster.create }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: '{{ .Values.hooks.cni.calico.helmAddonStrategy.defaultValueTemplatesConfigMaps.NutanixCluster.name }}' +data: + values.yaml: |- + installation: + cni: + type: Calico + calicoNetwork: + bgp: Enabled + ipPools:{{ printf "{{ range $cidr := .Cluster.spec.clusterNetwork.pods.cidrBlocks }}" }} + - cidr: {{ printf "{{ $cidr }}" }} + encapsulation: None + natOutgoing: Enabled + nodeSelector: all(){{ printf "{{ end }}" }} + nodeMetricsPort: 9091 + typhaMetricsPort: 9093 +{{- end -}} diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/csi/aws-ebs/manifests/aws-ebs-csi-configmap.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/csi/aws-ebs/manifests/aws-ebs-csi-configmap.yaml index 09e7ce18e..5529a361f 100644 --- a/charts/cluster-api-runtime-extensions-nutanix/templates/csi/aws-ebs/manifests/aws-ebs-csi-configmap.yaml +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/csi/aws-ebs/manifests/aws-ebs-csi-configmap.yaml @@ -8,16 +8,6 @@ apiVersion: v1 data: aws-ebs-csi.yaml: | - apiVersion: storage.k8s.io/v1 - kind: StorageClass - metadata: - name: ebs-sc - parameters: - csi.storage.k8s.io/fstype: ext4 - type: gp3 - provisioner: ebs.csi.aws.com - volumeBindingMode: WaitForFirstConsumer - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/csi/nutanix/manifests/helm-addon-installation.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/csi/nutanix/manifests/helm-addon-installation.yaml new file mode 100644 index 000000000..6dd0cd393 --- /dev/null +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/csi/nutanix/manifests/helm-addon-installation.yaml @@ -0,0 +1,14 @@ +# Copyright 2023 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +{{- if .Values.hooks.csi.nutanix.helmAddonStrategy.defaultValueTemplateConfigMap.create }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: '{{ .Values.hooks.csi.nutanix.helmAddonStrategy.defaultValueTemplateConfigMap.name }}' +data: + values.yaml: |- + # The Secret containing the credentials will be created by the handler. + createSecret: false + secretName: nutanix-csi-credentials +{{- end -}} diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/csi/nutanix/manifests/nutanix-storage-csi-configmap.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/csi/nutanix/manifests/nutanix-storage-csi-configmap.yaml new file mode 100644 index 000000000..8353d3f9a --- /dev/null +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/csi/nutanix/manifests/nutanix-storage-csi-configmap.yaml @@ -0,0 +1,543 @@ +# Copyright 2023 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +#================================================================= +# DO NOT EDIT THIS FILE +# IT HAS BEEN GENERATED BY /hack/addons/update-nutanix-storage-csi.sh +#================================================================= +apiVersion: v1 +data: + nutanix-storage-csi.yaml: | + apiVersion: v1 + kind: ServiceAccount + metadata: + name: nutanix-csi-controller + namespace: kube-system + --- + apiVersion: v1 + kind: ServiceAccount + metadata: + name: nutanix-csi-node + namespace: kube-system + --- + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRole + metadata: + name: nutanix-csi-controller-role + namespace: nutanix-system + rules: + - apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - nodes + verbs: + - get + - apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list + - watch + - create + - delete + - update + - patch + - apiGroups: + - "" + resources: + - persistentvolumeclaims + verbs: + - get + - list + - watch + - update + - apiGroups: + - "" + resources: + - persistentvolumeclaims/status + verbs: + - update + - patch + - apiGroups: + - storage.k8s.io + resources: + - storageclasses + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - events + verbs: + - list + - watch + - create + - update + - patch + - apiGroups: + - snapshot.storage.k8s.io + resources: + - volumesnapshotclasses + verbs: + - get + - list + - watch + - apiGroups: + - snapshot.storage.k8s.io + resources: + - volumesnapshots + verbs: + - get + - list + - watch + - update + - apiGroups: + - snapshot.storage.k8s.io + resources: + - volumesnapshots/status + verbs: + - update + - apiGroups: + - snapshot.storage.k8s.io + resources: + - volumesnapshotcontents + verbs: + - create + - get + - list + - watch + - update + - delete + - patch + - apiGroups: + - snapshot.storage.k8s.io + resources: + - volumesnapshotcontents/status + verbs: + - update + - patch + - apiGroups: + - storage.k8s.io + resources: + - csinodes + verbs: + - get + - list + - watch + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - create + - delete + - update + - patch + --- + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRole + metadata: + name: nutanix-csi-node-role + namespace: nutanix-system + rules: + - apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - apiGroups: + - "" + resources: + - nodes + verbs: + - get + - list + - update + - apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list + - apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list + - watch + - update + - apiGroups: + - storage.k8s.io + resources: + - volumeattachments + verbs: + - get + - list + - watch + - update + --- + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRoleBinding + metadata: + name: nutanix-csi-controller-binding + namespace: nutanix-system + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: nutanix-csi-controller-role + subjects: + - kind: ServiceAccount + name: nutanix-csi-controller + namespace: kube-system + --- + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRoleBinding + metadata: + name: nutanix-csi-node-binding + namespace: nutanix-system + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: nutanix-csi-node-role + subjects: + - kind: ServiceAccount + name: nutanix-csi-node + namespace: kube-system + --- + apiVersion: v1 + kind: Service + metadata: + labels: + app: nutanix-csi-metrics + name: nutanix-csi-metrics + namespace: kube-system + spec: + ports: + - name: provisioner + port: 9809 + protocol: TCP + targetPort: 9809 + - name: resizer + port: 9810 + protocol: TCP + targetPort: 9810 + selector: + app: nutanix-csi-controller + type: ClusterIP + --- + apiVersion: apps/v1 + kind: Deployment + metadata: + name: nutanix-csi-controller + namespace: kube-system + spec: + replicas: 2 + selector: + matchLabels: + app: nutanix-csi-controller + strategy: + rollingUpdate: + maxSurge: 0 + maxUnavailable: 1 + type: RollingUpdate + template: + metadata: + labels: + app: nutanix-csi-controller + spec: + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: + app: nutanix-csi-controller + topologyKey: kubernetes.io/hostname + weight: 100 + containers: + - args: + - --csi-address=$(ADDRESS) + - --timeout=60s + - --worker-threads=16 + - --extra-create-metadata=true + - --default-fstype=ext4 + - --http-endpoint=:9809 + - --v=2 + - --leader-election=true + env: + - name: ADDRESS + value: /var/lib/csi/sockets/pluginproxy/csi.sock + image: registry.k8s.io/sig-storage/csi-provisioner:v3.6.2 + imagePullPolicy: IfNotPresent + name: csi-provisioner + resources: + requests: + cpu: 100m + memory: 200Mi + volumeMounts: + - mountPath: /var/lib/csi/sockets/pluginproxy/ + name: socket-dir + - args: + - --v=2 + - --csi-address=$(ADDRESS) + - --timeout=60s + - --leader-election=true + - --handle-volume-inuse-error=false + - --http-endpoint=:9810 + env: + - name: ADDRESS + value: /var/lib/csi/sockets/pluginproxy/csi.sock + image: registry.k8s.io/sig-storage/csi-resizer:v1.9.2 + imagePullPolicy: IfNotPresent + name: csi-resizer + resources: + requests: + cpu: 5m + memory: 30Mi + volumeMounts: + - mountPath: /var/lib/csi/sockets/pluginproxy/ + name: socket-dir + - args: + - --csi-address=$(ADDRESS) + - --leader-election=true + - --logtostderr=true + - --timeout=300s + env: + - name: ADDRESS + value: /csi/csi.sock + image: registry.k8s.io/sig-storage/csi-snapshotter:v3.0.3 + imagePullPolicy: IfNotPresent + name: csi-snapshotter + resources: + requests: + cpu: 5m + memory: 30Mi + volumeMounts: + - mountPath: /csi + name: socket-dir + - args: + - --endpoint=$(CSI_ENDPOINT) + - --nodeid=$(NODE_ID) + - --drivername=csi.nutanix.com + env: + - name: CSI_ENDPOINT + value: unix:///var/lib/csi/sockets/pluginproxy/csi.sock + - name: NODE_ID + valueFrom: + fieldRef: + fieldPath: spec.nodeName + image: quay.io/karbon/ntnx-csi:v2.6.6 + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: http-endpoint + initialDelaySeconds: 10 + periodSeconds: 2 + timeoutSeconds: 3 + name: nutanix-csi-plugin + ports: + - containerPort: 9807 + name: http-endpoint + protocol: TCP + resources: + requests: + cpu: 100m + memory: 200Mi + securityContext: + allowPrivilegeEscalation: true + privileged: true + volumeMounts: + - mountPath: /var/lib/csi/sockets/pluginproxy/ + name: socket-dir + - mountPath: /host + name: root-dir + - args: + - --csi-address=/csi/csi.sock + - --http-endpoint=:9807 + image: registry.k8s.io/sig-storage/livenessprobe:v2.11.0 + imagePullPolicy: IfNotPresent + name: liveness-probe + resources: + requests: + cpu: 5m + memory: 20Mi + volumeMounts: + - mountPath: /csi + name: socket-dir + hostNetwork: true + priorityClassName: system-cluster-critical + serviceAccount: nutanix-csi-controller + volumes: + - emptyDir: {} + name: socket-dir + - hostPath: + path: / + type: Directory + name: root-dir + --- + apiVersion: apps/v1 + kind: DaemonSet + metadata: + name: nutanix-csi-node + namespace: kube-system + spec: + selector: + matchLabels: + app: nutanix-csi-node + template: + metadata: + labels: + app: nutanix-csi-node + spec: + containers: + - args: + - --v=2 + - --csi-address=$(ADDRESS) + - --kubelet-registration-path=$(DRIVER_REG_SOCK_PATH) + env: + - name: ADDRESS + value: /csi/csi.sock + - name: DRIVER_REG_SOCK_PATH + value: /var/lib/kubelet/plugins/csi.nutanix.com/csi.sock + - name: KUBE_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + image: registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.9.1 + imagePullPolicy: IfNotPresent + name: driver-registrar + resources: + requests: + cpu: 100m + memory: 20Mi + volumeMounts: + - mountPath: /csi/ + name: plugin-dir + - mountPath: /registration + name: registration-dir + - args: + - --endpoint=$(CSI_ENDPOINT) + - --nodeid=$(NODE_ID) + - --drivername=csi.nutanix.com + env: + - name: CSI_ENDPOINT + value: unix:///csi/csi.sock + - name: NODE_ID + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: NODE_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + image: quay.io/karbon/ntnx-csi:v2.6.6 + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: http-endpoint + initialDelaySeconds: 10 + periodSeconds: 2 + timeoutSeconds: 3 + name: nutanix-csi-node + ports: + - containerPort: 9808 + name: http-endpoint + protocol: TCP + resources: + requests: + cpu: 100m + memory: 200Mi + securityContext: + allowPrivilegeEscalation: true + privileged: true + volumeMounts: + - mountPath: /csi + name: plugin-dir + - mountPath: /var/lib/kubelet + mountPropagation: Bidirectional + name: pods-mount-dir + - mountPath: /dev + name: device-dir + - mountPath: /etc/iscsi + name: iscsi-dir + - mountPath: /host + mountPropagation: Bidirectional + name: root-dir + - args: + - --csi-address=/csi/csi.sock + - --http-endpoint=:9808 + image: registry.k8s.io/sig-storage/livenessprobe:v2.11.0 + imagePullPolicy: IfNotPresent + name: liveness-probe + resources: + requests: + cpu: 5m + memory: 20Mi + volumeMounts: + - mountPath: /csi + name: plugin-dir + hostNetwork: true + priorityClassName: system-cluster-critical + serviceAccount: nutanix-csi-node + volumes: + - hostPath: + path: /var/lib/kubelet/plugins_registry/ + type: Directory + name: registration-dir + - hostPath: + path: /var/lib/kubelet/plugins/csi.nutanix.com/ + type: DirectoryOrCreate + name: plugin-dir + - hostPath: + path: /var/lib/kubelet + type: Directory + name: pods-mount-dir + - hostPath: + path: /dev + name: device-dir + - hostPath: + path: /etc/iscsi + type: Directory + name: iscsi-dir + - hostPath: + path: / + type: Directory + name: root-dir + updateStrategy: + rollingUpdate: + maxUnavailable: 10% + type: RollingUpdate + --- + apiVersion: storage.k8s.io/v1 + kind: CSIDriver + metadata: + name: csi.nutanix.com + spec: + attachRequired: false + podInfoOnMount: true +kind: ConfigMap +metadata: + creationTimestamp: null + name: nutanix-storage-csi diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/deployment.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/deployment.yaml index 1ce472756..1b86dc255 100644 --- a/charts/cluster-api-runtime-extensions-nutanix/templates/deployment.yaml +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/deployment.yaml @@ -28,10 +28,14 @@ spec: image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default $.Chart.AppVersion }}" imagePullPolicy: "{{ .Values.image.pullPolicy }}" args: - - --webhook-cert-dir=/runtimehooks-certs/ + - --runtime-webhook-port=9444 + - --runtime-webhook-cert-dir=/tmp/k8s-webhook-server/serving-certs/ - --defaults-namespace=$(POD_NAMESPACE) + - --helm-addons-configmap={{ .Values.helmAddonsConfigMap }} - --cni.cilium.helm-addon.default-values-template-configmap-name={{ .Values.hooks.cni.cilium.helmAddonStrategy.defaultValueTemplateConfigMap.name }} - --nfd.helm-addon.default-values-template-configmap-name={{ .Values.hooks.nfd.helmAddonStrategy.defaultValueTemplateConfigMap.name }} + - --webhook-port=9443 + - --webhook-cert-dir=/tmp/k8s-webhook-server/serving-certs/ {{- range $key, $value := .Values.extraArgs }} - --{{ $key }}={{ $value }} {{- end }} @@ -42,9 +46,12 @@ spec: {{- end }} {{- end }} ports: - - containerPort: 9443 + - containerPort: 9444 name: runtimehooks protocol: TCP + - containerPort: 9443 + name: webhook-server + protocol: TCP - containerPort: 8080 name: metrics protocol: TCP @@ -61,8 +68,8 @@ spec: {{- toYaml . | nindent 10 }} {{- end }} volumeMounts: - - mountPath: /runtimehooks-certs - name: runtimehooks-cert + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert readOnly: true livenessProbe: httpGet: @@ -80,7 +87,7 @@ spec: {{- toYaml . | nindent 8}} {{- end }} volumes: - - name: runtimehooks-cert + - name: cert secret: defaultMode: 420 - secretName: {{ template "chart.name" . }}-runtimehooks-tls + secretName: {{ template "chart.name" . }}-service-cert diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/extensionconfig.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/extensionconfig.yaml index 4f6005b48..535568a19 100644 --- a/charts/cluster-api-runtime-extensions-nutanix/templates/extensionconfig.yaml +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/extensionconfig.yaml @@ -5,7 +5,7 @@ apiVersion: runtime.cluster.x-k8s.io/v1alpha1 kind: ExtensionConfig metadata: annotations: - runtime.cluster.x-k8s.io/inject-ca-from-secret: {{ .Release.Namespace }}/{{ template "chart.name" . }}-runtimehooks-tls + runtime.cluster.x-k8s.io/inject-ca-from-secret: {{ .Release.Namespace }}/{{ template "chart.name" . }}-service-cert name: {{ template "chart.name" . }} namespace: {{ .Release.Namespace }} spec: diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/helm-config.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/helm-config.yaml new file mode 100644 index 000000000..4dc513cd4 --- /dev/null +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/helm-config.yaml @@ -0,0 +1,41 @@ +# Copyright 2023 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +#================================================================= +# DO NOT EDIT THIS FILE +# IT HAS BEEN GENERATED BY /hack/tools/helm-cm/main.go +#================================================================= +apiVersion: v1 +data: + cilium: | + ChartName: cilium + ChartVersion: 1.15.0 + RepositoryURL: https://helm.cilium.io/ + cluster-autoscaler: | + ChartName: cluster-autoscaler + ChartVersion: 9.35.0 + RepositoryURL: https://kubernetes.github.io/autoscaler + nfd: | + 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 + RepositoryURL: https://nutanix.github.io/helm/ + nutanix-storage-csi: | + ChartName: nutanix-csi-storage + ChartVersion: v2.6.6 + RepositoryURL: https://nutanix.github.io/helm/ + tigera-operator: | + ChartName: tigera-operator + ChartVersion: v3.26.4 + RepositoryURL: https://docs.tigera.io/calico/charts +kind: ConfigMap +metadata: + creationTimestamp: null + name: {{ .Values.helmAddonsConfigMap }} diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/role.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/role.yaml index 9f909c5e3..e3cd174f7 100644 --- a/charts/cluster-api-runtime-extensions-nutanix/templates/role.yaml +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/role.yaml @@ -69,3 +69,13 @@ rules: - get - list - watch +- apiGroups: + - storage.k8s.io + resources: + - storageclasses + verbs: + - create + - get + - list + - patch + - update diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/webhook-service.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/webhook-service.yaml new file mode 100644 index 000000000..056648bc9 --- /dev/null +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/webhook-service.yaml @@ -0,0 +1,26 @@ +# Copyright 2023 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: v1 +kind: Service +metadata: + annotations: + {{- with .Values.webhooks.service.annotations }} + {{ toYaml . | nindent 4 }} + {{- end }} + labels: + {{- include "chart.labels" . | nindent 4 }} + name: {{ template "chart.name" . }}-webhook-service + namespace: {{ .Release.Namespace }} +spec: + type: {{.Values.webhooks.service.type}} + ports: + - name: https + port: {{ .Values.webhooks.service.port }} + protocol: TCP + targetPort: webhook-server + {{- if and .Values.webhooks.service.nodePort (eq "NodePort" .Values.service.type) }} + nodePort: {{ .Values.webhooks.service.nodePort }} + {{- end }} + selector: + {{- include "chart.selectorLabels" . | nindent 4 }} diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/webhook.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/webhook.yaml new file mode 100644 index 000000000..3454aa81e --- /dev/null +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/webhook.yaml @@ -0,0 +1,32 @@ +# Copyright 2023 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: {{ template "chart.name" . }}-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ template "chart.name" . }}-service-cert +webhooks: +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: {{ template "chart.name" . }}-webhook-service + namespace: {{ .Release.Namespace }} + path: /mutate-cluster-x-k8s-io-v1beta1-cluster + failurePolicy: Fail + matchPolicy: Equivalent + name: default.cluster.cluster.x-k8s.io + rules: + - apiGroups: + - cluster.x-k8s.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - clusters + sideEffects: None diff --git a/charts/cluster-api-runtime-extensions-nutanix/values.yaml b/charts/cluster-api-runtime-extensions-nutanix/values.yaml index 81576e8ba..519ea518a 100644 --- a/charts/cluster-api-runtime-extensions-nutanix/values.yaml +++ b/charts/cluster-api-runtime-extensions-nutanix/values.yaml @@ -19,6 +19,11 @@ hooks: configMap: name: calico-cni-crs-installation-awscluster content: "" + NutanixCluster: + create: true + configMap: + name: calico-cni-crs-installation-nutanixcluster + content: "" helmAddonStrategy: defaultValueTemplatesConfigMaps: DockerCluster: @@ -27,6 +32,9 @@ hooks: AWSCluster: create: true name: calico-cni-helm-values-template-awscluster + NutanixCluster: + create: true + name: calico-cni-helm-values-template-nutanixcluster cilium: crsStrategy: defaultCiliumConfigMap: @@ -35,6 +43,18 @@ hooks: defaultValueTemplateConfigMap: create: true name: default-cilium-cni-helm-values-template + csi: + nutanix: + helmAddonStrategy: + 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: @@ -52,6 +72,8 @@ hooks: create: true name: default-cluster-autoscaler-helm-values-template +helmAddonsConfigMap: default-helm-addons-config + deployDefaultClusterClasses: true deployment: @@ -106,3 +128,9 @@ tolerations: # -- Priority class to be used for the pod. priorityClassName: system-cluster-critical + +webhooks: + service: + annotations: {} + type: ClusterIP + port: 443 diff --git a/cmd/main.go b/cmd/main.go index fe4d25f5c..39887e02e 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -23,6 +23,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/manager" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" 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/common/pkg/capi/clustertopology/handlers" @@ -34,7 +35,11 @@ import ( dockermutation "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/docker/mutation" dockerworkerconfig "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/docker/workerconfig" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle" + nutanixclusterconfig "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/nutanix/clusterconfig" + nutanixmutation "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/nutanix/mutation" + nutanixworkerconfig "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/nutanix/workerconfig" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/options" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/webhooks" ) func main() { @@ -77,6 +82,9 @@ func main() { runtimeWebhookServerOpts := server.NewServerOptions() + // mutating/validating webhook server options + webhookServerOpts := webhooks.NewOptions() + globalOptions := options.NewGlobalOptions() genericLifecycleHandlers := lifecycle.New(globalOptions) @@ -86,6 +94,7 @@ func main() { logsv1.AddFlags(logOptions, pflag.CommandLine) globalOptions.AddFlags(pflag.CommandLine) runtimeWebhookServerOpts.AddFlags(pflag.CommandLine) + webhookServerOpts.AddFlags(pflag.CommandLine) genericLifecycleHandlers.AddFlags(pflag.CommandLine) pflag.CommandLine.SetNormalizeFunc(cliflag.WordSepNormalizeFunc) pflag.CommandLine.AddGoFlagSet(flag.CommandLine) @@ -102,6 +111,14 @@ func main() { // Add the klog logger in the context. ctrl.SetLogger(klog.Background()) + // Setup mutating/validating webhook server + mgrOptions.WebhookServer = webhook.NewServer( + webhook.Options{ + Port: webhookServerOpts.WebhookPort, + CertDir: webhookServerOpts.WebhookCertDir, + }, + ) + signalCtx := ctrl.SetupSignalHandler() mgr, err := newManager(mgrOptions) @@ -129,10 +146,20 @@ func main() { dockermutation.MetaWorkerPatchHandler(), } + // nutanixMetaHandlers combines all Nutanix patch and variable handlers under a single handler. + // It allows to specify configuration under a single variable. + nutanixMetaHandlers := []handlers.Named{ + nutanixclusterconfig.NewVariable(), + nutanixworkerconfig.NewVariable(), + nutanixmutation.MetaPatchHandler(mgr), + nutanixmutation.MetaWorkerPatchHandler(), + } + var allHandlers []handlers.Named allHandlers = append(allHandlers, genericLifecycleHandlers.AllHandlers(mgr)...) allHandlers = append(allHandlers, awsMetaHandlers...) allHandlers = append(allHandlers, dockerMetaHandlers...) + allHandlers = append(allHandlers, nutanixMetaHandlers...) runtimeWebhookServer := server.NewServer(runtimeWebhookServerOpts, allHandlers...) @@ -141,6 +168,11 @@ func main() { os.Exit(1) } + if err := (&webhooks.Cluster{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "Cluster") + os.Exit(1) + } + if err := mgr.Start(signalCtx); err != nil { setupLog.Error(err, "unable to start controller manager") os.Exit(1) diff --git a/common/go.mod b/common/go.mod index 7e97d6d90..c17bce065 100644 --- a/common/go.mod +++ b/common/go.mod @@ -11,6 +11,7 @@ require ( github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api v0.0.0-00010101000000-000000000000 github.com/evanphx/json-patch/v5 v5.9.0 github.com/go-logr/logr v1.4.1 + github.com/onsi/ginkgo/v2 v2.15.0 github.com/onsi/gomega v1.31.1 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.9.0 @@ -36,6 +37,7 @@ require ( github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect @@ -43,6 +45,7 @@ require ( github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect github.com/google/uuid v1.3.1 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -52,6 +55,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/nutanix-cloud-native/prism-go-client v0.3.4 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.18.0 // indirect @@ -61,12 +65,14 @@ require ( github.com/stoewer/go-strcase v1.2.0 // indirect github.com/valyala/fastjson v1.6.4 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/net v0.19.0 // indirect + golang.org/x/net v0.24.0 // indirect golang.org/x/oauth2 v0.14.0 // indirect - golang.org/x/sys v0.16.0 // indirect - golang.org/x/term v0.15.0 // indirect + golang.org/x/sync v0.5.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/term v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.16.1 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect diff --git a/common/go.sum b/common/go.sum index c18f9d540..c3fe85b1e 100644 --- a/common/go.sum +++ b/common/go.sum @@ -1,121 +1,409 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/99designs/gqlgen v0.15.1/go.mod h1:nbeSjFkqphIqpZsYe1ULVz0yfH8hjpJdJIQoX/e0G2I= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= +github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/PaesslerAG/gval v1.0.0/go.mod h1:y/nm5yEyTeX6av0OfKJNp9rBNj2XrGhAf5+v24IBN1I= +github.com/PaesslerAG/jsonpath v0.1.0/go.mod h1:4BzmtoM/PI8fPO4aQGIusjGxGir2BzcV0grWtFzq1Y8= +github.com/PaesslerAG/jsonpath v0.1.1/go.mod h1:lVboNxFGal/VwW6d9JzIy56bUsYAP6tH/x80vjnCseY= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/agnivade/levenshtein v1.1.0/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY= github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/aws/aws-sdk-go v1.42.23/go.mod h1:gyRszuZ/icHmHAVE4gc/r+cfCmhA1AD+vqfWbgI+eHs= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/bnkamalesh/webgo/v4 v4.1.11/go.mod h1:taIAonQTzao8G5rnB22WgKmQuIOWHpQ0n/YLAidBXlM= +github.com/bnkamalesh/webgo/v6 v6.2.2/go.mod h1:2Y+dEdTp1xC/ra+3PAVZV6hh4sCI+iPK7mcHt+t9bfM= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creasty/defaults v1.5.2/go.mod h1:FPZ+Y0WNrbqOVw+c6av63eyHUAl6pMHZwqLPvXUZGfY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE= +github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= +github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg= +github.com/go-chi/cors v1.2.0/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= +github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-playground/validator/v10 v10.10.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA= github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= +github.com/goccy/go-json v0.9.4/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/cel-go v0.17.7 h1:6ebJFzu1xO2n7TLtN+UBqShGBhlD85bhvglh5DpcfqQ= github.com/google/cel-go v0.17.7/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY= +github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/keploy/go-sdk v0.4.3/go.mod h1:tn62gQ8a/AD7mY51DvQfhudiBPTlD+w3XtXemDcbON4= +github.com/kevinmbeaulieu/eq-go v1.0.0/go.mod h1:G3S8ajA56gKBZm4UB9AOyoOS37JO3roToPzKNM8dtdM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labstack/echo/v4 v4.6.1/go.mod h1:RnjgMWNDB9g/HucVWhQYNQP9PvbYf6adqftqryo7s9k= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= +github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ= +github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++0Gf8MBnAvE= +github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc= +github.com/lestrrat-go/jwx v1.2.20/go.mod h1:tLE1XszaFgd7zaS5wHe4NxA+XVhu7xgdRvDpNyi3kNM= +github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= +github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/matryer/moq v0.2.3/go.mod h1:9RtPYjTnH1bSBIkpvtHkFN7nbWAnO7oRpdJkEIn6UtE= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.2.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nutanix-cloud-native/prism-go-client v0.3.4 h1:bHY3VPrHHYnbRtkpGaKK+2ZmvUjNVRC55CYZbXIfnOk= +github.com/nutanix-cloud-native/prism-go-client v0.3.4/go.mod h1:tTIH02E6o6AWSShr98QChoxuZl+jBhkXFixom9+fd1Y= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo= github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= @@ -124,22 +412,54 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.35.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/vektah/gqlparser/v2 v2.2.0/go.mod h1:i3mQIGIrbK2PD1RrCeMTlVbkF2FJ6WkU1KJlJlC+3F4= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/etcd/api/v3 v3.5.10 h1:szRajuUUbLyppkhs9K6BRtjY37l66XQQmw7oZRANE4k= go.etcd.io/etcd/api/v3 v3.5.10/go.mod h1:TidfmT4Uycad3NM/o25fG3J07odo4GBB9hoxaodFCtI= go.etcd.io/etcd/client/pkg/v3 v3.5.10 h1:kfYIdQftBnbAq8pUWFXfpuuxFSKzlmM5cSn76JByiT0= go.etcd.io/etcd/client/pkg/v3 v3.5.10/go.mod h1:DYivfIviIuQ8+/lCq4vcxuseg2P2XbHygkKwFo9fc8U= go.etcd.io/etcd/client/v3 v3.5.10 h1:W9TXNZ+oB3MCd/8UjxHTWK5J9Nquw9fQBLJd5ne5/Ao= go.etcd.io/etcd/client/v3 v3.5.10/go.mod h1:RVeBnDz2PUEZqTpgqwAtUd8nAPf5kjyFyND7P1VkOKc= +go.keploy.io/server v0.1.8/go.mod h1:ZqhwTZOBb+dzx5t30Wt6eUGI6kO5QizvPg6coNPtbow= +go.mongodb.org/mongo-driver v1.8.0/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= +go.mongodb.org/mongo-driver v1.8.1/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 h1:PzIubN4/sjByhDRHLviCjJuweBXWFZWhghjg7cS28+M= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0/go.mod h1:Ct6zzQEuGK3WpJs2n4dn+wfJYzd/+hNnxMRTWjGn30M= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.0 h1:1eHu3/pUSWaOgltNK3WJFaywKsTIr/PwvHyDmi0lQA0= @@ -156,53 +476,284 @@ go.opentelemetry.io/otel/sdk v1.20.0 h1:5Jf6imeFZlZtKv9Qbo6qt2ZkmWtdWx/wzcCbNUlA go.opentelemetry.io/otel/sdk v1.20.0/go.mod h1:rmkSx1cZCm/tn16iWDn1GQbLtsW/LvsdEEFzCSRM6V0= go.opentelemetry.io/otel/trace v1.20.0 h1:+yxVAPZPbQhbC3OfAkeIVTky6iTFpcr4SiY9om7mXSQ= go.opentelemetry.io/otel/trace v1.20.0/go.mod h1:HJSK7F/hA5RlzpZ0zKDCHCDHm556LCDtKaAo6JmBFUU= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0= golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200815165600-90abf76919f3/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -211,51 +762,186 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb h1:XFBgcDwm7irdHTbz4Zk2h7Mh+eis4nfJEFQFYzJzuIA= google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb h1:lK0oleSc7IQsUxO3U5TjL9DWlsxpEBemh+zpB7IqhWI= google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM= google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.24.2/go.mod h1:AHqbSkTm6YrQ0ObxjO3Pmp/ubFF/KuM7jU+3khoBsOg= k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw= k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80= k8s.io/apiextensions-apiserver v0.29.3 h1:9HF+EtZaVpFjStakF4yVufnXGPRppWFEQ87qnO91YeI= k8s.io/apiextensions-apiserver v0.29.3/go.mod h1:po0XiY5scnpJfFizNGo6puNU6Fq6D70UJY2Cb2KwAVc= +k8s.io/apimachinery v0.24.2/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/apimachinery v0.24.3/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU= k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= k8s.io/apiserver v0.29.3 h1:xR7ELlJ/BZSr2n4CnD3lfA4gzFivh0wwfNfz9L0WZcE= k8s.io/apiserver v0.29.3/go.mod h1:hrvXlwfRulbMbBgmWRQlFru2b/JySDpmzvQwwk4GUOs= +k8s.io/client-go v0.24.2/go.mod h1:zg4Xaoo+umDsfCWr4fCnmLEtQXyCNXCvJuSsglNcV30= k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg= k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0= k8s.io/cluster-bootstrap v0.29.3 h1:DIMDZSN8gbFMy9CS2mAS2Iqq/fIUG783WN/1lqi5TF8= k8s.io/cluster-bootstrap v0.29.3/go.mod h1:aPAg1VtXx3uRrx5qU2jTzR7p1rf18zLXWS+pGhiqPto= k8s.io/component-base v0.29.3 h1:Oq9/nddUxlnrCuuR2K/jp6aflVvc0uDvxMzAWxnGzAo= k8s.io/component-base v0.29.3/go.mod h1:Yuj33XXjuOk2BAaHsIGHhCKZQAgYKhqIxIjIr2UXYio= +k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0 h1:TgtAeesdhpm2SGwkQasmbeqDo8th5wOBA5h/AjTKA4I= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0/go.mod h1:VHVDI/KrK4fjnV61bE2g3sA7tiETLn8sooImelsCx3Y= sigs.k8s.io/cluster-api v1.6.3 h1:VOlPNg92PQLlhBVLc5pg+cbAuPvGOOBujeFLk9zgnoo= @@ -264,9 +950,13 @@ sigs.k8s.io/cluster-api/test v1.6.3 h1:ZCboLCTpKWzSbf+f7MpQT7EN8aeH9DNhJC1T9/vAu sigs.k8s.io/cluster-api/test v1.6.3/go.mod h1:AKs25dgW6AnyGaQBoWuXfWnBs+FT7vJmAI/aox64DEI= sigs.k8s.io/controller-runtime v0.17.2 h1:FwHwD1CTUemg0pW2otk7/U5/i5m2ymzvOXdbeGOUvw0= sigs.k8s.io/controller-runtime v0.17.2/go.mod h1:+MngTvIQQQhfXtwfdGw/UOQ/aIaqsYywfCINOtwMO/s= +sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/common/pkg/server/server.go b/common/pkg/server/server.go index cdafc567d..0c56d95ed 100644 --- a/common/pkg/server/server.go +++ b/common/pkg/server/server.go @@ -48,13 +48,13 @@ func NewServerOptions() *ServerOptions { } func (s *ServerOptions) AddFlags(fs *pflag.FlagSet) { - fs.IntVar(&s.webhookPort, "webhook-port", s.webhookPort, "Webhook Server port") + fs.IntVar(&s.webhookPort, "runtime-webhook-port", s.webhookPort, "Runtime webhook server port") fs.StringVar( &s.webhookCertDir, - "webhook-cert-dir", + "runtime-webhook-cert-dir", s.webhookCertDir, - "Runtime hooks server cert dir.", + "Runtime webhook server cert dir", ) } diff --git a/common/pkg/testutils/capitest/patches.go b/common/pkg/testutils/capitest/patches.go index 43ce653ca..cffaaabf5 100644 --- a/common/pkg/testutils/capitest/patches.go +++ b/common/pkg/testutils/capitest/patches.go @@ -7,8 +7,8 @@ import ( "context" "encoding/json" "fmt" - "testing" + . "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" "github.com/onsi/gomega/gstruct" gomegatypes "github.com/onsi/gomega/types" @@ -39,54 +39,102 @@ type JSONPatchMatcher struct { } func ValidateGeneratePatches[T mutation.GeneratePatches]( - t *testing.T, + t GinkgoTInterface, handlerCreator func() T, testDefs ...PatchTestDef, ) { t.Helper() + testFunc := func(tt *PatchTestDef) { + g := gomega.NewWithT(t) + h := handlerCreator() + req := &runtimehooksv1.GeneratePatchesRequest{ + Variables: tt.Vars, + Items: []runtimehooksv1.GeneratePatchesRequestItem{ + tt.RequestItem, + { + HolderReference: runtimehooksv1.HolderReference{ + APIVersion: capiv1.GroupVersion.String(), + Kind: "Cluster", + Namespace: request.Namespace, + Name: request.ClusterName, + }, + }, + }, + } + resp := &runtimehooksv1.GeneratePatchesResponse{} + h.GeneratePatches(context.Background(), req, resp) + expectedStatus := runtimehooksv1.ResponseStatusSuccess + if tt.ExpectedFailure { + expectedStatus = runtimehooksv1.ResponseStatusFailure + } + g.Expect(resp.Status). + To(gomega.Equal(expectedStatus), fmt.Sprintf("Message: %s", resp.Message)) + + if len(tt.ExpectedPatchMatchers) == 0 { + g.Expect(resp.Items).To(gomega.BeEmpty()) + return + } + g.Expect(resp.Items).To(containPatches(&tt.RequestItem, tt.ExpectedPatchMatchers...)) + + if len(tt.UnexpectedPatchMatchers) > 0 { + g.Expect(resp.Items). + ToNot(containPatches(&tt.RequestItem, tt.UnexpectedPatchMatchers...)) + } + } + // compose Ginkgo table arguments + // https://onsi.github.io/ginkgo/#table-specs for more details + testArgs := make([]TableEntry, 0, len(testDefs)) for testIdx := range testDefs { tt := testDefs[testIdx] + testArgs = append(testArgs, Entry(tt.Name, &tt)) + } + DescribeTable("Patches", testFunc, testArgs) +} - t.Run(tt.Name, func(t *testing.T) { - t.Parallel() - - g := gomega.NewWithT(t) - h := handlerCreator() - req := &runtimehooksv1.GeneratePatchesRequest{ - Variables: tt.Vars, - Items: []runtimehooksv1.GeneratePatchesRequestItem{ - tt.RequestItem, - { - HolderReference: runtimehooksv1.HolderReference{ - APIVersion: capiv1.GroupVersion.String(), - Kind: "Cluster", - Namespace: request.Namespace, - Name: request.ClusterName, - }, - }, +// TODO(shalinpatel): AssertGeneratePatches is a replacement of ValidateGeneratePatches function. +// remove ValidateGeneratePatches once all the shared test cases are moved to their own patch package. +func AssertGeneratePatches[T mutation.GeneratePatches]( + t GinkgoTInterface, + handlerCreator func() T, + tt *PatchTestDef, +) { + t.Helper() + + g := gomega.NewWithT(t) + h := handlerCreator() + req := &runtimehooksv1.GeneratePatchesRequest{ + Variables: tt.Vars, + Items: []runtimehooksv1.GeneratePatchesRequestItem{ + tt.RequestItem, + { + HolderReference: runtimehooksv1.HolderReference{ + APIVersion: capiv1.GroupVersion.String(), + Kind: "Cluster", + Namespace: request.Namespace, + Name: request.ClusterName, }, - } - resp := &runtimehooksv1.GeneratePatchesResponse{} - h.GeneratePatches(context.Background(), req, resp) - expectedStatus := runtimehooksv1.ResponseStatusSuccess - if tt.ExpectedFailure { - expectedStatus = runtimehooksv1.ResponseStatusFailure - } - g.Expect(resp.Status). - To(gomega.Equal(expectedStatus), fmt.Sprintf("Message: %s", resp.Message)) - - if len(tt.ExpectedPatchMatchers) == 0 { - g.Expect(resp.Items).To(gomega.BeEmpty()) - return - } - g.Expect(resp.Items).To(containPatches(&tt.RequestItem, tt.ExpectedPatchMatchers...)) - - if len(tt.UnexpectedPatchMatchers) > 0 { - g.Expect(resp.Items). - ToNot(containPatches(&tt.RequestItem, tt.UnexpectedPatchMatchers...)) - } - }) + }, + }, + } + resp := &runtimehooksv1.GeneratePatchesResponse{} + h.GeneratePatches(context.Background(), req, resp) + expectedStatus := runtimehooksv1.ResponseStatusSuccess + if tt.ExpectedFailure { + expectedStatus = runtimehooksv1.ResponseStatusFailure + } + g.Expect(resp.Status). + To(gomega.Equal(expectedStatus), fmt.Sprintf("Message: %s", resp.Message)) + + if len(tt.ExpectedPatchMatchers) == 0 { + g.Expect(resp.Items).To(gomega.BeEmpty()) + return + } + g.Expect(resp.Items).To(containPatches(&tt.RequestItem, tt.ExpectedPatchMatchers...)) + + if len(tt.UnexpectedPatchMatchers) > 0 { + g.Expect(resp.Items). + ToNot(containPatches(&tt.RequestItem, tt.UnexpectedPatchMatchers...)) } } diff --git a/common/pkg/testutils/capitest/request/items.go b/common/pkg/testutils/capitest/request/items.go index b3cd91664..dcc7b7144 100644 --- a/common/pkg/testutils/capitest/request/items.go +++ b/common/pkg/testutils/capitest/request/items.go @@ -15,6 +15,7 @@ import ( runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" capdv1 "sigs.k8s.io/cluster-api/test/infrastructure/docker/api/v1beta1" + capxv1 "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/external/github.com/nutanix-cloud-native/cluster-api-provider-nutanix/api/v1beta1" capav1 "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/external/sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/serializer" ) @@ -163,6 +164,39 @@ func NewAWSClusterTemplateRequestItem( ) } +func NewNutanixClusterTemplateRequestItem( + uid types.UID, + existingSpec ...capxv1.NutanixClusterTemplateSpec, +) runtimehooksv1.GeneratePatchesRequestItem { + nutanixClusterTemplate := &capxv1.NutanixClusterTemplate{ + TypeMeta: metav1.TypeMeta{ + APIVersion: capxv1.GroupVersion.String(), + Kind: "NutanixClusterTemplate", + }, + } + + switch len(existingSpec) { + case 0: + // Do nothing. + case 1: + nutanixClusterTemplate.Spec = existingSpec[0] + default: + panic("can only take at most one existing spec") + } + + return NewRequestItem( + nutanixClusterTemplate, + &runtimehooksv1.HolderReference{ + APIVersion: clusterv1.GroupVersion.String(), + Kind: "Cluster", + FieldPath: "spec.infrastructureRef", + Name: ClusterName, + Namespace: Namespace, + }, + uid, + ) +} + func NewCPDockerMachineTemplateRequestItem( uid types.UID, ) runtimehooksv1.GeneratePatchesRequestItem { @@ -254,3 +288,49 @@ func NewWorkerAWSMachineTemplateRequestItem( uid, ) } + +func NewCPNutanixMachineTemplateRequestItem( + uid types.UID, +) runtimehooksv1.GeneratePatchesRequestItem { + return NewRequestItem( + &capxv1.NutanixMachineTemplate{ + TypeMeta: metav1.TypeMeta{ + APIVersion: capxv1.GroupVersion.String(), + Kind: "NutanixMachineTemplate", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nutanix-machine-template", + Namespace: "nutanix-cluster", + }, + }, + &runtimehooksv1.HolderReference{ + APIVersion: controlplanev1.GroupVersion.String(), + Kind: "KubeadmControlPlane", + FieldPath: "spec.machineTemplate.infrastructureRef", + }, + uid, + ) +} + +func NewWorkerNutanixMachineTemplateRequestItem( + uid types.UID, +) runtimehooksv1.GeneratePatchesRequestItem { + return NewRequestItem( + &capxv1.NutanixMachineTemplate{ + TypeMeta: metav1.TypeMeta{ + APIVersion: capxv1.GroupVersion.String(), + Kind: "NutanixMachineTemplate", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nutanix-machine-template", + Namespace: "nutanix-cluster", + }, + }, + &runtimehooksv1.HolderReference{ + APIVersion: clusterv1.GroupVersion.String(), + Kind: "MachineDeployment", + FieldPath: "spec.template.spec.infrastructureRef", + }, + uid, + ) +} diff --git a/common/pkg/testutils/openapi/convert.go b/common/pkg/testutils/openapi/convert.go index 922e4f0b5..5eb248617 100644 --- a/common/pkg/testutils/openapi/convert.go +++ b/common/pkg/testutils/openapi/convert.go @@ -14,7 +14,7 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" ) -// convertToAPIExtensionsJSONSchemaProps converts a clusterv1.JSONSchemaProps to apiextensions.JSONSchemaProp. +// ConvertToAPIExtensionsJSONSchemaProps converts a clusterv1.JSONSchemaProps to apiextensions.JSONSchemaProp. // NOTE: This is used whenever we want to use one of the upstream libraries, as they use apiextensions.JSONSchemaProp. // NOTE: If new fields are added to clusterv1.JSONSchemaProps (e.g. to support complex types), the corresponding // schema validation must be added to validateRootSchema too. diff --git a/common/pkg/testutils/openapi/validate.go b/common/pkg/testutils/openapi/validate.go index 9ef8f4ea8..bf776db6d 100644 --- a/common/pkg/testutils/openapi/validate.go +++ b/common/pkg/testutils/openapi/validate.go @@ -10,6 +10,7 @@ import ( "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema" + "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting" structuralpruning "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/pruning" "k8s.io/apiextensions-apiserver/pkg/apiserver/validation" "k8s.io/apimachinery/pkg/util/validation/field" @@ -62,6 +63,18 @@ func ValidateClusterVariable( )} } + s, err := structuralschema.NewStructural(apiExtensionsSchema) + if err != nil { + return field.ErrorList{field.InternalError(fldPath, + fmt.Errorf( + "failed to create structural schema for variable %q; ClusterClass should be checked: %v", + value.Name, + err, + ), + )} + } + defaulting.Default(variableValue, s) + // Validate variable against the schema. // NOTE: We're reusing a library func used in CRD validation. if err := validation.ValidateCustomResource(fldPath, variableValue, validator); err != nil { diff --git a/devbox.lock b/devbox.lock index 73b569425..59ee0c2bb 100644 --- a/devbox.lock +++ b/devbox.lock @@ -8,43 +8,15 @@ "version": "1.6.27", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/nnhjp60w14pdy1hjgxn9dfsg4nbfs93v-actionlint-1.6.27", - "default": true - } - ], "store_path": "/nix/store/nnhjp60w14pdy1hjgxn9dfsg4nbfs93v-actionlint-1.6.27" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/p8rc44x83sld3wk1m4y27za9n1mjyk48-actionlint-1.6.27", - "default": true - } - ], "store_path": "/nix/store/p8rc44x83sld3wk1m4y27za9n1mjyk48-actionlint-1.6.27" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/7v1lh7c3pwlax5fy7pw233czn4hs89hl-actionlint-1.6.27", - "default": true - } - ], "store_path": "/nix/store/7v1lh7c3pwlax5fy7pw233czn4hs89hl-actionlint-1.6.27" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/56xxvs3yijdmhhxbfvps38nz95xdxgyl-actionlint-1.6.27", - "default": true - } - ], "store_path": "/nix/store/56xxvs3yijdmhhxbfvps38nz95xdxgyl-actionlint-1.6.27" } } @@ -56,43 +28,15 @@ "version": "3.10.1", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/5lygcps2ly736k0dvk2w4mx6yy8dzz6s-chart-testing-3.10.1", - "default": true - } - ], "store_path": "/nix/store/5lygcps2ly736k0dvk2w4mx6yy8dzz6s-chart-testing-3.10.1" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/103km469ldwrzs525m57bj409vxjlniw-chart-testing-3.10.1", - "default": true - } - ], "store_path": "/nix/store/103km469ldwrzs525m57bj409vxjlniw-chart-testing-3.10.1" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/c4qgh783pvhzqpv7skimzfs75pklbz4f-chart-testing-3.10.1", - "default": true - } - ], "store_path": "/nix/store/c4qgh783pvhzqpv7skimzfs75pklbz4f-chart-testing-3.10.1" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/wzscgl31rzxws05y7vxkl0f65fsiydmm-chart-testing-3.10.1", - "default": true - } - ], "store_path": "/nix/store/wzscgl31rzxws05y7vxkl0f65fsiydmm-chart-testing-3.10.1" } } @@ -104,43 +48,15 @@ "version": "1.6.3", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/6hsi8bj8ddbrv29jff467q1rqcfgs964-clusterctl-1.6.3", - "default": true - } - ], "store_path": "/nix/store/6hsi8bj8ddbrv29jff467q1rqcfgs964-clusterctl-1.6.3" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/jysji089cqrygcwl2ryvbb0sa3cjj8vv-clusterctl-1.6.3", - "default": true - } - ], "store_path": "/nix/store/jysji089cqrygcwl2ryvbb0sa3cjj8vv-clusterctl-1.6.3" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/gbch3dp061am97j79b56pp07121j781k-clusterctl-1.6.3", - "default": true - } - ], "store_path": "/nix/store/gbch3dp061am97j79b56pp07121j781k-clusterctl-1.6.3" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/1xy66gcyxaqjhb5h15slyxzw64d920c6-clusterctl-1.6.3", - "default": true - } - ], "store_path": "/nix/store/1xy66gcyxaqjhb5h15slyxzw64d920c6-clusterctl-1.6.3" } } @@ -152,67 +68,15 @@ "version": "9.4", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/vinxz6lkrilb72dkzr3ny02nnvql6z50-coreutils-9.4", - "default": true - }, - { - "name": "info", - "path": "/nix/store/mgvngj0x370glcix9wfaripy3bx5j3s0-coreutils-9.4-info" - } - ], "store_path": "/nix/store/vinxz6lkrilb72dkzr3ny02nnvql6z50-coreutils-9.4" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/77fyfwmz29cz9j5x6yw2wrlm4rvasldv-coreutils-9.4", - "default": true - }, - { - "name": "debug", - "path": "/nix/store/fkfnrbg62vbsdagml1c9vld3wpk3079n-coreutils-9.4-debug" - }, - { - "name": "info", - "path": "/nix/store/x14ppijm82bac50bgf2varjvdm0mr28f-coreutils-9.4-info" - } - ], "store_path": "/nix/store/77fyfwmz29cz9j5x6yw2wrlm4rvasldv-coreutils-9.4" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/spm5vvna0wwkknavwfgxdsbq48r5374a-coreutils-9.4", - "default": true - }, - { - "name": "info", - "path": "/nix/store/mpgbpc0iyn2wy1668b1f2lzw2b967zsh-coreutils-9.4-info" - } - ], "store_path": "/nix/store/spm5vvna0wwkknavwfgxdsbq48r5374a-coreutils-9.4" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/mb488rr560vq1xnl10hinnyfflcrd51n-coreutils-9.4", - "default": true - }, - { - "name": "debug", - "path": "/nix/store/cgw6a9mc6v28dfbxjgprwv9dam57djsd-coreutils-9.4-debug" - }, - { - "name": "info", - "path": "/nix/store/m2zz2awv8a8118axl7qliq6dwpwb7q0v-coreutils-9.4-info" - } - ], "store_path": "/nix/store/mb488rr560vq1xnl10hinnyfflcrd51n-coreutils-9.4" } } @@ -224,43 +88,15 @@ "version": "1.4.2", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/7lwyjdf9bhyihfdr9gm7ajhjbliphv34-envsubst-1.4.2", - "default": true - } - ], "store_path": "/nix/store/7lwyjdf9bhyihfdr9gm7ajhjbliphv34-envsubst-1.4.2" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/qsly6czilaakclr57zlaxl0y574f6lj3-envsubst-1.4.2", - "default": true - } - ], "store_path": "/nix/store/qsly6czilaakclr57zlaxl0y574f6lj3-envsubst-1.4.2" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/i5kjf63ns7b1scw1k3jvwlj6m5lpz3mi-envsubst-1.4.2", - "default": true - } - ], "store_path": "/nix/store/i5kjf63ns7b1scw1k3jvwlj6m5lpz3mi-envsubst-1.4.2" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/vgglbbxi4wa85g3fzb71jxbddkklhpvg-envsubst-1.4.2", - "default": true - } - ], "store_path": "/nix/store/vgglbbxi4wa85g3fzb71jxbddkklhpvg-envsubst-1.4.2" } } @@ -272,75 +108,15 @@ "version": "4.9.0", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/46ilsgv2hj073d3ghqv209bl95djki8q-findutils-4.9.0", - "default": true - }, - { - "name": "info", - "path": "/nix/store/a5bp80lnmw0cbmfqjxhkz1p5h825ii3p-findutils-4.9.0-info" - }, - { - "name": "locate", - "path": "/nix/store/af9i1zykrns1ha3rhzkpdvyxa1kkhg1z-findutils-4.9.0-locate" - } - ], "store_path": "/nix/store/46ilsgv2hj073d3ghqv209bl95djki8q-findutils-4.9.0" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/ry6g1kym7g3i8813msq7b0gzqbdj1rfk-findutils-4.9.0", - "default": true - }, - { - "name": "info", - "path": "/nix/store/fi8k5nm1279ag30y5ghgzhadcqp00mrn-findutils-4.9.0-info" - }, - { - "name": "locate", - "path": "/nix/store/m17gn49zqfg4z43816p77fzpxlqrlk8d-findutils-4.9.0-locate" - } - ], "store_path": "/nix/store/ry6g1kym7g3i8813msq7b0gzqbdj1rfk-findutils-4.9.0" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/iqka9ifkjwwz0cf5dz8w7fl9p41p142p-findutils-4.9.0", - "default": true - }, - { - "name": "info", - "path": "/nix/store/r2p9iavm6ypdijwdm96c2jfhl8krs0xx-findutils-4.9.0-info" - }, - { - "name": "locate", - "path": "/nix/store/qwsfnln108rys79d8fnfa3qrajz03h39-findutils-4.9.0-locate" - } - ], "store_path": "/nix/store/iqka9ifkjwwz0cf5dz8w7fl9p41p142p-findutils-4.9.0" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/rr5pqqck5f6fjkv7agwjyhaljvh27ncn-findutils-4.9.0", - "default": true - }, - { - "name": "info", - "path": "/nix/store/9c6pyndcn1lr2wm210zylp3wv37b04g0-findutils-4.9.0-info" - }, - { - "name": "locate", - "path": "/nix/store/jsyyg8i8rbxj1gaki1szcl66cj93z8xk-findutils-4.9.0-locate" - } - ], "store_path": "/nix/store/rr5pqqck5f6fjkv7agwjyhaljvh27ncn-findutils-4.9.0" } } @@ -352,43 +128,15 @@ "version": "2.45.0", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/ijbvh0hln26a39q5m7iavyvyj83adja9-gh-2.45.0", - "default": true - } - ], "store_path": "/nix/store/ijbvh0hln26a39q5m7iavyvyj83adja9-gh-2.45.0" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/33v7d9a7x91lzvyr8d76mzb5022glay4-gh-2.45.0", - "default": true - } - ], "store_path": "/nix/store/33v7d9a7x91lzvyr8d76mzb5022glay4-gh-2.45.0" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/snd8ridf0apgbk7ngyf4waqdk72mr9pm-gh-2.45.0", - "default": true - } - ], "store_path": "/nix/store/snd8ridf0apgbk7ngyf4waqdk72mr9pm-gh-2.45.0" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/1krwa1i8qyxapbp6n02r4niwiqhhnaln-gh-2.45.0", - "default": true - } - ], "store_path": "/nix/store/1krwa1i8qyxapbp6n02r4niwiqhhnaln-gh-2.45.0" } } @@ -400,43 +148,15 @@ "version": "2.17.0", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/lxq0cx9km60yjwf63pmf1j3rbfr82q7x-ginkgo-2.17.0", - "default": true - } - ], "store_path": "/nix/store/lxq0cx9km60yjwf63pmf1j3rbfr82q7x-ginkgo-2.17.0" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/1bf8v0b9k0pmmrs06h6zw13cxl0pmd9q-ginkgo-2.17.0", - "default": true - } - ], "store_path": "/nix/store/1bf8v0b9k0pmmrs06h6zw13cxl0pmd9q-ginkgo-2.17.0" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/7qyd3jfipsx9gj0wbkms245hkidlfb0f-ginkgo-2.17.0", - "default": true - } - ], "store_path": "/nix/store/7qyd3jfipsx9gj0wbkms245hkidlfb0f-ginkgo-2.17.0" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/bavqnxdhcvlpmkjvd0hcnhj3dlrrysmc-ginkgo-2.17.0", - "default": true - } - ], "store_path": "/nix/store/bavqnxdhcvlpmkjvd0hcnhj3dlrrysmc-ginkgo-2.17.0" } } @@ -448,67 +168,15 @@ "version": "2.43.2", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/4zyn11089z9i567c84d3628bq2k56q8r-git-2.43.2", - "default": true - }, - { - "name": "doc", - "path": "/nix/store/0vaafk5p4hh797n28w4nx1c5ykjq1kxc-git-2.43.2-doc" - } - ], "store_path": "/nix/store/4zyn11089z9i567c84d3628bq2k56q8r-git-2.43.2" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/6dmwwsmj6bp9ss0bsw5j042zjzx5p2kw-git-2.43.2", - "default": true - }, - { - "name": "debug", - "path": "/nix/store/yx6wsnivlrbwajqbf933pj733ypj4bbd-git-2.43.2-debug" - }, - { - "name": "doc", - "path": "/nix/store/bxyy779y05dkvbzvpic3knycd3iyx8m4-git-2.43.2-doc" - } - ], "store_path": "/nix/store/6dmwwsmj6bp9ss0bsw5j042zjzx5p2kw-git-2.43.2" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/ffnl9nk43qffalwvmb0bnhz3j2glkk1h-git-2.43.2", - "default": true - }, - { - "name": "doc", - "path": "/nix/store/8vpr8jl73k5m2cqbz4d16a6i3nw7x82s-git-2.43.2-doc" - } - ], "store_path": "/nix/store/ffnl9nk43qffalwvmb0bnhz3j2glkk1h-git-2.43.2" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/fmx804pc0bs1966xq5bb67kark2mww9r-git-2.43.2", - "default": true - }, - { - "name": "debug", - "path": "/nix/store/sga5blm5fi6ad5hj4kg5m9m4gxgqmciv-git-2.43.2-debug" - }, - { - "name": "doc", - "path": "/nix/store/z9784jl4061n5bnbwjz2qd93c50r98m9-git-2.43.2-doc" - } - ], "store_path": "/nix/store/fmx804pc0bs1966xq5bb67kark2mww9r-git-2.43.2" } } @@ -520,87 +188,15 @@ "version": "4.4.1", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/b4jbbx01f3n4r97g49jnxw3clf4p1szv-gnumake-4.4.1", - "default": true - }, - { - "name": "man", - "path": "/nix/store/maj11ssnlx9lqd3vfmpfh45l6p5bxvv4-gnumake-4.4.1-man", - "default": true - }, - { - "name": "info", - "path": "/nix/store/rp5gi5y0dd2hzww57x7gkya7g2qg6rrq-gnumake-4.4.1-info" - } - ], "store_path": "/nix/store/b4jbbx01f3n4r97g49jnxw3clf4p1szv-gnumake-4.4.1" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/hfyyfn6h7cw2fvdc0jmpfigwi3rp7d2x-gnumake-4.4.1", - "default": true - }, - { - "name": "man", - "path": "/nix/store/z20ifgpnah4nnls4ylkij1gmrhg6bv10-gnumake-4.4.1-man", - "default": true - }, - { - "name": "debug", - "path": "/nix/store/m8zg8w7m00hxjwxwcj1apcra1lh58j50-gnumake-4.4.1-debug" - }, - { - "name": "info", - "path": "/nix/store/sk9zc9r371pcpj5hhjck79h8a2i726qv-gnumake-4.4.1-info" - } - ], "store_path": "/nix/store/hfyyfn6h7cw2fvdc0jmpfigwi3rp7d2x-gnumake-4.4.1" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/ry772inn49ra4vmns2vg67bjpqv29j0k-gnumake-4.4.1", - "default": true - }, - { - "name": "man", - "path": "/nix/store/dbd3dygcpky1s2yls12wzy8rsh0dzjbr-gnumake-4.4.1-man", - "default": true - }, - { - "name": "info", - "path": "/nix/store/4yi8y2v3h62jz3jia5zi60nrxgfbn05w-gnumake-4.4.1-info" - } - ], "store_path": "/nix/store/ry772inn49ra4vmns2vg67bjpqv29j0k-gnumake-4.4.1" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/0pkbh7939p7npr02ayama32pa389m5p7-gnumake-4.4.1", - "default": true - }, - { - "name": "man", - "path": "/nix/store/h420v4pzqavirmna1fz0r5cz3nbq65xq-gnumake-4.4.1-man", - "default": true - }, - { - "name": "debug", - "path": "/nix/store/l8523cnr1cn189yzchcq30iml87dr5a4-gnumake-4.4.1-debug" - }, - { - "name": "info", - "path": "/nix/store/bnjzmbm99sgviynsqxhz831cc9gvidnl-gnumake-4.4.1-info" - } - ], "store_path": "/nix/store/0pkbh7939p7npr02ayama32pa389m5p7-gnumake-4.4.1" } } @@ -612,59 +208,15 @@ "version": "4.9", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/zmmrhy50wsyg5k9wrmbpcnjz6swdsk58-gnused-4.9", - "default": true - }, - { - "name": "info", - "path": "/nix/store/bbq8l0cn5rsbf3nmfl4sxkynwzjwzxk4-gnused-4.9-info" - } - ], "store_path": "/nix/store/zmmrhy50wsyg5k9wrmbpcnjz6swdsk58-gnused-4.9" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/jcpl9xd17v9c8aqkdwakhw3mymmagshp-gnused-4.9", - "default": true - }, - { - "name": "info", - "path": "/nix/store/8jz40706yyf04dwbm1nl0mc4z905iq17-gnused-4.9-info" - } - ], "store_path": "/nix/store/jcpl9xd17v9c8aqkdwakhw3mymmagshp-gnused-4.9" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/wjlj4v79x8zxnx5ylfgmiqp36vm28ii6-gnused-4.9", - "default": true - }, - { - "name": "info", - "path": "/nix/store/75dy9xdcnfj9j0z7wd8jrlrgb0n96mb1-gnused-4.9-info" - } - ], "store_path": "/nix/store/wjlj4v79x8zxnx5ylfgmiqp36vm28ii6-gnused-4.9" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/q7kq0naays5251ihghw0ccsz39id7kk5-gnused-4.9", - "default": true - }, - { - "name": "info", - "path": "/nix/store/a81qmwij2v1pjwsq739l482fxkkn4s07-gnused-4.9-info" - } - ], "store_path": "/nix/store/q7kq0naays5251ihghw0ccsz39id7kk5-gnused-4.9" } } @@ -676,43 +228,15 @@ "version": "1.22.1", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/k9srp8ngvblscg68fdpcyqkydh86429k-go-1.22.1", - "default": true - } - ], "store_path": "/nix/store/k9srp8ngvblscg68fdpcyqkydh86429k-go-1.22.1" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/i604lwmgw1s8w3bdvsiz4332rx7fsb87-go-1.22.1", - "default": true - } - ], "store_path": "/nix/store/i604lwmgw1s8w3bdvsiz4332rx7fsb87-go-1.22.1" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/5dyp491ka5hx1g788dvnvadzk9kn92bp-go-1.22.1", - "default": true - } - ], "store_path": "/nix/store/5dyp491ka5hx1g788dvnvadzk9kn92bp-go-1.22.1" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/wkbckbd30nlhq4dxzg64q6y4vm1xx4fk-go-1.22.1", - "default": true - } - ], "store_path": "/nix/store/wkbckbd30nlhq4dxzg64q6y4vm1xx4fk-go-1.22.1" } } @@ -724,43 +248,15 @@ "version": "0.12.14", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/yzkh6mg18q5c2dxlrsdi750imm41div4-gojq-0.12.14", - "default": true - } - ], "store_path": "/nix/store/yzkh6mg18q5c2dxlrsdi750imm41div4-gojq-0.12.14" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/xb0b2ng2m3xdm5iah0cjbqsaqiz87qza-gojq-0.12.14", - "default": true - } - ], "store_path": "/nix/store/xb0b2ng2m3xdm5iah0cjbqsaqiz87qza-gojq-0.12.14" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/1y68nsm5nx450lnvc6gr8as7231sfbhz-gojq-0.12.14", - "default": true - } - ], "store_path": "/nix/store/1y68nsm5nx450lnvc6gr8as7231sfbhz-gojq-0.12.14" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/1ivsp6qjrkr3d2gzax6w8iai55qagliv-gojq-0.12.14", - "default": true - } - ], "store_path": "/nix/store/1ivsp6qjrkr3d2gzax6w8iai55qagliv-gojq-0.12.14" } } @@ -772,43 +268,15 @@ "version": "0.12.2", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/225jl3pgxsgivmvggjbijywn0vfc0nd1-golines-0.12.2", - "default": true - } - ], "store_path": "/nix/store/225jl3pgxsgivmvggjbijywn0vfc0nd1-golines-0.12.2" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/nvfxf1ss0f2kq6cj1apa86yn7sxgxyb2-golines-0.12.2", - "default": true - } - ], "store_path": "/nix/store/nvfxf1ss0f2kq6cj1apa86yn7sxgxyb2-golines-0.12.2" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/jyyd2n5vbwl95a6jkb3xficbj54p8fj4-golines-0.12.2", - "default": true - } - ], "store_path": "/nix/store/jyyd2n5vbwl95a6jkb3xficbj54p8fj4-golines-0.12.2" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/nach7a7wc17kb36pvfvjcbms2p99kfr5-golines-0.12.2", - "default": true - } - ], "store_path": "/nix/store/nach7a7wc17kb36pvfvjcbms2p99kfr5-golines-0.12.2" } } @@ -820,43 +288,15 @@ "version": "1.24.0", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/8dd7j0fgivrb62jq43pyvmmwz5q9qgas-goreleaser-1.24.0", - "default": true - } - ], "store_path": "/nix/store/8dd7j0fgivrb62jq43pyvmmwz5q9qgas-goreleaser-1.24.0" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/d0z2c8vwijna7cfdp28r5kj6p5f1abzs-goreleaser-1.24.0", - "default": true - } - ], "store_path": "/nix/store/d0z2c8vwijna7cfdp28r5kj6p5f1abzs-goreleaser-1.24.0" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/4rnps0ylkz2jaw1qdp29m0pyx62mkv93-goreleaser-1.24.0", - "default": true - } - ], "store_path": "/nix/store/4rnps0ylkz2jaw1qdp29m0pyx62mkv93-goreleaser-1.24.0" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/ys8s2aigrqa8ylsj58wh3qvj61i0lirb-goreleaser-1.24.0", - "default": true - } - ], "store_path": "/nix/store/ys8s2aigrqa8ylsj58wh3qvj61i0lirb-goreleaser-1.24.0" } } @@ -868,43 +308,15 @@ "version": "1.10.1", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/0z0jj1xpcnbgmfymdll7qjggffv6x09f-gotestsum-1.10.1", - "default": true - } - ], "store_path": "/nix/store/0z0jj1xpcnbgmfymdll7qjggffv6x09f-gotestsum-1.10.1" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/ii73x40gwzwgm7wp5svkay6k65w722ab-gotestsum-1.10.1", - "default": true - } - ], "store_path": "/nix/store/ii73x40gwzwgm7wp5svkay6k65w722ab-gotestsum-1.10.1" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/glqkgvz7ll2vg7mfm1nlrd49a1dwfpb2-gotestsum-1.10.1", - "default": true - } - ], "store_path": "/nix/store/glqkgvz7ll2vg7mfm1nlrd49a1dwfpb2-gotestsum-1.10.1" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/bydqdbg757r6g5r24bf6s7yb9zdc1q4d-gotestsum-1.10.1", - "default": true - } - ], "store_path": "/nix/store/bydqdbg757r6g5r24bf6s7yb9zdc1q4d-gotestsum-1.10.1" } } @@ -916,43 +328,15 @@ "version": "1.0.4", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/kcqx91mgrw03wgqzzx96xprfjzkkss96-govulncheck-1.0.4", - "default": true - } - ], "store_path": "/nix/store/kcqx91mgrw03wgqzzx96xprfjzkkss96-govulncheck-1.0.4" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/4m6afbm7qm1rq5ql9a0x4xcyzlj5i627-govulncheck-1.0.4", - "default": true - } - ], "store_path": "/nix/store/4m6afbm7qm1rq5ql9a0x4xcyzlj5i627-govulncheck-1.0.4" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/nx4dsdypvbvizasrgjhpv82kw0fjlgm4-govulncheck-1.0.4", - "default": true - } - ], "store_path": "/nix/store/nx4dsdypvbvizasrgjhpv82kw0fjlgm4-govulncheck-1.0.4" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/kp3rjfdaxjx0m021nxp0kng5xx26p2j5-govulncheck-1.0.4", - "default": true - } - ], "store_path": "/nix/store/kp3rjfdaxjx0m021nxp0kng5xx26p2j5-govulncheck-1.0.4" } } @@ -964,43 +348,15 @@ "version": "1.11.2", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/dkky99zsrdkwz9hxprsq5zzha94jsrbj-helm-docs-1.11.2", - "default": true - } - ], "store_path": "/nix/store/dkky99zsrdkwz9hxprsq5zzha94jsrbj-helm-docs-1.11.2" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/3wamvq5mnmayksx0yy5ch9zb8mq42hd2-helm-docs-1.11.2", - "default": true - } - ], "store_path": "/nix/store/3wamvq5mnmayksx0yy5ch9zb8mq42hd2-helm-docs-1.11.2" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/1pfj5vzrss4xd34hi0b119hpm5ymd9hv-helm-docs-1.11.2", - "default": true - } - ], "store_path": "/nix/store/1pfj5vzrss4xd34hi0b119hpm5ymd9hv-helm-docs-1.11.2" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/zdzdnhdbqfrm2lczw4h38ziipr4ixs1d-helm-docs-1.11.2", - "default": true - } - ], "store_path": "/nix/store/zdzdnhdbqfrm2lczw4h38ziipr4ixs1d-helm-docs-1.11.2" } } @@ -1012,43 +368,15 @@ "version": "0.124.0", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/rfkfvwigv0282mw24n3cdym7cyi542jy-hugo-0.124.0", - "default": true - } - ], "store_path": "/nix/store/rfkfvwigv0282mw24n3cdym7cyi542jy-hugo-0.124.0" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/0rnv4sichypqbprm4nqd0jjc27z5286c-hugo-0.124.0", - "default": true - } - ], "store_path": "/nix/store/0rnv4sichypqbprm4nqd0jjc27z5286c-hugo-0.124.0" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/ip5zhc1rf2li8cmjwffjrj42r7nkkj4a-hugo-0.124.0", - "default": true - } - ], "store_path": "/nix/store/ip5zhc1rf2li8cmjwffjrj42r7nkkj4a-hugo-0.124.0" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/8syg9xrkp3nbpb1r4b469068kvk8gq2a-hugo-0.124.0", - "default": true - } - ], "store_path": "/nix/store/8syg9xrkp3nbpb1r4b469068kvk8gq2a-hugo-0.124.0" } } @@ -1060,43 +388,15 @@ "version": "0.22.0", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/87yk9f9zqzl4q6cawsw78l9c872xk3zb-kind-0.22.0", - "default": true - } - ], "store_path": "/nix/store/87yk9f9zqzl4q6cawsw78l9c872xk3zb-kind-0.22.0" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/hd04jgix5j5dj1grcgh9wwlz636243h6-kind-0.22.0", - "default": true - } - ], "store_path": "/nix/store/hd04jgix5j5dj1grcgh9wwlz636243h6-kind-0.22.0" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/114mq99ncln9x8h4mxpcjsflbh9qawxs-kind-0.22.0", - "default": true - } - ], "store_path": "/nix/store/114mq99ncln9x8h4mxpcjsflbh9qawxs-kind-0.22.0" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/d0wmw2i217vak50q2h94wic5ipi11jir-kind-0.22.0", - "default": true - } - ], "store_path": "/nix/store/d0wmw2i217vak50q2h94wic5ipi11jir-kind-0.22.0" } } @@ -1108,43 +408,15 @@ "version": "0.15.2", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/5d6ri1axxv9cwsac5lg1vz288zk5vf3s-ko-0.15.2", - "default": true - } - ], "store_path": "/nix/store/5d6ri1axxv9cwsac5lg1vz288zk5vf3s-ko-0.15.2" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/8d62nfbvadkbrqlp20451cm0pgp6zjxg-ko-0.15.2", - "default": true - } - ], "store_path": "/nix/store/8d62nfbvadkbrqlp20451cm0pgp6zjxg-ko-0.15.2" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/mbikn788gr04c9kkzkdic4w05l8nq7z9-ko-0.15.2", - "default": true - } - ], "store_path": "/nix/store/mbikn788gr04c9kkzkdic4w05l8nq7z9-ko-0.15.2" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/11h314lbbbj0989drrwc4bi2xrwvxywv-ko-0.15.2", - "default": true - } - ], "store_path": "/nix/store/11h314lbbbj0989drrwc4bi2xrwvxywv-ko-0.15.2" } } @@ -1156,43 +428,15 @@ "version": "3.14.0", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/yqlqfiab0nglynw6r74inrinbls15620-kubebuilder-3.14.0", - "default": true - } - ], "store_path": "/nix/store/yqlqfiab0nglynw6r74inrinbls15620-kubebuilder-3.14.0" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/g4nbjgznx6rcrlpg3xn4ynyyr5wvczjp-kubebuilder-3.14.0", - "default": true - } - ], "store_path": "/nix/store/g4nbjgznx6rcrlpg3xn4ynyyr5wvczjp-kubebuilder-3.14.0" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/xc309p246h3q5xn9gzkh49qcglhfj6ra-kubebuilder-3.14.0", - "default": true - } - ], "store_path": "/nix/store/xc309p246h3q5xn9gzkh49qcglhfj6ra-kubebuilder-3.14.0" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/3rzgxlj6msggxpy8zdf6ffk197wnmywr-kubebuilder-3.14.0", - "default": true - } - ], "store_path": "/nix/store/3rzgxlj6msggxpy8zdf6ffk197wnmywr-kubebuilder-3.14.0" } } @@ -1204,79 +448,15 @@ "version": "1.29.3", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/qrfxb19jr53r2kcsvga8wvg25v5dwyjh-kubectl-1.29.3", - "default": true - }, - { - "name": "man", - "path": "/nix/store/2m9dv8cvh6f90di79cmld40hmniw3h0i-kubectl-1.29.3-man", - "default": true - }, - { - "name": "convert", - "path": "/nix/store/j9rqnfgamf15lp5ijnp916hp3hl42xz8-kubectl-1.29.3-convert" - } - ], "store_path": "/nix/store/qrfxb19jr53r2kcsvga8wvg25v5dwyjh-kubectl-1.29.3" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/hmdnw2aivd5ywvg8h2d9sjghpp91jqmm-kubectl-1.29.3", - "default": true - }, - { - "name": "man", - "path": "/nix/store/av4rh67mx52js37csgyhl296bnp0z07s-kubectl-1.29.3-man", - "default": true - }, - { - "name": "convert", - "path": "/nix/store/w3pma5nyyxizibg6pbc6m1lqr0rfamn5-kubectl-1.29.3-convert" - } - ], "store_path": "/nix/store/hmdnw2aivd5ywvg8h2d9sjghpp91jqmm-kubectl-1.29.3" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/1xvm25fyz9afncqgjxixjsjxfihvqf9k-kubectl-1.29.3", - "default": true - }, - { - "name": "man", - "path": "/nix/store/351mgjyq1jllp6347v71zpk54j7bws0w-kubectl-1.29.3-man", - "default": true - }, - { - "name": "convert", - "path": "/nix/store/4mf6d3vzql08zkg8rwac6y5nsjkcv6d1-kubectl-1.29.3-convert" - } - ], "store_path": "/nix/store/1xvm25fyz9afncqgjxixjsjxfihvqf9k-kubectl-1.29.3" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/xha7i9pddiz31bm9cr33njwnmzdpd3rf-kubectl-1.29.3", - "default": true - }, - { - "name": "man", - "path": "/nix/store/7alsjj9dc5j2xj28v6c0369wa7v1qgg0-kubectl-1.29.3-man", - "default": true - }, - { - "name": "convert", - "path": "/nix/store/gn5sj2jjwy2fvikgjkc76h8lagyszys0-kubectl-1.29.3-convert" - } - ], "store_path": "/nix/store/xha7i9pddiz31bm9cr33njwnmzdpd3rf-kubectl-1.29.3" } } @@ -1288,43 +468,15 @@ "version": "0.14.0", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/jblk86f5bi2dwg2w8g42xl9mpw1yrbi4-controller-tools-0.14.0", - "default": true - } - ], "store_path": "/nix/store/jblk86f5bi2dwg2w8g42xl9mpw1yrbi4-controller-tools-0.14.0" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/sq48wn8yyqkya8as690h61d1dlxwlkbi-controller-tools-0.14.0", - "default": true - } - ], "store_path": "/nix/store/sq48wn8yyqkya8as690h61d1dlxwlkbi-controller-tools-0.14.0" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/nm2vx0f6ajmqpbi2c6z8wani5f1fdqb4-controller-tools-0.14.0", - "default": true - } - ], "store_path": "/nix/store/nm2vx0f6ajmqpbi2c6z8wani5f1fdqb4-controller-tools-0.14.0" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/f8h9pj08ksm49v980yb0slzrbpqqc98r-controller-tools-0.14.0", - "default": true - } - ], "store_path": "/nix/store/f8h9pj08ksm49v980yb0slzrbpqqc98r-controller-tools-0.14.0" } } @@ -1336,43 +488,15 @@ "version": "3.14.3", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/zy9621qww3klbwkffz2pp9kh3p0hdfs3-kubernetes-helm-3.14.3", - "default": true - } - ], "store_path": "/nix/store/zy9621qww3klbwkffz2pp9kh3p0hdfs3-kubernetes-helm-3.14.3" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/ki8zl3ccm68ma3kyjcnzdgfh4wslr2l6-kubernetes-helm-3.14.3", - "default": true - } - ], "store_path": "/nix/store/ki8zl3ccm68ma3kyjcnzdgfh4wslr2l6-kubernetes-helm-3.14.3" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/l6d6j4q4af0dhmjha0c7d010f3hj16b6-kubernetes-helm-3.14.3", - "default": true - } - ], "store_path": "/nix/store/l6d6j4q4af0dhmjha0c7d010f3hj16b6-kubernetes-helm-3.14.3" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/dnnm27a9s71pkgb7wjrdiw8gf9f7a003-kubernetes-helm-3.14.3", - "default": true - } - ], "store_path": "/nix/store/dnnm27a9s71pkgb7wjrdiw8gf9f7a003-kubernetes-helm-3.14.3" } } @@ -1384,43 +508,15 @@ "version": "5.3.0", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/452i1p4zhmbp0lij399pmlagvvhbz8qa-kustomize-5.3.0", - "default": true - } - ], "store_path": "/nix/store/452i1p4zhmbp0lij399pmlagvvhbz8qa-kustomize-5.3.0" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/db73m9xy7q4al1ypx3i4nbqw9wi25fpc-kustomize-5.3.0", - "default": true - } - ], "store_path": "/nix/store/db73m9xy7q4al1ypx3i4nbqw9wi25fpc-kustomize-5.3.0" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/r4kcp37cwrfskcdlk8r0hmwb5y6gh8lc-kustomize-5.3.0", - "default": true - } - ], "store_path": "/nix/store/r4kcp37cwrfskcdlk8r0hmwb5y6gh8lc-kustomize-5.3.0" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/y6q6p3fv79gahrkp6gz8q9lks4h0zgh8-kustomize-5.3.0", - "default": true - } - ], "store_path": "/nix/store/y6q6p3fv79gahrkp6gz8q9lks4h0zgh8-kustomize-5.3.0" } } @@ -1432,59 +528,15 @@ "version": "3.6.2", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/nwrn3g5v9bsrnd88v84q4q9japllx0jb-pre-commit-3.6.2", - "default": true - }, - { - "name": "dist", - "path": "/nix/store/y3r47sj7njflny4l0x0a96bk24hfh7s4-pre-commit-3.6.2-dist" - } - ], "store_path": "/nix/store/nwrn3g5v9bsrnd88v84q4q9japllx0jb-pre-commit-3.6.2" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/61bnh53x2c6laq7vd7ssr23wm85zifs6-pre-commit-3.6.2", - "default": true - }, - { - "name": "dist", - "path": "/nix/store/0rdbgni8iyzz32d24adg3rykkp4pc85m-pre-commit-3.6.2-dist" - } - ], "store_path": "/nix/store/61bnh53x2c6laq7vd7ssr23wm85zifs6-pre-commit-3.6.2" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/dpimv11qs1l3yyzb5nj94p2inpn35yj7-pre-commit-3.6.2", - "default": true - }, - { - "name": "dist", - "path": "/nix/store/y1sav86i99mz03dp988lf1yxgcsdbff1-pre-commit-3.6.2-dist" - } - ], "store_path": "/nix/store/dpimv11qs1l3yyzb5nj94p2inpn35yj7-pre-commit-3.6.2" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/1n78gswil3nq9fdrff4rb2li4y45y5w4-pre-commit-3.6.2", - "default": true - }, - { - "name": "dist", - "path": "/nix/store/ljfxnyrk6l9wpq2n5x79asgcw4pj7ay7-pre-commit-3.6.2-dist" - } - ], "store_path": "/nix/store/1n78gswil3nq9fdrff4rb2li4y45y5w4-pre-commit-3.6.2" } } @@ -1496,43 +548,15 @@ "version": "3.2.7", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/0kmy2vr9hznx2g22bvpavlcyrgjilx4z-rsync-3.2.7", - "default": true - } - ], "store_path": "/nix/store/0kmy2vr9hznx2g22bvpavlcyrgjilx4z-rsync-3.2.7" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/554m9gj7cd3qc7d9yyhvrw6l5gxwsf84-rsync-3.2.7", - "default": true - } - ], "store_path": "/nix/store/554m9gj7cd3qc7d9yyhvrw6l5gxwsf84-rsync-3.2.7" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/3f6wicmglmb803anbavd1gagbq16gqpb-rsync-3.2.7", - "default": true - } - ], "store_path": "/nix/store/3f6wicmglmb803anbavd1gagbq16gqpb-rsync-3.2.7" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/dc65w7qlnzqa9wfbyvyq5d8zv42v8zds-rsync-3.2.7", - "default": true - } - ], "store_path": "/nix/store/dc65w7qlnzqa9wfbyvyq5d8zv42v8zds-rsync-3.2.7" } } @@ -1544,43 +568,15 @@ "version": "3.8.0", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/6sicjz0jhnsazn34g9hlsjb7a80zm9i5-shfmt-3.8.0", - "default": true - } - ], "store_path": "/nix/store/6sicjz0jhnsazn34g9hlsjb7a80zm9i5-shfmt-3.8.0" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/l2l4hxcqypaqbp7ia8ig05345yx0sq5m-shfmt-3.8.0", - "default": true - } - ], "store_path": "/nix/store/l2l4hxcqypaqbp7ia8ig05345yx0sq5m-shfmt-3.8.0" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/6vh95c1bvnxr08ccy3p3gi02gxam5bns-shfmt-3.8.0", - "default": true - } - ], "store_path": "/nix/store/6vh95c1bvnxr08ccy3p3gi02gxam5bns-shfmt-3.8.0" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/sfdz20anszw4qfgwk71mmmfm0dzp94fv-shfmt-3.8.0", - "default": true - } - ], "store_path": "/nix/store/sfdz20anszw4qfgwk71mmmfm0dzp94fv-shfmt-3.8.0" } } @@ -1592,59 +588,15 @@ "version": "4.0.4", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/78j6khza1gr5zmbcldssr8zm8xxnzwps-python3.11-yamale-4.0.4", - "default": true - }, - { - "name": "dist", - "path": "/nix/store/jfglw0libz2z4a97vnb8lsy1lqyrckvq-python3.11-yamale-4.0.4-dist" - } - ], "store_path": "/nix/store/78j6khza1gr5zmbcldssr8zm8xxnzwps-python3.11-yamale-4.0.4" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/9wdk342n50063l5y9idh4pl9gxvwdfyb-python3.11-yamale-4.0.4", - "default": true - }, - { - "name": "dist", - "path": "/nix/store/4l5axk5c1878qmdpsgyc6kaf26gvvgms-python3.11-yamale-4.0.4-dist" - } - ], "store_path": "/nix/store/9wdk342n50063l5y9idh4pl9gxvwdfyb-python3.11-yamale-4.0.4" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/zy5pwvyhdyrkvgwwagf950pj0rvcdx68-python3.11-yamale-4.0.4", - "default": true - }, - { - "name": "dist", - "path": "/nix/store/77a332bqd0j6xydhsny3xlm6sd5n5g7n-python3.11-yamale-4.0.4-dist" - } - ], "store_path": "/nix/store/zy5pwvyhdyrkvgwwagf950pj0rvcdx68-python3.11-yamale-4.0.4" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/9pzsm66srma437qmn3inib0r40jjawkm-python3.11-yamale-4.0.4", - "default": true - }, - { - "name": "dist", - "path": "/nix/store/yf79fdb3nkb3ihklp9na61c74pnyh9h0-python3.11-yamale-4.0.4-dist" - } - ], "store_path": "/nix/store/9pzsm66srma437qmn3inib0r40jjawkm-python3.11-yamale-4.0.4" } } @@ -1656,59 +608,15 @@ "version": "1.35.1", "systems": { "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/shw3d5hqv1kjsxgs6favbb6icdf5zspz-python3.11-yamllint-1.35.1", - "default": true - }, - { - "name": "dist", - "path": "/nix/store/i9fmwxqhi001mjn1vmmvn2a33fsddlz7-python3.11-yamllint-1.35.1-dist" - } - ], "store_path": "/nix/store/shw3d5hqv1kjsxgs6favbb6icdf5zspz-python3.11-yamllint-1.35.1" }, "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/qhciblsdwcx382ybhv4qy3pv321rprpn-python3.11-yamllint-1.35.1", - "default": true - }, - { - "name": "dist", - "path": "/nix/store/3zx9gj6mma9g28vg9l6778h1la50s5zk-python3.11-yamllint-1.35.1-dist" - } - ], "store_path": "/nix/store/qhciblsdwcx382ybhv4qy3pv321rprpn-python3.11-yamllint-1.35.1" }, "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/0qjkix7ixdmh056zg2w98yqfx31ip722-python3.11-yamllint-1.35.1", - "default": true - }, - { - "name": "dist", - "path": "/nix/store/sbgn6iav52fp759y47h4j9kaxzmbqgkh-python3.11-yamllint-1.35.1-dist" - } - ], "store_path": "/nix/store/0qjkix7ixdmh056zg2w98yqfx31ip722-python3.11-yamllint-1.35.1" }, "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/04jdxm8pyzbsg9vjgs0y1piglnn6bzny-python3.11-yamllint-1.35.1", - "default": true - }, - { - "name": "dist", - "path": "/nix/store/1grzppn23d79mkj9vyf9rs1f4w0xvyd6-python3.11-yamllint-1.35.1-dist" - } - ], "store_path": "/nix/store/04jdxm8pyzbsg9vjgs0y1piglnn6bzny-python3.11-yamllint-1.35.1" } } diff --git a/docs/content/customization/docker/custom-image.md b/docs/content/customization/docker/custom-image.md index 773ea92ae..ed6dc1dd0 100644 --- a/docs/content/customization/docker/custom-image.md +++ b/docs/content/customization/docker/custom-image.md @@ -51,7 +51,7 @@ spec: Applying this configuration will result in the following value being set: -- control-plane `DockerMachineTemplte`: +- control-plane `DockerMachineTemplate`: - ```yaml spec: @@ -60,7 +60,7 @@ Applying this configuration will result in the following value being set: customImage: ghcr.io/mesosphere/kind-node:v1.2.3-cp ``` -- worker `DockerMachineTemplte`: +- worker `DockerMachineTemplate`: - ```yaml spec: diff --git a/docs/content/customization/generic/containerd-metrics b/docs/content/customization/generic/containerd-metrics new file mode 100644 index 000000000..b35c10927 --- /dev/null +++ b/docs/content/customization/generic/containerd-metrics @@ -0,0 +1,12 @@ ++++ +title = "Containerd metrics" ++++ + +Containerd exports metrics to a Prometheus endpoint. The metrics cover +containerd itself, its plugins, e.g. CRI, and information about the +containers managed by containerd. + +There are currently no configuration options for metrics, and this +customization will be automatically applied when the [provider-specific +cluster configuration patch]({{< ref ".." >}}) is included in the +`ClusterClass`. diff --git a/docs/content/customization/generic/users.md b/docs/content/customization/generic/users.md new file mode 100644 index 000000000..d0e1766ff --- /dev/null +++ b/docs/content/customization/generic/users.md @@ -0,0 +1,63 @@ ++++ +title = "Users" ++++ + +Configure users for all machines in the cluster, the user's superuser capabilities using `sudo` user specifications, and +the login authentication mechanism. + +> - SSH _authorized keys_ are just public SSH keys that are used to authenticate a login. See the [SSH man +> page](https://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE_FORMAT) for more information. +> +> - For information on sudo user specifications, see the [sudo +> documentation](https://www.sudo.ws/docs/man/sudoers.man/#User_specification). +> +> - Local password authentication is disabled for the user by default. It is enabled only when a hashed password is +> provided. + +## Examples + +### Admin user with SSH public key login + +Creates a user with the name `admin`, grants the user the ability to run any command as the superuser, and allows you to +login via SSH using the username and private key corresponding to the authorized public key. + +```yaml +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Cluster +metadata: + name: +spec: + topology: + variables: + - name: clusterConfig + value: + users: + - name: username + sshAuthorizedKeys: + - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAua0lo8BiGWgvIiDCKnQDKL5uERHfnehm0ns5CEJpJw optionalcomment" + sudo: "ALL=(ALL) NOPASSWD:ALL" +``` + +### Admin user with serial console password login + +Creates a user with the name `admin,` grants the user the ability to run any command as the superuser, and allows you to +login via serial console using the username and password. + +> Note that this does not allow you to login via SSH using the username and password; in most cases, you must also +> configure the SSH server to allow password authentication. + +```yaml +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Cluster +metadata: + name: +spec: + topology: + variables: + - name: clusterConfig + value: + users: + - name: admin + hashedPassword: "$y$j9T$UraH8eN4XvapXBmmSaUrP0$Nyxdf1cJDGZcp0WDKu.CFHprrkPG4ubirqSqiD43Ix3" + sudo: "ALL=(ALL) NOPASSWD:ALL" +``` diff --git a/docs/content/customization/nutanix/_index.md b/docs/content/customization/nutanix/_index.md new file mode 100644 index 000000000..30a6fc676 --- /dev/null +++ b/docs/content/customization/nutanix/_index.md @@ -0,0 +1,7 @@ ++++ +title = "Nutanix" +icon = "fa-brands fa-nutanix" ++++ + +The customizations in this section are applicable only to Nutanix clusters. They will only be applied to clusters that +use the `Nutanix` infrastructure provider, i.e. a CAPI `Cluster` that references an `NutanixCluster`. diff --git a/docs/content/customization/nutanix/control-plane-endpoint.md b/docs/content/customization/nutanix/control-plane-endpoint.md new file mode 100644 index 000000000..5c3bdbb75 --- /dev/null +++ b/docs/content/customization/nutanix/control-plane-endpoint.md @@ -0,0 +1,38 @@ ++++ +title = "Control Plane Endpoint" ++++ + +Configure Control Plane Endpoint. Defines the host IP and port of the CAPX Kubernetes cluster. + +## Examples + +### Set Control Plane Endpoint + +```yaml +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Cluster +metadata: + name: +spec: + topology: + variables: + - name: clusterConfig + value: + nutanix: + controlPlaneEndpoint: + host: x.x.x.x + port: 6443 +``` + +Applying this configuration will result in the following value being set: + +- `NutanixCluster`: + +```yaml +spec: + template: + spec: + controlPlaneEndpoint: + host: x.x.x.x + port: 6443 +``` diff --git a/docs/content/customization/nutanix/machine-details.md b/docs/content/customization/nutanix/machine-details.md new file mode 100644 index 000000000..03b218228 --- /dev/null +++ b/docs/content/customization/nutanix/machine-details.md @@ -0,0 +1,112 @@ ++++ +title = "Machine Details" ++++ + +Configure Machine Details of Control plane and Worker nodes + +## Examples + +### Set Machine details of Control Plane and Worker nodes + +```yaml +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Cluster +metadata: + name: +spec: + topology: + variables: + - name: clusterConfig + value: + controlPlane: + nutanix: + machineDetails: + bootType: legacy + cluster: + name: pe-cluster-name + type: name + image: + name: os-image-name + type: name + memorySize: 4Gi + subnets: + - name: subnet-name + type: name + systemDiskSize: 40Gi + vcpuSockets: 2 + vcpusPerSocket: 1 + - name: workerConfig + value: + nutanix: + machineDetails: + bootType: legacy + cluster: + name: pe-cluster-name + type: name + image: + name: os-image-name + type: name + memorySize: 4Gi + subnets: + - name: subnet-name + type: name + systemDiskSize: 40Gi + vcpuSockets: 2 + vcpusPerSocket: 1 +``` + +Applying this configuration will result in the following value being set: + +- control-plane `NutanixMachineTemplate`: + +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: NutanixMachineTemplate +metadata: + name: nutanix-quick-start-cp-nmt +spec: + template: + spec: + bootType: legacy + cluster: + name: pe-cluster-name + type: name + image: + name: os-image-name + type: name + memorySize: 4Gi + providerID: nutanix://vm-uuid + subnet: + - name: subnet-name + type: name + systemDiskSize: 40Gi + vcpuSockets: 2 + vcpusPerSocket: 1 +``` + +- worker `NutanixMachineTemplate`: + +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: NutanixMachineTemplate +metadata: + name: nutanix-quick-start-md-nmt +spec: + template: + spec: + bootType: legacy + cluster: + name: pe-cluster-name + type: name + image: + name: os-image-name + type: name + memorySize: 4Gi + providerID: nutanix://vm-uuid + subnet: + - name: subnet-name + type: name + systemDiskSize: 40Gi + vcpuSockets: 2 + vcpusPerSocket: 1 +``` diff --git a/docs/content/customization/nutanix/prism-central-endpoint.md b/docs/content/customization/nutanix/prism-central-endpoint.md new file mode 100644 index 000000000..12196b79c --- /dev/null +++ b/docs/content/customization/nutanix/prism-central-endpoint.md @@ -0,0 +1,88 @@ ++++ +title = "Prism Central Endpoint" ++++ + +Configure Prism Central Endpoint to create machines on. + +## Examples + +### Set Prism Central Endpoint + +```yaml +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Cluster +metadata: + name: +spec: + topology: + variables: + - name: clusterConfig + value: + nutanix: + prismCentralEndpoint: + credentials: + name: secret-name + url: https://x.x.x.x:9440 + insecure: false +``` + +Applying this configuration will result in the following value being set: + +- `NutanixClusterTemplate`: + +```yaml +spec: + template: + spec: + prismCentral: + address: x.x.x.x + insecure: false + port: 9440 + credentialRef: + kind: Secret + name: secret-name +``` + +### Provide an Optional Trusted CA Bundle + +If the Prism Central endpoint uses a self-signed certificate, you can provide an additional trust bundle +to be used by the Nutanix provider. +This is a base64 PEM encoded x509 cert for the RootCA that was used to create the certificate for a Prism Central + +See [Nutanix Security Guide] for more information. + +```yaml +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Cluster +metadata: + name: +spec: + topology: + variables: + - name: clusterConfig + value: + nutanix: + prismCentralEndpoint: + # ... + additionalTrustBundle: "LS0...=" +``` + +Applying this configuration will result in the following value being set: + +- `NutanixClusterTemplate`: + +```yaml +spec: + template: + spec: + prismCentral: + # ... + additionalTrustBundle: + kind: String + data: |- + -----BEGIN CERTIFICATE----- + ... + -----END CERTIFICATE----- +``` + +[Nutanix Security Guide]: https://portal.nutanix.com/page/documents/details?targetId=Nutanix-Security-Guide-v6_5:mul-security-ssl-certificate-pc-t.html diff --git a/examples/capi-quick-start/aws-cluster-calico-crs.yaml b/examples/capi-quick-start/aws-cluster-calico-crs.yaml index d0bb44005..790b4a640 100644 --- a/examples/capi-quick-start/aws-cluster-calico-crs.yaml +++ b/examples/capi-quick-start/aws-cluster-calico-crs.yaml @@ -21,15 +21,21 @@ spec: - name: clusterConfig value: addons: + ccm: {} clusterAutoscaler: strategy: ClusterResourceSet cni: provider: Calico strategy: ClusterResourceSet - cpi: {} csi: + defaultStorage: + providerName: aws-ebs + storageClassConfigName: aws-ebs providers: - name: aws-ebs + storageClassConfig: + - name: aws-ebs + strategy: ClusterResourceSet nfd: strategy: ClusterResourceSet aws: diff --git a/examples/capi-quick-start/aws-cluster-calico-helm-addon.yaml b/examples/capi-quick-start/aws-cluster-calico-helm-addon.yaml index de865c0a2..52bf9648a 100644 --- a/examples/capi-quick-start/aws-cluster-calico-helm-addon.yaml +++ b/examples/capi-quick-start/aws-cluster-calico-helm-addon.yaml @@ -21,15 +21,21 @@ spec: - name: clusterConfig value: addons: + ccm: {} clusterAutoscaler: strategy: HelmAddon cni: provider: Calico strategy: HelmAddon - cpi: {} csi: + defaultStorage: + providerName: aws-ebs + storageClassConfigName: aws-ebs providers: - name: aws-ebs + storageClassConfig: + - name: aws-ebs + strategy: ClusterResourceSet nfd: strategy: HelmAddon aws: diff --git a/examples/capi-quick-start/aws-cluster-cilium-crs.yaml b/examples/capi-quick-start/aws-cluster-cilium-crs.yaml index 582498ec8..e9541e789 100644 --- a/examples/capi-quick-start/aws-cluster-cilium-crs.yaml +++ b/examples/capi-quick-start/aws-cluster-cilium-crs.yaml @@ -21,15 +21,21 @@ spec: - name: clusterConfig value: addons: + ccm: {} clusterAutoscaler: strategy: ClusterResourceSet cni: provider: Cilium strategy: ClusterResourceSet - cpi: {} csi: + defaultStorage: + providerName: aws-ebs + storageClassConfigName: aws-ebs providers: - name: aws-ebs + storageClassConfig: + - name: aws-ebs + strategy: ClusterResourceSet nfd: strategy: ClusterResourceSet aws: diff --git a/examples/capi-quick-start/aws-cluster-cilium-helm-addon.yaml b/examples/capi-quick-start/aws-cluster-cilium-helm-addon.yaml index c710fa612..e803994e8 100644 --- a/examples/capi-quick-start/aws-cluster-cilium-helm-addon.yaml +++ b/examples/capi-quick-start/aws-cluster-cilium-helm-addon.yaml @@ -21,15 +21,21 @@ spec: - name: clusterConfig value: addons: + ccm: {} clusterAutoscaler: strategy: HelmAddon cni: provider: Cilium strategy: HelmAddon - cpi: {} csi: + defaultStorage: + providerName: aws-ebs + storageClassConfigName: aws-ebs providers: - name: aws-ebs + storageClassConfig: + - name: aws-ebs + strategy: ClusterResourceSet nfd: strategy: HelmAddon aws: diff --git a/examples/capi-quick-start/nutanix-cluster-calico-crs.yaml b/examples/capi-quick-start/nutanix-cluster-calico-crs.yaml new file mode 100644 index 000000000..19356a534 --- /dev/null +++ b/examples/capi-quick-start/nutanix-cluster-calico-crs.yaml @@ -0,0 +1,130 @@ +apiVersion: v1 +kind: Secret +metadata: + labels: + cluster.x-k8s.io/provider: nutanix + name: ${CLUSTER_NAME}-pe-creds-for-csi +stringData: + key: ${NUTANIX_PRISM_ELEMENT_ENDPOINT}:${NUTANIX_PORT}:${NUTANIX_USER}:${NUTANIX_PASSWORD} +--- +apiVersion: v1 +kind: Secret +metadata: + labels: + cluster.x-k8s.io/provider: nutanix + name: ${CLUSTER_NAME}-pc-creds +stringData: + credentials: | + [ + { + "type": "basic_auth", + "data": { + "prismCentral":{ + "username": "${NUTANIX_USER}", + "password": "${NUTANIX_PASSWORD}" + } + } + } + ] +--- +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Cluster +metadata: + labels: + cluster.x-k8s.io/cluster-name: ${CLUSTER_NAME} + cluster.x-k8s.io/provider: nutanix + name: ${CLUSTER_NAME} +spec: + clusterNetwork: + pods: + cidrBlocks: + - ${POD_CIDR:-192.168.0.0/16} + serviceDomain: ${SERVICE_DOMAIN:="cluster.local"} + services: + cidrBlocks: + - ${SERVICE_CIDR:-10.128.0.0/12} + topology: + class: nutanix-quick-start + controlPlane: + metadata: {} + replicas: ${CONTROL_PLANE_MACHINE_COUNT} + variables: + - name: clusterConfig + value: + addons: + ccm: + credentials: + name: ${CLUSTER_NAME}-pc-creds + clusterAutoscaler: + strategy: ClusterResourceSet + cni: + provider: Calico + strategy: ClusterResourceSet + csi: + defaultStorage: + providerName: nutanix + storageClassConfigName: nutanix-volume + providers: + - credentials: + name: ${CLUSTER_NAME}-pe-creds-for-csi + name: nutanix + storageClassConfig: + - name: nutanix-volume + parameters: + storageContainer: ${NUTANIX_STORAGE_CONTAINER_NAME} + strategy: HelmAddon + nfd: + strategy: ClusterResourceSet + controlPlane: + nutanix: + machineDetails: + bootType: legacy + cluster: + name: ${NUTANIX_PRISM_ELEMENT_CLUSTER_NAME} + type: name + image: + name: ${NUTANIX_MACHINE_TEMPLATE_IMAGE_NAME} + type: name + memorySize: 4Gi + subnets: + - name: ${NUTANIX_SUBNET_NAME} + type: name + systemDiskSize: 40Gi + vcpuSockets: 2 + vcpusPerSocket: 1 + nutanix: + controlPlaneEndpoint: + host: ${CONTROL_PLANE_ENDPOINT_IP} + port: 6443 + prismCentralEndpoint: + credentials: + name: ${CLUSTER_NAME}-pc-creds + insecure: ${NUTANIX_INSECURE} + url: https://${NUTANIX_ENDPOINT}:9440 + - name: workerConfig + value: + nutanix: + machineDetails: + bootType: legacy + cluster: + name: ${NUTANIX_PRISM_ELEMENT_CLUSTER_NAME} + type: name + image: + name: ${NUTANIX_MACHINE_TEMPLATE_IMAGE_NAME} + type: name + memorySize: 4Gi + subnets: + - name: ${NUTANIX_SUBNET_NAME} + type: name + systemDiskSize: 40Gi + vcpuSockets: 2 + vcpusPerSocket: 1 + version: ${KUBERNETES_VERSION} + workers: + machineDeployments: + - class: nutanix-quick-start-worker + metadata: + annotations: + cluster.x-k8s.io/cluster-api-autoscaler-node-group-max-size: "${WORKER_MACHINE_COUNT}" + cluster.x-k8s.io/cluster-api-autoscaler-node-group-min-size: "${WORKER_MACHINE_COUNT}" + name: md-0 diff --git a/examples/capi-quick-start/nutanix-cluster-calico-helm-addon.yaml b/examples/capi-quick-start/nutanix-cluster-calico-helm-addon.yaml new file mode 100644 index 000000000..73c60a3c6 --- /dev/null +++ b/examples/capi-quick-start/nutanix-cluster-calico-helm-addon.yaml @@ -0,0 +1,130 @@ +apiVersion: v1 +kind: Secret +metadata: + labels: + cluster.x-k8s.io/provider: nutanix + name: ${CLUSTER_NAME}-pe-creds-for-csi +stringData: + key: ${NUTANIX_PRISM_ELEMENT_ENDPOINT}:${NUTANIX_PORT}:${NUTANIX_USER}:${NUTANIX_PASSWORD} +--- +apiVersion: v1 +kind: Secret +metadata: + labels: + cluster.x-k8s.io/provider: nutanix + name: ${CLUSTER_NAME}-pc-creds +stringData: + credentials: | + [ + { + "type": "basic_auth", + "data": { + "prismCentral":{ + "username": "${NUTANIX_USER}", + "password": "${NUTANIX_PASSWORD}" + } + } + } + ] +--- +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Cluster +metadata: + labels: + cluster.x-k8s.io/cluster-name: ${CLUSTER_NAME} + cluster.x-k8s.io/provider: nutanix + name: ${CLUSTER_NAME} +spec: + clusterNetwork: + pods: + cidrBlocks: + - ${POD_CIDR:-192.168.0.0/16} + serviceDomain: ${SERVICE_DOMAIN:="cluster.local"} + services: + cidrBlocks: + - ${SERVICE_CIDR:-10.128.0.0/12} + topology: + class: nutanix-quick-start + controlPlane: + metadata: {} + replicas: ${CONTROL_PLANE_MACHINE_COUNT} + variables: + - name: clusterConfig + value: + addons: + ccm: + credentials: + name: ${CLUSTER_NAME}-pc-creds + clusterAutoscaler: + strategy: HelmAddon + cni: + provider: Calico + strategy: HelmAddon + csi: + defaultStorage: + providerName: nutanix + storageClassConfigName: nutanix-volume + providers: + - credentials: + name: ${CLUSTER_NAME}-pe-creds-for-csi + name: nutanix + storageClassConfig: + - name: nutanix-volume + parameters: + storageContainer: ${NUTANIX_STORAGE_CONTAINER_NAME} + strategy: HelmAddon + nfd: + strategy: HelmAddon + controlPlane: + nutanix: + machineDetails: + bootType: legacy + cluster: + name: ${NUTANIX_PRISM_ELEMENT_CLUSTER_NAME} + type: name + image: + name: ${NUTANIX_MACHINE_TEMPLATE_IMAGE_NAME} + type: name + memorySize: 4Gi + subnets: + - name: ${NUTANIX_SUBNET_NAME} + type: name + systemDiskSize: 40Gi + vcpuSockets: 2 + vcpusPerSocket: 1 + nutanix: + controlPlaneEndpoint: + host: ${CONTROL_PLANE_ENDPOINT_IP} + port: 6443 + prismCentralEndpoint: + credentials: + name: ${CLUSTER_NAME}-pc-creds + insecure: ${NUTANIX_INSECURE} + url: https://${NUTANIX_ENDPOINT}:9440 + - name: workerConfig + value: + nutanix: + machineDetails: + bootType: legacy + cluster: + name: ${NUTANIX_PRISM_ELEMENT_CLUSTER_NAME} + type: name + image: + name: ${NUTANIX_MACHINE_TEMPLATE_IMAGE_NAME} + type: name + memorySize: 4Gi + subnets: + - name: ${NUTANIX_SUBNET_NAME} + type: name + systemDiskSize: 40Gi + vcpuSockets: 2 + vcpusPerSocket: 1 + version: ${KUBERNETES_VERSION} + workers: + machineDeployments: + - class: nutanix-quick-start-worker + metadata: + annotations: + cluster.x-k8s.io/cluster-api-autoscaler-node-group-max-size: "${WORKER_MACHINE_COUNT}" + cluster.x-k8s.io/cluster-api-autoscaler-node-group-min-size: "${WORKER_MACHINE_COUNT}" + name: md-0 diff --git a/examples/capi-quick-start/nutanix-cluster-cilium-crs.yaml b/examples/capi-quick-start/nutanix-cluster-cilium-crs.yaml new file mode 100644 index 000000000..ebb5825cc --- /dev/null +++ b/examples/capi-quick-start/nutanix-cluster-cilium-crs.yaml @@ -0,0 +1,130 @@ +apiVersion: v1 +kind: Secret +metadata: + labels: + cluster.x-k8s.io/provider: nutanix + name: ${CLUSTER_NAME}-pe-creds-for-csi +stringData: + key: ${NUTANIX_PRISM_ELEMENT_ENDPOINT}:${NUTANIX_PORT}:${NUTANIX_USER}:${NUTANIX_PASSWORD} +--- +apiVersion: v1 +kind: Secret +metadata: + labels: + cluster.x-k8s.io/provider: nutanix + name: ${CLUSTER_NAME}-pc-creds +stringData: + credentials: | + [ + { + "type": "basic_auth", + "data": { + "prismCentral":{ + "username": "${NUTANIX_USER}", + "password": "${NUTANIX_PASSWORD}" + } + } + } + ] +--- +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Cluster +metadata: + labels: + cluster.x-k8s.io/cluster-name: ${CLUSTER_NAME} + cluster.x-k8s.io/provider: nutanix + name: ${CLUSTER_NAME} +spec: + clusterNetwork: + pods: + cidrBlocks: + - ${POD_CIDR:-192.168.0.0/16} + serviceDomain: ${SERVICE_DOMAIN:="cluster.local"} + services: + cidrBlocks: + - ${SERVICE_CIDR:-10.128.0.0/12} + topology: + class: nutanix-quick-start + controlPlane: + metadata: {} + replicas: ${CONTROL_PLANE_MACHINE_COUNT} + variables: + - name: clusterConfig + value: + addons: + ccm: + credentials: + name: ${CLUSTER_NAME}-pc-creds + clusterAutoscaler: + strategy: ClusterResourceSet + cni: + provider: Cilium + strategy: ClusterResourceSet + csi: + defaultStorage: + providerName: nutanix + storageClassConfigName: nutanix-volume + providers: + - credentials: + name: ${CLUSTER_NAME}-pe-creds-for-csi + name: nutanix + storageClassConfig: + - name: nutanix-volume + parameters: + storageContainer: ${NUTANIX_STORAGE_CONTAINER_NAME} + strategy: HelmAddon + nfd: + strategy: ClusterResourceSet + controlPlane: + nutanix: + machineDetails: + bootType: legacy + cluster: + name: ${NUTANIX_PRISM_ELEMENT_CLUSTER_NAME} + type: name + image: + name: ${NUTANIX_MACHINE_TEMPLATE_IMAGE_NAME} + type: name + memorySize: 4Gi + subnets: + - name: ${NUTANIX_SUBNET_NAME} + type: name + systemDiskSize: 40Gi + vcpuSockets: 2 + vcpusPerSocket: 1 + nutanix: + controlPlaneEndpoint: + host: ${CONTROL_PLANE_ENDPOINT_IP} + port: 6443 + prismCentralEndpoint: + credentials: + name: ${CLUSTER_NAME}-pc-creds + insecure: ${NUTANIX_INSECURE} + url: https://${NUTANIX_ENDPOINT}:9440 + - name: workerConfig + value: + nutanix: + machineDetails: + bootType: legacy + cluster: + name: ${NUTANIX_PRISM_ELEMENT_CLUSTER_NAME} + type: name + image: + name: ${NUTANIX_MACHINE_TEMPLATE_IMAGE_NAME} + type: name + memorySize: 4Gi + subnets: + - name: ${NUTANIX_SUBNET_NAME} + type: name + systemDiskSize: 40Gi + vcpuSockets: 2 + vcpusPerSocket: 1 + version: ${KUBERNETES_VERSION} + workers: + machineDeployments: + - class: nutanix-quick-start-worker + metadata: + annotations: + cluster.x-k8s.io/cluster-api-autoscaler-node-group-max-size: "${WORKER_MACHINE_COUNT}" + cluster.x-k8s.io/cluster-api-autoscaler-node-group-min-size: "${WORKER_MACHINE_COUNT}" + name: md-0 diff --git a/examples/capi-quick-start/nutanix-cluster-cilium-helm-addon.yaml b/examples/capi-quick-start/nutanix-cluster-cilium-helm-addon.yaml new file mode 100644 index 000000000..cedeee8ea --- /dev/null +++ b/examples/capi-quick-start/nutanix-cluster-cilium-helm-addon.yaml @@ -0,0 +1,130 @@ +apiVersion: v1 +kind: Secret +metadata: + labels: + cluster.x-k8s.io/provider: nutanix + name: ${CLUSTER_NAME}-pe-creds-for-csi +stringData: + key: ${NUTANIX_PRISM_ELEMENT_ENDPOINT}:${NUTANIX_PORT}:${NUTANIX_USER}:${NUTANIX_PASSWORD} +--- +apiVersion: v1 +kind: Secret +metadata: + labels: + cluster.x-k8s.io/provider: nutanix + name: ${CLUSTER_NAME}-pc-creds +stringData: + credentials: | + [ + { + "type": "basic_auth", + "data": { + "prismCentral":{ + "username": "${NUTANIX_USER}", + "password": "${NUTANIX_PASSWORD}" + } + } + } + ] +--- +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Cluster +metadata: + labels: + cluster.x-k8s.io/cluster-name: ${CLUSTER_NAME} + cluster.x-k8s.io/provider: nutanix + name: ${CLUSTER_NAME} +spec: + clusterNetwork: + pods: + cidrBlocks: + - ${POD_CIDR:-192.168.0.0/16} + serviceDomain: ${SERVICE_DOMAIN:="cluster.local"} + services: + cidrBlocks: + - ${SERVICE_CIDR:-10.128.0.0/12} + topology: + class: nutanix-quick-start + controlPlane: + metadata: {} + replicas: ${CONTROL_PLANE_MACHINE_COUNT} + variables: + - name: clusterConfig + value: + addons: + ccm: + credentials: + name: ${CLUSTER_NAME}-pc-creds + clusterAutoscaler: + strategy: HelmAddon + cni: + provider: Cilium + strategy: HelmAddon + csi: + defaultStorage: + providerName: nutanix + storageClassConfigName: nutanix-volume + providers: + - credentials: + name: ${CLUSTER_NAME}-pe-creds-for-csi + name: nutanix + storageClassConfig: + - name: nutanix-volume + parameters: + storageContainer: ${NUTANIX_STORAGE_CONTAINER_NAME} + strategy: HelmAddon + nfd: + strategy: HelmAddon + controlPlane: + nutanix: + machineDetails: + bootType: legacy + cluster: + name: ${NUTANIX_PRISM_ELEMENT_CLUSTER_NAME} + type: name + image: + name: ${NUTANIX_MACHINE_TEMPLATE_IMAGE_NAME} + type: name + memorySize: 4Gi + subnets: + - name: ${NUTANIX_SUBNET_NAME} + type: name + systemDiskSize: 40Gi + vcpuSockets: 2 + vcpusPerSocket: 1 + nutanix: + controlPlaneEndpoint: + host: ${CONTROL_PLANE_ENDPOINT_IP} + port: 6443 + prismCentralEndpoint: + credentials: + name: ${CLUSTER_NAME}-pc-creds + insecure: ${NUTANIX_INSECURE} + url: https://${NUTANIX_ENDPOINT}:9440 + - name: workerConfig + value: + nutanix: + machineDetails: + bootType: legacy + cluster: + name: ${NUTANIX_PRISM_ELEMENT_CLUSTER_NAME} + type: name + image: + name: ${NUTANIX_MACHINE_TEMPLATE_IMAGE_NAME} + type: name + memorySize: 4Gi + subnets: + - name: ${NUTANIX_SUBNET_NAME} + type: name + systemDiskSize: 40Gi + vcpuSockets: 2 + vcpusPerSocket: 1 + version: ${KUBERNETES_VERSION} + workers: + machineDeployments: + - class: nutanix-quick-start-worker + metadata: + annotations: + cluster.x-k8s.io/cluster-api-autoscaler-node-group-max-size: "${WORKER_MACHINE_COUNT}" + cluster.x-k8s.io/cluster-api-autoscaler-node-group-min-size: "${WORKER_MACHINE_COUNT}" + name: md-0 diff --git a/go.mod b/go.mod index c1df13beb..d2777daae 100644 --- a/go.mod +++ b/go.mod @@ -15,10 +15,13 @@ require ( github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api v0.0.0-00010101000000-000000000000 github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common v0.0.0-00010101000000-000000000000 github.com/go-logr/logr v1.4.1 + github.com/google/go-cmp v0.6.0 + github.com/nutanix-cloud-native/prism-go-client v0.3.4 github.com/onsi/ginkgo/v2 v2.16.0 github.com/onsi/gomega v1.31.1 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.9.0 + gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.29.3 k8s.io/apiextensions-apiserver v0.29.3 k8s.io/apimachinery v0.29.3 @@ -71,7 +74,6 @@ require ( github.com/golang/protobuf v1.5.4 // indirect github.com/google/cel-go v0.17.7 // indirect github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-github/v53 v53.2.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect @@ -116,14 +118,14 @@ require ( github.com/subosito/gotenv v1.6.0 // indirect github.com/valyala/fastjson v1.6.4 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.18.0 // indirect + golang.org/x/crypto v0.22.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.20.0 // indirect + golang.org/x/net v0.24.0 // indirect golang.org/x/oauth2 v0.14.0 // indirect golang.org/x/sync v0.6.0 // indirect - golang.org/x/sys v0.16.0 // indirect - golang.org/x/term v0.16.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/term v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.17.0 // indirect @@ -134,7 +136,6 @@ require ( google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/cluster-bootstrap v0.29.3 // indirect k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect diff --git a/go.sum b/go.sum index ca45f459f..a3a31052a 100644 --- a/go.sum +++ b/go.sum @@ -17,6 +17,9 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -36,8 +39,16 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/99designs/gqlgen v0.15.1/go.mod h1:nbeSjFkqphIqpZsYe1ULVz0yfH8hjpJdJIQoX/e0G2I= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= +github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= @@ -54,24 +65,43 @@ github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/PaesslerAG/gval v1.0.0/go.mod h1:y/nm5yEyTeX6av0OfKJNp9rBNj2XrGhAf5+v24IBN1I= +github.com/PaesslerAG/jsonpath v0.1.0/go.mod h1:4BzmtoM/PI8fPO4aQGIusjGxGir2BzcV0grWtFzq1Y8= +github.com/PaesslerAG/jsonpath v0.1.1/go.mod h1:lVboNxFGal/VwW6d9JzIy56bUsYAP6tH/x80vjnCseY= github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls= github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/agnivade/levenshtein v1.1.0/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY= github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/aws/aws-sdk-go v1.42.23/go.mod h1:gyRszuZ/icHmHAVE4gc/r+cfCmhA1AD+vqfWbgI+eHs= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/bnkamalesh/webgo/v4 v4.1.11/go.mod h1:taIAonQTzao8G5rnB22WgKmQuIOWHpQ0n/YLAidBXlM= +github.com/bnkamalesh/webgo/v6 v6.2.2/go.mod h1:2Y+dEdTp1xC/ra+3PAVZV6hh4sCI+iPK7mcHt+t9bfM= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -84,6 +114,10 @@ github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBS github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/coredns/caddy v1.1.0 h1:ezvsPrT/tA/7pYDBZxu0cT0VmWk75AfIaf6GSYCNMf0= github.com/coredns/caddy v1.1.0/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= github.com/coredns/corefile-migration v1.0.21 h1:W/DCETrHDiFo0Wj03EyMkaQ9fwsmSgqTCQDHpceaSsE= @@ -93,13 +127,18 @@ github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03V github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creasty/defaults v1.5.2/go.mod h1:FPZ+Y0WNrbqOVw+c6av63eyHUAl6pMHZwqLPvXUZGfY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE= +github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= @@ -110,8 +149,12 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46 h1:7QPwrLT79GlD5sizHf27aoY2RTvw62mO6x7mxkScNk0= github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46/go.mod h1:esf2rsHFNlZlxsqsZDojNBcnNs5REqIvRrWRHqX0vEU= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -119,7 +162,9 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= @@ -127,31 +172,66 @@ github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0 github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= +github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg= +github.com/go-chi/cors v1.2.0/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= +github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-playground/validator/v10 v10.10.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA= github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= +github.com/goccy/go-json v0.9.4/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -165,6 +245,7 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -179,12 +260,18 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/cel-go v0.17.7 h1:6ebJFzu1xO2n7TLtN+UBqShGBhlD85bhvglh5DpcfqQ= github.com/google/cel-go v0.17.7/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY= +github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -195,7 +282,10 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -204,6 +294,7 @@ github.com/google/go-github/v53 v53.2.0/go.mod h1:XhFRObz+m/l+UCm9b7KSIC3lT3NWSX github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -219,6 +310,9 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= @@ -226,25 +320,33 @@ github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 h1:SJ+NtwL6QaZ21U+ github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2/go.mod h1:Tv1PlzqC9t8wNnpPdctvtSUOPUUg4SHeE6vR1Ir2hmg= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= @@ -252,27 +354,60 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/keploy/go-sdk v0.4.3/go.mod h1:tn62gQ8a/AD7mY51DvQfhudiBPTlD+w3XtXemDcbON4= +github.com/kevinmbeaulieu/eq-go v1.0.0/go.mod h1:G3S8ajA56gKBZm4UB9AOyoOS37JO3roToPzKNM8dtdM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labstack/echo/v4 v4.6.1/go.mod h1:RnjgMWNDB9g/HucVWhQYNQP9PvbYf6adqftqryo7s9k= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= +github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ= +github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++0Gf8MBnAvE= +github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc= +github.com/lestrrat-go/jwx v1.2.20/go.mod h1:tLE1XszaFgd7zaS5wHe4NxA+XVhu7xgdRvDpNyi3kNM= +github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= +github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/matryer/moq v0.2.3/go.mod h1:9RtPYjTnH1bSBIkpvtHkFN7nbWAnO7oRpdJkEIn6UtE= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= @@ -281,25 +416,50 @@ github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQth github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.2.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nutanix-cloud-native/prism-go-client v0.3.4 h1:bHY3VPrHHYnbRtkpGaKK+2ZmvUjNVRC55CYZbXIfnOk= +github.com/nutanix-cloud-native/prism-go-client v0.3.4/go.mod h1:tTIH02E6o6AWSShr98QChoxuZl+jBhkXFixom9+fd1Y= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= github.com/onsi/ginkgo/v2 v2.16.0 h1:7q1w9frJDzninhXxjZd+Y/x54XNjG/UlRLIYPZafsPM= github.com/onsi/ginkgo/v2 v2.16.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo= github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -311,6 +471,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -327,19 +489,26 @@ github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lne github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ= github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -364,6 +533,7 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= @@ -371,12 +541,28 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.35.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/vektah/gqlparser/v2 v2.2.0/go.mod h1:i3mQIGIrbK2PD1RrCeMTlVbkF2FJ6WkU1KJlJlC+3F4= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/etcd/api/v3 v3.5.10 h1:szRajuUUbLyppkhs9K6BRtjY37l66XQQmw7oZRANE4k= go.etcd.io/etcd/api/v3 v3.5.10/go.mod h1:TidfmT4Uycad3NM/o25fG3J07odo4GBB9hoxaodFCtI= @@ -384,12 +570,16 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.10 h1:kfYIdQftBnbAq8pUWFXfpuuxFSKzlmM5cSn76JB go.etcd.io/etcd/client/pkg/v3 v3.5.10/go.mod h1:DYivfIviIuQ8+/lCq4vcxuseg2P2XbHygkKwFo9fc8U= go.etcd.io/etcd/client/v3 v3.5.10 h1:W9TXNZ+oB3MCd/8UjxHTWK5J9Nquw9fQBLJd5ne5/Ao= go.etcd.io/etcd/client/v3 v3.5.10/go.mod h1:RVeBnDz2PUEZqTpgqwAtUd8nAPf5kjyFyND7P1VkOKc= +go.keploy.io/server v0.1.8/go.mod h1:ZqhwTZOBb+dzx5t30Wt6eUGI6kO5QizvPg6coNPtbow= +go.mongodb.org/mongo-driver v1.8.0/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= +go.mongodb.org/mongo-driver v1.8.1/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 h1:PzIubN4/sjByhDRHLviCjJuweBXWFZWhghjg7cS28+M= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0/go.mod h1:Ct6zzQEuGK3WpJs2n4dn+wfJYzd/+hNnxMRTWjGn30M= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.0 h1:1eHu3/pUSWaOgltNK3WJFaywKsTIr/PwvHyDmi0lQA0= @@ -406,12 +596,21 @@ go.opentelemetry.io/otel/sdk v1.20.0 h1:5Jf6imeFZlZtKv9Qbo6qt2ZkmWtdWx/wzcCbNUlA go.opentelemetry.io/otel/sdk v1.20.0/go.mod h1:rmkSx1cZCm/tn16iWDn1GQbLtsW/LvsdEEFzCSRM6V0= go.opentelemetry.io/otel/trace v1.20.0 h1:+yxVAPZPbQhbC3OfAkeIVTky6iTFpcr4SiY9om7mXSQ= go.opentelemetry.io/otel/trace v1.20.0/go.mod h1:HJSK7F/hA5RlzpZ0zKDCHCDHm556LCDtKaAo6JmBFUU= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -419,12 +618,19 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -460,11 +666,14 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -475,6 +684,7 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -485,20 +695,31 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -508,6 +729,9 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0= golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -520,11 +744,14 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -532,10 +759,16 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -546,6 +779,7 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -553,31 +787,47 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= @@ -586,10 +836,13 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -597,12 +850,14 @@ golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -622,21 +877,26 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200815165600-90abf76919f3/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= @@ -665,6 +925,8 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -696,6 +958,7 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= @@ -703,12 +966,18 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb h1:XFBgcDwm7irdHTbz4Zk2h7Mh+eis4nfJEFQFYzJzuIA= google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb h1:lK0oleSc7IQsUxO3U5TjL9DWlsxpEBemh+zpB7IqhWI= @@ -728,9 +997,13 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -743,24 +1016,37 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -773,26 +1059,37 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.24.2/go.mod h1:AHqbSkTm6YrQ0ObxjO3Pmp/ubFF/KuM7jU+3khoBsOg= k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw= k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80= k8s.io/apiextensions-apiserver v0.29.3 h1:9HF+EtZaVpFjStakF4yVufnXGPRppWFEQ87qnO91YeI= k8s.io/apiextensions-apiserver v0.29.3/go.mod h1:po0XiY5scnpJfFizNGo6puNU6Fq6D70UJY2Cb2KwAVc= +k8s.io/apimachinery v0.24.2/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/apimachinery v0.24.3/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU= k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= k8s.io/apiserver v0.29.3 h1:xR7ELlJ/BZSr2n4CnD3lfA4gzFivh0wwfNfz9L0WZcE= k8s.io/apiserver v0.29.3/go.mod h1:hrvXlwfRulbMbBgmWRQlFru2b/JySDpmzvQwwk4GUOs= +k8s.io/client-go v0.24.2/go.mod h1:zg4Xaoo+umDsfCWr4fCnmLEtQXyCNXCvJuSsglNcV30= k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg= k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0= k8s.io/cluster-bootstrap v0.29.3 h1:DIMDZSN8gbFMy9CS2mAS2Iqq/fIUG783WN/1lqi5TF8= k8s.io/cluster-bootstrap v0.29.3/go.mod h1:aPAg1VtXx3uRrx5qU2jTzR7p1rf18zLXWS+pGhiqPto= k8s.io/component-base v0.29.3 h1:Oq9/nddUxlnrCuuR2K/jp6aflVvc0uDvxMzAWxnGzAo= k8s.io/component-base v0.29.3/go.mod h1:Yuj33XXjuOk2BAaHsIGHhCKZQAgYKhqIxIjIr2UXYio= +k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= k8s.io/kubelet v0.29.3 h1:X9h0ZHzc+eUeNTaksbN0ItHyvGhQ7Z0HPjnQD2oHdwU= k8s.io/kubelet v0.29.3/go.mod h1:jDiGuTkFOUynyBKzOoC1xRSWlgAZ9UPcTYeFyjr6vas= +k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ= k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= @@ -808,12 +1105,16 @@ sigs.k8s.io/cluster-api/test v1.6.3 h1:ZCboLCTpKWzSbf+f7MpQT7EN8aeH9DNhJC1T9/vAu sigs.k8s.io/cluster-api/test v1.6.3/go.mod h1:AKs25dgW6AnyGaQBoWuXfWnBs+FT7vJmAI/aox64DEI= sigs.k8s.io/controller-runtime v0.17.2 h1:FwHwD1CTUemg0pW2otk7/U5/i5m2ymzvOXdbeGOUvw0= sigs.k8s.io/controller-runtime v0.17.2/go.mod h1:+MngTvIQQQhfXtwfdGw/UOQ/aIaqsYywfCINOtwMO/s= +sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kind v0.20.0 h1:f0sc3v9mQbGnjBUaqSFST1dwIuiikKVGgoTwpoP33a8= sigs.k8s.io/kind v0.20.0/go.mod h1:aBlbxg08cauDgZ612shr017/rZwqd7AS563FvpWKPVs= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/hack/addons/add-warning-helm-configmap.sh b/hack/addons/add-warning-helm-configmap.sh new file mode 100755 index 000000000..484c229fc --- /dev/null +++ b/hack/addons/add-warning-helm-configmap.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly SCRIPT_DIR + +# shellcheck source=hack/common.sh +source "${SCRIPT_DIR}/../common.sh" +ASSETS_DIR="$(mktemp -d -p "${TMPDIR:-/tmp}")" +mv "${GIT_REPO_ROOT}/charts/cluster-api-runtime-extensions-nutanix/templates/helm-config.yaml" "${ASSETS_DIR}/helm-config.yaml" +# add warning not to edit file directly +cat <"${GIT_REPO_ROOT}/charts/cluster-api-runtime-extensions-nutanix/templates/helm-config.yaml" +$(cat "${GIT_REPO_ROOT}/hack/license-header.yaml.txt") + +#================================================================= +# DO NOT EDIT THIS FILE +# IT HAS BEEN GENERATED BY /hack/tools/helm-cm/main.go +#================================================================= +$(cat "${ASSETS_DIR}/helm-config.yaml") +EOF + +sed -i s/placeholder/"{{ .Values.helmAddonsConfigMap }}"/g "${GIT_REPO_ROOT}/charts/cluster-api-runtime-extensions-nutanix/templates/helm-config.yaml" diff --git a/hack/addons/kustomize/aws-cpi/kustomization.yaml.tmpl b/hack/addons/kustomize/aws-ccm/kustomization.yaml.tmpl similarity index 86% rename from hack/addons/kustomize/aws-cpi/kustomization.yaml.tmpl rename to hack/addons/kustomize/aws-ccm/kustomization.yaml.tmpl index 3aff2a7e1..e8238d6da 100644 --- a/hack/addons/kustomize/aws-cpi/kustomization.yaml.tmpl +++ b/hack/addons/kustomize/aws-ccm/kustomization.yaml.tmpl @@ -5,13 +5,13 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization metadata: - name: aws-cpi-kustomize + name: aws-ccm-kustomize helmCharts: - name: aws-cloud-controller-manager repo: https://kubernetes.github.io/cloud-provider-aws releaseName: aws-cloud-controller-manager - version: ${AWS_CPI_CHART_VERSION} + version: ${AWS_CCM_CHART_VERSION} includeCRDs: true skipTests: true @@ -25,4 +25,4 @@ patches: images: - name: registry.k8s.io/provider-aws/cloud-controller-manager - newTag: ${AWS_CPI_VERSION} + newTag: ${AWS_CCM_VERSION} diff --git a/hack/addons/kustomize/aws-ebs-csi/helm-values.yaml b/hack/addons/kustomize/aws-ebs-csi/helm-values.yaml index d1cf5632d..33bb95823 100644 --- a/hack/addons/kustomize/aws-ebs-csi/helm-values.yaml +++ b/hack/addons/kustomize/aws-ebs-csi/helm-values.yaml @@ -27,10 +27,3 @@ node: sidecars: snapshotter: forceEnable: true -storageClasses: -- metadata: - name: ebs-sc - volumeBindingMode: WaitForFirstConsumer - parameters: - csi.storage.k8s.io/fstype: ext4 - type: gp3 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/addons/kustomize/nutanix-snapshot-csi/kustomization.yaml.tmpl b/hack/addons/kustomize/nutanix-snapshot-csi/kustomization.yaml.tmpl new file mode 100644 index 000000000..7c1d21cdf --- /dev/null +++ b/hack/addons/kustomize/nutanix-snapshot-csi/kustomization.yaml.tmpl @@ -0,0 +1,19 @@ +# Copyright 2023 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +metadata: + name: nutanix-csi-kustomize + +namespace: kube-system + +helmCharts: +- name: nutanix-csi-snapshot + repo: https://nutanix.github.io/helm/ + releaseName: nutanix-csi-storage + version: ${NUTANIX_SNAPSHOT_CSI_CHART_VERSION} + includeCRDs: true + skipTests: true + namespace: nutanix-system diff --git a/hack/addons/kustomize/nutanix-storage-csi/helm-values.yaml b/hack/addons/kustomize/nutanix-storage-csi/helm-values.yaml new file mode 100644 index 000000000..eebd55d74 --- /dev/null +++ b/hack/addons/kustomize/nutanix-storage-csi/helm-values.yaml @@ -0,0 +1,4 @@ +# Copyright 2023 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +createSecret: false diff --git a/hack/addons/kustomize/nutanix-storage-csi/kustomization.yaml.tmpl b/hack/addons/kustomize/nutanix-storage-csi/kustomization.yaml.tmpl new file mode 100644 index 000000000..2e67558f4 --- /dev/null +++ b/hack/addons/kustomize/nutanix-storage-csi/kustomization.yaml.tmpl @@ -0,0 +1,20 @@ +# Copyright 2023 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +metadata: + name: nutanix-csi-kustomize + +namespace: kube-system + +helmCharts: +- name: nutanix-csi-storage + repo: https://nutanix.github.io/helm/ + releaseName: nutanix-csi-storage + version: ${NUTANIX_STORAGE_CSI_CHART_VERSION} + valuesFile: helm-values.yaml + includeCRDs: true + skipTests: true + namespace: nutanix-system diff --git a/hack/addons/update-aws-cpi.sh b/hack/addons/update-aws-ccm.sh similarity index 60% rename from hack/addons/update-aws-cpi.sh rename to hack/addons/update-aws-ccm.sh index 1b00910c0..cee75151d 100755 --- a/hack/addons/update-aws-cpi.sh +++ b/hack/addons/update-aws-ccm.sh @@ -8,13 +8,13 @@ readonly SCRIPT_DIR # shellcheck source=hack/common.sh source "${SCRIPT_DIR}/../common.sh" -AWS_CPI_VERSION=$1 -export AWS_CPI_VERSION -AWS_CPI_CHART_VERSION=$2 -export AWS_CPI_CHART_VERSION +AWS_CCM_VERSION=$1 +export AWS_CCM_VERSION +AWS_CCM_CHART_VERSION=$2 +export AWS_CCM_CHART_VERSION -if [ -z "${AWS_CPI_VERSION:-}" ]; then - echo "Missing argument: AWS_CPI_VERSION" +if [ -z "${AWS_CCM_VERSION:-}" ]; then + echo "Missing argument: AWS_CCM_VERSION" exit 1 fi @@ -22,23 +22,23 @@ ASSETS_DIR="$(mktemp -d -p "${TMPDIR:-/tmp}")" readonly ASSETS_DIR trap_add "rm -rf ${ASSETS_DIR}" EXIT -readonly KUSTOMIZE_BASE_DIR="${SCRIPT_DIR}/kustomize/aws-cpi/" +readonly KUSTOMIZE_BASE_DIR="${SCRIPT_DIR}/kustomize/aws-ccm/" envsubst -no-unset <"${KUSTOMIZE_BASE_DIR}/kustomization.yaml.tmpl" >"${ASSETS_DIR}/kustomization.yaml" -readonly FILE_NAME="aws-cpi-${AWS_CPI_VERSION}.yaml" +readonly FILE_NAME="aws-ccm-${AWS_CCM_VERSION}.yaml" kustomize build --enable-helm "${ASSETS_DIR}" >"${ASSETS_DIR}/${FILE_NAME}" -kubectl create configmap aws-cpi-"${AWS_CPI_VERSION}" --dry-run=client --output yaml \ +kubectl create configmap aws-ccm-"${AWS_CCM_VERSION}" --dry-run=client --output yaml \ --from-file "${ASSETS_DIR}/${FILE_NAME}" \ - >"${ASSETS_DIR}/aws-cpi-${AWS_CPI_VERSION}-configmap.yaml" + >"${ASSETS_DIR}/aws-ccm-${AWS_CCM_VERSION}-configmap.yaml" # add warning not to edit file directly -cat <"${GIT_REPO_ROOT}/charts/cluster-api-runtime-extensions-nutanix/templates/cpi/aws/manifests/aws-cpi-${AWS_CPI_VERSION}-configmap.yaml" +cat <"${GIT_REPO_ROOT}/charts/cluster-api-runtime-extensions-nutanix/templates/ccm/aws/manifests/aws-ccm-${AWS_CCM_VERSION}-configmap.yaml" $(cat "${GIT_REPO_ROOT}/hack/license-header.yaml.txt") #================================================================= # DO NOT EDIT THIS FILE -# IT HAS BEEN GENERATED BY /hack/addons/update-aws-cpi.sh +# IT HAS BEEN GENERATED BY /hack/addons/update-aws-ccm.sh #================================================================= -$(cat "${ASSETS_DIR}/aws-cpi-${AWS_CPI_VERSION}-configmap.yaml") +$(cat "${ASSETS_DIR}/aws-ccm-${AWS_CCM_VERSION}-configmap.yaml") EOF diff --git a/hack/addons/update-nutanix-csi.sh b/hack/addons/update-nutanix-csi.sh new file mode 100755 index 000000000..c6a488d7e --- /dev/null +++ b/hack/addons/update-nutanix-csi.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly SCRIPT_DIR + +# shellcheck source=hack/common.sh +source "${SCRIPT_DIR}/../common.sh" + +if [ -z "${NUTANIX_STORAGE_CSI_CHART_VERSION:-}" ]; then + echo "Missing environment variable: NUTANIX_STORAGE_CSI_CHART_VERSION" + exit 1 +fi + +ASSETS_DIR="$(mktemp -d -p "${TMPDIR:-/tmp}")" +readonly ASSETS_DIR +trap_add "rm -rf ${ASSETS_DIR}" EXIT + +readonly FILE_NAME="nutanix-storage-csi.yaml" + +readonly KUSTOMIZE_BASE_DIR="${SCRIPT_DIR}/kustomize/nutanix-storage-csi" +mkdir -p "${ASSETS_DIR}/nutanix-storage-csi" +envsubst -no-unset <"${KUSTOMIZE_BASE_DIR}/kustomization.yaml.tmpl" >"${ASSETS_DIR}/nutanix-storage-csi/kustomization.yaml" +cp -r "${KUSTOMIZE_BASE_DIR}"/*.yaml "${ASSETS_DIR}/nutanix-storage-csi/" + +kustomize build --enable-helm "${ASSETS_DIR}/nutanix-storage-csi/" >"${ASSETS_DIR}/${FILE_NAME}" + +kubectl create configmap nutanix-storage-csi --dry-run=client --output yaml \ + --from-file "${ASSETS_DIR}/${FILE_NAME}" \ + >"${ASSETS_DIR}/nutanix-storage-csi-configmap.yaml" + +# add warning not to edit file directly +cat <"${GIT_REPO_ROOT}/charts/cluster-api-runtime-extensions-nutanix/templates/csi/nutanix/manifests/nutanix-storage-csi-configmap.yaml" +$(cat "${GIT_REPO_ROOT}/hack/license-header.yaml.txt") + +#================================================================= +# DO NOT EDIT THIS FILE +# IT HAS BEEN GENERATED BY /hack/addons/update-nutanix-storage-csi.sh +#================================================================= +$(cat "${ASSETS_DIR}/nutanix-storage-csi-configmap.yaml") +EOF diff --git a/hack/examples/additional-resources/nutanix/csi-secret.yaml b/hack/examples/additional-resources/nutanix/csi-secret.yaml new file mode 100644 index 000000000..0011bfbe1 --- /dev/null +++ b/hack/examples/additional-resources/nutanix/csi-secret.yaml @@ -0,0 +1,10 @@ +# Copyright 2023 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +--- +apiVersion: v1 +kind: Secret +metadata: + name: ${CLUSTER_NAME}-pe-creds-for-csi +stringData: + key: "${NUTANIX_PRISM_ELEMENT_ENDPOINT}:${NUTANIX_PORT}:${NUTANIX_USER}:${NUTANIX_PASSWORD}" diff --git a/hack/examples/bases/aws/cluster/kustomization.yaml.tmpl b/hack/examples/bases/aws/cluster/kustomization.yaml.tmpl index 10c970d1c..983a38473 100644 --- a/hack/examples/bases/aws/cluster/kustomization.yaml.tmpl +++ b/hack/examples/bases/aws/cluster/kustomization.yaml.tmpl @@ -35,7 +35,7 @@ patches: path: ../../../patches/cluster-autoscaler.yaml - target: kind: Cluster - path: ../../../patches/cpi.yaml + path: ../../../patches/ccm.yaml - target: kind: Cluster path: ../../../patches/aws/csi.yaml diff --git a/hack/examples/bases/aws/kustomization.yaml.tmpl b/hack/examples/bases/aws/kustomization.yaml.tmpl deleted file mode 100644 index 11a33741f..000000000 --- a/hack/examples/bases/aws/kustomization.yaml.tmpl +++ /dev/null @@ -1,155 +0,0 @@ -# Copyright 2023 D2iQ, Inc. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -resources: -- ./calico/crs -- ./calico/helm-addon -- ./cilium/crs -- ./cilium/helm-addon - -namePrefix: aws- - -labels: -- includeSelectors: false - pairs: - cluster.x-k8s.io/provider: aws - -patches: -- target: - group: cluster.x-k8s.io - kind: Cluster - patch: |- - - op: "add" - path: "/spec/topology/class" - value: "aws-quick-start" - - op: "remove" - path: "/metadata/labels/cni" - - op: "add" - path: "/spec/topology/variables/0/value/aws" - value: - region: us-west-2 - - op: "add" - path: "/spec/topology/variables/0/value/controlPlane" - value: - aws: - ami: - lookup: - format: "$${AMI_LOOKUP_FORMAT}" - org: "$${AMI_LOOKUP_ORG}" - baseOS: "$${AMI_LOOKUP_BASEOS}" - - op: "add" - path: "/spec/topology/variables/-" - value: - name: "workerConfig" - value: - aws: - ami: - lookup: - format: "$${AMI_LOOKUP_FORMAT}" - org: "$${AMI_LOOKUP_ORG}" - baseOS: "$${AMI_LOOKUP_BASEOS}" - - op: "add" - path: "/spec/topology/variables/0/value/addons/cpi" - value: {} - - op: "add" - path: "/spec/topology/variables/0/value/addons/csi" - value: - providers: - - name: aws-ebs -- target: - group: cluster.x-k8s.io - kind: ClusterClass - patch: |- - - op: "add" - path: "/spec/controlPlane/machineInfrastructure/ref/name" - value: "aws-quick-start-control-plane" - - op: "add" - path: "/spec/controlPlane/ref/name" - value: "aws-quick-start-control-plane" - - op: "add" - path: "/spec/infrastructure/ref/name" - value: "aws-quick-start" - - op: "add" - path: "/spec/workers/machineDeployments/0/template/bootstrap/ref/name" - value: "aws-quick-start-worker-bootstraptemplate" - - op: "add" - path: "/spec/workers/machineDeployments/0/template/infrastructure/ref/name" - value: "aws-quick-start-worker-machinetemplate" - - op: "add" - path: "/spec/patches" - value: - - name: "cluster-config" - external: - generateExtension: "awsclusterconfigpatch.cluster-api-runtime-extensions-nutanix" - discoverVariablesExtension: "awsclusterconfigvars.cluster-api-runtime-extensions-nutanix" - - name: "worker-config" - external: - generateExtension: "awsworkerconfigpatch.cluster-api-runtime-extensions-nutanix" - discoverVariablesExtension: "awsworkerconfigvars.cluster-api-runtime-extensions-nutanix" - - name: identityRef - definitions: - - jsonPatches: - - op: add - path: /spec/template/spec/identityRef - value: - kind: AWSClusterControllerIdentity - name: default - selector: - apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 - kind: AWSClusterTemplate - matchResources: - infrastructureCluster: true - description: AWSClusterStaticIdentity identityRef to use when creating the cluster - - op: "remove" - path: "/spec/variables" -- target: - group: infrastructure.cluster.x-k8s.io - kind: AWSMachineTemplate - name: quick-start-worker-machinetemplate - patch: |- - - op: "add" - path: "/spec/template/spec/instanceType" - value: "m5.2xlarge" -- target: - group: infrastructure.cluster.x-k8s.io - kind: AWSMachineTemplate - name: quick-start-control-plane - patch: |- - - op: "add" - path: "/spec/template/spec/instanceType" - value: "m5.xlarge" -- target: - group: infrastructure.cluster.x-k8s.io - kind: AWSMachineTemplate - name: quick-start-worker-machinetemplate - patch: |- - - op: "add" - path: "/spec/template/spec/sshKeyName" - value: "" -- target: - group: infrastructure.cluster.x-k8s.io - kind: AWSMachineTemplate - name: quick-start-control-plane - patch: |- - - op: "add" - path: "/spec/template/spec/sshKeyName" - value: "" -- target: - kind: ConfigMap - patch: | - $$patch: delete - apiVersion: v1 - kind: ConfigMap - metadata: - name: aws-cni-$${CLUSTER_NAME}-crs-0 -- target: - kind: ClusterResourceSet - patch: | - $$patch: delete - apiVersion: addons.cluster.x-k8s.io/v1beta1 - kind: ConfigMap - metadata: - name: aws-$${CLUSTER_NAME}-crs-0 diff --git a/hack/examples/bases/docker/cluster/kustomization.yaml.tmpl b/hack/examples/bases/docker/cluster/kustomization.yaml.tmpl index 34bea6a80..31e38ab76 100644 --- a/hack/examples/bases/docker/cluster/kustomization.yaml.tmpl +++ b/hack/examples/bases/docker/cluster/kustomization.yaml.tmpl @@ -5,7 +5,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: -- https://github.com/kubernetes-sigs/cluster-api/releases/download/${CLUSTERCTL_VERSION}/cluster-template-development.yaml +- https://github.com/kubernetes-sigs/cluster-api/releases/download/${CAPI_VERSION}/cluster-template-development.yaml sortOptions: order: fifo diff --git a/hack/examples/bases/docker/clusterclass/kustomization.yaml.tmpl b/hack/examples/bases/docker/clusterclass/kustomization.yaml.tmpl index bfbef5ac5..56ca10a60 100644 --- a/hack/examples/bases/docker/clusterclass/kustomization.yaml.tmpl +++ b/hack/examples/bases/docker/clusterclass/kustomization.yaml.tmpl @@ -5,7 +5,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: -- https://github.com/kubernetes-sigs/cluster-api/releases/download/${CLUSTERCTL_VERSION}/clusterclass-quick-start.yaml +- https://github.com/kubernetes-sigs/cluster-api/releases/download/${CAPI_VERSION}/clusterclass-quick-start.yaml configurations: - kustomizeconfig.yaml diff --git a/hack/examples/bases/docker/kustomization.yaml.tmpl b/hack/examples/bases/docker/kustomization.yaml.tmpl deleted file mode 100644 index e8b14cc6c..000000000 --- a/hack/examples/bases/docker/kustomization.yaml.tmpl +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright 2023 D2iQ, Inc. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -resources: -- ./cilium/crs -- ./cilium/helm-addon -- ./calico/crs -- ./calico/helm-addon -- https://github.com/kubernetes-sigs/cluster-api/releases/download/${CLUSTERCTL_VERSION}/clusterclass-quick-start.yaml - -namePrefix: docker- - -labels: -- includeSelectors: false - pairs: - cluster.x-k8s.io/provider: docker - -patches: -- target: - group: cluster.x-k8s.io - kind: Cluster - patch: |- - - op: "remove" - path: "/metadata/namespace" - - op: "add" - path: "/spec/topology/class" - value: "docker-quick-start" - - op: "add" - path: "/spec/topology/variables/0/value/docker" - value: {} - - op: "remove" - path: "/spec/topology/workers/machinePools" -- target: - group: cluster.x-k8s.io - kind: ClusterClass - patch: |- - - op: "add" - path: "/spec/controlPlane/machineInfrastructure/ref/name" - value: "docker-quick-start-control-plane" - - op: "add" - path: "/spec/controlPlane/ref/name" - value: "docker-quick-start-control-plane" - - op: "add" - path: "/spec/infrastructure/ref/name" - value: "docker-quick-start-cluster" - - op: "add" - path: "/spec/workers/machineDeployments/0/template/bootstrap/ref/name" - value: "docker-quick-start-default-worker-bootstraptemplate" - - op: "add" - path: "/spec/workers/machineDeployments/0/template/infrastructure/ref/name" - value: "docker-quick-start-default-worker-machinetemplate" - - op: "add" - path: "/spec/patches" - value: - - name: "cluster-config" - external: - generateExtension: "dockerclusterconfigpatch.cluster-api-runtime-extensions-nutanix" - discoverVariablesExtension: "dockerclusterconfigvars.cluster-api-runtime-extensions-nutanix" - - name: "worker-config" - external: - generateExtension: "dockerworkerconfigpatch.cluster-api-runtime-extensions-nutanix" - discoverVariablesExtension: "dockerworkerconfigvars.cluster-api-runtime-extensions-nutanix" - - op: "remove" - path: "/spec/variables" - - op: "remove" - path: "/spec/workers/machinePools" diff --git a/hack/examples/bases/nutanix/cluster/kustomization.yaml.tmpl b/hack/examples/bases/nutanix/cluster/kustomization.yaml.tmpl new file mode 100644 index 000000000..4db679840 --- /dev/null +++ b/hack/examples/bases/nutanix/cluster/kustomization.yaml.tmpl @@ -0,0 +1,60 @@ +# Copyright 2024 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- ../../../additional-resources/nutanix/csi-secret.yaml +- https://raw.githubusercontent.com/nutanix-cloud-native/cluster-api-provider-nutanix/1a7cd69ba35de01e56dcf2dda7f31973111d2317/templates/cluster-template-topology.yaml + +sortOptions: + order: fifo + +labels: +- includeSelectors: false + pairs: + cluster.x-k8s.io/provider: nutanix + +patches: +- target: + kind: Cluster + path: ../../../patches/initialize-variables.yaml +- target: + kind: Cluster + path: ../../../patches/cluster-network.yaml +- target: + kind: Cluster + path: ../../../patches/cluster-autoscaler.yaml +- 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 + +# Remove Additional Trust Bundle ConfigMap +- target: + 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/bases/nutanix/clusterclass/kustomization.yaml.tmpl b/hack/examples/bases/nutanix/clusterclass/kustomization.yaml.tmpl new file mode 100644 index 000000000..d79935f3b --- /dev/null +++ b/hack/examples/bases/nutanix/clusterclass/kustomization.yaml.tmpl @@ -0,0 +1,52 @@ +# Copyright 2024 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- https://raw.githubusercontent.com/nutanix-cloud-native/cluster-api-provider-nutanix/1a7cd69ba35de01e56dcf2dda7f31973111d2317/templates/cluster-template-clusterclass.yaml + +configurations: + - kustomizeconfig.yaml + +sortOptions: + order: fifo + +namePrefix: + +labels: +- includeSelectors: false + pairs: + cluster.x-k8s.io/provider: nutanix + +patches: +# Delete the patch and variable definitions. +- target: + kind: ClusterClass + patch: |- + - op: "remove" + path: "/spec/patches" + - op: "remove" + path: "/spec/variables" + +# FIXME: Debug why some of the patches are needed. +# When the handler runs, it sends back multiple patches for individual fields. +# But CAPI fails applying them because of missing value. +- target: + kind: NutanixClusterTemplate + patch: |- + - op: "add" + path: "/spec/template/spec/controlPlaneEndpoint" + value: + host: PLACEHOLDER + port: 6443 + - op: "add" + path: "/spec/template/spec/prismCentral" + value: + address: PLACEHOLDER + port: 9440 + credentialRef: + name: PLACEHOLDER + kind: Secret + namespace: default diff --git a/hack/examples/bases/nutanix/clusterclass/kustomizeconfig.yaml b/hack/examples/bases/nutanix/clusterclass/kustomizeconfig.yaml new file mode 100644 index 000000000..97b1f3955 --- /dev/null +++ b/hack/examples/bases/nutanix/clusterclass/kustomizeconfig.yaml @@ -0,0 +1,22 @@ +# Copyright 2024 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +nameReference: + - kind: NutanixMachineTemplate + fieldSpecs: + - kind: ClusterClass + path: spec/controlPlane/machineInfrastructure/ref/name + - kind: ClusterClass + path: spec/workers/machineDeployments/template/infrastructure/ref/name + - kind: KubeadmControlPlaneTemplate + fieldSpecs: + - kind: ClusterClass + path: spec/controlPlane/ref/name + - kind: NutanixClusterTemplate + fieldSpecs: + - kind: ClusterClass + path: spec/infrastructure/ref/name + - kind: KubeadmConfigTemplate + fieldSpecs: + - kind: ClusterClass + path: spec/workers/machineDeployments/template/bootstrap/ref/name diff --git a/hack/examples/overlays/clusterclasses/nutanix/kustomization.yaml.tmpl b/hack/examples/overlays/clusterclasses/nutanix/kustomization.yaml.tmpl new file mode 100644 index 000000000..2c27d69fb --- /dev/null +++ b/hack/examples/overlays/clusterclasses/nutanix/kustomization.yaml.tmpl @@ -0,0 +1,27 @@ +# Copyright 2024 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- ../../../bases/nutanix/clusterclass + +sortOptions: + order: fifo + +patches: + - target: + kind: ClusterClass + patch: |- + - op: "add" + path: "/spec/patches" + value: + - name: "cluster-config" + external: + generateExtension: "nutanixclusterconfigpatch.cluster-api-runtime-extensions-nutanix" + discoverVariablesExtension: "nutanixclusterconfigvars.cluster-api-runtime-extensions-nutanix" + - name: "worker-config" + external: + generateExtension: "nutanixworkerconfigpatch.cluster-api-runtime-extensions-nutanix" + discoverVariablesExtension: "nutanixworkerconfigvars.cluster-api-runtime-extensions-nutanix" diff --git a/hack/examples/overlays/clusters/nutanix/calico/crs/kustomization.yaml.tmpl b/hack/examples/overlays/clusters/nutanix/calico/crs/kustomization.yaml.tmpl new file mode 100644 index 000000000..467d830d9 --- /dev/null +++ b/hack/examples/overlays/clusters/nutanix/calico/crs/kustomization.yaml.tmpl @@ -0,0 +1,19 @@ +# Copyright 2024 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- ../../../../../bases/nutanix/cluster + +sortOptions: + order: fifo + +patches: + - target: + kind: Cluster + path: ../../../../../patches/calico.yaml + - target: + kind: Cluster + path: ../../../../../patches/crs-strategy.yaml diff --git a/hack/examples/overlays/clusters/nutanix/calico/helm-addon/kustomization.yaml.tmpl b/hack/examples/overlays/clusters/nutanix/calico/helm-addon/kustomization.yaml.tmpl new file mode 100644 index 000000000..580fb8325 --- /dev/null +++ b/hack/examples/overlays/clusters/nutanix/calico/helm-addon/kustomization.yaml.tmpl @@ -0,0 +1,19 @@ +# Copyright 2024 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- ../../../../../bases/nutanix/cluster + +sortOptions: + order: fifo + +patches: + - target: + kind: Cluster + path: ../../../../../patches/calico.yaml + - target: + kind: Cluster + path: ../../../../../patches/helm-addon-strategy.yaml diff --git a/hack/examples/overlays/clusters/nutanix/cilium/crs/kustomization.yaml.tmpl b/hack/examples/overlays/clusters/nutanix/cilium/crs/kustomization.yaml.tmpl new file mode 100644 index 000000000..8583a6ab4 --- /dev/null +++ b/hack/examples/overlays/clusters/nutanix/cilium/crs/kustomization.yaml.tmpl @@ -0,0 +1,19 @@ +# Copyright 2024 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- ../../../../../bases/nutanix/cluster + +sortOptions: + order: fifo + +patches: + - target: + kind: Cluster + path: ../../../../../patches/cilium.yaml + - target: + kind: Cluster + path: ../../../../../patches/crs-strategy.yaml diff --git a/hack/examples/overlays/clusters/nutanix/cilium/helm-addon/kustomization.yaml.tmpl b/hack/examples/overlays/clusters/nutanix/cilium/helm-addon/kustomization.yaml.tmpl new file mode 100644 index 000000000..2c5917713 --- /dev/null +++ b/hack/examples/overlays/clusters/nutanix/cilium/helm-addon/kustomization.yaml.tmpl @@ -0,0 +1,19 @@ +# Copyright 2024 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- ../../../../../bases/nutanix/cluster + +sortOptions: + order: fifo + +patches: + - target: + kind: Cluster + path: ../../../../../patches/cilium.yaml + - target: + kind: Cluster + path: ../../../../../patches/helm-addon-strategy.yaml diff --git a/hack/examples/patches/aws/csi.yaml b/hack/examples/patches/aws/csi.yaml index 1d395d192..0bbe4c5bd 100644 --- a/hack/examples/patches/aws/csi.yaml +++ b/hack/examples/patches/aws/csi.yaml @@ -4,5 +4,11 @@ - op: "add" path: "/spec/topology/variables/0/value/addons/csi" value: + defaultStorage: + providerName: aws-ebs + storageClassConfigName: aws-ebs providers: - name: aws-ebs + storageClassConfig: + - name: aws-ebs + strategy: ClusterResourceSet diff --git a/hack/examples/patches/cpi.yaml b/hack/examples/patches/ccm.yaml similarity index 67% rename from hack/examples/patches/cpi.yaml rename to hack/examples/patches/ccm.yaml index dff7b8841..3263fe97b 100644 --- a/hack/examples/patches/cpi.yaml +++ b/hack/examples/patches/ccm.yaml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 - op: "add" - path: "/spec/topology/variables/0/value/addons/cpi" + path: "/spec/topology/variables/0/value/addons/ccm" value: {} 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/csi.yaml b/hack/examples/patches/nutanix/csi.yaml new file mode 100644 index 000000000..fffac03ef --- /dev/null +++ b/hack/examples/patches/nutanix/csi.yaml @@ -0,0 +1,18 @@ +# Copyright 2024 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +- op: "add" + path: "/spec/topology/variables/0/value/addons/csi" + value: + defaultStorage: + providerName: nutanix + storageClassConfigName: nutanix-volume + providers: + - name: nutanix + storageClassConfig: + - name: nutanix-volume + parameters: + storageContainer: ${NUTANIX_STORAGE_CONTAINER_NAME} + strategy: HelmAddon + credentials: + name: ${CLUSTER_NAME}-pe-creds-for-csi diff --git a/hack/examples/patches/nutanix/initialize-variables.yaml b/hack/examples/patches/nutanix/initialize-variables.yaml new file mode 100644 index 000000000..25f970e4e --- /dev/null +++ b/hack/examples/patches/nutanix/initialize-variables.yaml @@ -0,0 +1,54 @@ +# Copyright 2024 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +- op: "add" + path: "/spec/topology/class" + value: "nutanix-quick-start" +- op: "add" + path: "/spec/topology/variables/0/value/nutanix" + value: + controlPlaneEndpoint: + host: ${CONTROL_PLANE_ENDPOINT_IP} + port: 6443 + prismCentralEndpoint: + url: https://${NUTANIX_ENDPOINT}:9440 + insecure: ${NUTANIX_INSECURE} + credentials: + name: ${CLUSTER_NAME}-pc-creds +- op: "add" + path: "/spec/topology/variables/0/value/controlPlane" + value: + nutanix: + machineDetails: + bootType: legacy + cluster: + name: ${NUTANIX_PRISM_ELEMENT_CLUSTER_NAME} + type: name + image: + name: ${NUTANIX_MACHINE_TEMPLATE_IMAGE_NAME} + type: name + subnets: + - name: ${NUTANIX_SUBNET_NAME} + type: name + memorySize: 4Gi + systemDiskSize: 40Gi + vcpuSockets: 2 + vcpusPerSocket: 1 +- op: "add" + path: "/spec/topology/variables/1/value/nutanix" + value: + machineDetails: + bootType: legacy + cluster: + name: ${NUTANIX_PRISM_ELEMENT_CLUSTER_NAME} + type: name + image: + name: ${NUTANIX_MACHINE_TEMPLATE_IMAGE_NAME} + type: name + memorySize: 4Gi + subnets: + - name: ${NUTANIX_SUBNET_NAME} + type: name + systemDiskSize: 40Gi + vcpuSockets: 2 + vcpusPerSocket: 1 diff --git a/hack/examples/patches/nutanix/remove-additional-trust-bundle/cm.yaml b/hack/examples/patches/nutanix/remove-additional-trust-bundle/cm.yaml new file mode 100644 index 000000000..51ad983ba --- /dev/null +++ b/hack/examples/patches/nutanix/remove-additional-trust-bundle/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: ${CLUSTER_NAME}-pc-trusted-ca-bundle 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/hack/examples/sync.sh b/hack/examples/sync.sh index 4d11c0e70..a03967595 100755 --- a/hack/examples/sync.sh +++ b/hack/examples/sync.sh @@ -21,7 +21,7 @@ mkdir -p "${EXAMPLE_CLUSTERCLASSES_DIR}" readonly EXAMPLE_CLUSTERS_DIR=examples/capi-quick-start mkdir -p "${EXAMPLE_CLUSTERS_DIR}" -for provider in "aws" "docker"; do +for provider in "aws" "docker" "nutanix"; do kustomize build --load-restrictor LoadRestrictionsNone \ ./hack/examples/overlays/clusterclasses/"${provider}" >"${EXAMPLE_CLUSTERCLASSES_DIR}"/"${provider}"-cluster-class.yaml diff --git a/hack/third-party/caaph/go.mod b/hack/third-party/caaph/go.mod index 05dd21818..d908667ab 100644 --- a/hack/third-party/caaph/go.mod +++ b/hack/third-party/caaph/go.mod @@ -43,10 +43,10 @@ require ( github.com/stretchr/testify v1.8.4 // indirect go.uber.org/goleak v1.3.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/net v0.19.0 // indirect + golang.org/x/net v0.24.0 // indirect golang.org/x/oauth2 v0.14.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.15.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/term v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect diff --git a/hack/third-party/caaph/go.sum b/hack/third-party/caaph/go.sum index d7c5c8cae..1ac84ef41 100644 --- a/hack/third-party/caaph/go.sum +++ b/hack/third-party/caaph/go.sum @@ -126,8 +126,8 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0= golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -138,10 +138,10 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/hack/third-party/cabpk/go.mod b/hack/third-party/cabpk/go.mod index d4c0700c0..d792835be 100644 --- a/hack/third-party/cabpk/go.mod +++ b/hack/third-party/cabpk/go.mod @@ -35,8 +35,8 @@ require ( github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.11.1 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/net v0.18.0 // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/hack/third-party/cabpk/go.sum b/hack/third-party/cabpk/go.sum index 4c41d4fbe..f05bc632d 100644 --- a/hack/third-party/cabpk/go.sum +++ b/hack/third-party/cabpk/go.sum @@ -94,8 +94,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -103,8 +103,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= diff --git a/hack/third-party/capa/go.mod b/hack/third-party/capa/go.mod index 5045470dc..768cdfa73 100644 --- a/hack/third-party/capa/go.mod +++ b/hack/third-party/capa/go.mod @@ -46,10 +46,10 @@ require ( github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect - golang.org/x/net v0.21.0 // indirect + golang.org/x/net v0.24.0 // indirect golang.org/x/oauth2 v0.17.0 // indirect - golang.org/x/sys v0.17.0 // indirect - golang.org/x/term v0.17.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/term v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect diff --git a/hack/third-party/capa/go.sum b/hack/third-party/capa/go.sum index 61db925b3..f005c1b2d 100644 --- a/hack/third-party/capa/go.sum +++ b/hack/third-party/capa/go.sum @@ -124,8 +124,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -139,12 +139,12 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= diff --git a/hack/third-party/capd/go.mod b/hack/third-party/capd/go.mod index c9391b46d..3a5d52fe0 100644 --- a/hack/third-party/capd/go.mod +++ b/hack/third-party/capd/go.mod @@ -26,7 +26,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pkg/errors v0.9.1 // indirect - golang.org/x/net v0.18.0 // indirect + golang.org/x/net v0.24.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/hack/third-party/capd/go.sum b/hack/third-party/capd/go.sum index ee07964be..d645a2e73 100644 --- a/hack/third-party/capd/go.sum +++ b/hack/third-party/capd/go.sum @@ -77,8 +77,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/hack/third-party/capx/go.mod b/hack/third-party/capx/go.mod index 0c5771a41..8ef00494c 100644 --- a/hack/third-party/capx/go.mod +++ b/hack/third-party/capx/go.mod @@ -41,10 +41,10 @@ require ( github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/net v0.18.0 // indirect + golang.org/x/net v0.24.0 // indirect golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 // indirect - golang.org/x/sys v0.14.0 // indirect - golang.org/x/term v0.14.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/term v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect diff --git a/hack/third-party/capx/go.sum b/hack/third-party/capx/go.sum index 93f909ca6..fa1c645bf 100644 --- a/hack/third-party/capx/go.sum +++ b/hack/third-party/capx/go.sum @@ -598,8 +598,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -694,13 +694,13 @@ golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= -golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/hack/tools/helm-cm/main.go b/hack/tools/helm-cm/main.go new file mode 100644 index 000000000..54f919e50 --- /dev/null +++ b/hack/tools/helm-cm/main.go @@ -0,0 +1,167 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "bytes" + "context" + "flag" + "fmt" + "io/fs" + "os" + "path" + "strings" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/yaml" + ctrl "sigs.k8s.io/controller-runtime" + yamlMarshal "sigs.k8s.io/yaml" +) + +const ( + createHelmAddonsConfigMap = "helm-addons" +) + +var log = ctrl.LoggerFrom(context.Background()) + +func main() { + args := os.Args + var ( + kustomizeDirectory string + outputFile string + ) + flagSet := flag.NewFlagSet(createHelmAddonsConfigMap, flag.ExitOnError) + flagSet.StringVar(&kustomizeDirectory, "kustomize-directory", "", + "Kustomize base directory for all addons") + flagSet.StringVar(&outputFile, "output-file", "", + "output file name to write config map to.") + err := flagSet.Parse(args[1:]) + if err != nil { + log.Error(err, "failed to parse args") + } + cm, err := createConfigMapFromDir(kustomizeDirectory) + if err != nil { + log.Error(err, "failed to create configMap") + return + } + b, err := yamlMarshal.Marshal(*cm) + if err != nil { + log.Error(err, "failed ") + } + fullOutputfilePath := outputFile + if !path.IsAbs(outputFile) { + wd, err := os.Getwd() + if err != nil { + log.Error(err, "failed") + } + fullOutputfilePath = path.Join(wd, outputFile) + } + f, err := os.OpenFile(fullOutputfilePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o666) + if err != nil { + log.Error(err, "failed to create file") + } + defer f.Close() + _, err = bytes.NewBuffer(b).WriteTo(f) + if err != nil { + log.Error(err, "failed to write to file") + } +} + +type configMapInfo struct { + configMapFieldName string + RepositoryURL string `json:"RepositoryURL"` + ChartVersion string `json:"ChartVersion"` + ChartName string `json:"ChartName"` +} + +func createConfigMapFromDir(kustomizeDir string) (*corev1.ConfigMap, error) { + fullPath := kustomizeDir + if !path.IsAbs(fullPath) { + wd, err := os.Getwd() + if err != nil { + return nil, fmt.Errorf("failed to get wd %w", err) + } + fullPath = path.Join(wd, kustomizeDir) + } + configDirFS := os.DirFS(fullPath) + results := []configMapInfo{} + err := fs.WalkDir(configDirFS, ".", func(filepath string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if strings.Contains(filepath, "kustomization.yaml.tmpl") && !isIgnored(filepath) { + f, err := os.Open(path.Join(fullPath, filepath)) + if err != nil { + return fmt.Errorf("failed to open file: %w", err) + } + defer f.Close() + obj := make(map[string]interface{}) + err = yaml.NewYAMLOrJSONDecoder(f, 1024).Decode(&obj) + if err != nil { + return err + } + charts, ok := obj["helmCharts"] + if !ok { + log.Info("obj %v does not have field helmCharts. skipping \n", obj) + return nil + } + parsedCharts, ok := charts.([]interface{}) + if !ok { + return fmt.Errorf("charts obj %v is not of type []interface", charts) + } + info, ok := parsedCharts[0].(map[string]interface{}) + if !ok { + return fmt.Errorf("info obj %v is not of type map[string]interface", parsedCharts) + } + repo := info["repo"].(string) + name := info["name"].(string) + dirName := strings.Split(filepath, "/")[0] + i := configMapInfo{ + configMapFieldName: dirName, + RepositoryURL: repo, + ChartName: name, + } + versionEnvVar := info["version"].(string) + version := os.ExpandEnv(versionEnvVar) + i.ChartVersion = version + results = append(results, i) + return nil + } + return nil + }) + + finalCM := corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "placeholder", + }, + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "ConfigMap", + }, + Data: make(map[string]string), + } + for _, res := range results { + d, err := yamlMarshal.Marshal(res) + if err != nil { + return &finalCM, err + } + finalCM.Data[res.configMapFieldName] = string(d) + } + return &finalCM, err +} + +var ignored = []string{ + "aws-ccm", + "aws-ebs-csi", +} + +func isIgnored(filepath string) bool { + for _, i := range ignored { + if strings.Contains(filepath, i) { + return true + } + } + return false +} diff --git a/make/addons.mk b/make/addons.mk index 60c04554f..36ae1b33e 100644 --- a/make/addons.mk +++ b/make/addons.mk @@ -1,20 +1,24 @@ # Copyright 2023 D2iQ, Inc. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -export CALICO_VERSION := $(shell goprintconst -file pkg/handlers/generic/lifecycle/cni/calico/strategy_helmaddon.go -name defaultCalicoHelmChartVersion) -export CILIUM_VERSION := $(shell goprintconst -file pkg/handlers/generic/lifecycle/cni/cilium/strategy_helmaddon.go -name defaultCiliumHelmChartVersion) -export NODE_FEATURE_DISCOVERY_VERSION := $(shell goprintconst -file pkg/handlers/generic/lifecycle/nfd/strategy_helmaddon.go -name defaultHelmChartVersion) +export CALICO_VERSION := v3.26.4 +export CILIUM_VERSION := 1.15.0 +export NODE_FEATURE_DISCOVERY_VERSION := 0.15.2 export CLUSTER_AUTOSCALER_VERSION := 9.35.0 export AWS_CSI_SNAPSHOT_CONTROLLER_VERSION := v6.3.3 export AWS_EBS_CSI_CHART_VERSION := v2.28.1 -# a map of AWS CPI versions -export AWS_CPI_VERSION_127 := v1.27.1 -export AWS_CPI_CHART_VERSION_127 := 0.0.8 -export AWS_CPI_VERSION_128 := v1.28.1 -export AWS_CPI_CHART_VERSION_128 := 0.0.8 +export NUTANIX_STORAGE_CSI_CHART_VERSION := v2.6.6 +export NUTANIX_SNAPSHOT_CSI_CHART_VERSION := v6.3.2 +# a map of AWS CCM versions +export AWS_CCM_VERSION_127 := v1.27.1 +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-cpi.127 aws-cpi.128) +addons.sync: $(addprefix update-addon.,calico cilium nfd cluster-autoscaler aws-ebs-csi aws-ccm.127 nutanix-storage-csi aws-ccm.128) .PHONY: update-addon.calico update-addon.calico: ; $(info $(M) updating calico manifests) @@ -36,6 +40,15 @@ update-addon.cluster-autoscaler: ; $(info $(M) updating cluster-autoscaler manif update-addon.aws-ebs-csi: ; $(info $(M) updating aws ebs csi manifests) ./hack/addons/update-aws-ebs-csi.sh -.PHONY: update-addon.aws-cpi.% -update-addon.aws-cpi.%: ; $(info $(M) updating aws cpi $* manifests) - ./hack/addons/update-aws-cpi.sh $(AWS_CPI_VERSION_$*) $(AWS_CPI_CHART_VERSION_$*) +.PHONY: update-addon.aws-ccm.% +update-addon.aws-ccm.%: ; $(info $(M) updating aws ccm $* manifests) + ./hack/addons/update-aws-ccm.sh $(AWS_CCM_VERSION_$*) $(AWS_CCM_CHART_VERSION_$*) + +.PHONY: update-addon.nutanix-storage-csi +update-addon.nutanix-storage-csi: ; $(info $(M) updating nutanix-storage csi manifests) + ./hack/addons/update-nutanix-csi.sh + +.PHONY: generate-helm-configmap +generate-helm-configmap: + go run hack/tools/helm-cm/main.go -kustomize-directory="./hack/addons/kustomize" -output-file="./charts/cluster-api-runtime-extensions-nutanix/templates/helm-config.yaml" + ./hack/addons/add-warning-helm-configmap.sh diff --git a/make/clusterctl.mk b/make/clusterctl.mk index 9fd35d427..a556f719e 100644 --- a/make/clusterctl.mk +++ b/make/clusterctl.mk @@ -1,6 +1,13 @@ # Copyright 2023 D2iQ, Inc. All rights reserved. # SPDX-License-Identifier: Apache-2.0 +export CAPI_VERSION := $(shell go list -m -f '{{ .Version }}' sigs.k8s.io/cluster-api) +export CAPD_VERSION := $(shell go list -m -f '{{ .Version }}' sigs.k8s.io/cluster-api/test) +export CAPA_VERSION := $(shell cd hack/third-party/capa && go list -m -f '{{ .Version }}' sigs.k8s.io/cluster-api-provider-aws/v2) +export CAPX_VERSION := $(shell cd hack/third-party/capx && go list -m -f '{{ .Version }}' github.com/nutanix-cloud-native/cluster-api-provider-nutanix) +export CAAPH_VERSION := $(shell cd hack/third-party/caaph && go list -m -f '{{ .Version }}' sigs.k8s.io/cluster-api-addon-provider-helm) + +# Leave Nutanix credentials empty here and set it when creating the clusters .PHONY: clusterctl.init clusterctl.init: env CLUSTER_TOPOLOGY=true \ @@ -8,10 +15,14 @@ clusterctl.init: EXP_CLUSTER_RESOURCE_SET=true \ EXP_MACHINE_POOL=true \ AWS_B64ENCODED_CREDENTIALS=$$(clusterawsadm bootstrap credentials encode-as-profile) \ + NUTANIX_ENDPOINT="" NUTANIX_PASSWORD="" NUTANIX_USER="" \ clusterctl init \ --kubeconfig=$(KIND_KUBECONFIG) \ - --infrastructure docker,aws \ - --addon helm \ + --core cluster-api:$(CAPI_VERSION) \ + --bootstrap kubeadm:$(CAPI_VERSION) \ + --control-plane kubeadm:$(CAPI_VERSION) \ + --infrastructure docker:$(CAPD_VERSION),aws:$(CAPA_VERSION),nutanix:$(CAPX_VERSION) \ + --addon helm:$(CAAPH_VERSION) \ --wait-providers .PHONY: clusterctl.delete diff --git a/make/examples.mk b/make/examples.mk index b23f8f28e..b45306ece 100644 --- a/make/examples.mk +++ b/make/examples.mk @@ -3,9 +3,6 @@ export KUBERNETES_VERSION := v1.27.5 -export CLUSTERCTL_VERSION := $(shell clusterctl version -o short 2>/dev/null) -export CAPA_VERSION := $(shell cd hack/third-party/capa && go list -m -f '{{ .Version }}' sigs.k8s.io/cluster-api-provider-aws/v2) - .PHONY: examples.sync examples.sync: ## Syncs the examples by fetching upstream examples and applying kustomize patches hack/examples/sync.sh diff --git a/make/go.mk b/make/go.mk index 25d799efb..d405e33f0 100644 --- a/make/go.mk +++ b/make/go.mk @@ -5,8 +5,9 @@ # to be private (not available publicly) and should therefore not use the proxy or checksum database export GOPRIVATE ?= -ALL_GO_SUBMODULES := $(shell PATH='$(PATH)'; find -mindepth 2 -maxdepth 2 -name go.mod -printf '%P\n' | sort) +ALL_GO_SUBMODULES := $(shell find -mindepth 2 -maxdepth 2 -name go.mod -printf '%P\n' | sort) GO_SUBMODULES_NO_DOCS := $(filter-out $(addsuffix /go.mod,docs),$(ALL_GO_SUBMODULES)) +THIRD_PARTY_GO_SUBMODULES := $(shell find hack/third-party -mindepth 2 -name go.mod -printf 'hack/third-party/%P\n' | sort) ifndef GOOS export GOOS := $(OS) @@ -26,6 +27,7 @@ define go_test -covermode=atomic \ -coverprofile=coverage.out \ -short \ + -race \ -v \ $(if $(GOTEST_RUN),-run "$(GOTEST_RUN)") \ ./... && \ @@ -74,8 +76,8 @@ bench.%: ; $(info $(M) running benchmarks$(if $(GOTEST_RUN), matching "$(GOTEST_ E2E_PARALLEL_NODES ?= $(shell nproc --ignore=1) E2E_FLAKE_ATTEMPTS ?= 1 -E2E_CONF_FILE ?= $(REPO_ROOT)/test/e2e/config/cre.yaml -E2E_CONF_FILE_ENVSUBST ?= $(REPO_ROOT)/test/e2e/config/cre-envsubst.yaml +E2E_CONF_FILE ?= $(REPO_ROOT)/test/e2e/config/caren.yaml +E2E_CONF_FILE_ENVSUBST ?= $(basename $(E2E_CONF_FILE))-envsubst.yaml ARTIFACTS ?= ${REPO_ROOT}/_artifacts .PHONY: e2e-test @@ -145,12 +147,16 @@ endif ifneq ($(words $(GO_SUBMODULES_NO_DOCS)),0) mod-tidy: $(addprefix mod-tidy.,$(GO_SUBMODULES_NO_DOCS:/go.mod=)) endif +ifneq ($(words $(THIRD_PARTY_GO_SUBMODULES)),0) +mod-tidy: $(addprefix mod-tidy.,$(THIRD_PARTY_GO_SUBMODULES:/go.mod=)) +endif .PHONY: mod-tidy.% -mod-tidy.%: ## Runs go mod tidy for a specific module -mod-tidy.%: ; $(info $(M) running go mod tidy for $* module) - $(if $(filter-out root,$*),cd $* && )go mod tidy -v - $(if $(filter-out root,$*),cd $* && )go mod verify +.PHONY: mod-tidy.hack/third-party/% +mod-tidy.% mod-tidy.hack/third-party/%: ## Runs go mod tidy for a specific module +mod-tidy.% mod-tidy.hack/third-party/%: ; $(info $(M) running go mod tidy for $* module) + $(if $(filter-out root,$*),cd $(@:mod-tidy.%=%) && )go mod tidy -v + $(if $(filter-out root,$*),cd $(@:mod-tidy.%=%) && )go mod verify .PHONY: go-clean go-clean: ## Cleans go build, test and modules caches for all modules @@ -184,7 +190,10 @@ go-fix.%: ; $(info $(M) go fixing $* module) go-generate: ## Runs go generate go-generate: ; $(info $(M) running go generate) go generate -x ./... - controller-gen paths="./..." rbac:headerFile="hack/license-header.yaml.txt",roleName=cluster-api-runtime-extensions-nutanix-manager-role output:rbac:artifacts:config=charts/cluster-api-runtime-extensions-nutanix/templates + controller-gen \ + paths="./..." \ + rbac:headerFile="hack/license-header.yaml.txt",roleName=cluster-api-runtime-extensions-nutanix-manager-role \ + output:rbac:artifacts:config=charts/cluster-api-runtime-extensions-nutanix/templates sed --in-place 's/cluster-api-runtime-extensions-nutanix-manager-role/{{ include "chart.name" . }}-manager-role/' charts/cluster-api-runtime-extensions-nutanix/templates/role.yaml controller-gen paths="./api/v1alpha1/..." object:headerFile="hack/license-header.go.txt" output:object:artifacts:config=/dev/null $(MAKE) go-fix @@ -206,4 +215,4 @@ endif .PHONY: ggovulncheck.% govulncheck.%: ## Runs golangci-lint for a specific module govulncheck.%: ; $(info $(M) running govulncheck on $* module) - $(if $(filter-out root,$*),cd $* && )govulncheck ./... + $(if $(filter-out root .,$*),cd $* && )govulncheck ./... diff --git a/metadata.yaml b/metadata.yaml index 79bb8b1e5..cd6638adc 100644 --- a/metadata.yaml +++ b/metadata.yaml @@ -16,3 +16,6 @@ releaseSeries: - major: 0 minor: 5 contract: v1beta1 +- major: 0 + minor: 6 + contract: v1beta1 diff --git a/pkg/handlers/aws/mutation/ami/inject_control_plane_test.go b/pkg/handlers/aws/mutation/ami/inject_control_plane_test.go new file mode 100644 index 000000000..638446022 --- /dev/null +++ b/pkg/handlers/aws/mutation/ami/inject_control_plane_test.go @@ -0,0 +1,100 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package ami + +import ( + . "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" +) + +var _ = Describe("Generate AMI patches for ControlPlane", func() { + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewControlPlanePatch()).(mutation.GeneratePatches) + } + + testDefs := []capitest.PatchTestDef{ + { + Name: "AMI set for control plane", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + clusterconfig.MetaVariableName, + v1alpha1.AMISpec{ID: "ami-controlplane"}, + clusterconfig.MetaControlPlaneConfigName, + v1alpha1.AWSVariableName, + VariableName, + ), + }, + RequestItem: request.NewCPAWSMachineTemplateRequestItem("1234"), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{ + { + Operation: "add", + Path: "/spec/template/spec/ami/id", + ValueMatcher: gomega.Equal("ami-controlplane"), + }, + }, + }, + { + Name: "AMI lookup format set for control plane", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + clusterconfig.MetaVariableName, + v1alpha1.AMISpec{ + Lookup: &v1alpha1.AMILookup{ + Format: "test-{{.kubernetesVersion}}-format", + Org: "1234", + BaseOS: "testOS", + }, + }, + clusterconfig.MetaControlPlaneConfigName, + v1alpha1.AWSVariableName, + VariableName, + ), + }, + RequestItem: request.NewCPAWSMachineTemplateRequestItem("1234"), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{ + { + Operation: "add", + Path: "/spec/template/spec/imageLookupFormat", + ValueMatcher: gomega.Equal("test-{{.kubernetesVersion}}-format"), + }, + { + Operation: "add", + Path: "/spec/template/spec/imageLookupOrg", + ValueMatcher: gomega.Equal("1234"), + }, + { + Operation: "add", + Path: "/spec/template/spec/imageLookupBaseOS", + ValueMatcher: gomega.Equal("testOS"), + }, + }, + UnexpectedPatchMatchers: []capitest.JSONPatchMatcher{ + { + Operation: "add", + Path: "/spec/template/spec/ami/id", + ValueMatcher: gomega.Equal(""), + }, + }, + }, + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches( + GinkgoT(), + patchGenerator, + &tt, + ) + }) + } +}) diff --git a/pkg/handlers/aws/mutation/ami/inject_suite_test.go b/pkg/handlers/aws/mutation/ami/inject_suite_test.go new file mode 100644 index 000000000..bb77ba57e --- /dev/null +++ b/pkg/handlers/aws/mutation/ami/inject_suite_test.go @@ -0,0 +1,16 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package ami + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestAMIPatch(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "AMI patches for ControlPlane and Workers suite") +} diff --git a/pkg/handlers/aws/mutation/ami/tests/generate_patches.go b/pkg/handlers/aws/mutation/ami/inject_worker_test.go similarity index 52% rename from pkg/handlers/aws/mutation/ami/tests/generate_patches.go rename to pkg/handlers/aws/mutation/ami/inject_worker_test.go index 36f86066b..237f0963a 100644 --- a/pkg/handlers/aws/mutation/ami/tests/generate_patches.go +++ b/pkg/handlers/aws/mutation/ami/inject_worker_test.go @@ -1,11 +1,10 @@ // Copyright 2023 D2iQ, Inc. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -package tests +package ami import ( - "testing" - + . "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" @@ -14,99 +13,23 @@ import ( "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/workerconfig" ) -func TestControlPlaneGeneratePatches( - t *testing.T, - generatorFunc func() mutation.GeneratePatches, - variableName string, - variablePath ...string, -) { - t.Helper() - - capitest.ValidateGeneratePatches( - t, - generatorFunc, - capitest.PatchTestDef{ - Name: "AMI set for control plane", - Vars: []runtimehooksv1.Variable{ - capitest.VariableWithValue( - variableName, - v1alpha1.AMISpec{ID: "ami-controlplane"}, - variablePath..., - ), - }, - RequestItem: request.NewCPAWSMachineTemplateRequestItem("1234"), - ExpectedPatchMatchers: []capitest.JSONPatchMatcher{ - { - Operation: "add", - Path: "/spec/template/spec/ami/id", - ValueMatcher: gomega.Equal("ami-controlplane"), - }, - }, - }, - capitest.PatchTestDef{ - Name: "AMI lookup format set for control plane", - Vars: []runtimehooksv1.Variable{ - capitest.VariableWithValue( - variableName, - v1alpha1.AMISpec{ - Lookup: &v1alpha1.AMILookup{ - Format: "test-{{.kubernetesVersion}}-format", - Org: "12345", - BaseOS: "testOS", - }, - }, - variablePath..., - ), - }, - RequestItem: request.NewCPAWSMachineTemplateRequestItem("1234"), - ExpectedPatchMatchers: []capitest.JSONPatchMatcher{ - { - Operation: "add", - Path: "/spec/template/spec/imageLookupFormat", - ValueMatcher: gomega.Equal("test-{{.kubernetesVersion}}-format"), - }, - { - Operation: "add", - Path: "/spec/template/spec/imageLookupOrg", - ValueMatcher: gomega.Equal("12345"), - }, - { - Operation: "add", - Path: "/spec/template/spec/imageLookupBaseOS", - ValueMatcher: gomega.Equal("testOS"), - }, - }, - UnexpectedPatchMatchers: []capitest.JSONPatchMatcher{ - { - Operation: "add", - Path: "/spec/template/spec/ami/id", - ValueMatcher: gomega.Equal(""), - }, - }, - }, - ) -} - -func TestWorkerGeneratePatches( - t *testing.T, - generatorFunc func() mutation.GeneratePatches, - variableName string, - variablePath ...string, -) { - t.Helper() +var _ = Describe("Generate AMI patches for Worker", func() { + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewWorkerPatch()).(mutation.GeneratePatches) + } - capitest.ValidateGeneratePatches( - t, - generatorFunc, - capitest.PatchTestDef{ + testDefs := []capitest.PatchTestDef{ + { Name: "AMI set for workers", Vars: []runtimehooksv1.Variable{ capitest.VariableWithValue( - variableName, + workerconfig.MetaVariableName, v1alpha1.AMISpec{ID: "ami-controlplane"}, - variablePath..., + v1alpha1.AWSVariableName, + VariableName, ), capitest.VariableWithValue( "builtin", @@ -124,11 +47,11 @@ func TestWorkerGeneratePatches( }, }, }, - capitest.PatchTestDef{ + { Name: "AMI lookup format set for worker", Vars: []runtimehooksv1.Variable{ capitest.VariableWithValue( - variableName, + workerconfig.MetaVariableName, v1alpha1.AMISpec{ Lookup: &v1alpha1.AMILookup{ Format: "test-{{.kubernetesVersion}}-format", @@ -136,8 +59,8 @@ func TestWorkerGeneratePatches( BaseOS: "testOS", }, }, - - variablePath..., + v1alpha1.AWSVariableName, + VariableName, ), capitest.VariableWithValue( "builtin", @@ -172,5 +95,17 @@ func TestWorkerGeneratePatches( }, }, }, - ) -} + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches( + GinkgoT(), + patchGenerator, + &tt, + ) + }) + } +}) diff --git a/pkg/handlers/aws/mutation/cni/calico/tests/generate_patches.go b/pkg/handlers/aws/mutation/cni/calico/inject_test.go similarity index 89% rename from pkg/handlers/aws/mutation/cni/calico/tests/generate_patches.go rename to pkg/handlers/aws/mutation/cni/calico/inject_test.go index d9bf7be65..2bba0f09f 100644 --- a/pkg/handlers/aws/mutation/cni/calico/tests/generate_patches.go +++ b/pkg/handlers/aws/mutation/cni/calico/inject_test.go @@ -1,13 +1,13 @@ // Copyright 2023 D2iQ, Inc. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -package tests +package calico import ( "testing" + . "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" - "github.com/onsi/gomega/format" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" capav1 "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/external/sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2" @@ -15,34 +15,33 @@ import ( "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" ) -func TestGeneratePatches( - t *testing.T, - generatorFunc func() mutation.GeneratePatches, - variableName string, - variablePath ...string, -) { - t.Helper() +func TestCalicoPatch(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "AWS Calico CNI ingress mutator suite") +} - format.MaxLength = 0 - format.TruncatedDiff = false +var _ = Describe("Generate AWS Calico CNI ingress patches", func() { + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewPatch()).(mutation.GeneratePatches) + } - capitest.ValidateGeneratePatches( - t, - generatorFunc, - capitest.PatchTestDef{ + testDefs := []capitest.PatchTestDef{ + { Name: "unset variable", }, - capitest.PatchTestDef{ + { Name: "provider set with AWSClusterTemplate", Vars: []runtimehooksv1.Variable{ capitest.VariableWithValue( - variableName, + clusterconfig.MetaVariableName, v1alpha1.CNI{ Provider: v1alpha1.CNIProviderCalico, }, - variablePath..., + "addons", + v1alpha1.CNIVariableName, ), }, RequestItem: request.NewAWSClusterTemplateRequestItem("1234"), @@ -101,15 +100,16 @@ func TestGeneratePatches( ), }}, }, - capitest.PatchTestDef{ + { Name: "provider set with AWSClusterTemplate pre-existing rules", Vars: []runtimehooksv1.Variable{ capitest.VariableWithValue( - variableName, + clusterconfig.MetaVariableName, v1alpha1.CNI{ Provider: v1alpha1.CNIProviderCalico, }, - variablePath..., + "addons", + v1alpha1.CNIVariableName, ), }, RequestItem: request.NewAWSClusterTemplateRequestItem( @@ -193,15 +193,16 @@ func TestGeneratePatches( ), }}, }, - capitest.PatchTestDef{ + { Name: "provider set with AWSClusterTemplate conflicting pre-existing rules", Vars: []runtimehooksv1.Variable{ capitest.VariableWithValue( - variableName, + clusterconfig.MetaVariableName, v1alpha1.CNI{ Provider: v1alpha1.CNIProviderCalico, }, - variablePath..., + "addons", + v1alpha1.CNIVariableName, ), }, RequestItem: request.NewAWSClusterTemplateRequestItem( @@ -273,5 +274,17 @@ func TestGeneratePatches( ), }}, }, - ) -} + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches( + GinkgoT(), + patchGenerator, + &tt, + ) + }) + } +}) diff --git a/pkg/handlers/aws/mutation/controlplaneloadbalancer/tests/generate_patches.go b/pkg/handlers/aws/mutation/controlplaneloadbalancer/inject_test.go similarity index 65% rename from pkg/handlers/aws/mutation/controlplaneloadbalancer/tests/generate_patches.go rename to pkg/handlers/aws/mutation/controlplaneloadbalancer/inject_test.go index f799d0624..60a5c4ec5 100644 --- a/pkg/handlers/aws/mutation/controlplaneloadbalancer/tests/generate_patches.go +++ b/pkg/handlers/aws/mutation/controlplaneloadbalancer/inject_test.go @@ -1,11 +1,12 @@ // Copyright 2023 D2iQ, Inc. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -package tests +package controlplaneloadbalancer import ( "testing" + . "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" @@ -14,31 +15,33 @@ import ( "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" ) -func TestGeneratePatches( - t *testing.T, - generatorFunc func() mutation.GeneratePatches, - variableName string, - variablePath ...string, -) { - t.Helper() +func TestControlPlaneLoadBalancerPatch(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "AWS ControlPlane LoadBalancer mutator suite") +} + +var _ = Describe("Generate AWS ControlPlane LoadBalancer patches", func() { + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewPatch()).(mutation.GeneratePatches) + } - capitest.ValidateGeneratePatches( - t, - generatorFunc, - capitest.PatchTestDef{ + testDefs := []capitest.PatchTestDef{ + { Name: "unset variable", }, - capitest.PatchTestDef{ + { Name: "ControlPlaneLoadbalancer scheme set to internet-facing", Vars: []runtimehooksv1.Variable{ capitest.VariableWithValue( - variableName, + clusterconfig.MetaVariableName, v1alpha1.AWSLoadBalancerSpec{ Scheme: &capav1.ELBSchemeInternetFacing, }, - variablePath..., + v1alpha1.AWSVariableName, + VariableName, ), }, RequestItem: request.NewAWSClusterTemplateRequestItem("1234"), @@ -50,15 +53,16 @@ func TestGeneratePatches( ), }}, }, - capitest.PatchTestDef{ + { Name: "ControlPlaneLoadbalancer scheme set to internal", Vars: []runtimehooksv1.Variable{ capitest.VariableWithValue( - variableName, + clusterconfig.MetaVariableName, v1alpha1.AWSLoadBalancerSpec{ Scheme: &capav1.ELBSchemeInternal, }, - variablePath..., + v1alpha1.AWSVariableName, + VariableName, ), }, RequestItem: request.NewAWSClusterTemplateRequestItem("1234"), @@ -70,5 +74,17 @@ func TestGeneratePatches( ), }}, }, - ) -} + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches( + GinkgoT(), + patchGenerator, + &tt, + ) + }) + } +}) diff --git a/pkg/handlers/aws/mutation/iaminstanceprofile/inject_control_plane_test.go b/pkg/handlers/aws/mutation/iaminstanceprofile/inject_control_plane_test.go new file mode 100644 index 000000000..11d7b0ed9 --- /dev/null +++ b/pkg/handlers/aws/mutation/iaminstanceprofile/inject_control_plane_test.go @@ -0,0 +1,58 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package iaminstanceprofile + +import ( + . "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" +) + +var _ = Describe("Generate IAMInstanceProfile patches for ControlPlane", func() { + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewControlPlanePatch()).(mutation.GeneratePatches) + } + + testDefs := []capitest.PatchTestDef{ + { + Name: "unset variable", + }, + { + Name: "iamInstanceProfile for control plane set", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + clusterconfig.MetaVariableName, + "control-plane.cluster-api-provider-aws.sigs.k8s.io", + clusterconfig.MetaControlPlaneConfigName, + v1alpha1.AWSVariableName, + VariableName, + ), + }, + RequestItem: request.NewCPAWSMachineTemplateRequestItem("1234"), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{ + Operation: "add", + Path: "/spec/template/spec/iamInstanceProfile", + ValueMatcher: gomega.Equal("control-plane.cluster-api-provider-aws.sigs.k8s.io"), + }}, + }, + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches( + GinkgoT(), + patchGenerator, + &tt, + ) + }) + } +}) diff --git a/pkg/handlers/aws/mutation/iaminstanceprofile/inject_suite_test.go b/pkg/handlers/aws/mutation/iaminstanceprofile/inject_suite_test.go new file mode 100644 index 000000000..6cf5402dc --- /dev/null +++ b/pkg/handlers/aws/mutation/iaminstanceprofile/inject_suite_test.go @@ -0,0 +1,16 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package iaminstanceprofile + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestIAMInstnaceProfilePatch(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "IAMInstanceProfile patches for ControlPlane and Workers suite") +} diff --git a/pkg/handlers/aws/mutation/iaminstanceprofile/tests/generate_patches.go b/pkg/handlers/aws/mutation/iaminstanceprofile/inject_worker_test.go similarity index 50% rename from pkg/handlers/aws/mutation/iaminstanceprofile/tests/generate_patches.go rename to pkg/handlers/aws/mutation/iaminstanceprofile/inject_worker_test.go index 6f36d196f..a3044de6d 100644 --- a/pkg/handlers/aws/mutation/iaminstanceprofile/tests/generate_patches.go +++ b/pkg/handlers/aws/mutation/iaminstanceprofile/inject_worker_test.go @@ -1,74 +1,38 @@ // Copyright 2023 D2iQ, Inc. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -package tests +package iaminstanceprofile import ( - "testing" - + . "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/workerconfig" ) -func TestControlPlaneGeneratePatches( - t *testing.T, - generatorFunc func() mutation.GeneratePatches, - variableName string, - variablePath ...string, -) { - t.Helper() +var _ = Describe("Generate IAMInstanceProfile patches for Worker", func() { + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewWorkerPatch()).(mutation.GeneratePatches) + } - capitest.ValidateGeneratePatches( - t, - generatorFunc, - capitest.PatchTestDef{ + testDefs := []capitest.PatchTestDef{ + { Name: "unset variable", }, - capitest.PatchTestDef{ - Name: "iamInstanceProfile set", + { + Name: "iamInstanceProfile for worker set", Vars: []runtimehooksv1.Variable{ capitest.VariableWithValue( - variableName, - "control-plane.cluster-api-provider-aws.sigs.k8s.io", - variablePath..., - ), - }, - RequestItem: request.NewCPAWSMachineTemplateRequestItem("1234"), - ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{ - Operation: "add", - Path: "/spec/template/spec/iamInstanceProfile", - ValueMatcher: gomega.Equal("control-plane.cluster-api-provider-aws.sigs.k8s.io"), - }}, - }, - ) -} - -func TestWorkerGeneratePatches( - t *testing.T, - generatorFunc func() mutation.GeneratePatches, - variableName string, - variablePath ...string, -) { - t.Helper() - - capitest.ValidateGeneratePatches( - t, - generatorFunc, - capitest.PatchTestDef{ - Name: "unset variable", - }, - capitest.PatchTestDef{ - Name: "iamInstanceProfile set", - Vars: []runtimehooksv1.Variable{ - capitest.VariableWithValue( - variableName, + workerconfig.MetaVariableName, "nodes.cluster-api-provider-aws.sigs.k8s.io", - variablePath..., + v1alpha1.AWSVariableName, + VariableName, ), capitest.VariableWithValue( "builtin", @@ -84,5 +48,17 @@ func TestWorkerGeneratePatches( ValueMatcher: gomega.Equal("nodes.cluster-api-provider-aws.sigs.k8s.io"), }}, }, - ) -} + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches( + GinkgoT(), + patchGenerator, + &tt, + ) + }) + } +}) diff --git a/pkg/handlers/aws/mutation/instancetype/inject_control_plane_test.go b/pkg/handlers/aws/mutation/instancetype/inject_control_plane_test.go new file mode 100644 index 000000000..e13189a47 --- /dev/null +++ b/pkg/handlers/aws/mutation/instancetype/inject_control_plane_test.go @@ -0,0 +1,58 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package instancetype + +import ( + . "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" +) + +var _ = Describe("Generate InstanceType patches for ControlPlane", func() { + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewControlPlanePatch()).(mutation.GeneratePatches) + } + + testDefs := []capitest.PatchTestDef{ + { + Name: "unset variable", + }, + { + Name: "instanceType for controlplane set", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + clusterconfig.MetaVariableName, + "m5.xlarge", + clusterconfig.MetaControlPlaneConfigName, + v1alpha1.AWSVariableName, + VariableName, + ), + }, + RequestItem: request.NewCPAWSMachineTemplateRequestItem("1234"), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{ + Operation: "replace", + Path: "/spec/template/spec/instanceType", + ValueMatcher: gomega.Equal("m5.xlarge"), + }}, + }, + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches( + GinkgoT(), + patchGenerator, + &tt, + ) + }) + } +}) diff --git a/pkg/handlers/aws/mutation/instancetype/inject_suite_test.go b/pkg/handlers/aws/mutation/instancetype/inject_suite_test.go new file mode 100644 index 000000000..393d232d7 --- /dev/null +++ b/pkg/handlers/aws/mutation/instancetype/inject_suite_test.go @@ -0,0 +1,16 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package instancetype + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestInstanceTypePatch(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "InstanceType patches for ControlPlane and Workers suite") +} diff --git a/pkg/handlers/aws/mutation/instancetype/tests/generate_patches.go b/pkg/handlers/aws/mutation/instancetype/inject_worker_test.go similarity index 50% rename from pkg/handlers/aws/mutation/instancetype/tests/generate_patches.go rename to pkg/handlers/aws/mutation/instancetype/inject_worker_test.go index ef9959535..03cbec647 100644 --- a/pkg/handlers/aws/mutation/instancetype/tests/generate_patches.go +++ b/pkg/handlers/aws/mutation/instancetype/inject_worker_test.go @@ -1,74 +1,38 @@ // Copyright 2023 D2iQ, Inc. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -package tests +package instancetype import ( - "testing" - + . "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/workerconfig" ) -func TestControlPlaneGeneratePatches( - t *testing.T, - generatorFunc func() mutation.GeneratePatches, - variableName string, - variablePath ...string, -) { - t.Helper() +var _ = Describe("Generate InstanceType patches for Worker", func() { + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewWorkerPatch()).(mutation.GeneratePatches) + } - capitest.ValidateGeneratePatches( - t, - generatorFunc, - capitest.PatchTestDef{ + testDefs := []capitest.PatchTestDef{ + { Name: "unset variable", }, - capitest.PatchTestDef{ - Name: "instanceType set", + { + Name: "instanceType for workers set", Vars: []runtimehooksv1.Variable{ capitest.VariableWithValue( - variableName, - "m5.xlarge", - variablePath..., - ), - }, - RequestItem: request.NewCPAWSMachineTemplateRequestItem("1234"), - ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{ - Operation: "replace", - Path: "/spec/template/spec/instanceType", - ValueMatcher: gomega.Equal("m5.xlarge"), - }}, - }, - ) -} - -func TestWorkerGeneratePatches( - t *testing.T, - generatorFunc func() mutation.GeneratePatches, - variableName string, - variablePath ...string, -) { - t.Helper() - - capitest.ValidateGeneratePatches( - t, - generatorFunc, - capitest.PatchTestDef{ - Name: "unset variable", - }, - capitest.PatchTestDef{ - Name: "instanceType set", - Vars: []runtimehooksv1.Variable{ - capitest.VariableWithValue( - variableName, + workerconfig.MetaVariableName, "m5.2xlarge", - variablePath..., + v1alpha1.AWSVariableName, + VariableName, ), capitest.VariableWithValue( "builtin", @@ -84,5 +48,17 @@ func TestWorkerGeneratePatches( ValueMatcher: gomega.Equal("m5.2xlarge"), }}, }, - ) -} + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches( + GinkgoT(), + patchGenerator, + &tt, + ) + }) + } +}) diff --git a/pkg/handlers/aws/mutation/metapatch_handler_test.go b/pkg/handlers/aws/mutation/metapatch_handler_test.go deleted file mode 100644 index e09beec54..000000000 --- a/pkg/handlers/aws/mutation/metapatch_handler_test.go +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2023 D2iQ, Inc. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package mutation - -import ( - "testing" - - "sigs.k8s.io/controller-runtime/pkg/manager" - - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/ami" - amitests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/ami/tests" - calicotests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/cni/calico/tests" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/controlplaneloadbalancer" - controlplaneloadbalancertests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/controlplaneloadbalancer/tests" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/iaminstanceprofile" - iaminstanceprofiletests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/iaminstanceprofile/tests" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/instancetype" - instancetypetests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/instancetype/tests" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/network" - networktests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/network/tests" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/region" - regiontests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/region/tests" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" - auditpolicytests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/auditpolicy/tests" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/etcd" - etcdtests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/etcd/tests" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/extraapiservercertsans" - extraapiservercertsanstests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/extraapiservercertsans/tests" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/httpproxy" - httpproxytests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/httpproxy/tests" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/imageregistries" - imageregistrycredentialstests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/imageregistries/credentials/tests" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/kubernetesimagerepository" - kubernetesimagerepositorytests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/kubernetesimagerepository/tests" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/mirrors" - globalimageregistrymirrortests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/mirrors/tests" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/workerconfig" -) - -func metaPatchGeneratorFunc(mgr manager.Manager) func() mutation.GeneratePatches { - return func() mutation.GeneratePatches { - return MetaPatchHandler(mgr).(mutation.GeneratePatches) - } -} - -func workerPatchGeneratorFunc() func() mutation.GeneratePatches { - return func() mutation.GeneratePatches { - return MetaWorkerPatchHandler().(mutation.GeneratePatches) - } -} - -func TestGeneratePatches(t *testing.T) { - t.Parallel() - - mgr := testEnv.Manager - - regiontests.TestGeneratePatches( - t, - metaPatchGeneratorFunc(mgr), - clusterconfig.MetaVariableName, - v1alpha1.AWSVariableName, - region.VariableName, - ) - - iaminstanceprofiletests.TestControlPlaneGeneratePatches( - t, - metaPatchGeneratorFunc(mgr), - clusterconfig.MetaVariableName, - clusterconfig.MetaControlPlaneConfigName, - v1alpha1.AWSVariableName, - iaminstanceprofile.VariableName, - ) - - iaminstanceprofiletests.TestWorkerGeneratePatches( - t, - workerPatchGeneratorFunc(), - workerconfig.MetaVariableName, - v1alpha1.AWSVariableName, - iaminstanceprofile.VariableName, - ) - - instancetypetests.TestControlPlaneGeneratePatches( - t, - metaPatchGeneratorFunc(mgr), - clusterconfig.MetaVariableName, - clusterconfig.MetaControlPlaneConfigName, - v1alpha1.AWSVariableName, - instancetype.VariableName, - ) - - instancetypetests.TestWorkerGeneratePatches( - t, - workerPatchGeneratorFunc(), - workerconfig.MetaVariableName, - v1alpha1.AWSVariableName, - instancetype.VariableName, - ) - - calicotests.TestGeneratePatches( - t, - metaPatchGeneratorFunc(mgr), - clusterconfig.MetaVariableName, - "addons", - v1alpha1.CNIVariableName, - ) - - auditpolicytests.TestGeneratePatches( - t, - metaPatchGeneratorFunc(mgr), - ) - - httpproxytests.TestGeneratePatches( - t, - metaPatchGeneratorFunc(mgr), - clusterconfig.MetaVariableName, - httpproxy.VariableName, - ) - - etcdtests.TestGeneratePatches( - t, - metaPatchGeneratorFunc(mgr), - clusterconfig.MetaVariableName, - etcd.VariableName, - ) - - extraapiservercertsanstests.TestGeneratePatches( - t, - metaPatchGeneratorFunc(mgr), - clusterconfig.MetaVariableName, - extraapiservercertsans.VariableName, - ) - - kubernetesimagerepositorytests.TestGeneratePatches( - t, - metaPatchGeneratorFunc(mgr), - clusterconfig.MetaVariableName, - kubernetesimagerepository.VariableName, - ) - - imageregistrycredentialstests.TestGeneratePatches( - t, - metaPatchGeneratorFunc(mgr), - mgr.GetClient(), - clusterconfig.MetaVariableName, - imageregistries.VariableName, - ) - - globalimageregistrymirrortests.TestGeneratePatches( - t, - metaPatchGeneratorFunc(mgr), - mgr.GetClient(), - clusterconfig.MetaVariableName, - mirrors.GlobalMirrorVariableName, - ) - - amitests.TestControlPlaneGeneratePatches( - t, - metaPatchGeneratorFunc(mgr), - clusterconfig.MetaVariableName, - clusterconfig.MetaControlPlaneConfigName, - v1alpha1.AWSVariableName, - ami.VariableName, - ) - - amitests.TestWorkerGeneratePatches( - t, - workerPatchGeneratorFunc(), - workerconfig.MetaVariableName, - v1alpha1.AWSVariableName, - ami.VariableName, - ) - - networktests.TestGeneratePatches( - t, - metaPatchGeneratorFunc(mgr), - clusterconfig.MetaVariableName, - v1alpha1.AWSVariableName, - network.VariableName, - ) - - controlplaneloadbalancertests.TestGeneratePatches( - t, - metaPatchGeneratorFunc(mgr), - clusterconfig.MetaVariableName, - v1alpha1.AWSVariableName, - controlplaneloadbalancer.VariableName, - ) -} diff --git a/pkg/handlers/aws/mutation/network/tests/generate_patches.go b/pkg/handlers/aws/mutation/network/inject_test.go similarity index 69% rename from pkg/handlers/aws/mutation/network/tests/generate_patches.go rename to pkg/handlers/aws/mutation/network/inject_test.go index 09dcdfd78..f91185cf3 100644 --- a/pkg/handlers/aws/mutation/network/tests/generate_patches.go +++ b/pkg/handlers/aws/mutation/network/inject_test.go @@ -1,11 +1,12 @@ // Copyright 2023 D2iQ, Inc. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -package tests +package network import ( "testing" + . "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" @@ -13,33 +14,35 @@ import ( "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" ) -func TestGeneratePatches( - t *testing.T, - generatorFunc func() mutation.GeneratePatches, - variableName string, - variablePath ...string, -) { - t.Helper() +func TestNetworkPatch(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "AWS Network mutator suite") +} + +var _ = Describe("Generate AWS Network patches", func() { + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewPatch()).(mutation.GeneratePatches) + } - capitest.ValidateGeneratePatches( - t, - generatorFunc, - capitest.PatchTestDef{ + testDefs := []capitest.PatchTestDef{ + { Name: "unset variable", }, - capitest.PatchTestDef{ + { Name: "VPC ID set", Vars: []runtimehooksv1.Variable{ capitest.VariableWithValue( - variableName, + clusterconfig.MetaVariableName, v1alpha1.AWSNetwork{ VPC: &v1alpha1.VPC{ ID: "vpc-1234", }, }, - variablePath..., + v1alpha1.AWSVariableName, + VariableName, ), }, RequestItem: request.NewAWSClusterTemplateRequestItem("1234"), @@ -49,11 +52,11 @@ func TestGeneratePatches( ValueMatcher: gomega.Equal("vpc-1234"), }}, }, - capitest.PatchTestDef{ + { Name: "Subnet IDs set", Vars: []runtimehooksv1.Variable{ capitest.VariableWithValue( - variableName, + clusterconfig.MetaVariableName, v1alpha1.AWSNetwork{ Subnets: v1alpha1.Subnets{ {ID: "subnet-1"}, @@ -61,7 +64,8 @@ func TestGeneratePatches( {ID: "subnet-3"}, }, }, - variablePath..., + v1alpha1.AWSVariableName, + VariableName, ), }, RequestItem: request.NewAWSClusterTemplateRequestItem("1234"), @@ -71,11 +75,11 @@ func TestGeneratePatches( ValueMatcher: gomega.HaveLen(3), }}, }, - capitest.PatchTestDef{ + { Name: "both VPC ID and Subnet IDs set", Vars: []runtimehooksv1.Variable{ capitest.VariableWithValue( - variableName, + clusterconfig.MetaVariableName, v1alpha1.AWSNetwork{ VPC: &v1alpha1.VPC{ ID: "vpc-1234", @@ -86,7 +90,8 @@ func TestGeneratePatches( {ID: "subnet-3"}, }, }, - variablePath..., + v1alpha1.AWSVariableName, + VariableName, ), }, RequestItem: request.NewAWSClusterTemplateRequestItem("1234"), @@ -100,5 +105,17 @@ func TestGeneratePatches( ValueMatcher: gomega.HaveLen(3), }}, }, - ) -} + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches( + GinkgoT(), + patchGenerator, + &tt, + ) + }) + } +}) diff --git a/pkg/handlers/aws/mutation/region/tests/generate_patches.go b/pkg/handlers/aws/mutation/region/inject_test.go similarity index 50% rename from pkg/handlers/aws/mutation/region/tests/generate_patches.go rename to pkg/handlers/aws/mutation/region/inject_test.go index 909d2f5c3..d38da4eda 100644 --- a/pkg/handlers/aws/mutation/region/tests/generate_patches.go +++ b/pkg/handlers/aws/mutation/region/inject_test.go @@ -1,40 +1,45 @@ // Copyright 2023 D2iQ, Inc. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -package tests +package region import ( "testing" + . "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" ) -func TestGeneratePatches( - t *testing.T, - generatorFunc func() mutation.GeneratePatches, - variableName string, - variablePath ...string, -) { - t.Helper() - - capitest.ValidateGeneratePatches( - t, - generatorFunc, - capitest.PatchTestDef{ +func TestRegionPatch(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "AWS Region mutator suite") +} + +var _ = Describe("Generate AWS Region patches", func() { + // only add aws region patch + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewPatch()).(mutation.GeneratePatches) + } + + testDefs := []capitest.PatchTestDef{ + { Name: "unset variable", }, - capitest.PatchTestDef{ + { Name: "region set", Vars: []runtimehooksv1.Variable{ capitest.VariableWithValue( - variableName, + clusterconfig.MetaVariableName, "a-specific-region", - variablePath..., + v1alpha1.AWSVariableName, + VariableName, ), }, RequestItem: request.NewAWSClusterTemplateRequestItem("1234"), @@ -44,5 +49,17 @@ func TestGeneratePatches( ValueMatcher: gomega.Equal("a-specific-region"), }}, }, - ) -} + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches( + GinkgoT(), + patchGenerator, + &tt, + ) + }) + } +}) diff --git a/pkg/handlers/aws/mutation/securitygroups/inject_control_plane_test.go b/pkg/handlers/aws/mutation/securitygroups/inject_control_plane_test.go new file mode 100644 index 000000000..8c67e96f2 --- /dev/null +++ b/pkg/handlers/aws/mutation/securitygroups/inject_control_plane_test.go @@ -0,0 +1,75 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package securitygroups + +import ( + . "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + "k8s.io/utils/ptr" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" +) + +var _ = Describe("Generate SecurityGroup patches for ControlPlane", func() { + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewControlPlanePatch()).(mutation.GeneratePatches) + } + + testDefs := []capitest.PatchTestDef{ + { + Name: "unset variable", + }, + { + Name: "SecurityGroups for controlplane set", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + clusterconfig.MetaVariableName, + v1alpha1.AdditionalSecurityGroup{ + {ID: ptr.To("sg-1")}, + {ID: ptr.To("sg-2")}, + {ID: ptr.To("sg-3")}, + }, + clusterconfig.MetaControlPlaneConfigName, + v1alpha1.AWSVariableName, + VariableName, + ), + }, + RequestItem: request.NewCPAWSMachineTemplateRequestItem("1234"), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{ + { + Operation: "add", + Path: "/spec/template/spec/additionalSecurityGroups", + ValueMatcher: gomega.HaveLen(3), + }, + // TODO(shalinpatel): add matcher to check if all SG are set + // { + // Operation: "add", + // Path: "/spec/template/spec/additionalSecurityGroups", + // ValueMatcher: gomega.ContainElements( + // gomega.HaveKeyWithValue("id", "sg-1"), + // gomega.HaveKeyWithValue("id", "sg-2"), + // gomega.HaveKeyWithValue("id", "sg-3"), + // ), + // }, + }, + }, + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches( + GinkgoT(), + patchGenerator, + &tt, + ) + }) + } +}) diff --git a/pkg/handlers/aws/mutation/securitygroups/inject_suite_test.go b/pkg/handlers/aws/mutation/securitygroups/inject_suite_test.go new file mode 100644 index 000000000..09ae3ad05 --- /dev/null +++ b/pkg/handlers/aws/mutation/securitygroups/inject_suite_test.go @@ -0,0 +1,16 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package securitygroups + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestSecurityGroupsPatch(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "AWS security groups patches for ControlPlane and Workers suite") +} diff --git a/pkg/handlers/aws/mutation/securitygroups/inject_worker_test.go b/pkg/handlers/aws/mutation/securitygroups/inject_worker_test.go new file mode 100644 index 000000000..2e4d90844 --- /dev/null +++ b/pkg/handlers/aws/mutation/securitygroups/inject_worker_test.go @@ -0,0 +1,81 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package securitygroups + +import ( + . "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/utils/ptr" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/workerconfig" +) + +var _ = Describe("Generate AWS SecurityGroups patches for Worker", func() { + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewWorkerPatch()).(mutation.GeneratePatches) + } + + testDefs := []capitest.PatchTestDef{ + { + Name: "unset variable", + }, + { + Name: "SecurityGroups for workers set", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + workerconfig.MetaVariableName, + v1alpha1.AdditionalSecurityGroup{ + {ID: ptr.To("sg-1")}, + {ID: ptr.To("sg-2")}, + {ID: ptr.To("sg-3")}, + }, + v1alpha1.AWSVariableName, + VariableName, + ), + capitest.VariableWithValue( + "builtin", + apiextensionsv1.JSON{ + Raw: []byte(`{"machineDeployment": {"class": "a-worker"}}`), + }, + ), + }, + RequestItem: request.NewWorkerAWSMachineTemplateRequestItem("1234"), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{ + { + Operation: "add", + Path: "/spec/template/spec/additionalSecurityGroups", + ValueMatcher: gomega.HaveLen(3), + }, + // TODO(shalinpatel): add matcher to check if all SG are set + // { + // Operation: "add", + // Path: "/spec/template/spec/additionalSecurityGroups", + // ValueMatcher: gomega.ContainElements( + // gomega.HaveKeyWithValue("id", "sg-1"), + // gomega.HaveKeyWithValue("id", "sg-2"), + // gomega.HaveKeyWithValue("id", "sg-3"), + // ), + // }, + }, + }, + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches( + GinkgoT(), + patchGenerator, + &tt, + ) + }) + } +}) diff --git a/pkg/handlers/aws/mutation/suite_test.go b/pkg/handlers/aws/mutation/suite_test.go deleted file mode 100644 index 20633d531..000000000 --- a/pkg/handlers/aws/mutation/suite_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2024 D2iQ, Inc. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package mutation - -import ( - "fmt" - "testing" - - ctrl "sigs.k8s.io/controller-runtime" - - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/test/helpers" -) - -var ( - testEnv *helpers.TestEnvironment - ctx = ctrl.SetupSignalHandler() -) - -func TestMain(m *testing.M) { - setup() - defer teardown() - m.Run() -} - -func setup() { - testEnvConfig := helpers.NewTestEnvironmentConfiguration() - var err error - testEnv, err = testEnvConfig.Build() - if err != nil { - panic(err) - } - go func() { - fmt.Println("Starting the manager") - if err := testEnv.StartManager(ctx); err != nil { - panic(fmt.Sprintf("Failed to start the envtest manager: %v", err)) - } - }() -} - -func teardown() { - if err := testEnv.Stop(); err != nil { - panic(fmt.Sprintf("Failed to stop envtest: %v", err)) - } -} diff --git a/pkg/handlers/docker/mutation/customimage/inject_control_plane_test.go b/pkg/handlers/docker/mutation/customimage/inject_control_plane_test.go new file mode 100644 index 000000000..fa40cbd5d --- /dev/null +++ b/pkg/handlers/docker/mutation/customimage/inject_control_plane_test.go @@ -0,0 +1,77 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package customimage + +import ( + . "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + dockerclusterconfig "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/docker/clusterconfig" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" +) + +var _ = Describe("Docker CustomImage patches for ControlPlane", func() { + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewControlPlanePatch()).(mutation.GeneratePatches) + } + + testDefs := []capitest.PatchTestDef{ + { + Name: "image unset for control plane", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + "builtin", + apiextensionsv1.JSON{Raw: []byte(`{"controlPlane": {"version": "v1.2.3"}}`)}, + ), + }, + RequestItem: request.NewCPDockerMachineTemplateRequestItem("1234"), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{ + Operation: "add", + Path: "/spec/template/spec/customImage", + ValueMatcher: gomega.Equal("ghcr.io/mesosphere/kind-node:v1.2.3"), + }}, + }, + { + Name: "image set for control plane", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + clusterconfig.MetaVariableName, + "a-specific-image", + clusterconfig.MetaControlPlaneConfigName, + dockerclusterconfig.DockerVariableName, + VariableName, + ), + capitest.VariableWithValue( + "builtin", + apiextensionsv1.JSON{ + Raw: []byte(`{"machineDeployment": {"class": "a-worker"}}`), + }, + ), + }, + RequestItem: request.NewCPDockerMachineTemplateRequestItem("1234"), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{ + Operation: "add", + Path: "/spec/template/spec/customImage", + ValueMatcher: gomega.Equal("a-specific-image"), + }}, + }, + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches( + GinkgoT(), + patchGenerator, + &tt, + ) + }) + } +}) diff --git a/pkg/handlers/docker/mutation/customimage/inject_suite_test.go b/pkg/handlers/docker/mutation/customimage/inject_suite_test.go new file mode 100644 index 000000000..9f17edd7c --- /dev/null +++ b/pkg/handlers/docker/mutation/customimage/inject_suite_test.go @@ -0,0 +1,16 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package customimage + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestCustomImagePatch(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Docker CustomImage patches for ControlPlane and Workers suite") +} diff --git a/pkg/handlers/docker/mutation/customimage/inject_worker_test.go b/pkg/handlers/docker/mutation/customimage/inject_worker_test.go new file mode 100644 index 000000000..b13f582d7 --- /dev/null +++ b/pkg/handlers/docker/mutation/customimage/inject_worker_test.go @@ -0,0 +1,80 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package customimage + +import ( + . "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + dockerworkerconfig "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/docker/workerconfig" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/workerconfig" +) + +var _ = Describe("Docker CustomImage patches for workers", func() { + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewWorkerPatch()).(mutation.GeneratePatches) + } + + testDefs := []capitest.PatchTestDef{ + { + Name: "image unset for workers", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + "builtin", + apiextensionsv1.JSON{ + Raw: []byte( + `{"machineDeployment": {"class": "a-worker", "version": "v1.2.3"}}`, + ), + }, + ), + }, + RequestItem: request.NewWorkerDockerMachineTemplateRequestItem("1234"), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{ + Operation: "add", + Path: "/spec/template/spec/customImage", + ValueMatcher: gomega.Equal("ghcr.io/mesosphere/kind-node:v1.2.3"), + }}, + }, + { + Name: "image set for workers", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + workerconfig.MetaVariableName, + "a-specific-image", + dockerworkerconfig.DockerVariableName, + VariableName, + ), + capitest.VariableWithValue( + "builtin", + apiextensionsv1.JSON{ + Raw: []byte(`{"machineDeployment": {"class": "a-worker"}}`), + }, + ), + }, + RequestItem: request.NewWorkerDockerMachineTemplateRequestItem("1234"), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{ + Operation: "add", + Path: "/spec/template/spec/customImage", + ValueMatcher: gomega.Equal("a-specific-image"), + }}, + }, + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches( + GinkgoT(), + patchGenerator, + &tt, + ) + }) + } +}) diff --git a/pkg/handlers/docker/mutation/customimage/tests/generate_patches.go b/pkg/handlers/docker/mutation/customimage/tests/generate_patches.go deleted file mode 100644 index 7b1b9b6e4..000000000 --- a/pkg/handlers/docker/mutation/customimage/tests/generate_patches.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2023 D2iQ, Inc. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package tests - -import ( - "testing" - - "github.com/onsi/gomega" - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" - - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" -) - -func TestControlPlaneGeneratePatches( - t *testing.T, - generatorFunc func() mutation.GeneratePatches, - variableName string, - variablePath ...string, -) { - t.Helper() - - capitest.ValidateGeneratePatches( - t, - generatorFunc, - capitest.PatchTestDef{ - Name: "image unset for control plane", - Vars: []runtimehooksv1.Variable{ - capitest.VariableWithValue( - "builtin", - apiextensionsv1.JSON{Raw: []byte(`{"controlPlane": {"version": "v1.2.3"}}`)}, - ), - }, - RequestItem: request.NewCPDockerMachineTemplateRequestItem("1234"), - ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{ - Operation: "add", - Path: "/spec/template/spec/customImage", - ValueMatcher: gomega.Equal("ghcr.io/mesosphere/kind-node:v1.2.3"), - }}, - }, - capitest.PatchTestDef{ - Name: "image set for control plane", - Vars: []runtimehooksv1.Variable{ - capitest.VariableWithValue( - variableName, - "a-specific-image", - variablePath..., - ), - capitest.VariableWithValue( - "builtin", - apiextensionsv1.JSON{ - Raw: []byte(`{"machineDeployment": {"class": "a-worker"}}`), - }, - ), - }, - RequestItem: request.NewCPDockerMachineTemplateRequestItem("1234"), - ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{ - Operation: "add", - Path: "/spec/template/spec/customImage", - ValueMatcher: gomega.Equal("a-specific-image"), - }}, - }, - ) -} - -func TestWorkerGeneratePatches( - t *testing.T, - generatorFunc func() mutation.GeneratePatches, - variableName string, - variablePath ...string, -) { - t.Helper() - - capitest.ValidateGeneratePatches( - t, - generatorFunc, - capitest.PatchTestDef{ - Name: "image unset for workers", - Vars: []runtimehooksv1.Variable{ - capitest.VariableWithValue( - "builtin", - apiextensionsv1.JSON{ - Raw: []byte( - `{"machineDeployment": {"class": "a-worker", "version": "v1.2.3"}}`, - ), - }, - ), - }, - RequestItem: request.NewWorkerDockerMachineTemplateRequestItem("1234"), - ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{ - Operation: "add", - Path: "/spec/template/spec/customImage", - ValueMatcher: gomega.Equal("ghcr.io/mesosphere/kind-node:v1.2.3"), - }}, - }, - capitest.PatchTestDef{ - Name: "image set for workers", - Vars: []runtimehooksv1.Variable{ - capitest.VariableWithValue( - variableName, - "a-specific-image", - variablePath..., - ), - capitest.VariableWithValue( - "builtin", - apiextensionsv1.JSON{ - Raw: []byte(`{"machineDeployment": {"class": "a-worker"}}`), - }, - ), - }, - RequestItem: request.NewWorkerDockerMachineTemplateRequestItem("1234"), - ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{ - Operation: "add", - Path: "/spec/template/spec/customImage", - ValueMatcher: gomega.Equal("a-specific-image"), - }}, - }, - ) -} diff --git a/pkg/handlers/docker/mutation/metapatch_handler_test.go b/pkg/handlers/docker/mutation/metapatch_handler_test.go deleted file mode 100644 index 0004da3b2..000000000 --- a/pkg/handlers/docker/mutation/metapatch_handler_test.go +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2023 D2iQ, Inc. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package mutation - -import ( - "testing" - - "sigs.k8s.io/controller-runtime/pkg/manager" - - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" - dockerclusterconfig "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/docker/clusterconfig" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/docker/mutation/customimage" - customimagetests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/docker/mutation/customimage/tests" - dockerworkerconfig "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/docker/workerconfig" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" - auditpolicytests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/auditpolicy/tests" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/etcd" - etcdtests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/etcd/tests" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/extraapiservercertsans" - extraapiservercertsanstests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/extraapiservercertsans/tests" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/httpproxy" - httpproxytests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/httpproxy/tests" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/imageregistries" - imageregistrycredentialstests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/imageregistries/credentials/tests" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/kubernetesimagerepository" - kubernetesimagerepositorytests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/kubernetesimagerepository/tests" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/mirrors" - globalimageregistrymirrortests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/mirrors/tests" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/workerconfig" -) - -func metaPatchGeneratorFunc(mgr manager.Manager) func() mutation.GeneratePatches { - return func() mutation.GeneratePatches { - return MetaPatchHandler(mgr).(mutation.GeneratePatches) - } -} - -func workerPatchGeneratorFunc() func() mutation.GeneratePatches { - return func() mutation.GeneratePatches { - return MetaWorkerPatchHandler().(mutation.GeneratePatches) - } -} - -func TestGeneratePatches(t *testing.T) { - t.Parallel() - - mgr := testEnv.Manager - - customimagetests.TestControlPlaneGeneratePatches( - t, - metaPatchGeneratorFunc(mgr), - clusterconfig.MetaVariableName, - clusterconfig.MetaControlPlaneConfigName, - dockerclusterconfig.DockerVariableName, - customimage.VariableName, - ) - - customimagetests.TestWorkerGeneratePatches( - t, - workerPatchGeneratorFunc(), - workerconfig.MetaVariableName, - dockerworkerconfig.DockerVariableName, - customimage.VariableName, - ) - - auditpolicytests.TestGeneratePatches( - t, - metaPatchGeneratorFunc(mgr), - ) - - httpproxytests.TestGeneratePatches( - t, - metaPatchGeneratorFunc(mgr), - clusterconfig.MetaVariableName, - httpproxy.VariableName, - ) - - etcdtests.TestGeneratePatches( - t, - metaPatchGeneratorFunc(mgr), - clusterconfig.MetaVariableName, - etcd.VariableName, - ) - - extraapiservercertsanstests.TestGeneratePatches( - t, - metaPatchGeneratorFunc(mgr), - clusterconfig.MetaVariableName, - extraapiservercertsans.VariableName, - ) - - kubernetesimagerepositorytests.TestGeneratePatches( - t, - metaPatchGeneratorFunc(mgr), - clusterconfig.MetaVariableName, - kubernetesimagerepository.VariableName, - ) - - imageregistrycredentialstests.TestGeneratePatches( - t, - metaPatchGeneratorFunc(mgr), - mgr.GetClient(), - clusterconfig.MetaVariableName, - imageregistries.VariableName, - ) - - globalimageregistrymirrortests.TestGeneratePatches( - t, - metaPatchGeneratorFunc(mgr), - mgr.GetClient(), - clusterconfig.MetaVariableName, - mirrors.GlobalMirrorVariableName, - ) -} diff --git a/pkg/handlers/docker/mutation/suite_test.go b/pkg/handlers/docker/mutation/suite_test.go deleted file mode 100644 index 20633d531..000000000 --- a/pkg/handlers/docker/mutation/suite_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2024 D2iQ, Inc. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package mutation - -import ( - "fmt" - "testing" - - ctrl "sigs.k8s.io/controller-runtime" - - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/test/helpers" -) - -var ( - testEnv *helpers.TestEnvironment - ctx = ctrl.SetupSignalHandler() -) - -func TestMain(m *testing.M) { - setup() - defer teardown() - m.Run() -} - -func setup() { - testEnvConfig := helpers.NewTestEnvironmentConfiguration() - var err error - testEnv, err = testEnvConfig.Build() - if err != nil { - panic(err) - } - go func() { - fmt.Println("Starting the manager") - if err := testEnv.StartManager(ctx); err != nil { - panic(fmt.Sprintf("Failed to start the envtest manager: %v", err)) - } - }() -} - -func teardown() { - if err := testEnv.Stop(); err != nil { - panic(fmt.Sprintf("Failed to stop envtest: %v", err)) - } -} diff --git a/pkg/handlers/generic/lifecycle/ccm/aws/handler.go b/pkg/handlers/generic/lifecycle/ccm/aws/handler.go new file mode 100644 index 000000000..626de46fb --- /dev/null +++ b/pkg/handlers/generic/lifecycle/ccm/aws/handler.go @@ -0,0 +1,130 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package aws + +import ( + "context" + "fmt" + + "github.com/blang/semver/v4" + "github.com/spf13/pflag" + corev1 "k8s.io/api/core/v1" + 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" + + "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" +) + +type AWSCCMConfig struct { + *options.GlobalOptions + + kubernetesMinorVersionToCCMConfigMapNames map[string]string +} + +func (a *AWSCCMConfig) AddFlags(prefix string, flags *pflag.FlagSet) { + flags.StringToStringVar( + &a.kubernetesMinorVersionToCCMConfigMapNames, + prefix+".default-aws-ccm-configmap-names", + map[string]string{ + "1.27": "aws-ccm-v1.27.1", + "1.28": "aws-ccm-v1.28.1", + }, + "map of provider cluster implementation type to default installation ConfigMap name", + ) +} + +type AWSCCM struct { + client ctrlclient.Client + config *AWSCCMConfig +} + +func New( + c ctrlclient.Client, + cfg *AWSCCMConfig, +) *AWSCCM { + return &AWSCCM{ + client: c, + config: cfg, + } +} + +func (a *AWSCCM) Apply( + ctx context.Context, + cluster *clusterv1.Cluster, + _ *v1alpha1.ClusterConfigSpec, +) error { + log := ctrl.LoggerFrom(ctx).WithValues( + "cluster", + cluster.Name, + ) + log.Info("Creating AWS CCM ConfigMap for Cluster") + version, err := semver.ParseTolerant(cluster.Spec.Topology.Version) + if err != nil { + return fmt.Errorf("failed to parse version from cluster %w", err) + } + minorVersion := fmt.Sprintf("%d.%d", version.Major, version.Minor) + configMapForMinorVersion := a.config.kubernetesMinorVersionToCCMConfigMapNames[minorVersion] + ccmConfigMapForMinorVersion := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: a.config.DefaultsNamespace(), + Name: configMapForMinorVersion, + }, + } + objName := ctrlclient.ObjectKeyFromObject( + ccmConfigMapForMinorVersion, + ) + err = a.client.Get(ctx, objName, ccmConfigMapForMinorVersion) + if err != nil { + log.Error(err, "failed to fetch CCM template for cluster") + return fmt.Errorf( + "failed to retrieve default AWS CCM manifests ConfigMap %q: %w", + objName, + err, + ) + } + + ccmConfigMap := generateCCMConfigMapForCluster(ccmConfigMapForMinorVersion, cluster) + if err = client.ServerSideApply(ctx, a.client, ccmConfigMap); err != nil { + log.Error(err, "failed to apply CCM configmap for cluster") + return fmt.Errorf( + "failed to apply AWS CCM manifests ConfigMap: %w", + err, + ) + } + + err = lifecycleutils.EnsureCRSForClusterFromObjects( + ctx, + ccmConfigMap.Name, + a.client, + cluster, + ccmConfigMap, + ) + if err != nil { + return fmt.Errorf("failed to generate CCM CRS for cluster: %w", err) + } + + return nil +} + +func generateCCMConfigMapForCluster( + ccmConfigMapForVersion *corev1.ConfigMap, cluster *clusterv1.Cluster, +) *corev1.ConfigMap { + ccmConfigMapForCluster := &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: cluster.Namespace, + Name: fmt.Sprintf("%s-%s", ccmConfigMapForVersion.Name, cluster.Name), + }, + Data: ccmConfigMapForVersion.Data, + } + return ccmConfigMapForCluster +} diff --git a/pkg/handlers/generic/lifecycle/cpi/aws/handler_test.go b/pkg/handlers/generic/lifecycle/ccm/aws/handler_test.go similarity index 89% rename from pkg/handlers/generic/lifecycle/cpi/aws/handler_test.go rename to pkg/handlers/generic/lifecycle/ccm/aws/handler_test.go index 0fd559f5e..de34b4bae 100644 --- a/pkg/handlers/generic/lifecycle/cpi/aws/handler_test.go +++ b/pkg/handlers/generic/lifecycle/ccm/aws/handler_test.go @@ -12,7 +12,7 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" ) -var startAWSCPIConfigMap = ` +var startAWSCCMConfigMap = ` apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: @@ -77,7 +77,7 @@ spec: type: RollingUpdate ` -func Test_generateCPIConfigMapForCluster(t *testing.T) { +func Test_generateCCMConfigMapForCluster(t *testing.T) { tests := []struct { name string startConfigMap *corev1.ConfigMap @@ -87,11 +87,11 @@ func Test_generateCPIConfigMapForCluster(t *testing.T) { name: "Can set cluster name in arguments", startConfigMap: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ - Name: "aws-cpi-v1.27.1", + Name: "aws-ccm-v1.27.1", Namespace: "default", }, Data: map[string]string{ - "aws-cpi-v1.27.1.yaml": startAWSCPIConfigMap, + "aws-ccm-v1.27.1.yaml": startAWSCCMConfigMap, }, }, cluster: &clusterv1.Cluster{ @@ -104,19 +104,19 @@ func Test_generateCPIConfigMapForCluster(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - cm := generateCPIConfigMapForCluster( + cm := generateCCMConfigMapForCluster( test.startConfigMap, test.cluster, ) - cpiConfigMapExpectedName := fmt.Sprintf( + ccmConfigMapExpectedName := fmt.Sprintf( "%s-%s", test.startConfigMap.Name, test.cluster.Name, ) - if cm.Name != cpiConfigMapExpectedName { + if cm.Name != ccmConfigMapExpectedName { t.Errorf( "expected configmap name to be %s. got: %s", - cpiConfigMapExpectedName, + ccmConfigMapExpectedName, cm.Name, ) } diff --git a/pkg/handlers/generic/lifecycle/cpi/doc.go b/pkg/handlers/generic/lifecycle/ccm/doc.go similarity index 98% rename from pkg/handlers/generic/lifecycle/cpi/doc.go rename to pkg/handlers/generic/lifecycle/ccm/doc.go index 10ac8b7f3..8fe9f0035 100644 --- a/pkg/handlers/generic/lifecycle/cpi/doc.go +++ b/pkg/handlers/generic/lifecycle/ccm/doc.go @@ -9,4 +9,4 @@ // // +kubebuilder:rbac:groups=addons.cluster.x-k8s.io,resources=clusterresourcesets,verbs=watch;list;get;create;patch;update;delete // +kubebuilder:rbac:groups="",resources=configmaps,verbs=watch;list;get;create;patch;update;delete -package cpi +package ccm diff --git a/pkg/handlers/generic/lifecycle/cpi/handler.go b/pkg/handlers/generic/lifecycle/ccm/handler.go similarity index 57% rename from pkg/handlers/generic/lifecycle/cpi/handler.go rename to pkg/handlers/generic/lifecycle/ccm/handler.go index e9e1237ed..29a22eb5d 100644 --- a/pkg/handlers/generic/lifecycle/cpi/handler.go +++ b/pkg/handlers/generic/lifecycle/ccm/handler.go @@ -1,14 +1,13 @@ // Copyright 2023 D2iQ, Inc. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -package cpi +package ccm import ( "context" "fmt" "strings" - corev1 "k8s.io/api/core/v1" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" ctrl "sigs.k8s.io/controller-runtime" @@ -19,34 +18,33 @@ import ( "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/lifecycle" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/variables" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" - lifecycleutils "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/utils" ) const ( - variableRootName = "cpi" + variableRootName = "ccm" ) -type CPIProvider interface { - EnsureCPIConfigMapForCluster(context.Context, *clusterv1.Cluster) (*corev1.ConfigMap, error) +type CCMProvider interface { + Apply(context.Context, *clusterv1.Cluster, *v1alpha1.ClusterConfigSpec) error } -type CPIHandler struct { +type CCMHandler struct { client ctrlclient.Client variableName string variablePath []string - ProviderHandler map[string]CPIProvider + ProviderHandler map[string]CCMProvider } var ( - _ commonhandlers.Named = &CPIHandler{} - _ lifecycle.AfterControlPlaneInitialized = &CPIHandler{} + _ commonhandlers.Named = &CCMHandler{} + _ lifecycle.AfterControlPlaneInitialized = &CCMHandler{} ) func New( c ctrlclient.Client, - handlers map[string]CPIProvider, -) *CPIHandler { - return &CPIHandler{ + handlers map[string]CCMProvider, +) *CCMHandler { + return &CCMHandler{ client: c, variableName: clusterconfig.MetaVariableName, variablePath: []string{"addons", variableRootName}, @@ -54,11 +52,11 @@ func New( } } -func (c *CPIHandler) Name() string { - return "CPIHandler" +func (c *CCMHandler) Name() string { + return "CCMHandler" } -func (c *CPIHandler) AfterControlPlaneInitialized( +func (c *CCMHandler) AfterControlPlaneInitialized( ctx context.Context, req *runtimehooksv1.AfterControlPlaneInitializedRequest, resp *runtimehooksv1.AfterControlPlaneInitializedResponse, @@ -72,59 +70,68 @@ func (c *CPIHandler) AfterControlPlaneInitialized( varMap := variables.ClusterVariablesToVariablesMap(req.Cluster.Spec.Topology.Variables) - _, found, err := variables.Get[v1alpha1.CPI](varMap, c.variableName, c.variablePath...) + _, found, err := variables.Get[v1alpha1.CCM](varMap, c.variableName, c.variablePath...) if err != nil { log.Error( err, - "failed to read CPI from cluster definition", + "failed to read CCM from cluster definition", ) resp.SetStatus(runtimehooksv1.ResponseStatusFailure) resp.SetMessage( - fmt.Sprintf("failed to read CPI provider from cluster definition: %v", + fmt.Sprintf("failed to read CCM from cluster definition: %v", err, ), ) return } if !found { - log.V(4).Info("Skipping CPI handler.") + log.V(4).Info("Skipping CCM handler.") return } - infraKind := req.Cluster.Spec.InfrastructureRef.Kind - log.Info(fmt.Sprintf("finding cpi handler for %s", infraKind)) - var handler CPIProvider - switch { - case strings.Contains(strings.ToLower(infraKind), v1alpha1.CPIProviderAWS): - handler = c.ProviderHandler[v1alpha1.CPIProviderAWS] - default: - log.Info(fmt.Sprintf("No CPI handler provided for infra kind %s", infraKind)) - return - } - cm, err := handler.EnsureCPIConfigMapForCluster(ctx, &req.Cluster) + + clusterConfigVar, _, err := variables.Get[v1alpha1.ClusterConfigSpec]( + varMap, + clusterconfig.MetaVariableName, + ) if err != nil { log.Error( err, - "failed to generate CPI configmap", + "failed to read clusterConfig variable from cluster definition", ) resp.SetStatus(runtimehooksv1.ResponseStatusFailure) resp.SetMessage( - fmt.Sprintf("failed to generate CPI configmap: %v", + fmt.Sprintf("failed to read clusterConfig variable from cluster definition: %v", err, ), ) return } - err = lifecycleutils.EnsureCRSForClusterFromConfigMaps(ctx, cm.Name, c.client, &req.Cluster, cm) + + 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, &clusterConfigVar) if err != nil { log.Error( err, - "failed to generate CPI CRS for cluster", + "failed to deploy CCM for cluster", ) resp.SetStatus(runtimehooksv1.ResponseStatusFailure) resp.SetMessage( - fmt.Sprintf("failed to generate CPI CRS: %v", + fmt.Sprintf("failed to deploy CCM for cluster: %v", err, ), ) + return } } 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/handler.go b/pkg/handlers/generic/lifecycle/clusterautoscaler/handler.go index 6ad7c5925..a7110d40e 100644 --- a/pkg/handlers/generic/lifecycle/clusterautoscaler/handler.go +++ b/pkg/handlers/generic/lifecycle/clusterautoscaler/handler.go @@ -18,6 +18,7 @@ import ( "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/lifecycle" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/variables" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/config" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/options" ) @@ -43,8 +44,9 @@ func (c *Config) AddFlags(prefix string, flags *pflag.FlagSet) { } type DefaultClusterAutoscaler struct { - client ctrlclient.Client - config *Config + client ctrlclient.Client + config *Config + helmChartInfoGetter *config.HelmChartGetter variableName string // points to the global config variable variablePath []string // path of this variable on the global config variable @@ -58,12 +60,14 @@ var ( func New( c ctrlclient.Client, cfg *Config, + helmChartInfoGetter *config.HelmChartGetter, ) *DefaultClusterAutoscaler { return &DefaultClusterAutoscaler{ - client: c, - config: cfg, - variableName: clusterconfig.MetaVariableName, - variablePath: []string{"addons", v1alpha1.ClusterAutoscalerVariableName}, + client: c, + config: cfg, + helmChartInfoGetter: helmChartInfoGetter, + variableName: clusterconfig.MetaVariableName, + variablePath: []string{"addons", v1alpha1.ClusterAutoscalerVariableName}, } } @@ -117,9 +121,28 @@ func (n *DefaultClusterAutoscaler) AfterControlPlaneInitialized( client: n.client, } case v1alpha1.AddonStrategyHelmAddon: + helmChart, err := n.helmChartInfoGetter.For( + ctx, + log, + config.Autoscaler, + ) + if err != nil { + log.Error( + err, + "failed to get configmap with helm settings", + ) + resp.SetStatus(runtimehooksv1.ResponseStatusFailure) + resp.SetMessage( + fmt.Sprintf("failed to get config to create helm addon: %v", + err, + ), + ) + return + } strategy = helmAddonStrategy{ - config: n.config.helmAddonConfig, - client: n.client, + config: n.config.helmAddonConfig, + client: n.client, + helmChart: helmChart, } default: resp.SetStatus(runtimehooksv1.ResponseStatusFailure) diff --git a/pkg/handlers/generic/lifecycle/clusterautoscaler/strategy_crs.go b/pkg/handlers/generic/lifecycle/clusterautoscaler/strategy_crs.go index 981d4ec01..432efba62 100644 --- a/pkg/handlers/generic/lifecycle/clusterautoscaler/strategy_crs.go +++ b/pkg/handlers/generic/lifecycle/clusterautoscaler/strategy_crs.go @@ -121,7 +121,7 @@ func (s crsStrategy) apply( ) } - if err = utils.EnsureCRSForClusterFromConfigMaps(ctx, cm.Name, s.client, targetCluster, cm); err != nil { + if err = utils.EnsureCRSForClusterFromObjects(ctx, cm.Name, s.client, targetCluster, cm); err != nil { return fmt.Errorf( "failed to apply cluster-autoscaler installation ClusterResourceSet: %w", err, diff --git a/pkg/handlers/generic/lifecycle/clusterautoscaler/strategy_helmaddon.go b/pkg/handlers/generic/lifecycle/clusterautoscaler/strategy_helmaddon.go index 007d19ab4..246c52992 100644 --- a/pkg/handlers/generic/lifecycle/clusterautoscaler/strategy_helmaddon.go +++ b/pkg/handlers/generic/lifecycle/clusterautoscaler/strategy_helmaddon.go @@ -9,7 +9,6 @@ import ( "github.com/go-logr/logr" "github.com/spf13/pflag" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" capiv1 "sigs.k8s.io/cluster-api/api/v1beta1" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" @@ -18,12 +17,11 @@ import ( 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/common/pkg/k8s/client" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/config" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/utils" ) const ( - defaultHelmRepositoryURL = "https://kubernetes.github.io/autoscaler" - defaultHelmChartVersion = "9.35.0" - defaultHelmChartName = "cluster-autoscaler" defaultHelmReleaseNameTemplate = "cluster-autoscaler-%s" ) @@ -43,7 +41,8 @@ func (c *helmAddonConfig) AddFlags(prefix string, flags *pflag.FlagSet) { type helmAddonStrategy struct { config helmAddonConfig - client ctrlclient.Client + client ctrlclient.Client + helmChart *config.HelmChart } func (s helmAddonStrategy) apply( @@ -53,7 +52,12 @@ func (s helmAddonStrategy) apply( log logr.Logger, ) error { log.Info("Retrieving cluster-autoscaler installation values template for cluster") - valuesTemplateConfigMap, err := s.retrieveValuesTemplateConfigMap(ctx, defaultsNamespace) + valuesTemplateConfigMap, err := utils.RetrieveValuesTemplateConfigMap( + ctx, + s.client, + s.config.defaultValuesTemplateConfigMapName, + defaultsNamespace, + ) if err != nil { return fmt.Errorf( "failed to retrieve cluster-autoscaler installation values template ConfigMap for cluster: %w", @@ -83,14 +87,14 @@ func (s helmAddonStrategy) apply( Name: "cluster-autoscaler-" + req.Cluster.Name, }, Spec: caaphv1.HelmChartProxySpec{ - RepoURL: defaultHelmRepositoryURL, - ChartName: defaultHelmChartName, + RepoURL: s.helmChart.Repository, + ChartName: s.helmChart.Name, ClusterSelector: metav1.LabelSelector{ MatchLabels: map[string]string{capiv1.ClusterNameLabel: targetCluster.Name}, }, ReleaseNamespace: req.Cluster.Namespace, ReleaseName: fmt.Sprintf(defaultHelmReleaseNameTemplate, req.Cluster.Name), - Version: defaultHelmChartVersion, + Version: s.helmChart.Version, ValuesTemplate: values, }, } @@ -108,28 +112,3 @@ func (s helmAddonStrategy) apply( return nil } - -func (s helmAddonStrategy) retrieveValuesTemplateConfigMap( - ctx context.Context, - defaultsNamespace string, -) (*corev1.ConfigMap, error) { - configMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: defaultsNamespace, - Name: s.config.defaultValuesTemplateConfigMapName, - }, - } - configMapObjName := ctrlclient.ObjectKeyFromObject( - configMap, - ) - err := s.client.Get(ctx, configMapObjName, configMap) - if err != nil { - return nil, fmt.Errorf( - "failed to retrieve installation values template ConfigMap %q: %w", - configMapObjName, - err, - ) - } - - return configMap, nil -} diff --git a/pkg/handlers/generic/lifecycle/cni/calico/handler.go b/pkg/handlers/generic/lifecycle/cni/calico/handler.go index ed3b82259..3ff526d02 100644 --- a/pkg/handlers/generic/lifecycle/cni/calico/handler.go +++ b/pkg/handlers/generic/lifecycle/cni/calico/handler.go @@ -18,6 +18,7 @@ import ( "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/lifecycle" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/variables" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/config" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/options" ) @@ -43,8 +44,9 @@ func (c *CNIConfig) AddFlags(prefix string, flags *pflag.FlagSet) { } type CalicoCNI struct { - client ctrlclient.Client - config *CNIConfig + client ctrlclient.Client + config *CNIConfig + helmChartInfoGetter *config.HelmChartGetter variableName string variablePath []string @@ -58,20 +60,22 @@ var ( func New( c ctrlclient.Client, cfg *CNIConfig, + helmChartInfoGetter *config.HelmChartGetter, ) *CalicoCNI { return &CalicoCNI{ - client: c, - config: cfg, - variableName: clusterconfig.MetaVariableName, - variablePath: []string{"addons", v1alpha1.CNIVariableName}, + client: c, + config: cfg, + helmChartInfoGetter: helmChartInfoGetter, + variableName: clusterconfig.MetaVariableName, + variablePath: []string{"addons", v1alpha1.CNIVariableName}, } } -func (s *CalicoCNI) Name() string { +func (c *CalicoCNI) Name() string { return "CalicoCNI" } -func (s *CalicoCNI) AfterControlPlaneInitialized( +func (c *CalicoCNI) AfterControlPlaneInitialized( ctx context.Context, req *runtimehooksv1.AfterControlPlaneInitializedRequest, resp *runtimehooksv1.AfterControlPlaneInitializedResponse, @@ -85,7 +89,7 @@ func (s *CalicoCNI) AfterControlPlaneInitialized( varMap := variables.ClusterVariablesToVariablesMap(req.Cluster.Spec.Topology.Variables) - cniVar, found, err := variables.Get[v1alpha1.CNI](varMap, s.variableName, s.variablePath...) + cniVar, found, err := variables.Get[v1alpha1.CNI](varMap, c.variableName, c.variablePath...) if err != nil { log.Error( err, @@ -120,13 +124,31 @@ func (s *CalicoCNI) AfterControlPlaneInitialized( switch cniVar.Strategy { case v1alpha1.AddonStrategyClusterResourceSet: strategy = crsStrategy{ - config: s.config.crsConfig, - client: s.client, + config: c.config.crsConfig, + client: c.client, } case v1alpha1.AddonStrategyHelmAddon: + // this is tigera and not calico because we deploy calico via operataor + log.Info("fetching settings for tigera-operator-config") + helmChart, err := c.helmChartInfoGetter.For(ctx, log, config.Tigera) + if err != nil { + log.Error( + err, + "failed to get configmap with helm settings", + ) + resp.SetStatus(runtimehooksv1.ResponseStatusFailure) + resp.SetMessage( + fmt.Sprintf("failed to get configration to create helm addon: %v", + err, + ), + ) + return + } + log.Info(fmt.Sprintf("using settings %v to install helm chart config", helmChart)) strategy = helmAddonStrategy{ - config: s.config.helmAddonConfig, - client: s.client, + config: c.config.helmAddonConfig, + client: c.client, + helmChart: helmChart, } default: resp.SetStatus(runtimehooksv1.ResponseStatusFailure) @@ -134,7 +156,7 @@ func (s *CalicoCNI) AfterControlPlaneInitialized( return } - if err := strategy.apply(ctx, req, s.config.DefaultsNamespace(), log); err != nil { + if err := strategy.apply(ctx, req, c.config.DefaultsNamespace(), log); err != nil { resp.SetStatus(runtimehooksv1.ResponseStatusFailure) resp.SetMessage(err.Error()) return diff --git a/pkg/handlers/generic/lifecycle/cni/calico/strategy_crs.go b/pkg/handlers/generic/lifecycle/cni/calico/strategy_crs.go index 7faf3ff71..4a5ddf7cf 100644 --- a/pkg/handlers/generic/lifecycle/cni/calico/strategy_crs.go +++ b/pkg/handlers/generic/lifecycle/cni/calico/strategy_crs.go @@ -42,8 +42,9 @@ func (c *crsConfig) AddFlags(prefix string, flags *pflag.FlagSet) { &c.defaultProviderInstallationConfigMapNames, prefix+".default-provider-installation-configmap-names", map[string]string{ - "DockerCluster": "calico-cni-crs-installation-dockercluster", - "AWSCluster": "calico-cni-crs-installation-awscluster", + "DockerCluster": "calico-cni-crs-installation-dockercluster", + "AWSCluster": "calico-cni-crs-installation-awscluster", + "NutanixCluster": "calico-cni-crs-installation-nutanixcluster", }, "map of provider cluster implementation type to default installation ConfigMap name", ) @@ -150,7 +151,7 @@ func (s crsStrategy) ensureCNICRSForCluster( ) } - if err := utils.EnsureCRSForClusterFromConfigMaps(ctx, cm.Name, s.client, cluster, tigeraConfigMap, cm); err != nil { + if err := utils.EnsureCRSForClusterFromObjects(ctx, cm.Name, s.client, cluster, tigeraConfigMap, cm); err != nil { return fmt.Errorf( "failed to apply Calico CNI installation ClusterResourceSet: %w", err, @@ -204,7 +205,7 @@ func generateTigeraOperatorConfigMap( }, ObjectMeta: metav1.ObjectMeta{ Namespace: cluster.Namespace, - Name: defaultTigeraOperatorConfigMap.Name, + Name: fmt.Sprintf("%s-%s", defaultTigeraOperatorConfigMap.Name, cluster.Name), }, Data: defaultTigeraOperatorConfigMap.Data, BinaryData: defaultTigeraOperatorConfigMap.BinaryData, diff --git a/pkg/handlers/generic/lifecycle/cni/calico/strategy_helmaddon.go b/pkg/handlers/generic/lifecycle/cni/calico/strategy_helmaddon.go index 9f328082f..545f2fcef 100644 --- a/pkg/handlers/generic/lifecycle/cni/calico/strategy_helmaddon.go +++ b/pkg/handlers/generic/lifecycle/cni/calico/strategy_helmaddon.go @@ -18,12 +18,10 @@ import ( 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/common/pkg/k8s/client" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/config" ) const ( - defaultCalicoHelmRepositoryURL = "https://docs.tigera.io/calico/charts" - defaultCalicoHelmChartVersion = "v3.26.4" - defaultTigeraOperatorChartName = "tigera-operator" defaultTigeraOperatorReleaseName = "tigera-operator" defaultTigerOperatorNamespace = "tigera-operator" ) @@ -37,17 +35,18 @@ func (c *helmAddonConfig) AddFlags(prefix string, flags *pflag.FlagSet) { &c.defaultProviderInstallationValuesTemplatesConfigMapNames, prefix+".default-provider-installation-values-templates-configmap-names", map[string]string{ - "DockerCluster": "calico-cni-helm-values-template-dockercluster", - "AWSCluster": "calico-cni-helm-values-template-awscluster", + "DockerCluster": "calico-cni-helm-values-template-dockercluster", + "AWSCluster": "calico-cni-helm-values-template-awscluster", + "NutanixCluster": "calico-cni-helm-values-template-nutanixcluster", }, "map of provider cluster implementation type to default installation values ConfigMap name", ) } type helmAddonStrategy struct { - config helmAddonConfig - - client ctrlclient.Client + config helmAddonConfig + helmChart *config.HelmChart + client ctrlclient.Client } func (s helmAddonStrategy) apply( @@ -91,14 +90,14 @@ func (s helmAddonStrategy) apply( Name: "calico-cni-installation-" + req.Cluster.Name, }, Spec: caaphv1.HelmChartProxySpec{ - RepoURL: defaultCalicoHelmRepositoryURL, - ChartName: defaultTigeraOperatorChartName, + RepoURL: s.helmChart.Repository, + ChartName: s.helmChart.Name, ClusterSelector: metav1.LabelSelector{ MatchLabels: map[string]string{capiv1.ClusterNameLabel: req.Cluster.Name}, }, ReleaseNamespace: defaultTigerOperatorNamespace, ReleaseName: defaultTigeraOperatorReleaseName, - Version: defaultCalicoHelmChartVersion, + Version: s.helmChart.Version, ValuesTemplate: valuesTemplateConfigMap.Data["values.yaml"], }, } diff --git a/pkg/handlers/generic/lifecycle/cni/cilium/handler.go b/pkg/handlers/generic/lifecycle/cni/cilium/handler.go index 1e7c7db3c..aef9b93a2 100644 --- a/pkg/handlers/generic/lifecycle/cni/cilium/handler.go +++ b/pkg/handlers/generic/lifecycle/cni/cilium/handler.go @@ -18,6 +18,7 @@ import ( "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/lifecycle" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/variables" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/config" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/options" ) @@ -43,8 +44,9 @@ func (c *CNIConfig) AddFlags(prefix string, flags *pflag.FlagSet) { } type CiliumCNI struct { - client ctrlclient.Client - config *CNIConfig + client ctrlclient.Client + config *CNIConfig + helmChartInfoGetter *config.HelmChartGetter variableName string variablePath []string @@ -58,20 +60,22 @@ var ( func New( c ctrlclient.Client, cfg *CNIConfig, + helmChartInfoGetter *config.HelmChartGetter, ) *CiliumCNI { return &CiliumCNI{ - client: c, - config: cfg, - variableName: clusterconfig.MetaVariableName, - variablePath: []string{"addons", v1alpha1.CNIVariableName}, + client: c, + config: cfg, + helmChartInfoGetter: helmChartInfoGetter, + variableName: clusterconfig.MetaVariableName, + variablePath: []string{"addons", v1alpha1.CNIVariableName}, } } -func (s *CiliumCNI) Name() string { +func (c *CiliumCNI) Name() string { return "CiliumCNI" } -func (s *CiliumCNI) AfterControlPlaneInitialized( +func (c *CiliumCNI) AfterControlPlaneInitialized( ctx context.Context, req *runtimehooksv1.AfterControlPlaneInitializedRequest, resp *runtimehooksv1.AfterControlPlaneInitializedResponse, @@ -85,7 +89,7 @@ func (s *CiliumCNI) AfterControlPlaneInitialized( varMap := variables.ClusterVariablesToVariablesMap(req.Cluster.Spec.Topology.Variables) - cniVar, found, err := variables.Get[v1alpha1.CNI](varMap, s.variableName, s.variablePath...) + cniVar, found, err := variables.Get[v1alpha1.CNI](varMap, c.variableName, c.variablePath...) if err != nil { log.Error( err, @@ -120,13 +124,28 @@ func (s *CiliumCNI) AfterControlPlaneInitialized( switch cniVar.Strategy { case v1alpha1.AddonStrategyClusterResourceSet: strategy = crsStrategy{ - config: s.config.crsConfig, - client: s.client, + config: c.config.crsConfig, + client: c.client, } case v1alpha1.AddonStrategyHelmAddon: + helmChart, err := c.helmChartInfoGetter.For(ctx, log, config.Cilium) + if err != nil { + log.Error( + err, + "failed to get configmap with helm settings", + ) + resp.SetStatus(runtimehooksv1.ResponseStatusFailure) + resp.SetMessage( + fmt.Sprintf("failed to get configration to create helm addon: %v", + err, + ), + ) + return + } strategy = helmAddonStrategy{ - config: s.config.helmAddonConfig, - client: s.client, + config: c.config.helmAddonConfig, + client: c.client, + helmChart: helmChart, } default: resp.SetStatus(runtimehooksv1.ResponseStatusFailure) @@ -134,7 +153,7 @@ func (s *CiliumCNI) AfterControlPlaneInitialized( return } - if err := strategy.apply(ctx, req, s.config.DefaultsNamespace(), log); err != nil { + if err := strategy.apply(ctx, req, c.config.DefaultsNamespace(), log); err != nil { resp.SetStatus(runtimehooksv1.ResponseStatusFailure) resp.SetMessage(err.Error()) return diff --git a/pkg/handlers/generic/lifecycle/cni/cilium/strategy_crs.go b/pkg/handlers/generic/lifecycle/cni/cilium/strategy_crs.go index 28f07a77c..6a9b805ac 100644 --- a/pkg/handlers/generic/lifecycle/cni/cilium/strategy_crs.go +++ b/pkg/handlers/generic/lifecycle/cni/cilium/strategy_crs.go @@ -87,7 +87,7 @@ func (s crsStrategy) apply( ) } - if err := utils.EnsureCRSForClusterFromConfigMaps(ctx, cm.Name, s.client, cluster, cm); err != nil { + if err := utils.EnsureCRSForClusterFromObjects(ctx, cm.Name, s.client, cluster, cm); err != nil { return fmt.Errorf( "failed to apply Cilium CNI installation ClusterResourceSet: %w", err, diff --git a/pkg/handlers/generic/lifecycle/cni/cilium/strategy_helmaddon.go b/pkg/handlers/generic/lifecycle/cni/cilium/strategy_helmaddon.go index ecf8fe3ab..b4ab4b29d 100644 --- a/pkg/handlers/generic/lifecycle/cni/cilium/strategy_helmaddon.go +++ b/pkg/handlers/generic/lifecycle/cni/cilium/strategy_helmaddon.go @@ -18,14 +18,12 @@ import ( 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/common/pkg/k8s/client" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/config" ) const ( - defaultCiliumHelmRepositoryURL = "https://helm.cilium.io/" - defaultCiliumHelmChartVersion = "1.15.0" - defaultCiliumChartName = "cilium" - defaultCiliumReleaseName = "cilium" - defaultCiliumNamespace = "kube-system" + defaultCiliumReleaseName = "cilium" + defaultCiliumNamespace = "kube-system" ) type helmAddonConfig struct { @@ -42,9 +40,9 @@ func (c *helmAddonConfig) AddFlags(prefix string, flags *pflag.FlagSet) { } type helmAddonStrategy struct { - config helmAddonConfig - - client ctrlclient.Client + config helmAddonConfig + client ctrlclient.Client + helmChart *config.HelmChart } func (s helmAddonStrategy) apply( @@ -72,14 +70,14 @@ func (s helmAddonStrategy) apply( Name: "cilium-cni-installation-" + req.Cluster.Name, }, Spec: caaphv1.HelmChartProxySpec{ - RepoURL: defaultCiliumHelmRepositoryURL, - ChartName: defaultCiliumChartName, + RepoURL: s.helmChart.Repository, + ChartName: s.helmChart.Name, ClusterSelector: metav1.LabelSelector{ MatchLabels: map[string]string{capiv1.ClusterNameLabel: req.Cluster.Name}, }, ReleaseNamespace: defaultCiliumNamespace, ReleaseName: defaultCiliumReleaseName, - Version: defaultCiliumHelmChartVersion, + Version: s.helmChart.Version, ValuesTemplate: valuesTemplateConfigMap.Data["values.yaml"], }, } diff --git a/pkg/handlers/generic/lifecycle/config/cm.go b/pkg/handlers/generic/lifecycle/config/cm.go new file mode 100644 index 000000000..89b44fa20 --- /dev/null +++ b/pkg/handlers/generic/lifecycle/config/cm.go @@ -0,0 +1,95 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package config + +import ( + "context" + "fmt" + + "github.com/go-logr/logr" + "gopkg.in/yaml.v2" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" +) + +type Component string + +const ( + Autoscaler Component = "cluster-autoscaler" + Tigera Component = "tigera-operator" + Cilium Component = "cilium" + NFD Component = "nfd" + NutanixStorageCSI Component = "nutanix-storage-csi" + NutanixSnapshotCSI Component = "nutanix-snapshot-csi" + NutanixCCM Component = "nutanix-ccm" +) + +type HelmChartGetter struct { + cl ctrlclient.Reader + cmName string + cmNamespace string +} + +type HelmChart struct { + Name string `yaml:"ChartName"` + Version string `yaml:"ChartVersion"` + Repository string `yaml:"RepositoryURL"` +} + +func NewHelmChartGetterFromConfigMap( + cmName, cmNamespace string, + cl ctrlclient.Reader, +) *HelmChartGetter { + return &HelmChartGetter{ + cl, + cmName, + cmNamespace, + } +} + +func (h *HelmChartGetter) get( + ctx context.Context, +) (*corev1.ConfigMap, error) { + cm := &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: h.cmNamespace, + Name: h.cmName, + }, + } + err := h.cl.Get( + ctx, + ctrlclient.ObjectKeyFromObject(cm), + cm, + ) + return cm, err +} + +func (h *HelmChartGetter) For( + ctx context.Context, + log logr.Logger, + name Component, +) (*HelmChart, error) { + log.Info( + fmt.Sprintf("Fetching HelmChart info for %s from configmap %s/%s", + string(name), + h.cmName, + h.cmNamespace), + ) + cm, err := h.get(ctx) + if err != nil { + return nil, err + } + d, ok := cm.Data[string(name)] + if !ok { + return nil, fmt.Errorf("did not find key %s in %v", name, cm.Data) + } + var settings HelmChart + err = yaml.Unmarshal([]byte(d), &settings) + return &settings, err +} diff --git a/pkg/handlers/generic/lifecycle/cpi/aws/handler.go b/pkg/handlers/generic/lifecycle/cpi/aws/handler.go deleted file mode 100644 index 3490af192..000000000 --- a/pkg/handlers/generic/lifecycle/cpi/aws/handler.go +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2023 D2iQ, Inc. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package aws - -import ( - "context" - "fmt" - - "github.com/blang/semver/v4" - "github.com/spf13/pflag" - corev1 "k8s.io/api/core/v1" - 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" - - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/k8s/client" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/options" -) - -type AWSCPIConfig struct { - *options.GlobalOptions - - kubernetesMinorVersionToCPIConfigMapNames map[string]string -} - -func (a *AWSCPIConfig) AddFlags(prefix string, flags *pflag.FlagSet) { - flags.StringToStringVar( - &a.kubernetesMinorVersionToCPIConfigMapNames, - prefix+".default-aws-cpi-configmap-names", - map[string]string{ - "1.27": "aws-cpi-v1.27.1", - "1.28": "aws-cpi-v1.28.1", - }, - "map of provider cluster implementation type to default installation ConfigMap name", - ) -} - -type AWSCPI struct { - client ctrlclient.Client - config *AWSCPIConfig -} - -func New( - c ctrlclient.Client, - cfg *AWSCPIConfig, -) *AWSCPI { - return &AWSCPI{ - client: c, - config: cfg, - } -} - -func (a *AWSCPI) EnsureCPIConfigMapForCluster( - ctx context.Context, - cluster *clusterv1.Cluster, -) (*corev1.ConfigMap, error) { - log := ctrl.LoggerFrom(ctx).WithValues( - "cluster", - cluster.Name, - ) - log.Info("Creating AWS CPI ConfigMap for Cluster") - version, err := semver.ParseTolerant(cluster.Spec.Topology.Version) - if err != nil { - return nil, fmt.Errorf("failed to parse version from cluster %w", err) - } - minorVersion := fmt.Sprintf("%d.%d", version.Major, version.Minor) - configMapForMinorVersion := a.config.kubernetesMinorVersionToCPIConfigMapNames[minorVersion] - cpiConfigMapForMinorVersion := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: a.config.DefaultsNamespace(), - Name: configMapForMinorVersion, - }, - } - objName := ctrlclient.ObjectKeyFromObject( - cpiConfigMapForMinorVersion, - ) - err = a.client.Get(ctx, objName, cpiConfigMapForMinorVersion) - if err != nil { - log.Error(err, "failed to fetch CPI template for cluster") - return nil, fmt.Errorf( - "failed to retrieve default AWS CPI manifests ConfigMap %q: %w", - objName, - err, - ) - } - - cpiConfigMap := generateCPIConfigMapForCluster(cpiConfigMapForMinorVersion, cluster) - if err := client.ServerSideApply(ctx, a.client, cpiConfigMap); err != nil { - log.Error(err, "failed to apply CPI configmap for cluster") - return nil, fmt.Errorf( - "failed to apply AWS CPI manifests ConfigMap: %w", - err, - ) - } - return cpiConfigMap, nil -} - -func generateCPIConfigMapForCluster( - cpiConfigMapForVersion *corev1.ConfigMap, cluster *clusterv1.Cluster, -) *corev1.ConfigMap { - cpiConfigMapForCluster := &corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{ - APIVersion: corev1.SchemeGroupVersion.String(), - Kind: "ConfigMap", - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: cluster.Namespace, - Name: fmt.Sprintf("%s-%s", cpiConfigMapForVersion.Name, cluster.Name), - }, - Data: cpiConfigMapForVersion.Data, - } - return cpiConfigMapForCluster -} diff --git a/pkg/handlers/generic/lifecycle/csi/aws-ebs/handler.go b/pkg/handlers/generic/lifecycle/csi/aws-ebs/handler.go index 0d75a2212..c08ef0c18 100644 --- a/pkg/handlers/generic/lifecycle/csi/aws-ebs/handler.go +++ b/pkg/handlers/generic/lifecycle/csi/aws-ebs/handler.go @@ -10,13 +10,22 @@ import ( "github.com/spf13/pflag" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" 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" ) +var defaultStorageClassParams = map[string]string{ + "csi.storage.k8s.io/fstype": "ext4", + "type": "gp3", +} + type AWSEBSConfig struct { *options.GlobalOptions defaultAWSEBSConfigMapName string @@ -46,10 +55,71 @@ func New( } } -func (a *AWSEBS) EnsureCSIConfigMapForCluster( +func (a *AWSEBS) Apply( ctx context.Context, + provider v1alpha1.CSIProvider, + defaultStorageConfig *v1alpha1.DefaultStorage, + req *runtimehooksv1.AfterControlPlaneInitializedRequest, +) error { + strategy := provider.Strategy + switch strategy { + case v1alpha1.AddonStrategyClusterResourceSet: + err := a.handleCRSApply(ctx, req) + if err != nil { + return err + } + case v1alpha1.AddonStrategyHelmAddon: + default: + return fmt.Errorf("stategy %s not implemented", strategy) + } + return a.createStorageClasses( + ctx, + provider.StorageClassConfig, + &req.Cluster, + defaultStorageConfig, + ) +} + +func (a *AWSEBS) createStorageClasses(ctx context.Context, + configs []v1alpha1.StorageClassConfig, cluster *clusterv1.Cluster, -) (*corev1.ConfigMap, error) { + defaultStorageConfig *v1alpha1.DefaultStorage, +) error { + allStorageClasses := make([]runtime.Object, 0, len(configs)) + for _, config := range configs { + setAsDefault := config.Name == defaultStorageConfig.StorageClassConfigName && + v1alpha1.CSIProviderAWSEBS == defaultStorageConfig.ProviderName + allStorageClasses = append(allStorageClasses, lifecycleutils.CreateStorageClass( + config, + v1alpha1.AWSEBSProvisioner, + setAsDefault, + defaultStorageClassParams, + )) + } + cm, err := lifecycleutils.CreateConfigMapForCRS( + fmt.Sprintf("aws-storageclass-cm-%s", cluster.Name), + a.config.DefaultsNamespace(), + allStorageClasses..., + ) + if err != nil { + return err + } + err = client.ServerSideApply(ctx, a.client, cm) + if err != nil { + return err + } + return lifecycleutils.EnsureCRSForClusterFromObjects( + ctx, + "aws-storageclass-crs", + a.client, + cluster, + cm, + ) +} + +func (a *AWSEBS) handleCRSApply(ctx context.Context, + req *runtimehooksv1.AfterControlPlaneInitializedRequest, +) error { awsEBSCSIConfigMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Namespace: a.config.DefaultsNamespace(), @@ -61,22 +131,31 @@ func (a *AWSEBS) EnsureCSIConfigMapForCluster( ) err := a.client.Get(ctx, defaultAWSEBSCSIConfigMapObjName, awsEBSCSIConfigMap) if err != nil { - return nil, fmt.Errorf( + return fmt.Errorf( "failed to retrieve default AWS EBS CSI manifests ConfigMap %q: %w", defaultAWSEBSCSIConfigMapObjName, err, ) } - - awsEBSConfigMap := generateAWSEBSCSIConfigMap(awsEBSCSIConfigMap, cluster) - if err := client.ServerSideApply(ctx, a.client, awsEBSConfigMap); err != nil { - return nil, fmt.Errorf( + cluster := req.Cluster + cm := generateAWSEBSCSIConfigMap(awsEBSCSIConfigMap, &cluster) + if err := client.ServerSideApply(ctx, a.client, cm); err != nil { + return fmt.Errorf( "failed to apply AWS EBS CSI manifests ConfigMap: %w", err, ) } - - return awsEBSConfigMap, nil + err = lifecycleutils.EnsureCRSForClusterFromObjects( + ctx, + cm.Name, + a.client, + &req.Cluster, + cm, + ) + if err != nil { + return err + } + return nil } func generateAWSEBSCSIConfigMap( diff --git a/pkg/handlers/generic/lifecycle/csi/doc.go b/pkg/handlers/generic/lifecycle/csi/doc.go index 9a2cc0ff4..41a048f24 100644 --- a/pkg/handlers/generic/lifecycle/csi/doc.go +++ b/pkg/handlers/generic/lifecycle/csi/doc.go @@ -9,4 +9,5 @@ // // +kubebuilder:rbac:groups=addons.cluster.x-k8s.io,resources=clusterresourcesets,verbs=watch;list;get;create;patch;update;delete // +kubebuilder:rbac:groups="",resources=configmaps,verbs=watch;list;get;create;patch;update;delete +// +kubebuilder:rbac:groups="storage.k8s.io",resources=storageclasses,verbs=list;get;create;patch;update package csi diff --git a/pkg/handlers/generic/lifecycle/csi/handler.go b/pkg/handlers/generic/lifecycle/csi/handler.go index c084c05f1..178869808 100644 --- a/pkg/handlers/generic/lifecycle/csi/handler.go +++ b/pkg/handlers/generic/lifecycle/csi/handler.go @@ -7,11 +7,7 @@ import ( "context" "fmt" - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" - utilyaml "sigs.k8s.io/cluster-api/util/yaml" ctrl "sigs.k8s.io/controller-runtime" ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" @@ -20,23 +16,19 @@ import ( "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/lifecycle" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/variables" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" - lifecycleutils "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/utils" ) const ( variableRootName = "csi" - kindStorageClass = "StorageClass" -) - -var ( - defualtStorageClassKey = "storageclass.kubernetes.io/is-default-class" - defaultStorageClassMap = map[string]string{ - defualtStorageClassKey: "true", - } ) type CSIProvider interface { - EnsureCSIConfigMapForCluster(context.Context, *clusterv1.Cluster) (*corev1.ConfigMap, error) + Apply( + context.Context, + v1alpha1.CSIProvider, + *v1alpha1.DefaultStorage, + *runtimehooksv1.AfterControlPlaneInitializedRequest, + ) error } type CSIHandler struct { @@ -80,7 +72,7 @@ func (c *CSIHandler) AfterControlPlaneInitialized( ) varMap := variables.ClusterVariablesToVariablesMap(req.Cluster.Spec.Topology.Variables) resp.SetStatus(runtimehooksv1.ResponseStatusSuccess) - csiProviders, found, err := variables.Get[v1alpha1.CSIProviders]( + csiProviders, found, err := variables.Get[v1alpha1.CSI]( varMap, c.variableName, c.variablePath...) @@ -106,84 +98,48 @@ func (c *CSIHandler) AfterControlPlaneInitialized( ) return } + if len(csiProviders.Providers) == 1 && + len(csiProviders.Providers[0].StorageClassConfig) == 1 && + csiProviders.DefaultStorage == nil { + csiProviders.DefaultStorage = &v1alpha1.DefaultStorage{ + ProviderName: csiProviders.Providers[0].Name, + StorageClassConfigName: csiProviders.Providers[0].StorageClassConfig[0].Name, + } + } + for _, provider := range csiProviders.Providers { handler, ok := c.ProviderHandler[provider.Name] if !ok { log.V(4).Info( fmt.Sprintf( - "Skipping CSI handler, for provider given in %q. Provider handler not given ", - provider, + "Skipping CSI handler, for provider given in %s. Provider handler not given.", + provider.Name, ), ) continue } - log.Info(fmt.Sprintf("Creating config map for csi provider %s", provider)) - cm, err := handler.EnsureCSIConfigMapForCluster(ctx, &req.Cluster) + log.Info(fmt.Sprintf("Creating CSI provider %s", provider.Name)) + err = handler.Apply( + ctx, + provider, + csiProviders.DefaultStorage, + req, + ) if err != nil { log.Error( err, fmt.Sprintf( - "failed to ensure %s csi driver installation manifests ConfigMap", + "failed to delpoy %s CSI driver", provider.Name, ), ) resp.SetStatus(runtimehooksv1.ResponseStatusFailure) - } - if cm != nil { - if provider.Name == csiProviders.DefaultClassName { - log.Info("Setting default storage class ", provider, csiProviders.DefaultClassName) - err = setDefaultStorageClass(log, cm) - if err != nil { - log.Error(err, "failed to set default storage class") - resp.SetStatus(runtimehooksv1.ResponseStatusFailure) - } - if err := c.client.Update(ctx, cm); err != nil { - log.Error(err, "failed to apply default storage class annotation to configmap") - resp.SetStatus(runtimehooksv1.ResponseStatusFailure) - } - } - err = lifecycleutils.EnsureCRSForClusterFromConfigMaps( - ctx, - cm.Name, - c.client, - &req.Cluster, - cm, - ) - if err != nil { - log.Error( + resp.SetMessage( + fmt.Sprintf( + "failed to deploy CSI driver: %v", err, - fmt.Sprintf( - "failed to ensure %s csi driver installation manifests ConfigMap", - provider.Name, - ), - ) - resp.SetStatus(runtimehooksv1.ResponseStatusFailure) - } - } - } -} - -func setDefaultStorageClass( - log logr.Logger, - cm *corev1.ConfigMap, -) error { - for k, contents := range cm.Data { - objs, err := utilyaml.ToUnstructured([]byte(contents)) - if err != nil { - log.Error(err, "failed to parse yaml") - continue - } - for i := range objs { - obj := objs[i] - if obj.GetKind() == kindStorageClass { - obj.SetAnnotations(defaultStorageClassMap) - } - } - rawObjs, err := utilyaml.FromUnstructured(objs) - if err != nil { - return fmt.Errorf("failed to convert unstructured objects back to string %w", err) + ), + ) } - cm.Data[k] = string(rawObjs) } - return nil } diff --git a/pkg/handlers/generic/lifecycle/csi/handler_test.go b/pkg/handlers/generic/lifecycle/csi/handler_test.go deleted file mode 100644 index de5015895..000000000 --- a/pkg/handlers/generic/lifecycle/csi/handler_test.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2023 D2iQ, Inc. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package csi - -import ( - "strings" - "testing" - - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -var startAWSConfigMap = ` -apiVersion: storage.k8s.io/v1 -kind: StorageClass -metadata: - name: ebs-sc -parameters: - csi.storage.k8s.io/fstype: ext4 - type: gp3 -provisioner: ebs.csi.aws.com -volumeBindingMode: WaitForFirstConsumer -` - -func Test_setDefaultStorageClass(t *testing.T) { - tests := []struct { - name string - startConfigMap *corev1.ConfigMap - key string - }{ - { - name: "aws config map", - startConfigMap: &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test1", - Namespace: "default", - }, - Data: map[string]string{ - "aws-ebs-csi.yaml": startAWSConfigMap, - }, - }, - key: "aws-ebs-csi.yaml", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := setDefaultStorageClass( - logr.Discard(), - tt.startConfigMap, - ) - if err != nil { - t.Fatal("failed to set default storage class", err) - } - if !strings.Contains(tt.startConfigMap.Data[tt.key], defualtStorageClassKey) { - t.Logf( - "expected %s to containe %s", - tt.startConfigMap.Data[tt.key], - defualtStorageClassKey, - ) - t.Fail() - } - }) - } -} diff --git a/pkg/handlers/generic/lifecycle/csi/nutanix-csi/handler.go b/pkg/handlers/generic/lifecycle/csi/nutanix-csi/handler.go new file mode 100644 index 000000000..f695eeae8 --- /dev/null +++ b/pkg/handlers/generic/lifecycle/csi/nutanix-csi/handler.go @@ -0,0 +1,261 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package nutanix + +import ( + "context" + "fmt" + + "github.com/spf13/pflag" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + 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 ( + defaultStorageHelmReleaseName = "nutanix-csi-storage" + defaultStorageHelmReleaseNamespace = "ntnx-system" + + defaultSnapshotHelmReleaseName = "nutanix-csi-snapshot" + defaultSnapshotHelmReleaseNamespace = "ntnx-system" + + //nolint:gosec // Does not contain hard coded credentials. + defaultCredentialsSecretName = "nutanix-csi-credentials" +) + +var defaultStorageClassParameters = map[string]string{ + "storageType": "NutanixVolumes", + "csi.storage.k8s.io/fstype": "xfs", + "csi.storage.k8s.io/provisioner-secret-name": defaultCredentialsSecretName, + "csi.storage.k8s.io/provisioner-secret-namespace": defaultStorageHelmReleaseNamespace, + "csi.storage.k8s.io/node-publish-secret-name": defaultCredentialsSecretName, + "csi.storage.k8s.io/node-publish-secret-namespace": defaultStorageHelmReleaseNamespace, + "csi.storage.k8s.io/controller-expand-secret-name": defaultCredentialsSecretName, + "csi.storage.k8s.io/controller-expand-secret-namespace": defaultStorageHelmReleaseNamespace, +} + +type NutanixCSIConfig struct { + *options.GlobalOptions + defaultValuesTemplateConfigMapName string +} + +func (n *NutanixCSIConfig) AddFlags(prefix string, flags *pflag.FlagSet) { + flags.StringVar( + &n.defaultValuesTemplateConfigMapName, + prefix+".default-values-template-configmap-name", + "default-nutanix-csi-helm-values-template", + "default values ConfigMap name", + ) +} + +type NutanixCSI struct { + client ctrlclient.Client + config *NutanixCSIConfig + helmChartInfoGetter *config.HelmChartGetter +} + +func New( + c ctrlclient.Client, + cfg *NutanixCSIConfig, + helmChartInfoGetter *config.HelmChartGetter, +) *NutanixCSI { + return &NutanixCSI{ + client: c, + config: cfg, + helmChartInfoGetter: helmChartInfoGetter, + } +} + +func (n *NutanixCSI) Apply( + ctx context.Context, + provider v1alpha1.CSIProvider, + defaultStorageConfig *v1alpha1.DefaultStorage, + req *runtimehooksv1.AfterControlPlaneInitializedRequest, +) error { + strategy := provider.Strategy + switch strategy { + case v1alpha1.AddonStrategyHelmAddon: + err := n.handleHelmAddonApply(ctx, req) + if err != nil { + return err + } + case v1alpha1.AddonStrategyClusterResourceSet: + default: + return fmt.Errorf("stategy %s not implemented", strategy) + } + + if provider.Credentials != nil { + key := ctrlclient.ObjectKey{ + Name: defaultCredentialsSecretName, + Namespace: defaultStorageHelmReleaseNamespace, + } + err := lifecycleutils.CopySecretToRemoteCluster( + ctx, + n.client, + provider.Credentials.Name, + key, + &req.Cluster, + ) + if err != nil { + return fmt.Errorf( + "error creating credentials Secret for the Nutanix CSI driver: %w", + err, + ) + } + } + + err := n.createStorageClasses( + ctx, + provider.StorageClassConfig, + &req.Cluster, + defaultStorageConfig, + ) + if err != nil { + return fmt.Errorf("error creating StorageClasses for the Nutanix CSI driver: %w", err) + } + + return nil +} + +func (n *NutanixCSI) handleHelmAddonApply( + ctx context.Context, + req *runtimehooksv1.AfterControlPlaneInitializedRequest, +) error { + valuesTemplateConfigMap, err := lifecycleutils.RetrieveValuesTemplateConfigMap(ctx, + n.client, + n.config.defaultValuesTemplateConfigMapName, + n.config.DefaultsNamespace()) + if err != nil { + return fmt.Errorf( + "failed to retrieve nutanix csi installation values template ConfigMap for cluster: %w", + err, + ) + } + values := valuesTemplateConfigMap.Data["values.yaml"] + log := ctrl.LoggerFrom(ctx).WithValues( + "cluster", + ctrlclient.ObjectKeyFromObject(&req.Cluster), + ) + helmChart, err := n.helmChartInfoGetter.For(ctx, log, config.NutanixStorageCSI) + if err != nil { + return fmt.Errorf("failed to get values for nutanix-csi-config %w", err) + } + + hcp := &caaphv1.HelmChartProxy{ + TypeMeta: metav1.TypeMeta{ + APIVersion: caaphv1.GroupVersion.String(), + Kind: "HelmChartProxy", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: req.Cluster.Namespace, + Name: "nutanix-csi-" + req.Cluster.Name, + }, + Spec: caaphv1.HelmChartProxySpec{ + RepoURL: helmChart.Repository, + ChartName: helmChart.Name, + ClusterSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{clusterv1.ClusterNameLabel: req.Cluster.Name}, + }, + ReleaseNamespace: defaultStorageHelmReleaseNamespace, + ReleaseName: defaultStorageHelmReleaseName, + Version: helmChart.Version, + ValuesTemplate: values, + }, + } + + if err = controllerutil.SetOwnerReference(&req.Cluster, hcp, n.client.Scheme()); err != nil { + return fmt.Errorf( + "failed to set owner reference on nutanix-csi installation HelmChartProxy: %w", + err, + ) + } + + if err = client.ServerSideApply(ctx, n.client, hcp); err != nil { + return fmt.Errorf("failed to apply nutanix-csi installation HelmChartProxy: %w", err) + } + + snapshotHelmChart, err := n.helmChartInfoGetter.For(ctx, log, config.NutanixSnapshotCSI) + if err != nil { + return fmt.Errorf("failed to get values for nutanix-csi-config %w", err) + } + + snapshotChart := &caaphv1.HelmChartProxy{ + TypeMeta: metav1.TypeMeta{ + APIVersion: caaphv1.GroupVersion.String(), + Kind: "HelmChartProxy", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: req.Cluster.Namespace, + Name: "nutanix-csi-snapshot-" + req.Cluster.Name, + }, + Spec: caaphv1.HelmChartProxySpec{ + RepoURL: snapshotHelmChart.Repository, + ChartName: snapshotHelmChart.Name, + ClusterSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{clusterv1.ClusterNameLabel: req.Cluster.Name}, + }, + ReleaseNamespace: defaultSnapshotHelmReleaseNamespace, + ReleaseName: defaultSnapshotHelmReleaseName, + Version: snapshotHelmChart.Version, + }, + } + + if err = client.ServerSideApply(ctx, n.client, snapshotChart); err != nil { + return fmt.Errorf( + "failed to apply nutanix-csi-snapshot installation HelmChartProxy: %w", + err, + ) + } + + return nil +} + +func (n *NutanixCSI) createStorageClasses( + ctx context.Context, + configs []v1alpha1.StorageClassConfig, + cluster *clusterv1.Cluster, + defaultStorageConfig *v1alpha1.DefaultStorage, +) error { + allStorageClasses := make([]runtime.Object, 0, len(configs)) + for _, config := range configs { + setAsDefault := config.Name == defaultStorageConfig.StorageClassConfigName && + v1alpha1.CSIProviderNutanix == defaultStorageConfig.ProviderName + allStorageClasses = append(allStorageClasses, lifecycleutils.CreateStorageClass( + config, + v1alpha1.NutanixProvisioner, + setAsDefault, + defaultStorageClassParameters, + )) + } + cm, err := lifecycleutils.CreateConfigMapForCRS( + fmt.Sprintf("nutanix-storageclass-cm-%s", cluster.Name), + n.config.DefaultsNamespace(), + allStorageClasses..., + ) + if err != nil { + return err + } + err = client.ServerSideApply(ctx, n.client, cm) + if err != nil { + return err + } + return lifecycleutils.EnsureCRSForClusterFromObjects( + ctx, + "nutanix-storageclass-crs", + n.client, + cluster, + cm, + ) +} diff --git a/pkg/handlers/generic/lifecycle/handlers.go b/pkg/handlers/generic/lifecycle/handlers.go index fcfc07d10..0452ec4fc 100644 --- a/pkg/handlers/generic/lifecycle/handlers.go +++ b/pkg/handlers/generic/lifecycle/handlers.go @@ -9,54 +9,77 @@ import ( "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" "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" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/cpi" - awscpi "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/cpi/aws" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/config" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/csi" awsebs "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/csi/aws-ebs" + nutanixcsi "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/csi/nutanix-csi" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/nfd" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/servicelbgc" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/options" ) type Handlers struct { + globalOptions *options.GlobalOptions calicoCNIConfig *calico.CNIConfig ciliumCNIConfig *cilium.CNIConfig nfdConfig *nfd.Config clusterAutoscalerConfig *clusterautoscaler.Config ebsConfig *awsebs.AWSEBSConfig - awsCPIConfig *awscpi.AWSCPIConfig + nutnaixCSIConfig *nutanixcsi.NutanixCSIConfig + awsccmConfig *awsccm.AWSCCMConfig + nutanixCCMConfig *nutanixccm.Config } -func New(globalOptions *options.GlobalOptions) *Handlers { +func New( + globalOptions *options.GlobalOptions, +) *Handlers { return &Handlers{ - calicoCNIConfig: &calico.CNIConfig{GlobalOptions: globalOptions}, + globalOptions: globalOptions, + calicoCNIConfig: &calico.CNIConfig{ + GlobalOptions: globalOptions, + }, ciliumCNIConfig: &cilium.CNIConfig{GlobalOptions: globalOptions}, nfdConfig: &nfd.Config{GlobalOptions: globalOptions}, clusterAutoscalerConfig: &clusterautoscaler.Config{GlobalOptions: globalOptions}, ebsConfig: &awsebs.AWSEBSConfig{GlobalOptions: globalOptions}, - awsCPIConfig: &awscpi.AWSCPIConfig{GlobalOptions: globalOptions}, + awsccmConfig: &awsccm.AWSCCMConfig{GlobalOptions: globalOptions}, + nutnaixCSIConfig: &nutanixcsi.NutanixCSIConfig{GlobalOptions: globalOptions}, + nutanixCCMConfig: &nutanixccm.Config{GlobalOptions: globalOptions}, } } func (h *Handlers) AllHandlers(mgr manager.Manager) []handlers.Named { + helmChartInfoGetter := config.NewHelmChartGetterFromConfigMap( + h.globalOptions.HelmAddonsConfigMapName(), + h.globalOptions.DefaultsNamespace(), + mgr.GetClient(), + ) csiHandlers := map[string]csi.CSIProvider{ v1alpha1.CSIProviderAWSEBS: awsebs.New(mgr.GetClient(), h.ebsConfig), + v1alpha1.CSIProviderNutanix: nutanixcsi.New( + mgr.GetClient(), + h.nutnaixCSIConfig, + helmChartInfoGetter, + ), } - cpiHandlers := map[string]cpi.CPIProvider{ - v1alpha1.CPIProviderAWS: awscpi.New(mgr.GetClient(), h.awsCPIConfig), + ccmHandlers := map[string]ccm.CCMProvider{ + 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), - cilium.New(mgr.GetClient(), h.ciliumCNIConfig), - nfd.New(mgr.GetClient(), h.nfdConfig), - clusterautoscaler.New(mgr.GetClient(), h.clusterAutoscalerConfig), + calico.New(mgr.GetClient(), h.calicoCNIConfig, helmChartInfoGetter), + cilium.New(mgr.GetClient(), h.ciliumCNIConfig, helmChartInfoGetter), + nfd.New(mgr.GetClient(), h.nfdConfig, helmChartInfoGetter), + clusterautoscaler.New(mgr.GetClient(), h.clusterAutoscalerConfig, helmChartInfoGetter), servicelbgc.New(mgr.GetClient()), csi.New(mgr.GetClient(), csiHandlers), - cpi.New(mgr.GetClient(), cpiHandlers), + ccm.New(mgr.GetClient(), ccmHandlers), } } @@ -66,5 +89,7 @@ func (h *Handlers) AddFlags(flagSet *pflag.FlagSet) { h.calicoCNIConfig.AddFlags("cni.calico", flagSet) h.ciliumCNIConfig.AddFlags("cni.cilium", flagSet) h.ebsConfig.AddFlags("awsebs", pflag.CommandLine) - h.awsCPIConfig.AddFlags("awscpi", pflag.CommandLine) + h.awsccmConfig.AddFlags("awsccm", pflag.CommandLine) + h.nutnaixCSIConfig.AddFlags("nutanixcsi", flagSet) + h.nutanixCCMConfig.AddFlags("nutanixccm", flagSet) } diff --git a/pkg/handlers/generic/lifecycle/nfd/handler.go b/pkg/handlers/generic/lifecycle/nfd/handler.go index baa3903d7..8eab1edc6 100644 --- a/pkg/handlers/generic/lifecycle/nfd/handler.go +++ b/pkg/handlers/generic/lifecycle/nfd/handler.go @@ -18,6 +18,7 @@ import ( "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/lifecycle" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/variables" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/config" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/options" ) @@ -43,8 +44,9 @@ func (c *Config) AddFlags(prefix string, flags *pflag.FlagSet) { } type DefaultNFD struct { - client ctrlclient.Client - config *Config + client ctrlclient.Client + config *Config + helmChartInfoGetter *config.HelmChartGetter variableName string // points to the global config variable variablePath []string // path of this variable on the global config variable @@ -58,12 +60,14 @@ var ( func New( c ctrlclient.Client, cfg *Config, + helmChartInfoGetter *config.HelmChartGetter, ) *DefaultNFD { return &DefaultNFD{ - client: c, - config: cfg, - variableName: clusterconfig.MetaVariableName, - variablePath: []string{"addons", v1alpha1.NFDVariableName}, + client: c, + config: cfg, + helmChartInfoGetter: helmChartInfoGetter, + variableName: clusterconfig.MetaVariableName, + variablePath: []string{"addons", v1alpha1.NFDVariableName}, } } @@ -112,9 +116,24 @@ func (n *DefaultNFD) AfterControlPlaneInitialized( client: n.client, } case v1alpha1.AddonStrategyHelmAddon: + helmChart, err := n.helmChartInfoGetter.For(ctx, log, config.NFD) + if err != nil { + log.Error( + err, + "failed to get configmap with helm settings", + ) + resp.SetStatus(runtimehooksv1.ResponseStatusFailure) + resp.SetMessage( + fmt.Sprintf("failed to get configration to create helm addon: %v", + err, + ), + ) + return + } strategy = helmAddonStrategy{ - config: n.config.helmAddonConfig, - client: n.client, + config: n.config.helmAddonConfig, + client: n.client, + helmChart: helmChart, } default: resp.SetStatus(runtimehooksv1.ResponseStatusFailure) diff --git a/pkg/handlers/generic/lifecycle/nfd/strategy_crs.go b/pkg/handlers/generic/lifecycle/nfd/strategy_crs.go index 461db5318..26498d482 100644 --- a/pkg/handlers/generic/lifecycle/nfd/strategy_crs.go +++ b/pkg/handlers/generic/lifecycle/nfd/strategy_crs.go @@ -87,7 +87,7 @@ func (s crsStrategy) apply( ) } - if err := utils.EnsureCRSForClusterFromConfigMaps(ctx, cm.Name, s.client, cluster, cm); err != nil { + if err := utils.EnsureCRSForClusterFromObjects(ctx, cm.Name, s.client, cluster, cm); err != nil { return fmt.Errorf( "failed to apply NFD installation ClusterResourceSet: %w", err, diff --git a/pkg/handlers/generic/lifecycle/nfd/strategy_helmaddon.go b/pkg/handlers/generic/lifecycle/nfd/strategy_helmaddon.go index 602400017..4ef0834b3 100644 --- a/pkg/handlers/generic/lifecycle/nfd/strategy_helmaddon.go +++ b/pkg/handlers/generic/lifecycle/nfd/strategy_helmaddon.go @@ -18,12 +18,10 @@ import ( 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/common/pkg/k8s/client" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/config" ) const ( - defaultHelmRepositoryURL = "https://kubernetes-sigs.github.io/node-feature-discovery/charts" - defaultHelmChartVersion = "0.15.2" - defaultHelmChartName = "node-feature-discovery" defaultHelmReleaseName = "node-feature-discovery" defaultHelmReleaseNamespace = "node-feature-discovery" ) @@ -44,7 +42,8 @@ func (c *helmAddonConfig) AddFlags(prefix string, flags *pflag.FlagSet) { type helmAddonStrategy struct { config helmAddonConfig - client ctrlclient.Client + client ctrlclient.Client + helmChart *config.HelmChart } func (s helmAddonStrategy) apply( @@ -66,7 +65,7 @@ func (s helmAddonStrategy) apply( values += fmt.Sprintf(` image: tag: v%s-minimal -`, defaultHelmChartVersion) +`, s.helmChart.Version) hcp := &caaphv1.HelmChartProxy{ TypeMeta: metav1.TypeMeta{ @@ -78,14 +77,14 @@ image: Name: "node-feature-discovery-" + req.Cluster.Name, }, Spec: caaphv1.HelmChartProxySpec{ - RepoURL: defaultHelmRepositoryURL, - ChartName: defaultHelmChartName, + RepoURL: s.helmChart.Repository, + ChartName: s.helmChart.Name, ClusterSelector: metav1.LabelSelector{ MatchLabels: map[string]string{capiv1.ClusterNameLabel: req.Cluster.Name}, }, ReleaseNamespace: defaultHelmReleaseNamespace, ReleaseName: defaultHelmReleaseName, - Version: defaultHelmChartVersion, + Version: s.helmChart.Version, ValuesTemplate: values, }, } diff --git a/pkg/handlers/generic/lifecycle/utils/scs.go b/pkg/handlers/generic/lifecycle/utils/scs.go new file mode 100644 index 000000000..6bcb66bd3 --- /dev/null +++ b/pkg/handlers/generic/lifecycle/utils/scs.go @@ -0,0 +1,52 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + storagev1 "k8s.io/api/storage/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" +) + +const ( + kindStorageClass = "StorageClass" +) + +func CreateStorageClass( + storageConfig v1alpha1.StorageClassConfig, + provisionerName v1alpha1.StorageProvisioner, + isDefault bool, + defaultParameters map[string]string, +) *storagev1.StorageClass { + parameters := make(map[string]string) + // set the defaults first so that user provided parameters can override them + for k, v := range defaultParameters { + parameters[k] = v + } + // set user provided parameters, overriding any defaults with the same key + for k, v := range storageConfig.Parameters { + parameters[k] = v + } + + sc := storagev1.StorageClass{ + TypeMeta: metav1.TypeMeta{ + Kind: kindStorageClass, + APIVersion: storagev1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: storageConfig.Name, + }, + Provisioner: string(provisionerName), + Parameters: parameters, + VolumeBindingMode: ptr.To(storageConfig.VolumeBindingMode), + ReclaimPolicy: ptr.To(storageConfig.ReclaimPolicy), + AllowVolumeExpansion: ptr.To(storageConfig.AllowExpansion), + } + if isDefault { + sc.ObjectMeta.Annotations = defaultStorageClassMap + } + return &sc +} diff --git a/pkg/handlers/generic/lifecycle/utils/scs_test.go b/pkg/handlers/generic/lifecycle/utils/scs_test.go new file mode 100644 index 000000000..a75a5fdd8 --- /dev/null +++ b/pkg/handlers/generic/lifecycle/utils/scs_test.go @@ -0,0 +1,144 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + corev1 "k8s.io/api/core/v1" + storagev1 "k8s.io/api/storage/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" +) + +var ( + defaultParameters = map[string]string{ + "csi.storage.k8s.io/fstype": "ext4", + "type": "gp3", + } + userProviderParameters = map[string]string{ + "csi.storage.k8s.io/fstype": "xfs", + "flashMode": "ENABLED", + "storageContainer": "storage-container-name", + "chapAuth": "ENABLED", + "storageType": "NutanixVolumes", + "whitelistIPMode": "ENABLED", + "whitelistIPAddr": "1.1.1.1", + } + + combinedParameters = map[string]string{ + "csi.storage.k8s.io/fstype": "xfs", + "type": "gp3", + "flashMode": "ENABLED", + "storageContainer": "storage-container-name", + "chapAuth": "ENABLED", + "storageType": "NutanixVolumes", + "whitelistIPMode": "ENABLED", + "whitelistIPAddr": "1.1.1.1", + } +) + +func TestCreateStorageClass(t *testing.T) { + tests := []struct { + name string + storageConfig v1alpha1.StorageClassConfig + provisioner v1alpha1.StorageProvisioner + setAsDefault bool + defaultParameters map[string]string + expectedStorageClass *storagev1.StorageClass + }{ + { + name: "with only default parameters", + storageConfig: v1alpha1.StorageClassConfig{ + Name: "aws-ebs", + ReclaimPolicy: v1alpha1.VolumeReclaimDelete, + VolumeBindingMode: v1alpha1.VolumeBindingWaitForFirstConsumer, + Parameters: nil, + AllowExpansion: true, + }, + provisioner: v1alpha1.AWSEBSProvisioner, + defaultParameters: defaultParameters, + expectedStorageClass: &storagev1.StorageClass{ + TypeMeta: metav1.TypeMeta{ + Kind: kindStorageClass, + APIVersion: storagev1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "aws-ebs", + }, + Parameters: defaultParameters, + ReclaimPolicy: ptr.To(corev1.PersistentVolumeReclaimDelete), + VolumeBindingMode: ptr.To(storagev1.VolumeBindingWaitForFirstConsumer), + Provisioner: string(v1alpha1.AWSEBSProvisioner), + AllowVolumeExpansion: ptr.To(true), + }, + }, + { + name: "with only user provided parameters", + storageConfig: v1alpha1.StorageClassConfig{ + Name: "nutanix-volumes", + ReclaimPolicy: v1alpha1.VolumeReclaimDelete, + VolumeBindingMode: v1alpha1.VolumeBindingWaitForFirstConsumer, + Parameters: userProviderParameters, + }, + provisioner: v1alpha1.NutanixProvisioner, + expectedStorageClass: &storagev1.StorageClass{ + TypeMeta: metav1.TypeMeta{ + Kind: kindStorageClass, + APIVersion: storagev1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nutanix-volumes", + }, + Parameters: userProviderParameters, + ReclaimPolicy: ptr.To(corev1.PersistentVolumeReclaimDelete), + VolumeBindingMode: ptr.To(storagev1.VolumeBindingWaitForFirstConsumer), + Provisioner: string(v1alpha1.NutanixProvisioner), + AllowVolumeExpansion: ptr.To(false), + }, + }, + { + name: "with both default and user provided parameters", + storageConfig: v1alpha1.StorageClassConfig{ + Name: "aws-ebs", + ReclaimPolicy: v1alpha1.VolumeReclaimDelete, + VolumeBindingMode: v1alpha1.VolumeBindingWaitForFirstConsumer, + Parameters: userProviderParameters, + AllowExpansion: true, + }, + provisioner: v1alpha1.AWSEBSProvisioner, + defaultParameters: defaultParameters, + expectedStorageClass: &storagev1.StorageClass{ + TypeMeta: metav1.TypeMeta{ + Kind: kindStorageClass, + APIVersion: storagev1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "aws-ebs", + }, + Parameters: combinedParameters, + ReclaimPolicy: ptr.To(corev1.PersistentVolumeReclaimDelete), + VolumeBindingMode: ptr.To(storagev1.VolumeBindingWaitForFirstConsumer), + Provisioner: string(v1alpha1.AWSEBSProvisioner), + AllowVolumeExpansion: ptr.To(true), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + sc := CreateStorageClass( + tt.storageConfig, + tt.provisioner, + tt.setAsDefault, + tt.defaultParameters, + ) + if diff := cmp.Diff(sc, tt.expectedStorageClass); diff != "" { + t.Errorf("CreateStorageClass() mismatch (-want +got):\n%s", diff) + } + }) + } +} diff --git a/pkg/handlers/generic/lifecycle/utils/secrets.go b/pkg/handlers/generic/lifecycle/utils/secrets.go new file mode 100644 index 000000000..7c3660913 --- /dev/null +++ b/pkg/handlers/generic/lifecycle/utils/secrets.go @@ -0,0 +1,73 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "context" + "fmt" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "sigs.k8s.io/cluster-api/controllers/remote" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/k8s/client" +) + +// CopySecretToRemoteCluster will get the Secret from srcSecretName +// and create it on the remote cluster, copying Data and StringData to dstSecretKey Secret. +func CopySecretToRemoteCluster( + ctx context.Context, + cl ctrlclient.Client, + srcSecretName string, + dstSecretKey ctrlclient.ObjectKey, + cluster *clusterv1.Cluster, +) error { + sourceSecret := &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: srcSecretName, + Namespace: cluster.Namespace, + }, + } + err := cl.Get(ctx, ctrlclient.ObjectKeyFromObject(sourceSecret), sourceSecret) + if err != nil { + return err + } + + credentialsOnRemote := &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: dstSecretKey.Name, + Namespace: dstSecretKey.Namespace, + }, + Data: sourceSecret.Data, + StringData: sourceSecret.StringData, + } + + clusterKey := ctrlclient.ObjectKeyFromObject(cluster) + remoteClient, err := remote.NewClusterClient(ctx, "", cl, clusterKey) + if err != nil { + return fmt.Errorf("error creating client for remote cluster: %w", err) + } + + err = EnsureNamespace(ctx, remoteClient, dstSecretKey.Namespace) + if err != nil { + return fmt.Errorf("error creating namespace on the remote cluster: %w", err) + } + + err = client.ServerSideApply(ctx, remoteClient, credentialsOnRemote) + if err != nil { + return fmt.Errorf("error creating Secret on the remote cluster: %w", err) + } + + return nil +} diff --git a/pkg/handlers/generic/lifecycle/utils/utils.go b/pkg/handlers/generic/lifecycle/utils/utils.go index 00915b7ab..8a78b4d15 100644 --- a/pkg/handlers/generic/lifecycle/utils/utils.go +++ b/pkg/handlers/generic/lifecycle/utils/utils.go @@ -9,26 +9,57 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" crsv1 "sigs.k8s.io/cluster-api/exp/addons/api/v1beta1" + utilyaml "sigs.k8s.io/cluster-api/util/yaml" ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/k8s/client" ) -func EnsureCRSForClusterFromConfigMaps( +const ( + defaultCRSConfigMapKey = "custom-resources.yaml" +) + +var ( + defaultStorageClassKey = "storageclass.kubernetes.io/is-default-class" + defaultStorageClassMap = map[string]string{ + defaultStorageClassKey: "true", + } +) + +func EnsureCRSForClusterFromObjects( ctx context.Context, crsName string, c ctrlclient.Client, cluster *clusterv1.Cluster, - configMaps ...*corev1.ConfigMap, + objects ...runtime.Object, ) error { - resources := make([]crsv1.ResourceRef, 0, len(configMaps)) - for _, cm := range configMaps { + resources := make([]crsv1.ResourceRef, 0, len(objects)) + for _, obj := range objects { + var name string + var kind crsv1.ClusterResourceSetResourceKind + cm, ok := obj.(*corev1.ConfigMap) + if !ok { + sec, secOk := obj.(*corev1.Secret) + if !secOk { + return fmt.Errorf( + "cannot create ClusterResourceSet with obj %v only secrets and configmaps are supported", + obj, + ) + } + name = sec.Name + kind = crsv1.SecretClusterResourceSetResourceKind + } else { + name = cm.Name + kind = crsv1.ConfigMapClusterResourceSetResourceKind + } resources = append(resources, crsv1.ResourceRef{ - Kind: string(crsv1.ConfigMapClusterResourceSetResourceKind), - Name: cm.Name, + Name: name, + Kind: string(kind), }) } @@ -86,3 +117,63 @@ func EnsureNamespace(ctx context.Context, c ctrlclient.Client, name string) erro return nil } + +func RetrieveValuesTemplateConfigMap( + ctx context.Context, + c ctrlclient.Client, + configMapName, + defaultsNamespace string, +) (*corev1.ConfigMap, error) { + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: defaultsNamespace, + Name: configMapName, + }, + } + configMapObjName := ctrlclient.ObjectKeyFromObject( + configMap, + ) + err := c.Get(ctx, configMapObjName, configMap) + if err != nil { + return nil, fmt.Errorf( + "failed to retrieve installation values template ConfigMap %q: %w", + configMapObjName, + err, + ) + } + return configMap, nil +} + +func CreateConfigMapForCRS(configMapName, configMapNamespace string, + objs ...runtime.Object, +) (*corev1.ConfigMap, error) { + cm := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: configMapName, + Namespace: configMapNamespace, + }, + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "ConfigMap", + }, + Data: make(map[string]string), + } + l := make([][]byte, 0, len(objs)) + for _, v := range objs { + obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(v) + if err != nil { + return nil, err + } + objYaml, err := utilyaml.FromUnstructured([]unstructured.Unstructured{ + { + Object: obj, + }, + }) + if err != nil { + return nil, err + } + l = append(l, objYaml) + } + cm.Data[defaultCRSConfigMapKey] = string(utilyaml.JoinYaml(l...)) + return cm, nil +} diff --git a/pkg/handlers/generic/lifecycle/utils/utils_test.go b/pkg/handlers/generic/lifecycle/utils/utils_test.go new file mode 100644 index 000000000..26ec622e6 --- /dev/null +++ b/pkg/handlers/generic/lifecycle/utils/utils_test.go @@ -0,0 +1,94 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "testing" + + corev1 "k8s.io/api/core/v1" + storagev1 "k8s.io/api/storage/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +func TestCreateConfigMapForCRS(t *testing.T) { + tests := []struct { + name string + testCMName string + testNamespace string + objs []runtime.Object + expectedCM corev1.ConfigMap + }{ + { + name: "multiple storage class objects", + testCMName: "test", + testNamespace: "default", + objs: []runtime.Object{ + &storagev1.StorageClass{ + TypeMeta: metav1.TypeMeta{ + Kind: kindStorageClass, + APIVersion: storagev1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + }, + &storagev1.StorageClass{ + TypeMeta: metav1.TypeMeta{ + Kind: kindStorageClass, + APIVersion: storagev1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-2", + Namespace: "default", + }, + }, + }, + expectedCM: corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "ConfigMap", + }, + Data: map[string]string{ + defaultCRSConfigMapKey: `apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + creationTimestamp: null + name: test + namespace: default +provisioner: "" +--- +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + creationTimestamp: null + name: test-2 + namespace: default +provisioner: ""`, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cm, err := CreateConfigMapForCRS(tt.testCMName, tt.testNamespace, tt.objs...) + if err != nil { + t.Errorf("failed to create cm with error %v", err) + } + data, ok := cm.Data[defaultCRSConfigMapKey] + if !ok { + t.Errorf("expected %s to exist in cm.Data. got %v", defaultCRSConfigMapKey, cm.Data) + } + expected := tt.expectedCM.Data[defaultCRSConfigMapKey] + if data != expected { + t.Errorf("expected %s \n got %s", expected, data) + } + }) + } +} diff --git a/pkg/handlers/generic/mutation/auditpolicy/tests/generate_patches.go b/pkg/handlers/generic/mutation/auditpolicy/inject_test.go similarity index 64% rename from pkg/handlers/generic/mutation/auditpolicy/tests/generate_patches.go rename to pkg/handlers/generic/mutation/auditpolicy/inject_test.go index 7d7d78ac7..685707528 100644 --- a/pkg/handlers/generic/mutation/auditpolicy/tests/generate_patches.go +++ b/pkg/handlers/generic/mutation/auditpolicy/inject_test.go @@ -1,11 +1,12 @@ // Copyright 2023 D2iQ, Inc. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -package tests +package auditpolicy import ( "testing" + . "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" @@ -13,24 +14,31 @@ import ( "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" ) -func TestGeneratePatches( - t *testing.T, generatorFunc func() mutation.GeneratePatches, -) { - t.Helper() +func TestAuditPolicyPatch(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "Audit Policy mutator suite") +} + +var _ = Describe("Generate Audit Policy patches", func() { + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewPatch()).(mutation.GeneratePatches) + } - capitest.ValidateGeneratePatches( - t, - generatorFunc, - capitest.PatchTestDef{ + testDefs := []capitest.PatchTestDef{ + { Name: "unset variable", }, - capitest.PatchTestDef{ - Name: "http proxy set for KubeadmControlPlaneTemplate", + { + Name: "auditpolicy set for KubeadmControlPlaneTemplate", RequestItem: request.NewKubeadmControlPlaneTemplateRequestItem(""), ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{ - Operation: "add", - Path: "/spec/template/spec/kubeadmConfigSpec/files", - ValueMatcher: gomega.HaveLen(1), + Operation: "add", + Path: "/spec/template/spec/kubeadmConfigSpec/files", + ValueMatcher: gomega.ContainElements( + gomega.HaveKeyWithValue( + "path", "/etc/kubernetes/audit-policy/apiserver-audit-policy.yaml", + ), + ), }, { Operation: "add", Path: "/spec/template/spec/kubeadmConfigSpec/clusterConfiguration", @@ -67,5 +75,13 @@ func TestGeneratePatches( ), }}, }, - ) -} + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches(GinkgoT(), patchGenerator, &tt) + }) + } +}) diff --git a/pkg/handlers/generic/mutation/containerdmetrics/files/metrics-config.toml b/pkg/handlers/generic/mutation/containerdmetrics/files/metrics-config.toml new file mode 100644 index 000000000..e1908f5aa --- /dev/null +++ b/pkg/handlers/generic/mutation/containerdmetrics/files/metrics-config.toml @@ -0,0 +1,3 @@ +[metrics] + address = "0.0.0.0:1338" + grpc_histogram = false diff --git a/pkg/handlers/generic/mutation/containerdmetrics/inject.go b/pkg/handlers/generic/mutation/containerdmetrics/inject.go new file mode 100644 index 000000000..d2c5bc954 --- /dev/null +++ b/pkg/handlers/generic/mutation/containerdmetrics/inject.go @@ -0,0 +1,73 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +package containerdmetrics + +import ( + "context" + + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" + controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + ctrl "sigs.k8s.io/controller-runtime" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches/selectors" +) + +type containerdMetricsPatchHandler struct{} + +func NewPatch() *containerdMetricsPatchHandler { + return &containerdMetricsPatchHandler{} +} + +func (h *containerdMetricsPatchHandler) Mutate( + ctx context.Context, + obj *unstructured.Unstructured, + vars map[string]apiextensionsv1.JSON, + holderRef runtimehooksv1.HolderReference, + _ ctrlclient.ObjectKey, +) error { + log := ctrl.LoggerFrom(ctx).WithValues( + "holderRef", holderRef, + ) + + metricsConfigDropIn := generateMetricsConfigDropIn() + + if err := patches.MutateIfApplicable( + obj, vars, &holderRef, selectors.ControlPlane(), log, + func(obj *controlplanev1.KubeadmControlPlaneTemplate) error { + log.WithValues( + "patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(), + "patchedObjectName", ctrlclient.ObjectKeyFromObject(obj), + ).Info("adding containerd metrics config to control plane kubeadm config spec") + obj.Spec.Template.Spec.KubeadmConfigSpec.Files = append( + obj.Spec.Template.Spec.KubeadmConfigSpec.Files, + metricsConfigDropIn, + ) + + return nil + }); err != nil { + return err + } + + if err := patches.MutateIfApplicable( + obj, vars, &holderRef, selectors.WorkersKubeadmConfigTemplateSelector(), log, + func(obj *bootstrapv1.KubeadmConfigTemplate) error { + log.WithValues( + "patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(), + "patchedObjectName", ctrlclient.ObjectKeyFromObject(obj), + ).Info("adding containerd metrics config to worker node kubeadm config template") + obj.Spec.Template.Spec.Files = append( + obj.Spec.Template.Spec.Files, + metricsConfigDropIn) + + return nil + }); err != nil { + return err + } + + return nil +} diff --git a/pkg/handlers/generic/mutation/containerdmetrics/inject_test.go b/pkg/handlers/generic/mutation/containerdmetrics/inject_test.go new file mode 100644 index 000000000..20a531bba --- /dev/null +++ b/pkg/handlers/generic/mutation/containerdmetrics/inject_test.go @@ -0,0 +1,83 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package containerdmetrics + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" +) + +func TestContainerdMetricsPatch(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "Containerd metrics mutator suite") +} + +var _ = Describe("Generate containerd metrics patches", func() { + // only add aws region patch + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewPatch()).(mutation.GeneratePatches) + } + + testDefs := []capitest.PatchTestDef{ + { + Name: "containerd metrics config added to control plane kubeadm config spec", + RequestItem: request.NewKubeadmControlPlaneTemplateRequestItem(""), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{ + { + Operation: "add", + Path: "/spec/template/spec/kubeadmConfigSpec/files", + ValueMatcher: gomega.ContainElements( + gomega.HaveKeyWithValue( + "path", metricsConfigDropInFileOnRemote, + ), + ), + }, + }, + }, + { + Name: "containerd metrics config added to worker node kubeadm config template", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + "builtin", + map[string]any{ + "machineDeployment": map[string]any{ + "class": "*", + }, + }, + ), + }, + RequestItem: request.NewKubeadmConfigTemplateRequestItem(""), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{ + { + Operation: "add", + Path: "/spec/template/spec/files", + ValueMatcher: gomega.ContainElements( + gomega.HaveKeyWithValue( + "path", metricsConfigDropInFileOnRemote, + ), + ), + }, + }, + }, + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches( + GinkgoT(), + patchGenerator, + &tt, + ) + }) + } +}) diff --git a/pkg/handlers/generic/mutation/containerdmetrics/metrics.go b/pkg/handlers/generic/mutation/containerdmetrics/metrics.go new file mode 100644 index 000000000..d0ef0d0b4 --- /dev/null +++ b/pkg/handlers/generic/mutation/containerdmetrics/metrics.go @@ -0,0 +1,32 @@ +// Copyright 2024 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +package containerdmetrics + +import ( + _ "embed" + "path" + + cabpkv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" +) + +const ( + // TODO Factor out this constant to a common package. + containerdPatchesDirOnRemote = "/etc/containerd/cre.d" +) + +var ( + //go:embed files/metrics-config.toml + metricsConfigDropIn []byte + metricsConfigDropInFileOnRemote = path.Join( + containerdPatchesDirOnRemote, + "metrics-config.toml", + ) +) + +func generateMetricsConfigDropIn() cabpkv1.File { + return cabpkv1.File{ + Path: metricsConfigDropInFileOnRemote, + Content: string(metricsConfigDropIn), + Permissions: "0600", + } +} diff --git a/pkg/handlers/generic/mutation/containerdrestart/inject.go b/pkg/handlers/generic/mutation/containerdrestart/inject.go new file mode 100644 index 000000000..92edab3de --- /dev/null +++ b/pkg/handlers/generic/mutation/containerdrestart/inject.go @@ -0,0 +1,86 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +package containerdrestart + +import ( + "context" + + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" + controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + ctrl "sigs.k8s.io/controller-runtime" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches/selectors" +) + +type containerdRestartPatchHandler struct{} + +func NewPatch() *containerdRestartPatchHandler { + return &containerdRestartPatchHandler{} +} + +func (h *containerdRestartPatchHandler) Mutate( + ctx context.Context, + obj *unstructured.Unstructured, + vars map[string]apiextensionsv1.JSON, + holderRef runtimehooksv1.HolderReference, + clusterKey ctrlclient.ObjectKey, +) error { + log := ctrl.LoggerFrom(ctx).WithValues( + "holderRef", holderRef, + ) + + file, command := generateContainerdRestartScript() + + if err := patches.MutateIfApplicable( + obj, vars, &holderRef, selectors.ControlPlane(), log, + func(obj *controlplanev1.KubeadmControlPlaneTemplate) error { + log.WithValues( + "patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(), + "patchedObjectName", ctrlclient.ObjectKeyFromObject(obj), + ).Info("adding containerd restart script to control plane kubeadm config spec") + obj.Spec.Template.Spec.KubeadmConfigSpec.Files = append( + obj.Spec.Template.Spec.KubeadmConfigSpec.Files, + file, + ) + + log.WithValues( + "patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(), + "patchedObjectName", ctrlclient.ObjectKeyFromObject(obj), + ).Info("adding containerd restart command to control plane kubeadm config spec") + obj.Spec.Template.Spec.KubeadmConfigSpec.PreKubeadmCommands = append( + obj.Spec.Template.Spec.KubeadmConfigSpec.PreKubeadmCommands, + command, + ) + + return nil + }); err != nil { + return err + } + + if err := patches.MutateIfApplicable( + obj, vars, &holderRef, selectors.WorkersKubeadmConfigTemplateSelector(), log, + func(obj *bootstrapv1.KubeadmConfigTemplate) error { + log.WithValues( + "patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(), + "patchedObjectName", ctrlclient.ObjectKeyFromObject(obj), + ).Info("adding containerd restart script to worker node kubeadm config template") + obj.Spec.Template.Spec.Files = append(obj.Spec.Template.Spec.Files, file) + + log.WithValues( + "patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(), + "patchedObjectName", ctrlclient.ObjectKeyFromObject(obj), + ).Info("adding containerd restart command to worker node kubeadm config template") + obj.Spec.Template.Spec.PreKubeadmCommands = append(obj.Spec.Template.Spec.PreKubeadmCommands, command) + + return nil + }); err != nil { + return err + } + + return nil +} diff --git a/pkg/handlers/generic/mutation/containerdrestart/inject_test.go b/pkg/handlers/generic/mutation/containerdrestart/inject_test.go new file mode 100644 index 000000000..c654edc1d --- /dev/null +++ b/pkg/handlers/generic/mutation/containerdrestart/inject_test.go @@ -0,0 +1,97 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package containerdrestart + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" +) + +func TestContainerdRestartPatch(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "Containerd restart mutator suite") +} + +var _ = Describe("Generate Containerd restart patches", func() { + // only add aws region patch + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewPatch()).(mutation.GeneratePatches) + } + + testDefs := []capitest.PatchTestDef{ + { + Name: "restart script and command added to control plane kubeadm config spec", + RequestItem: request.NewKubeadmControlPlaneTemplateRequestItem(""), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{ + { + Operation: "add", + Path: "/spec/template/spec/kubeadmConfigSpec/files", + ValueMatcher: gomega.ContainElements( + gomega.HaveKeyWithValue( + "path", ContainerdRestartScriptOnRemote, + ), + ), + }, + { + Operation: "add", + Path: "/spec/template/spec/kubeadmConfigSpec/preKubeadmCommands", + ValueMatcher: gomega.ContainElements( + ContainerdRestartScriptOnRemoteCommand, + ), + }, + }, + }, + { + Name: "restart script and command added to worker node kubeadm config template", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + "builtin", + map[string]any{ + "machineDeployment": map[string]any{ + "class": "*", + }, + }, + ), + }, + RequestItem: request.NewKubeadmConfigTemplateRequestItem(""), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{ + { + Operation: "add", + Path: "/spec/template/spec/files", + ValueMatcher: gomega.ContainElements( + gomega.HaveKeyWithValue( + "path", ContainerdRestartScriptOnRemote, + ), + ), + }, + { + Operation: "add", + Path: "/spec/template/spec/preKubeadmCommands", + ValueMatcher: gomega.ContainElements( + ContainerdRestartScriptOnRemoteCommand, + ), + }, + }, + }, + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches( + GinkgoT(), + patchGenerator, + &tt, + ) + }) + } +}) diff --git a/pkg/handlers/generic/mutation/containerdrestart/restart.go b/pkg/handlers/generic/mutation/containerdrestart/restart.go new file mode 100644 index 000000000..8826cceed --- /dev/null +++ b/pkg/handlers/generic/mutation/containerdrestart/restart.go @@ -0,0 +1,27 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +package containerdrestart + +import ( + _ "embed" + + bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" +) + +const ( + ContainerdRestartScriptOnRemote = "/etc/containerd/restart.sh" + ContainerdRestartScriptOnRemoteCommand = "/bin/bash " + ContainerdRestartScriptOnRemote +) + +//go:embed templates/containerd-restart.sh +var containerdRestartScript []byte + +//nolint:gocritic // no need for named return values +func generateContainerdRestartScript() (bootstrapv1.File, string) { + return bootstrapv1.File{ + Path: ContainerdRestartScriptOnRemote, + Content: string(containerdRestartScript), + Permissions: "0700", + }, + ContainerdRestartScriptOnRemoteCommand +} diff --git a/pkg/handlers/generic/mutation/mirrors/templates/containerd-restart.sh b/pkg/handlers/generic/mutation/containerdrestart/templates/containerd-restart.sh similarity index 100% rename from pkg/handlers/generic/mutation/mirrors/templates/containerd-restart.sh rename to pkg/handlers/generic/mutation/containerdrestart/templates/containerd-restart.sh diff --git a/pkg/handlers/generic/mutation/etcd/tests/generate_patches.go b/pkg/handlers/generic/mutation/etcd/inject_test.go similarity index 76% rename from pkg/handlers/generic/mutation/etcd/tests/generate_patches.go rename to pkg/handlers/generic/mutation/etcd/inject_test.go index 833a86ede..f71925b33 100644 --- a/pkg/handlers/generic/mutation/etcd/tests/generate_patches.go +++ b/pkg/handlers/generic/mutation/etcd/inject_test.go @@ -1,11 +1,12 @@ // Copyright 2023 D2iQ, Inc. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -package tests +package etcd import ( "testing" + . "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" @@ -13,34 +14,35 @@ import ( "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" ) -func TestGeneratePatches( - t *testing.T, - generatorFunc func() mutation.GeneratePatches, - variableName string, - variablePath ...string, -) { - t.Helper() +func TestEtcdPolicyPatch(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "etcd mutator suite") +} + +var _ = Describe("Generate etcd patches", func() { + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewPatch()).(mutation.GeneratePatches) + } - capitest.ValidateGeneratePatches( - t, - generatorFunc, - capitest.PatchTestDef{ + testDefs := []capitest.PatchTestDef{ + { Name: "unset variable", }, - capitest.PatchTestDef{ + { Name: "etcd imageRepository and imageTag set", Vars: []runtimehooksv1.Variable{ capitest.VariableWithValue( - variableName, + clusterconfig.MetaVariableName, v1alpha1.Etcd{ Image: &v1alpha1.Image{ Repository: "my-registry.io/my-org/my-repo", Tag: "v3.5.99_custom.0", }, }, - variablePath..., + VariableName, ), }, RequestItem: request.NewKubeadmControlPlaneTemplateRequestItem(""), @@ -60,17 +62,17 @@ func TestGeneratePatches( }, }, }, - capitest.PatchTestDef{ + { Name: "etcd imageRepository set", Vars: []runtimehooksv1.Variable{ capitest.VariableWithValue( - variableName, + clusterconfig.MetaVariableName, v1alpha1.Etcd{ Image: &v1alpha1.Image{ Repository: "my-registry.io/my-org/my-repo", }, }, - variablePath..., + VariableName, ), }, RequestItem: request.NewKubeadmControlPlaneTemplateRequestItem(""), @@ -89,17 +91,17 @@ func TestGeneratePatches( }, }, }, - capitest.PatchTestDef{ + { Name: "etcd imageTag set", Vars: []runtimehooksv1.Variable{ capitest.VariableWithValue( - variableName, + clusterconfig.MetaVariableName, v1alpha1.Etcd{ Image: &v1alpha1.Image{ Tag: "v3.5.99_custom.0", }, }, - variablePath..., + VariableName, ), }, RequestItem: request.NewKubeadmControlPlaneTemplateRequestItem(""), @@ -118,5 +120,13 @@ func TestGeneratePatches( }, }, }, - ) -} + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches(GinkgoT(), patchGenerator, &tt) + }) + } +}) diff --git a/pkg/handlers/generic/mutation/extraapiservercertsans/tests/generate_patches.go b/pkg/handlers/generic/mutation/extraapiservercertsans/inject_test.go similarity index 60% rename from pkg/handlers/generic/mutation/extraapiservercertsans/tests/generate_patches.go rename to pkg/handlers/generic/mutation/extraapiservercertsans/inject_test.go index d70beb7ad..3c4cb3745 100644 --- a/pkg/handlers/generic/mutation/extraapiservercertsans/tests/generate_patches.go +++ b/pkg/handlers/generic/mutation/extraapiservercertsans/inject_test.go @@ -1,11 +1,12 @@ // Copyright 2023 D2iQ, Inc. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -package tests +package extraapiservercertsans import ( "testing" + . "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" @@ -13,29 +14,30 @@ import ( "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" ) -func TestGeneratePatches( - t *testing.T, - generatorFunc func() mutation.GeneratePatches, - variableName string, - variablePath ...string, -) { - t.Helper() - - capitest.ValidateGeneratePatches( - t, - generatorFunc, - capitest.PatchTestDef{ +func TestExtraAPIServerCertSANsPatch(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "Extra API server certificate mutator suite") +} + +var _ = Describe("Generate Extra API server certificate patches", func() { + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewPatch()).(mutation.GeneratePatches) + } + + testDefs := []capitest.PatchTestDef{ + { Name: "unset variable", }, - capitest.PatchTestDef{ + { Name: "extra API server cert SANs set", Vars: []runtimehooksv1.Variable{ capitest.VariableWithValue( - variableName, + clusterconfig.MetaVariableName, v1alpha1.ExtraAPIServerCertSANs{"a.b.c.example.com", "d.e.f.example.com"}, - variablePath..., + VariableName, ), }, RequestItem: request.NewKubeadmControlPlaneTemplateRequestItem(""), @@ -51,5 +53,13 @@ func TestGeneratePatches( ), }}, }, - ) -} + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches(GinkgoT(), patchGenerator, &tt) + }) + } +}) diff --git a/pkg/handlers/generic/mutation/handlers.go b/pkg/handlers/generic/mutation/handlers.go index 087582b0f..c37babe6c 100644 --- a/pkg/handlers/generic/mutation/handlers.go +++ b/pkg/handlers/generic/mutation/handlers.go @@ -9,12 +9,15 @@ import ( "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/cni/calico" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/auditpolicy" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/containerdmetrics" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/containerdrestart" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/etcd" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/extraapiservercertsans" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/httpproxy" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/imageregistries/credentials" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/kubernetesimagerepository" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/mirrors" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/users" ) // MetaMutators returns all generic patch handlers. @@ -28,5 +31,16 @@ func MetaMutators(mgr manager.Manager) []mutation.MetaMutator { credentials.NewPatch(mgr.GetClient()), mirrors.NewPatch(mgr.GetClient()), calico.NewPatch(), + users.NewPatch(), + containerdmetrics.NewPatch(), + + // Some patches may have changed containerd configuration. + // We must restart containerd for the configuration to take effect. + // Therefore, we must apply this patch last. + // + // Containerd restart and readiness altogether could take ~5s. + // We want to keep patch independent of each other and not share any state. + // Therefore, We must always apply this patch regardless any other patch modified containerd configuration. + containerdrestart.NewPatch(), } } diff --git a/pkg/handlers/generic/mutation/httpproxy/inject_test.go b/pkg/handlers/generic/mutation/httpproxy/inject_test.go index caae06f15..0b697397c 100644 --- a/pkg/handlers/generic/mutation/httpproxy/inject_test.go +++ b/pkg/handlers/generic/mutation/httpproxy/inject_test.go @@ -6,9 +6,19 @@ package httpproxy import ( "testing" + . "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" v1 "k8s.io/api/core/v1" + "k8s.io/apiserver/pkg/storage/names" capiv1 "sigs.k8s.io/cluster-api/api/v1beta1" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/test/helpers" ) func TestGenerateNoProxy(t *testing.T) { @@ -179,3 +189,99 @@ func TestGenerateNoProxy(t *testing.T) { }) } } + +func TestHTTPProxyPatch(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "HTTP Proxy mutator suite") +} + +var _ = Describe("Generate HTTPProxy Patches", func() { + // only add HTTPProxy patch + patchGenerator := func() mutation.GeneratePatches { + // Always initialize the testEnv variable in the closure. + // This will allow ginkgo to initialize testEnv variable during test execution time. + testEnv := helpers.TestEnv + return mutation.NewMetaGeneratePatchesHandler( + "", + NewPatch(testEnv.Client)).(mutation.GeneratePatches) + } + + testDefs := []capitest.PatchTestDef{ + { + Name: "unset variable", + }, + { + Name: "http proxy set for KubeadmConfigTemplate generic worker", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + clusterconfig.MetaVariableName, + v1alpha1.HTTPProxy{ + HTTP: "http://example.com", + HTTPS: "https://example.com", + AdditionalNo: []string{"no-proxy.example.com"}, + }, + VariableName, + ), + capitest.VariableWithValue( + "builtin", + map[string]any{ + "machineDeployment": map[string]any{ + "class": names.SimpleNameGenerator.GenerateName("worker-"), + }, + }, + ), + }, + RequestItem: request.NewKubeadmConfigTemplateRequestItem(""), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{ + Operation: "add", + Path: "/spec/template/spec/files", + ValueMatcher: gomega.ContainElements( + gomega.HaveKeyWithValue( + "path", "/etc/systemd/system/containerd.service.d/http-proxy.conf", + ), + gomega.HaveKeyWithValue( + "path", "/etc/systemd/system/kubelet.service.d/http-proxy.conf", + ), + ), + }}, + }, + { + Name: "http proxy set for KubeadmControlPlaneTemplate", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + clusterconfig.MetaVariableName, + v1alpha1.HTTPProxy{ + HTTP: "http://example.com", + HTTPS: "https://example.com", + AdditionalNo: []string{"no-proxy.example.com"}, + }, + VariableName, + ), + }, + RequestItem: request.NewKubeadmControlPlaneTemplateRequestItem(""), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{ + Operation: "add", + Path: "/spec/template/spec/kubeadmConfigSpec/files", + ValueMatcher: gomega.ContainElements( + gomega.HaveKeyWithValue( + "path", "/etc/systemd/system/containerd.service.d/http-proxy.conf", + ), + gomega.HaveKeyWithValue( + "path", "/etc/systemd/system/kubelet.service.d/http-proxy.conf", + ), + ), + }}, + }, + } + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches( + GinkgoT(), + patchGenerator, + &tt, + ) + }) + } +}) diff --git a/pkg/handlers/generic/mutation/httpproxy/tests/generate_patches.go b/pkg/handlers/generic/mutation/httpproxy/tests/generate_patches.go deleted file mode 100644 index 79cb4f2e1..000000000 --- a/pkg/handlers/generic/mutation/httpproxy/tests/generate_patches.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2023 D2iQ, Inc. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package tests - -import ( - "testing" - - "github.com/onsi/gomega" - "k8s.io/apiserver/pkg/storage/names" - runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" - - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" -) - -func TestGeneratePatches( - t *testing.T, - generatorFunc func() mutation.GeneratePatches, - variableName string, - variablePath ...string, -) { - t.Helper() - - capitest.ValidateGeneratePatches( - t, - generatorFunc, - capitest.PatchTestDef{ - Name: "unset variable", - }, - capitest.PatchTestDef{ - Name: "http proxy set for KubeadmConfigTemplate generic worker", - Vars: []runtimehooksv1.Variable{ - capitest.VariableWithValue( - variableName, - v1alpha1.HTTPProxy{ - HTTP: "http://example.com", - HTTPS: "https://example.com", - AdditionalNo: []string{"no-proxy.example.com"}, - }, - variablePath..., - ), - capitest.VariableWithValue( - "builtin", - map[string]any{ - "machineDeployment": map[string]any{ - "class": names.SimpleNameGenerator.GenerateName("worker-"), - }, - }, - ), - }, - RequestItem: request.NewKubeadmConfigTemplateRequestItem(""), - ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{ - Operation: "add", - Path: "/spec/template/spec/files", - ValueMatcher: gomega.HaveLen(2), - }}, - }, - capitest.PatchTestDef{ - Name: "http proxy set for KubeadmControlPlaneTemplate", - Vars: []runtimehooksv1.Variable{ - capitest.VariableWithValue( - variableName, - v1alpha1.HTTPProxy{ - HTTP: "http://example.com", - HTTPS: "https://example.com", - AdditionalNo: []string{"no-proxy.example.com"}, - }, - variablePath..., - ), - }, - RequestItem: request.NewKubeadmControlPlaneTemplateRequestItem(""), - ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{ - Operation: "add", - Path: "/spec/template/spec/kubeadmConfigSpec/files", - ValueMatcher: gomega.ContainElements( - gomega.HaveKeyWithValue( - "path", "/etc/systemd/system/containerd.service.d/http-proxy.conf", - ), - gomega.HaveKeyWithValue( - "path", "/etc/systemd/system/kubelet.service.d/http-proxy.conf", - ), - ), - }}, - }, - ) -} diff --git a/pkg/handlers/generic/mutation/imageregistries/credentials/inject_test.go b/pkg/handlers/generic/mutation/imageregistries/credentials/inject_test.go index e51af2b70..76498969e 100644 --- a/pkg/handlers/generic/mutation/imageregistries/credentials/inject_test.go +++ b/pkg/handlers/generic/mutation/imageregistries/credentials/inject_test.go @@ -6,7 +6,26 @@ package credentials import ( "testing" + . "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apiserver/pkg/storage/names" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/imageregistries" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/test/helpers" +) + +const ( + validSecretName = "myregistry-credentials" + registryStaticCredentialsSecretSuffix = "registry-creds" ) func Test_needImageRegistryCredentialsConfiguration(t *testing.T) { @@ -100,3 +119,330 @@ func Test_needImageRegistryCredentialsConfiguration(t *testing.T) { }) } } + +func TestImageRegistriesPatch(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "Image registry mutator suite") +} + +var _ = Describe("Generate Image registry patches", func() { + patchGenerator := func() mutation.GeneratePatches { + // Always initialize the testEnv variable in the closure. + // This will allow ginkgo to initialize testEnv variable during test execution time. + testEnv := helpers.TestEnv + // use direct client instead of controller client. This will allow the patch handler to read k8s object + // that are written by the tests. + // Test cases writes credentials secret that the mutator handler reads. + // Using direct client will enable reading it immediately. + client, err := testEnv.GetK8sClient() + gomega.Expect(err).To(gomega.BeNil()) + return mutation.NewMetaGeneratePatchesHandler("", NewPatch(client)).(mutation.GeneratePatches) + } + + testDefs := []capitest.PatchTestDef{ + { + Name: "unset variable", + }, + { + Name: "files added in KubeadmControlPlaneTemplate for ECR without a Secret", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + clusterconfig.MetaVariableName, + v1alpha1.ImageRegistries{ + v1alpha1.ImageRegistry{ + URL: "https://123456789.dkr.ecr.us-east-1.amazonaws.com", + }, + }, + imageregistries.VariableName, + ), + }, + RequestItem: request.NewKubeadmControlPlaneTemplateRequestItem(""), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{ + { + Operation: "add", + Path: "/spec/template/spec/kubeadmConfigSpec/files", + ValueMatcher: gomega.ContainElements( + gomega.HaveKeyWithValue( + "path", "/etc/cre/install-kubelet-credential-providers.sh", + ), + gomega.HaveKeyWithValue( + "path", "/etc/kubernetes/image-credential-provider-config.yaml", + ), + gomega.HaveKeyWithValue( + "path", "/etc/kubernetes/dynamic-credential-provider-config.yaml", + ), + ), + }, + { + Operation: "add", + Path: "/spec/template/spec/kubeadmConfigSpec/preKubeadmCommands", + ValueMatcher: gomega.ContainElement( + "/bin/bash /etc/cre/install-kubelet-credential-providers.sh", + ), + }, + { + Operation: "add", + Path: "/spec/template/spec/kubeadmConfigSpec/initConfiguration/nodeRegistration/kubeletExtraArgs", + ValueMatcher: gomega.HaveKeyWithValue( + "image-credential-provider-bin-dir", + "/etc/kubernetes/image-credential-provider/", + ), + }, + { + Operation: "add", + Path: "/spec/template/spec/kubeadmConfigSpec/joinConfiguration/nodeRegistration/kubeletExtraArgs", + ValueMatcher: gomega.HaveKeyWithValue( + "image-credential-provider-config", + "/etc/kubernetes/image-credential-provider-config.yaml", + ), + }, + }, + }, + { + Name: "files added in KubeadmControlPlaneTemplate for registry with a Secret", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + clusterconfig.MetaVariableName, + v1alpha1.ImageRegistries{ + v1alpha1.ImageRegistry{ + URL: "https://registry.example.com", + Credentials: &v1alpha1.RegistryCredentials{ + SecretRef: &corev1.LocalObjectReference{ + Name: validSecretName, + }, + }, + }, + }, + imageregistries.VariableName, + ), + }, + RequestItem: request.NewKubeadmControlPlaneTemplateRequest( + "", + "test-kubeadmconfigtemplate", + ), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{ + { + Operation: "add", + Path: "/spec/template/spec/kubeadmConfigSpec/files", + ValueMatcher: gomega.ContainElements( + gomega.HaveKeyWithValue( + "path", "/etc/cre/install-kubelet-credential-providers.sh", + ), + gomega.HaveKeyWithValue( + "path", "/etc/kubernetes/image-credential-provider-config.yaml", + ), + gomega.HaveKeyWithValue( + "path", "/etc/kubernetes/dynamic-credential-provider-config.yaml", + ), + gomega.HaveKeyWithValue( + "path", "/etc/kubernetes/static-image-credentials.json", + ), + ), + }, + { + Operation: "add", + Path: "/spec/template/spec/kubeadmConfigSpec/preKubeadmCommands", + ValueMatcher: gomega.ContainElement( + "/bin/bash /etc/cre/install-kubelet-credential-providers.sh", + ), + }, + { + Operation: "add", + Path: "/spec/template/spec/kubeadmConfigSpec/initConfiguration/nodeRegistration/kubeletExtraArgs", + ValueMatcher: gomega.HaveKeyWithValue( + "image-credential-provider-bin-dir", + "/etc/kubernetes/image-credential-provider/", + ), + }, + { + Operation: "add", + Path: "/spec/template/spec/kubeadmConfigSpec/joinConfiguration/nodeRegistration/kubeletExtraArgs", + ValueMatcher: gomega.HaveKeyWithValue( + "image-credential-provider-config", + "/etc/kubernetes/image-credential-provider-config.yaml", + ), + }, + }, + }, + { + Name: "files added in KubeadmConfigTemplate for ECR without a Secret", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + clusterconfig.MetaVariableName, + v1alpha1.ImageRegistries{ + v1alpha1.ImageRegistry{ + URL: "https://123456789.dkr.ecr.us-east-1.amazonaws.com", + }, + }, + imageregistries.VariableName, + ), + capitest.VariableWithValue( + "builtin", + map[string]any{ + "machineDeployment": map[string]any{ + "class": names.SimpleNameGenerator.GenerateName("worker-"), + }, + }, + ), + }, + RequestItem: request.NewKubeadmConfigTemplateRequestItem(""), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{ + { + Operation: "add", + Path: "/spec/template/spec/files", + ValueMatcher: gomega.ContainElements( + gomega.HaveKeyWithValue( + "path", "/etc/cre/install-kubelet-credential-providers.sh", + ), + gomega.HaveKeyWithValue( + "path", "/etc/kubernetes/image-credential-provider-config.yaml", + ), + gomega.HaveKeyWithValue( + "path", "/etc/kubernetes/dynamic-credential-provider-config.yaml", + ), + ), + }, + { + Operation: "add", + Path: "/spec/template/spec/preKubeadmCommands", + ValueMatcher: gomega.ContainElement( + "/bin/bash /etc/cre/install-kubelet-credential-providers.sh", + ), + }, + { + Operation: "add", + Path: "/spec/template/spec/joinConfiguration/nodeRegistration/kubeletExtraArgs", + ValueMatcher: gomega.HaveKeyWithValue( + "image-credential-provider-bin-dir", + "/etc/kubernetes/image-credential-provider/", + ), + }, + }, + }, + { + Name: "files added in KubeadmConfigTemplate for registry with a Secret", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + clusterconfig.MetaVariableName, + v1alpha1.ImageRegistries{ + v1alpha1.ImageRegistry{ + URL: "https://registry.example.com", + Credentials: &v1alpha1.RegistryCredentials{ + SecretRef: &corev1.LocalObjectReference{ + Name: validSecretName, + }, + }, + }, + }, + imageregistries.VariableName, + ), + capitest.VariableWithValue( + "builtin", + map[string]any{ + "machineDeployment": map[string]any{ + "class": names.SimpleNameGenerator.GenerateName("worker-"), + }, + }, + ), + }, + RequestItem: request.NewKubeadmConfigTemplateRequest("", "test-kubeadmconfigtemplate"), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{ + { + Operation: "add", + Path: "/spec/template/spec/files", + ValueMatcher: gomega.ContainElements( + gomega.HaveKeyWithValue( + "path", "/etc/cre/install-kubelet-credential-providers.sh", + ), + gomega.HaveKeyWithValue( + "path", "/etc/kubernetes/image-credential-provider-config.yaml", + ), + gomega.HaveKeyWithValue( + "path", "/etc/kubernetes/dynamic-credential-provider-config.yaml", + ), + gomega.HaveKeyWithValue( + "path", "/etc/kubernetes/static-image-credentials.json", + ), + ), + }, + { + Operation: "add", + Path: "/spec/template/spec/preKubeadmCommands", + ValueMatcher: gomega.ContainElement( + "/bin/bash /etc/cre/install-kubelet-credential-providers.sh", + ), + }, + { + Operation: "add", + Path: "/spec/template/spec/joinConfiguration/nodeRegistration/kubeletExtraArgs", + ValueMatcher: gomega.HaveKeyWithValue( + "image-credential-provider-bin-dir", + "/etc/kubernetes/image-credential-provider/", + ), + }, + }, + }, + { + Name: "error for a registry with no credentials", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + clusterconfig.MetaVariableName, + v1alpha1.ImageRegistries{ + v1alpha1.ImageRegistry{ + URL: "https://registry.example.com", + }, + }, + imageregistries.VariableName, + ), + }, + RequestItem: request.NewKubeadmControlPlaneTemplateRequestItem(""), + ExpectedFailure: true, + }, + } + + // Create credentials secret before each test + BeforeEach(func(ctx SpecContext) { + client, err := helpers.TestEnv.GetK8sClient() + gomega.Expect(err).To(gomega.BeNil()) + gomega.Expect(client.Create( + ctx, + newRegistryCredentialsSecret(validSecretName, request.Namespace), + )).To(gomega.BeNil()) + }) + + // Delete credentials secret after each test + AfterEach(func(ctx SpecContext) { + client, err := helpers.TestEnv.GetK8sClient() + gomega.Expect(err).To(gomega.BeNil()) + gomega.Expect(client.Delete( + ctx, + newRegistryCredentialsSecret(validSecretName, request.Namespace), + )).To(gomega.BeNil()) + }) + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches(GinkgoT(), patchGenerator, &tt) + }) + } +}) + +func newRegistryCredentialsSecret(name, namespace string) *corev1.Secret { + secretData := map[string][]byte{ + "username": []byte("myuser"), + "password": []byte("mypassword"), + } + return &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Data: secretData, + Type: corev1.SecretTypeOpaque, + } +} diff --git a/pkg/handlers/generic/mutation/imageregistries/credentials/tests/generate_patches.go b/pkg/handlers/generic/mutation/imageregistries/credentials/tests/generate_patches.go deleted file mode 100644 index 05b797312..000000000 --- a/pkg/handlers/generic/mutation/imageregistries/credentials/tests/generate_patches.go +++ /dev/null @@ -1,327 +0,0 @@ -// Copyright 2023 D2iQ, Inc. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package tests - -import ( - "context" - "testing" - - "github.com/onsi/gomega" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apiserver/pkg/storage/names" - runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" - "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/capi/clustertopology/handlers/mutation" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" -) - -const ( - validSecretName = "myregistry-credentials" - registryStaticCredentialsSecretSuffix = "registry-creds" -) - -func TestGeneratePatches( - t *testing.T, - generatorFunc func() mutation.GeneratePatches, - fakeClient client.Client, - variableName string, - variablePath ...string, -) { - t.Helper() - - require.NoError( - t, - fakeClient.Create( - context.Background(), - newRegistryCredentialsSecret(validSecretName, request.Namespace), - ), - ) - - capitest.ValidateGeneratePatches( - t, - generatorFunc, - capitest.PatchTestDef{ - Name: "unset variable", - }, - capitest.PatchTestDef{ - Name: "files added in KubeadmControlPlaneTemplate for ECR without a Secret", - Vars: []runtimehooksv1.Variable{ - capitest.VariableWithValue( - variableName, - v1alpha1.ImageRegistries{ - v1alpha1.ImageRegistry{ - URL: "https://123456789.dkr.ecr.us-east-1.amazonaws.com", - }, - }, - variablePath..., - ), - }, - RequestItem: request.NewKubeadmControlPlaneTemplateRequestItem(""), - ExpectedPatchMatchers: []capitest.JSONPatchMatcher{ - { - Operation: "add", - Path: "/spec/template/spec/kubeadmConfigSpec/files", - ValueMatcher: gomega.ContainElements( - gomega.HaveKeyWithValue( - "path", "/etc/cre/install-kubelet-credential-providers.sh", - ), - gomega.HaveKeyWithValue( - "path", "/etc/kubernetes/image-credential-provider-config.yaml", - ), - gomega.HaveKeyWithValue( - "path", "/etc/kubernetes/dynamic-credential-provider-config.yaml", - ), - ), - }, - { - Operation: "add", - Path: "/spec/template/spec/kubeadmConfigSpec/preKubeadmCommands", - ValueMatcher: gomega.ContainElement( - "/bin/bash /etc/cre/install-kubelet-credential-providers.sh", - ), - }, - { - Operation: "add", - Path: "/spec/template/spec/kubeadmConfigSpec/initConfiguration/nodeRegistration/kubeletExtraArgs", - ValueMatcher: gomega.HaveKeyWithValue( - "image-credential-provider-bin-dir", - "/etc/kubernetes/image-credential-provider/", - ), - }, - { - Operation: "add", - Path: "/spec/template/spec/kubeadmConfigSpec/joinConfiguration/nodeRegistration/kubeletExtraArgs", - ValueMatcher: gomega.HaveKeyWithValue( - "image-credential-provider-config", - "/etc/kubernetes/image-credential-provider-config.yaml", - ), - }, - }, - }, - capitest.PatchTestDef{ - Name: "files added in KubeadmControlPlaneTemplate for registry with a Secret", - Vars: []runtimehooksv1.Variable{ - capitest.VariableWithValue( - variableName, - v1alpha1.ImageRegistries{ - v1alpha1.ImageRegistry{ - URL: "https://registry.example.com", - Credentials: &v1alpha1.RegistryCredentials{ - SecretRef: &corev1.LocalObjectReference{ - Name: validSecretName, - }, - }, - }, - }, - variablePath..., - ), - }, - RequestItem: request.NewKubeadmControlPlaneTemplateRequest( - "", - "test-kubeadmconfigtemplate", - ), - ExpectedPatchMatchers: []capitest.JSONPatchMatcher{ - { - Operation: "add", - Path: "/spec/template/spec/kubeadmConfigSpec/files", - ValueMatcher: gomega.ContainElements( - gomega.HaveKeyWithValue( - "path", "/etc/cre/install-kubelet-credential-providers.sh", - ), - gomega.HaveKeyWithValue( - "path", "/etc/kubernetes/image-credential-provider-config.yaml", - ), - gomega.HaveKeyWithValue( - "path", "/etc/kubernetes/dynamic-credential-provider-config.yaml", - ), - gomega.HaveKeyWithValue( - "path", "/etc/kubernetes/static-image-credentials.json", - ), - ), - }, - { - Operation: "add", - Path: "/spec/template/spec/kubeadmConfigSpec/preKubeadmCommands", - ValueMatcher: gomega.ContainElement( - "/bin/bash /etc/cre/install-kubelet-credential-providers.sh", - ), - }, - { - Operation: "add", - Path: "/spec/template/spec/kubeadmConfigSpec/initConfiguration/nodeRegistration/kubeletExtraArgs", - ValueMatcher: gomega.HaveKeyWithValue( - "image-credential-provider-bin-dir", - "/etc/kubernetes/image-credential-provider/", - ), - }, - { - Operation: "add", - Path: "/spec/template/spec/kubeadmConfigSpec/joinConfiguration/nodeRegistration/kubeletExtraArgs", - ValueMatcher: gomega.HaveKeyWithValue( - "image-credential-provider-config", - "/etc/kubernetes/image-credential-provider-config.yaml", - ), - }, - }, - }, - capitest.PatchTestDef{ - Name: "files added in KubeadmConfigTemplate for ECR without a Secret", - Vars: []runtimehooksv1.Variable{ - capitest.VariableWithValue( - variableName, - v1alpha1.ImageRegistries{ - v1alpha1.ImageRegistry{ - URL: "https://123456789.dkr.ecr.us-east-1.amazonaws.com", - }, - }, - variablePath..., - ), - capitest.VariableWithValue( - "builtin", - map[string]any{ - "machineDeployment": map[string]any{ - "class": names.SimpleNameGenerator.GenerateName("worker-"), - }, - }, - ), - }, - RequestItem: request.NewKubeadmConfigTemplateRequestItem(""), - ExpectedPatchMatchers: []capitest.JSONPatchMatcher{ - { - Operation: "add", - Path: "/spec/template/spec/files", - ValueMatcher: gomega.ContainElements( - gomega.HaveKeyWithValue( - "path", "/etc/cre/install-kubelet-credential-providers.sh", - ), - gomega.HaveKeyWithValue( - "path", "/etc/kubernetes/image-credential-provider-config.yaml", - ), - gomega.HaveKeyWithValue( - "path", "/etc/kubernetes/dynamic-credential-provider-config.yaml", - ), - ), - }, - { - Operation: "add", - Path: "/spec/template/spec/preKubeadmCommands", - ValueMatcher: gomega.ContainElement( - "/bin/bash /etc/cre/install-kubelet-credential-providers.sh", - ), - }, - { - Operation: "add", - Path: "/spec/template/spec/joinConfiguration/nodeRegistration/kubeletExtraArgs", - ValueMatcher: gomega.HaveKeyWithValue( - "image-credential-provider-bin-dir", - "/etc/kubernetes/image-credential-provider/", - ), - }, - }, - }, - capitest.PatchTestDef{ - Name: "files added in KubeadmConfigTemplate for registry with a Secret", - Vars: []runtimehooksv1.Variable{ - capitest.VariableWithValue( - variableName, - v1alpha1.ImageRegistries{ - v1alpha1.ImageRegistry{ - URL: "https://registry.example.com", - Credentials: &v1alpha1.RegistryCredentials{ - SecretRef: &corev1.LocalObjectReference{ - Name: validSecretName, - }, - }, - }, - }, - variablePath..., - ), - capitest.VariableWithValue( - "builtin", - map[string]any{ - "machineDeployment": map[string]any{ - "class": names.SimpleNameGenerator.GenerateName("worker-"), - }, - }, - ), - }, - RequestItem: request.NewKubeadmConfigTemplateRequest("", "test-kubeadmconfigtemplate"), - ExpectedPatchMatchers: []capitest.JSONPatchMatcher{ - { - Operation: "add", - Path: "/spec/template/spec/files", - ValueMatcher: gomega.ContainElements( - gomega.HaveKeyWithValue( - "path", "/etc/cre/install-kubelet-credential-providers.sh", - ), - gomega.HaveKeyWithValue( - "path", "/etc/kubernetes/image-credential-provider-config.yaml", - ), - gomega.HaveKeyWithValue( - "path", "/etc/kubernetes/dynamic-credential-provider-config.yaml", - ), - gomega.HaveKeyWithValue( - "path", "/etc/kubernetes/static-image-credentials.json", - ), - ), - }, - { - Operation: "add", - Path: "/spec/template/spec/preKubeadmCommands", - ValueMatcher: gomega.ContainElement( - "/bin/bash /etc/cre/install-kubelet-credential-providers.sh", - ), - }, - { - Operation: "add", - Path: "/spec/template/spec/joinConfiguration/nodeRegistration/kubeletExtraArgs", - ValueMatcher: gomega.HaveKeyWithValue( - "image-credential-provider-bin-dir", - "/etc/kubernetes/image-credential-provider/", - ), - }, - }, - }, - capitest.PatchTestDef{ - Name: "error for a registry with no credentials", - Vars: []runtimehooksv1.Variable{ - capitest.VariableWithValue( - variableName, - v1alpha1.ImageRegistries{ - v1alpha1.ImageRegistry{ - URL: "https://registry.example.com", - }, - }, - variablePath..., - ), - }, - RequestItem: request.NewKubeadmControlPlaneTemplateRequestItem(""), - ExpectedFailure: true, - }, - ) -} - -func newRegistryCredentialsSecret(name, namespace string) *corev1.Secret { - secretData := map[string][]byte{ - "username": []byte("myuser"), - "password": []byte("mypassword"), - } - return &corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "Secret", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Data: secretData, - Type: corev1.SecretTypeOpaque, - } -} diff --git a/pkg/handlers/generic/mutation/kubernetesimagerepository/tests/generate_patches.go b/pkg/handlers/generic/mutation/kubernetesimagerepository/inject_test.go similarity index 58% rename from pkg/handlers/generic/mutation/kubernetesimagerepository/tests/generate_patches.go rename to pkg/handlers/generic/mutation/kubernetesimagerepository/inject_test.go index eefe534c2..e29d47aa7 100644 --- a/pkg/handlers/generic/mutation/kubernetesimagerepository/tests/generate_patches.go +++ b/pkg/handlers/generic/mutation/kubernetesimagerepository/inject_test.go @@ -1,11 +1,12 @@ // Copyright 2023 D2iQ, Inc. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -package tests +package kubernetesimagerepository import ( "testing" + . "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" @@ -13,29 +14,30 @@ import ( "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" ) -func TestGeneratePatches( - t *testing.T, - generatorFunc func() mutation.GeneratePatches, - variableName string, - variablePath ...string, -) { - t.Helper() - - capitest.ValidateGeneratePatches( - t, - generatorFunc, - capitest.PatchTestDef{ +func TestKubernetesImageRepositoryPatch(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "Kubernetes Image Repository mutator suite") +} + +var _ = Describe("Generate Kubernetes Image Repository patches", func() { + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewPatch()).(mutation.GeneratePatches) + } + + testDefs := []capitest.PatchTestDef{ + { Name: "unset variable", }, - capitest.PatchTestDef{ + { Name: "kubernetesImageRepository set", Vars: []runtimehooksv1.Variable{ capitest.VariableWithValue( - variableName, + clusterconfig.MetaVariableName, v1alpha1.KubernetesImageRepository("my-registry.io/my-org/my-repo"), - variablePath..., + VariableName, ), }, RequestItem: request.NewKubeadmControlPlaneTemplateRequestItem(""), @@ -48,5 +50,13 @@ func TestGeneratePatches( ), }}, }, - ) -} + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches(GinkgoT(), patchGenerator, &tt) + }) + } +}) diff --git a/pkg/handlers/generic/mutation/mirrors/inject.go b/pkg/handlers/generic/mutation/mirrors/inject.go index a1e5e8e3b..42ad077d0 100644 --- a/pkg/handlers/generic/mutation/mirrors/inject.go +++ b/pkg/handlers/generic/mutation/mirrors/inject.go @@ -191,10 +191,6 @@ func generateFilesAndCommands( } files = append(files, applyPatchesFile...) commands = append(commands, applyPatchesCommand) - // generate Containerd restart script and command - restartFile, restartCommand := generateContainerdRestartScript() - files = append(files, restartFile...) - commands = append(commands, restartCommand) return files, commands, err } diff --git a/pkg/handlers/generic/mutation/mirrors/tests/generate_patches.go b/pkg/handlers/generic/mutation/mirrors/inject_test.go similarity index 75% rename from pkg/handlers/generic/mutation/mirrors/tests/generate_patches.go rename to pkg/handlers/generic/mutation/mirrors/inject_test.go index dd3a040a2..24ca2ae17 100644 --- a/pkg/handlers/generic/mutation/mirrors/tests/generate_patches.go +++ b/pkg/handlers/generic/mutation/mirrors/inject_test.go @@ -1,24 +1,24 @@ // Copyright 2023 D2iQ, Inc. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -package tests +package mirrors import ( - "context" "testing" + . "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" - "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apiserver/pkg/storage/names" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" - "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/capi/clustertopology/handlers/mutation" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/test/helpers" ) const ( @@ -29,35 +29,35 @@ const ( workerRegistryAsMirrorCreds = "kubeadmConfigTemplateRegistryAsMirrorCreds" ) -func TestGeneratePatches( - t *testing.T, - generatorFunc func() mutation.GeneratePatches, - fakeClient client.Client, - variableName string, - variablePath ...string, -) { - t.Helper() +func TestMirrorsPatch(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "Global mirror mutator suite") +} - require.NoError( - t, - fakeClient.Create( - context.Background(), - newMirrorSecret(validMirrorCASecretName, request.Namespace), - ), - ) +var _ = Describe("Generate Global mirror patches", func() { + patchGenerator := func() mutation.GeneratePatches { + // Always initialize the testEnv variable in the closure. + // This will allow ginkgo to initialize testEnv variable during test execution time. + testEnv := helpers.TestEnv + // use direct client instead of controller client. This will allow the patch handler to read k8s object + // that are written by the tests. + // Test cases writes credentials secret that the mutator handler reads. + // Using direct client will enable reading it immediately. + client, err := testEnv.GetK8sClient() + gomega.Expect(err).To(gomega.BeNil()) + return mutation.NewMetaGeneratePatchesHandler("", NewPatch(client)).(mutation.GeneratePatches) + } - capitest.ValidateGeneratePatches( - t, - generatorFunc, - capitest.PatchTestDef{ + testDefs := []capitest.PatchTestDef{ + { Name: "files added in KubeadmControlPlaneTemplate for registry with mirror without CA Certificate", Vars: []runtimehooksv1.Variable{ capitest.VariableWithValue( - variableName, + clusterconfig.MetaVariableName, v1alpha1.GlobalImageRegistryMirror{ URL: "https://123456789.dkr.ecr.us-east-1.amazonaws.com", }, - variablePath..., + GlobalMirrorVariableName, ), }, RequestItem: request.NewKubeadmControlPlaneTemplateRequestItem(""), @@ -75,9 +75,6 @@ func TestGeneratePatches( gomega.HaveKeyWithValue( "path", "/etc/containerd/apply-patches.sh", ), - gomega.HaveKeyWithValue( - "path", "/etc/containerd/restart.sh", - ), ), }, { @@ -85,16 +82,15 @@ func TestGeneratePatches( Path: "/spec/template/spec/kubeadmConfigSpec/preKubeadmCommands", ValueMatcher: gomega.ContainElements( "/bin/bash /etc/containerd/apply-patches.sh", - "/bin/bash /etc/containerd/restart.sh", ), }, }, }, - capitest.PatchTestDef{ + { Name: "files added in KubeadmControlPlaneTemplate for registry with mirror with CA Certificate", Vars: []runtimehooksv1.Variable{ capitest.VariableWithValue( - variableName, + clusterconfig.MetaVariableName, v1alpha1.GlobalImageRegistryMirror{ URL: "https://registry.example.com", Credentials: &v1alpha1.RegistryCredentials{ @@ -103,7 +99,7 @@ func TestGeneratePatches( }, }, }, - variablePath..., + GlobalMirrorVariableName, ), }, RequestItem: request.NewKubeadmControlPlaneTemplateRequest("", cpRegistryAsMirrorCreds), @@ -124,9 +120,6 @@ func TestGeneratePatches( gomega.HaveKeyWithValue( "path", "/etc/containerd/apply-patches.sh", ), - gomega.HaveKeyWithValue( - "path", "/etc/containerd/restart.sh", - ), ), }, { @@ -134,20 +127,19 @@ func TestGeneratePatches( Path: "/spec/template/spec/kubeadmConfigSpec/preKubeadmCommands", ValueMatcher: gomega.ContainElements( "/bin/bash /etc/containerd/apply-patches.sh", - "/bin/bash /etc/containerd/restart.sh", ), }, }, }, - capitest.PatchTestDef{ + { Name: "files added in KubeadmConfigTemplate for registry mirror wihthout CA certificate", Vars: []runtimehooksv1.Variable{ capitest.VariableWithValue( - variableName, + clusterconfig.MetaVariableName, v1alpha1.GlobalImageRegistryMirror{ URL: "https://123456789.dkr.ecr.us-east-1.amazonaws.com", }, - variablePath..., + GlobalMirrorVariableName, ), capitest.VariableWithValue( "builtin", @@ -173,9 +165,6 @@ func TestGeneratePatches( gomega.HaveKeyWithValue( "path", "/etc/containerd/apply-patches.sh", ), - gomega.HaveKeyWithValue( - "path", "/etc/containerd/restart.sh", - ), ), }, { @@ -183,16 +172,15 @@ func TestGeneratePatches( Path: "/spec/template/spec/preKubeadmCommands", ValueMatcher: gomega.ContainElements( "/bin/bash /etc/containerd/apply-patches.sh", - "/bin/bash /etc/containerd/restart.sh", ), }, }, }, - capitest.PatchTestDef{ + { Name: "files added in KubeadmConfigTemplate for registry mirror with secret for CA certificate", Vars: []runtimehooksv1.Variable{ capitest.VariableWithValue( - variableName, + clusterconfig.MetaVariableName, v1alpha1.GlobalImageRegistryMirror{ URL: "https://registry.example.com", Credentials: &v1alpha1.RegistryCredentials{ @@ -201,7 +189,7 @@ func TestGeneratePatches( }, }, }, - variablePath..., + GlobalMirrorVariableName, ), capitest.VariableWithValue( "builtin", @@ -230,9 +218,6 @@ func TestGeneratePatches( gomega.HaveKeyWithValue( "path", "/etc/containerd/apply-patches.sh", ), - gomega.HaveKeyWithValue( - "path", "/etc/containerd/restart.sh", - ), ), }, { @@ -240,13 +225,40 @@ func TestGeneratePatches( Path: "/spec/template/spec/preKubeadmCommands", ValueMatcher: gomega.ContainElements( "/bin/bash /etc/containerd/apply-patches.sh", - "/bin/bash /etc/containerd/restart.sh", ), }, }, }, - ) -} + } + + // Create credentials secret before each test + BeforeEach(func(ctx SpecContext) { + client, err := helpers.TestEnv.GetK8sClient() + gomega.Expect(err).To(gomega.BeNil()) + gomega.Expect(client.Create( + ctx, + newMirrorSecret(validMirrorCASecretName, request.Namespace), + )).To(gomega.BeNil()) + }) + + // Delete credentials secret after each test + AfterEach(func(ctx SpecContext) { + client, err := helpers.TestEnv.GetK8sClient() + gomega.Expect(err).To(gomega.BeNil()) + gomega.Expect(client.Delete( + ctx, + newMirrorSecret(validMirrorCASecretName, request.Namespace), + )).To(gomega.BeNil()) + }) + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches(GinkgoT(), patchGenerator, &tt) + }) + } +}) func newMirrorSecret(name, namespace string) *corev1.Secret { secretData := map[string][]byte{ diff --git a/pkg/handlers/generic/mutation/mirrors/mirror.go b/pkg/handlers/generic/mutation/mirrors/mirror.go index ff14c2b62..8ab281f9c 100644 --- a/pkg/handlers/generic/mutation/mirrors/mirror.go +++ b/pkg/handlers/generic/mutation/mirrors/mirror.go @@ -28,9 +28,6 @@ const ( containerdPatchesDirOnRemote = "/etc/containerd/cre.d" containerdApplyPatchesScriptOnRemote = "/etc/containerd/apply-patches.sh" containerdApplyPatchesScriptOnRemoteCommand = "/bin/bash " + containerdApplyPatchesScriptOnRemote - - containerdRestartScriptOnRemote = "/etc/containerd/restart.sh" - containerdRestartScriptOnRemoteCommand = "/bin/bash " + containerdRestartScriptOnRemote ) var ( @@ -50,9 +47,6 @@ var ( //go:embed templates/containerd-apply-patches.sh.gotmpl containerdApplyConfigPatchesScript []byte - - //go:embed templates/containerd-restart.sh - containerdRestartScript []byte ) type mirrorConfig struct { @@ -226,14 +220,3 @@ func generateContainerdApplyPatchesScript() ([]cabpkv1.File, string, error) { }, }, containerdApplyPatchesScriptOnRemoteCommand, nil } - -//nolint:gocritic // no need for named return values -func generateContainerdRestartScript() ([]cabpkv1.File, string) { - return []cabpkv1.File{ - { - Path: containerdRestartScriptOnRemote, - Content: string(containerdRestartScript), - Permissions: "0700", - }, - }, containerdRestartScriptOnRemoteCommand -} diff --git a/pkg/handlers/generic/mutation/users/doc.go b/pkg/handlers/generic/mutation/users/doc.go new file mode 100644 index 000000000..4584d2d23 --- /dev/null +++ b/pkg/handlers/generic/mutation/users/doc.go @@ -0,0 +1,4 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package users diff --git a/pkg/handlers/generic/mutation/users/inject.go b/pkg/handlers/generic/mutation/users/inject.go new file mode 100644 index 000000000..9d41e0495 --- /dev/null +++ b/pkg/handlers/generic/mutation/users/inject.go @@ -0,0 +1,147 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package users + +import ( + "context" + + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/utils/ptr" + bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" + controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + 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/capi/clustertopology/patches" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches/selectors" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/variables" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" +) + +const ( + // VariableName is the external patch variable name. + VariableName = "users" +) + +type usersPatchHandler struct { + variableName string + variableFieldPath []string +} + +func NewPatch() *usersPatchHandler { + return newUsersPatchHandler( + clusterconfig.MetaVariableName, + VariableName) +} + +func newUsersPatchHandler( + variableName string, + variableFieldPath ...string, +) *usersPatchHandler { + return &usersPatchHandler{ + variableName: variableName, + variableFieldPath: variableFieldPath, + } +} + +func (h *usersPatchHandler) Mutate( + ctx context.Context, + obj *unstructured.Unstructured, + vars map[string]apiextensionsv1.JSON, + holderRef runtimehooksv1.HolderReference, + _ ctrlclient.ObjectKey, +) error { + log := ctrl.LoggerFrom(ctx, "holderRef", holderRef) + + usersVariable, found, err := variables.Get[v1alpha1.Users]( + vars, + h.variableName, + h.variableFieldPath..., + ) + if err != nil { + return err + } + if !found { + log.V(5).Info("users variable not defined") + return nil + } + + log = log.WithValues( + "variableName", + h.variableName, + "variableFieldPath", + h.variableFieldPath, + "variableValue", + usersVariable, + ) + + if err := patches.MutateIfApplicable( + obj, vars, &holderRef, selectors.ControlPlane(), log, + func(obj *controlplanev1.KubeadmControlPlaneTemplate) error { + log.WithValues( + "patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(), + "patchedObjectName", ctrlclient.ObjectKeyFromObject(obj), + ).Info("setting users in control plane kubeadm config template") + bootstrapUsers := []bootstrapv1.User{} + for _, userFromVariable := range usersVariable { + bootstrapUsers = append(bootstrapUsers, generateBootstrapUser(userFromVariable)) + } + obj.Spec.Template.Spec.KubeadmConfigSpec.Users = bootstrapUsers + return nil + }); err != nil { + return err + } + + if err := patches.MutateIfApplicable( + obj, vars, &holderRef, selectors.WorkersKubeadmConfigTemplateSelector(), log, + func(obj *bootstrapv1.KubeadmConfigTemplate) error { + log.WithValues( + "patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(), + "patchedObjectName", ctrlclient.ObjectKeyFromObject(obj), + ).Info("setting users in worker node kubeadm config template") + bootstrapUsers := []bootstrapv1.User{} + for _, userFromVariable := range usersVariable { + bootstrapUsers = append(bootstrapUsers, generateBootstrapUser(userFromVariable)) + } + obj.Spec.Template.Spec.Users = bootstrapUsers + return nil + }); err != nil { + return err + } + + return nil +} + +func generateBootstrapUser(userFromVariable v1alpha1.User) bootstrapv1.User { + bootstrapUser := bootstrapv1.User{ + Name: userFromVariable.Name, + SSHAuthorizedKeys: userFromVariable.SSHAuthorizedKeys, + } + + // LockPassword is not part of our API, because we can derive its value + // for the use cases our API supports. + // + // We do not support these edge cases: + // (a) Hashed password is defined, password authentication is not enabled. + // (b) Hashed password is not defined, password authentication is enabled. + // + // We disable password authentication by default. + bootstrapUser.LockPassword = ptr.To(true) + + if userFromVariable.HashedPassword != "" { + // We enable password authentication only if a hashed password is defined. + bootstrapUser.LockPassword = ptr.To(false) + + bootstrapUser.Passwd = ptr.To(userFromVariable.HashedPassword) + } + + if userFromVariable.Sudo != "" { + bootstrapUser.Sudo = ptr.To(userFromVariable.Sudo) + } + + return bootstrapUser +} diff --git a/pkg/handlers/generic/mutation/users/inject_test.go b/pkg/handlers/generic/mutation/users/inject_test.go new file mode 100644 index 000000000..924321800 --- /dev/null +++ b/pkg/handlers/generic/mutation/users/inject_test.go @@ -0,0 +1,204 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package users + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + . "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + "k8s.io/apiserver/pkg/storage/names" + "k8s.io/utils/ptr" + bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" +) + +func Test_generateBootstrapUser(t *testing.T) { + type args struct { + userFromVariable v1alpha1.User + } + tests := []struct { + name string + args args + want bootstrapv1.User + }{ + { + name: "if user sets hashed password, enable password auth and set passwd", + args: args{ + userFromVariable: v1alpha1.User{ + Name: "example", + HashedPassword: "example", + }, + }, + want: bootstrapv1.User{ + Name: "example", + Passwd: ptr.To("example"), + LockPassword: ptr.To(false), + }, + }, + { + name: "if user does not set hashed password, disable password auth and do not set passwd", + args: args{ + userFromVariable: v1alpha1.User{ + Name: "example", + }, + }, + want: bootstrapv1.User{ + Name: "example", + Passwd: nil, + LockPassword: ptr.To(true), + }, + }, + { + name: "if user sets empty hashed password, disable password auth and do not set passwd", + args: args{ + userFromVariable: v1alpha1.User{ + Name: "example", + HashedPassword: "", + }, + }, + want: bootstrapv1.User{ + Name: "example", + Passwd: nil, + LockPassword: ptr.To(true), + }, + }, + { + name: "if user sets sudo, include it in the patch", + args: args{ + userFromVariable: v1alpha1.User{ + Name: "example", + Sudo: "example", + }, + }, + want: bootstrapv1.User{ + Name: "example", + Sudo: ptr.To("example"), + LockPassword: ptr.To(true), + }, + }, + { + name: "if user does not set sudo, do not include in the patch", + args: args{ + userFromVariable: v1alpha1.User{ + Name: "example", + }, + }, + want: bootstrapv1.User{ + Name: "example", + Sudo: nil, + LockPassword: ptr.To(true), + }, + }, + { + name: "if user sets empty sudo, do not include in the patch", + args: args{ + userFromVariable: v1alpha1.User{ + Name: "example", + Sudo: "", + }, + }, + want: bootstrapv1.User{ + Name: "example", + Sudo: nil, + LockPassword: ptr.To(true), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := generateBootstrapUser(tt.args.userFromVariable) + if diff := cmp.Diff(tt.want, got); diff != "" { + t.Errorf("generateBootstrapUser() mismatch (-want +got):\n%s", diff) + } + }) + } +} + +var ( + testUser1 = v1alpha1.User{ + Name: "complete", + HashedPassword: "password", + SSHAuthorizedKeys: []string{ + "key1", + "key2", + }, + Sudo: "ALL=(ALL) NOPASSWD:ALL", + } + testUser2 = v1alpha1.User{ + Name: "onlyname", + } +) + +func TestUsersPatch(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "Users mutator suite") +} + +var _ = Describe("Generate Users patches", func() { + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewPatch()).(mutation.GeneratePatches) + } + + testDefs := []capitest.PatchTestDef{ + { + Name: "unset variable", + }, + { + Name: "users set for KubeadmControlPlaneTemplate", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + clusterconfig.MetaVariableName, + []v1alpha1.User{testUser1, testUser2}, + VariableName, + ), + }, + RequestItem: request.NewKubeadmControlPlaneTemplateRequestItem(""), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{ + Operation: "add", + Path: "/spec/template/spec/kubeadmConfigSpec/users", + ValueMatcher: gomega.HaveLen(2), + }}, + }, + { + Name: "users set for KubeadmConfigTemplate generic worker", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + clusterconfig.MetaVariableName, + []v1alpha1.User{testUser1, testUser2}, + VariableName, + ), + capitest.VariableWithValue( + "builtin", + map[string]any{ + "machineDeployment": map[string]any{ + "class": names.SimpleNameGenerator.GenerateName("worker-"), + }, + }, + ), + }, + RequestItem: request.NewKubeadmConfigTemplateRequestItem(""), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{ + Operation: "add", + Path: "/spec/template/spec/users", + ValueMatcher: gomega.HaveLen(2), + }}, + }, + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches(GinkgoT(), patchGenerator, &tt) + }) + } +}) diff --git a/pkg/handlers/generic/mutation/users/variables_test.go b/pkg/handlers/generic/mutation/users/variables_test.go new file mode 100644 index 000000000..227318484 --- /dev/null +++ b/pkg/handlers/generic/mutation/users/variables_test.go @@ -0,0 +1,43 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package users + +import ( + "testing" + + "k8s.io/utils/ptr" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" +) + +func TestVariableValidation(t *testing.T) { + capitest.ValidateDiscoverVariables( + t, + clusterconfig.MetaVariableName, + ptr.To(v1alpha1.GenericClusterConfig{}.VariableSchema()), + false, + clusterconfig.NewVariable, + capitest.VariableTestDef{ + Name: "valid users", + Vals: v1alpha1.GenericClusterConfig{ + Users: []v1alpha1.User{ + { + Name: "complete", + HashedPassword: "password", + SSHAuthorizedKeys: []string{ + "key1", + "key2", + }, + Sudo: "ALL=(ALL) NOPASSWD:ALL", + }, + { + Name: "onlyname", + }, + }, + }, + }, + ) +} diff --git a/pkg/handlers/nutanix/clusterconfig/variables.go b/pkg/handlers/nutanix/clusterconfig/variables.go new file mode 100644 index 000000000..cebff93c6 --- /dev/null +++ b/pkg/handlers/nutanix/clusterconfig/variables.go @@ -0,0 +1,52 @@ +// Copyright 2024 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package clusterconfig + +import ( + "context" + + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + commonhandlers "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" +) + +var ( + _ commonhandlers.Named = &nutanixClusterConfigVariableHandler{} + _ mutation.DiscoverVariables = &nutanixClusterConfigVariableHandler{} +) + +const ( + // HandlerNameVariable is the name of the variable handler. + HandlerNameVariable = "NutanixClusterConfigVars" + + // NutanixVariableName is the Nutanix config patch variable name. + NutanixVariableName = "nutanix" +) + +func NewVariable() *nutanixClusterConfigVariableHandler { + return &nutanixClusterConfigVariableHandler{} +} + +type nutanixClusterConfigVariableHandler struct{} + +func (h *nutanixClusterConfigVariableHandler) Name() string { + return HandlerNameVariable +} + +func (h *nutanixClusterConfigVariableHandler) DiscoverVariables( + ctx context.Context, + _ *runtimehooksv1.DiscoverVariablesRequest, + resp *runtimehooksv1.DiscoverVariablesResponse, +) { + resp.Variables = append(resp.Variables, clusterv1.ClusterClassVariable{ + Name: clusterconfig.MetaVariableName, + Required: true, + Schema: v1alpha1.ClusterConfigSpec{Nutanix: &v1alpha1.NutanixSpec{}}.VariableSchema(), + }) + resp.SetStatus(runtimehooksv1.ResponseStatusSuccess) +} diff --git a/pkg/handlers/nutanix/mutation/controlplaneendpoint/inject.go b/pkg/handlers/nutanix/mutation/controlplaneendpoint/inject.go new file mode 100644 index 000000000..1043689ba --- /dev/null +++ b/pkg/handlers/nutanix/mutation/controlplaneendpoint/inject.go @@ -0,0 +1,134 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package controlplaneendpoint + +import ( + "context" + "fmt" + + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + capiv1 "sigs.k8s.io/cluster-api/api/v1beta1" + controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + capxv1 "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/external/github.com/nutanix-cloud-native/cluster-api-provider-nutanix/api/v1beta1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches/selectors" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/variables" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" +) + +const ( + // VariableName is the external patch variable name. + VariableName = "controlPlaneEndpoint" +) + +type nutanixControlPlaneEndpoint struct { + variableName string + variableFieldPath []string +} + +func NewPatch() *nutanixControlPlaneEndpoint { + return newNutanixControlPlaneEndpoint( + clusterconfig.MetaVariableName, + v1alpha1.NutanixVariableName, + VariableName, + ) +} + +func newNutanixControlPlaneEndpoint( + variableName string, + variableFieldPath ...string, +) *nutanixControlPlaneEndpoint { + return &nutanixControlPlaneEndpoint{ + variableName: variableName, + variableFieldPath: variableFieldPath, + } +} + +func (h *nutanixControlPlaneEndpoint) Mutate( + ctx context.Context, + obj *unstructured.Unstructured, + vars map[string]apiextensionsv1.JSON, + holderRef runtimehooksv1.HolderReference, + _ client.ObjectKey, +) error { + log := ctrl.LoggerFrom(ctx).WithValues( + "holderRef", holderRef, + ) + + controlPlaneEndpointVar, found, err := variables.Get[v1alpha1.ControlPlaneEndpointSpec]( + vars, + h.variableName, + h.variableFieldPath..., + ) + if err != nil { + return err + } + if !found { + log.V(5).Info("Nutanix ControlPlaneEndpoint variable not defined") + return nil + } + + log = log.WithValues( + "variableName", + h.variableName, + "variableFieldPath", + h.variableFieldPath, + "variableValue", + controlPlaneEndpointVar, + ) + + if err := patches.MutateIfApplicable( + obj, + vars, + &holderRef, + selectors.ControlPlane(), + log, + func(obj *controlplanev1.KubeadmControlPlaneTemplate) error { + commands := []string{ + fmt.Sprintf("sed -i 's/control_plane_endpoint_ip/%s/g' /etc/kubernetes/manifests/kube-vip.yaml", + controlPlaneEndpointVar.Host), + fmt.Sprintf("sed -i 's/control_plane_endpoint_port/%d/g' /etc/kubernetes/manifests/kube-vip.yaml", + controlPlaneEndpointVar.Port), + } + log.WithValues( + "patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(), + "patchedObjectName", client.ObjectKeyFromObject(obj), + ).Info("adding PreKubeadmCommands to control plane kubeadm config spec") + obj.Spec.Template.Spec.KubeadmConfigSpec.PreKubeadmCommands = append( + obj.Spec.Template.Spec.KubeadmConfigSpec.PreKubeadmCommands, + commands..., + ) + return nil + }, + ); err != nil { + return err + } + + return patches.MutateIfApplicable( + obj, + vars, + &holderRef, + selectors.InfrastructureCluster(capxv1.GroupVersion.Version, "NutanixClusterTemplate"), + log, + func(obj *capxv1.NutanixClusterTemplate) error { + log.WithValues( + "patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(), + "patchedObjectName", client.ObjectKeyFromObject(obj), + ).Info("setting controlPlaneEndpoint in NutanixCluster spec") + + obj.Spec.Template.Spec.ControlPlaneEndpoint = capiv1.APIEndpoint{ + Host: controlPlaneEndpointVar.Host, + Port: controlPlaneEndpointVar.Port, + } + + return nil + }, + ) +} diff --git a/pkg/handlers/nutanix/mutation/controlplaneendpoint/inject_test.go b/pkg/handlers/nutanix/mutation/controlplaneendpoint/inject_test.go new file mode 100644 index 000000000..6ae946e79 --- /dev/null +++ b/pkg/handlers/nutanix/mutation/controlplaneendpoint/inject_test.go @@ -0,0 +1,75 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package controlplaneendpoint + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" + nutanixclusterconfig "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/nutanix/clusterconfig" +) + +func TestControlPlaneEndpointPatch(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "Nutanix ControlPlane endpoint suite") +} + +var _ = Describe("Generate Nutanix ControlPlane endpoint patches", func() { + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewPatch()).(mutation.GeneratePatches) + } + + testDefs := []capitest.PatchTestDef{ + { + Name: "unset variable", + }, + { + Name: "ControlPlaneEndpoint set to valid host and port", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + clusterconfig.MetaVariableName, + clusterv1.APIEndpoint{ + Host: "10.20.100.10", + Port: 6443, + }, + nutanixclusterconfig.NutanixVariableName, + VariableName, + ), + }, + RequestItem: request.NewNutanixClusterTemplateRequestItem(""), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{ + { + Operation: "replace", + Path: "/spec/template/spec/controlPlaneEndpoint/host", + ValueMatcher: gomega.Equal("10.20.100.10"), + }, + { + Operation: "replace", + Path: "/spec/template/spec/controlPlaneEndpoint/port", + ValueMatcher: gomega.BeEquivalentTo(6443), + }, + }, + }, + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches( + GinkgoT(), + patchGenerator, + &tt, + ) + }) + } +}) diff --git a/pkg/handlers/nutanix/mutation/controlplaneendpoint/variables_test.go b/pkg/handlers/nutanix/mutation/controlplaneendpoint/variables_test.go new file mode 100644 index 000000000..eec2f61f9 --- /dev/null +++ b/pkg/handlers/nutanix/mutation/controlplaneendpoint/variables_test.go @@ -0,0 +1,86 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package controlplaneendpoint + +import ( + "fmt" + "testing" + + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/ptr" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" + nutanixclusterconfig "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/nutanix/clusterconfig" +) + +var testPrismCentralURL = fmt.Sprintf("https://prism-central.nutanix.com:%d", v1alpha1.DefaultPrismCentralPort) + +func TestVariableValidation(t *testing.T) { + capitest.ValidateDiscoverVariables( + t, + clusterconfig.MetaVariableName, + ptr.To(v1alpha1.ClusterConfigSpec{Nutanix: &v1alpha1.NutanixSpec{}}.VariableSchema()), + true, + nutanixclusterconfig.NewVariable, + capitest.VariableTestDef{ + Name: "valid host and port", + Vals: v1alpha1.ClusterConfigSpec{ + Nutanix: &v1alpha1.NutanixSpec{ + ControlPlaneEndpoint: clusterv1.APIEndpoint{ + Host: "10.20.100.10", + Port: 6443, + }, + // PrismCentralEndpoint is a required field and must always be set + PrismCentralEndpoint: v1alpha1.NutanixPrismCentralEndpointSpec{ + URL: testPrismCentralURL, + Credentials: corev1.LocalObjectReference{ + Name: "credentials", + }, + }, + }, + }, + }, + capitest.VariableTestDef{ + Name: "empty host", + Vals: v1alpha1.ClusterConfigSpec{ + Nutanix: &v1alpha1.NutanixSpec{ + ControlPlaneEndpoint: clusterv1.APIEndpoint{ + Host: "", + Port: 6443, + }, + // PrismCentralEndpoint is a required field and must always be set + PrismCentralEndpoint: v1alpha1.NutanixPrismCentralEndpointSpec{ + URL: testPrismCentralURL, + Credentials: corev1.LocalObjectReference{ + Name: "credentials", + }, + }, + }, + }, + ExpectError: true, + }, + capitest.VariableTestDef{ + Name: "port set to 0", + Vals: v1alpha1.ClusterConfigSpec{ + Nutanix: &v1alpha1.NutanixSpec{ + ControlPlaneEndpoint: clusterv1.APIEndpoint{ + Host: "10.20.100.10", + Port: 0, + }, + // PrismCentralEndpoint is a required field and must always be set + PrismCentralEndpoint: v1alpha1.NutanixPrismCentralEndpointSpec{ + URL: testPrismCentralURL, + Credentials: corev1.LocalObjectReference{ + Name: "credentials", + }, + }, + }, + }, + ExpectError: true, + }, + ) +} diff --git a/pkg/handlers/nutanix/mutation/machinedetails/inject.go b/pkg/handlers/nutanix/mutation/machinedetails/inject.go new file mode 100644 index 000000000..9f17a07e9 --- /dev/null +++ b/pkg/handlers/nutanix/mutation/machinedetails/inject.go @@ -0,0 +1,113 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package machinedetails + +import ( + "context" + + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + capxv1 "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/external/github.com/nutanix-cloud-native/cluster-api-provider-nutanix/api/v1beta1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/variables" +) + +const ( + // VariableName is the external patch variable name. + VariableName = "machineDetails" +) + +type nutanixMachineDetailsPatchHandler struct { + metaVariableName string + variableFieldPath []string + patchSelector clusterv1.PatchSelector +} + +func newNutanixMachineDetailsPatchHandler( + metaVariableName string, + variableFieldPath []string, + patchSelector clusterv1.PatchSelector, +) *nutanixMachineDetailsPatchHandler { + return &nutanixMachineDetailsPatchHandler{ + metaVariableName: metaVariableName, + variableFieldPath: variableFieldPath, + patchSelector: patchSelector, + } +} + +func (h *nutanixMachineDetailsPatchHandler) Mutate( + ctx context.Context, + obj *unstructured.Unstructured, + vars map[string]apiextensionsv1.JSON, + holderRef runtimehooksv1.HolderReference, + _ client.ObjectKey, +) error { + log := ctrl.LoggerFrom(ctx).WithValues( + "holderRef", holderRef, + ) + + nutanixMachineDetailsVar, found, err := variables.Get[v1alpha1.NutanixMachineDetails]( + vars, + h.metaVariableName, + h.variableFieldPath..., + ) + if err != nil { + return err + } + if !found { + log.V(5).Info("Nutanix machine details variable for workers not defined") + return nil + } + + log = log.WithValues( + "variableName", + h.metaVariableName, + "variableFieldPath", + h.variableFieldPath, + "variableValue", + nutanixMachineDetailsVar, + ) + + return patches.MutateIfApplicable( + obj, + vars, + &holderRef, + h.patchSelector, + log, + func(obj *capxv1.NutanixMachineTemplate) error { + log.WithValues( + "patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(), + "patchedObjectName", client.ObjectKeyFromObject(obj), + ).Info("setting Nutanix machine details in worker NutanixMachineTemplate spec") + + spec := obj.Spec.Template.Spec + + spec.BootType = capxv1.NutanixBootType(nutanixMachineDetailsVar.BootType) + spec.Cluster = capxv1.NutanixResourceIdentifier(nutanixMachineDetailsVar.Cluster) + spec.Image = capxv1.NutanixResourceIdentifier(nutanixMachineDetailsVar.Image) + + spec.VCPUSockets = nutanixMachineDetailsVar.VCPUSockets + spec.VCPUsPerSocket = nutanixMachineDetailsVar.VCPUsPerSocket + spec.MemorySize = nutanixMachineDetailsVar.MemorySize + spec.SystemDiskSize = nutanixMachineDetailsVar.SystemDiskSize + + spec.Subnets = make( + []capxv1.NutanixResourceIdentifier, + len(nutanixMachineDetailsVar.Subnets), + ) + for i, subnet := range nutanixMachineDetailsVar.Subnets { + spec.Subnets[i] = capxv1.NutanixResourceIdentifier(subnet) + } + + obj.Spec.Template.Spec = spec + return nil + }, + ) +} diff --git a/pkg/handlers/nutanix/mutation/machinedetails/inject_control_plane.go b/pkg/handlers/nutanix/mutation/machinedetails/inject_control_plane.go new file mode 100644 index 000000000..04a51361f --- /dev/null +++ b/pkg/handlers/nutanix/mutation/machinedetails/inject_control_plane.go @@ -0,0 +1,25 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package machinedetails + +import ( + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches/selectors" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" +) + +func NewControlPlanePatch() *nutanixMachineDetailsPatchHandler { + return newNutanixMachineDetailsPatchHandler( + clusterconfig.MetaVariableName, + []string{ + clusterconfig.MetaControlPlaneConfigName, + v1alpha1.NutanixVariableName, + VariableName, + }, + selectors.InfrastructureControlPlaneMachines( + "v1beta1", + "NutanixMachineTemplate", + ), + ) +} diff --git a/pkg/handlers/nutanix/mutation/machinedetails/inject_control_plane_test.go b/pkg/handlers/nutanix/mutation/machinedetails/inject_control_plane_test.go new file mode 100644 index 000000000..3d05ef071 --- /dev/null +++ b/pkg/handlers/nutanix/mutation/machinedetails/inject_control_plane_test.go @@ -0,0 +1,135 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package machinedetails + +import ( + . "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/utils/ptr" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + + capxv1 "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/external/github.com/nutanix-cloud-native/cluster-api-provider-nutanix/api/v1beta1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" + nutanixclusterconfig "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/nutanix/clusterconfig" +) + +var ( + variableWithAllFieldsSet = v1alpha1.NutanixMachineDetails{ + BootType: v1alpha1.NutanixBootType(capxv1.NutanixBootTypeLegacy), + VCPUSockets: 2, + VCPUsPerSocket: 1, + Image: v1alpha1.NutanixResourceIdentifier{ + Type: capxv1.NutanixIdentifierName, + Name: ptr.To("fake-image"), + }, + Cluster: v1alpha1.NutanixResourceIdentifier{ + Type: capxv1.NutanixIdentifierName, + Name: ptr.To("fake-pe-cluster"), + }, + MemorySize: resource.MustParse("8Gi"), + SystemDiskSize: resource.MustParse("40Gi"), + Subnets: []v1alpha1.NutanixResourceIdentifier{ + { + Type: capxv1.NutanixIdentifierName, + Name: ptr.To("fake-subnet"), + }, + }, + } + + matchersForAllFieldsSet = []capitest.JSONPatchMatcher{ + { + Operation: "add", + Path: "/spec/template/spec/bootType", + ValueMatcher: gomega.BeEquivalentTo(capxv1.NutanixBootTypeLegacy), + }, + { + Operation: "add", + Path: "/spec/template/spec/image/name", + ValueMatcher: gomega.BeEquivalentTo("fake-image"), + }, + { + Operation: "replace", + Path: "/spec/template/spec/image/type", + ValueMatcher: gomega.BeEquivalentTo(capxv1.NutanixIdentifierName), + }, + { + Operation: "add", + Path: "/spec/template/spec/cluster/name", + ValueMatcher: gomega.BeEquivalentTo("fake-pe-cluster"), + }, + { + Operation: "replace", + Path: "/spec/template/spec/cluster/type", + ValueMatcher: gomega.BeEquivalentTo(capxv1.NutanixIdentifierName), + }, + { + Operation: "replace", + Path: "/spec/template/spec/vcpuSockets", + ValueMatcher: gomega.BeEquivalentTo(2), + }, + { + Operation: "replace", + Path: "/spec/template/spec/vcpusPerSocket", + ValueMatcher: gomega.BeEquivalentTo(1), + }, + { + Operation: "replace", + Path: "/spec/template/spec/memorySize", + ValueMatcher: gomega.BeEquivalentTo("8Gi"), + }, + { + Operation: "replace", + Path: "/spec/template/spec/systemDiskSize", + ValueMatcher: gomega.BeEquivalentTo("40Gi"), + }, + { + Operation: "replace", + Path: "/spec/template/spec/subnet", + ValueMatcher: gomega.HaveLen(1), + }, + } +) + +var _ = Describe("Generate Nutanix Machine Details patches for ControlPlane", func() { + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewControlPlanePatch()).(mutation.GeneratePatches) + } + + testDefs := []capitest.PatchTestDef{ + { + Name: "unset variable", + }, + { + Name: "all fields set for control-plane", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + clusterconfig.MetaVariableName, + variableWithAllFieldsSet, + clusterconfig.MetaControlPlaneConfigName, + nutanixclusterconfig.NutanixVariableName, + VariableName, + ), + }, + RequestItem: request.NewCPNutanixMachineTemplateRequestItem(""), + ExpectedPatchMatchers: matchersForAllFieldsSet, + }, + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches( + GinkgoT(), + patchGenerator, + &tt, + ) + }) + } +}) diff --git a/pkg/handlers/nutanix/mutation/machinedetails/inject_suite_test.go b/pkg/handlers/nutanix/mutation/machinedetails/inject_suite_test.go new file mode 100644 index 000000000..0bc40008e --- /dev/null +++ b/pkg/handlers/nutanix/mutation/machinedetails/inject_suite_test.go @@ -0,0 +1,16 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package machinedetails + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestMachineDetailsPatch(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Nutanix Machine Details patches for ControlPlane and Workers suite") +} diff --git a/pkg/handlers/nutanix/mutation/machinedetails/inject_worker.go b/pkg/handlers/nutanix/mutation/machinedetails/inject_worker.go new file mode 100644 index 000000000..200141060 --- /dev/null +++ b/pkg/handlers/nutanix/mutation/machinedetails/inject_worker.go @@ -0,0 +1,24 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package machinedetails + +import ( + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches/selectors" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/workerconfig" +) + +func NewWorkerPatch() *nutanixMachineDetailsPatchHandler { + return newNutanixMachineDetailsPatchHandler( + workerconfig.MetaVariableName, + []string{ + v1alpha1.NutanixVariableName, + VariableName, + }, + selectors.InfrastructureWorkerMachineTemplates( + "v1beta1", + "NutanixMachineTemplate", + ), + ) +} diff --git a/pkg/handlers/nutanix/mutation/machinedetails/inject_worker_test.go b/pkg/handlers/nutanix/mutation/machinedetails/inject_worker_test.go new file mode 100644 index 000000000..9481bf960 --- /dev/null +++ b/pkg/handlers/nutanix/mutation/machinedetails/inject_worker_test.go @@ -0,0 +1,59 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package machinedetails + +import ( + . "github.com/onsi/ginkgo/v2" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/workerconfig" + nutanixclusterconfig "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/nutanix/clusterconfig" +) + +var _ = Describe("Generate Nutanix Machine Details patches for Worker", func() { + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewWorkerPatch()).(mutation.GeneratePatches) + } + + testDefs := []capitest.PatchTestDef{ + { + Name: "unset variable", + }, + { + Name: "all fields set for workers", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + workerconfig.MetaVariableName, + variableWithAllFieldsSet, + nutanixclusterconfig.NutanixVariableName, + VariableName, + ), + capitest.VariableWithValue( + "builtin", + apiextensionsv1.JSON{ + Raw: []byte(`{"machineDeployment": {"class": "a-worker"}}`), + }, + ), + }, + RequestItem: request.NewWorkerNutanixMachineTemplateRequestItem(""), + ExpectedPatchMatchers: matchersForAllFieldsSet, + }, + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches( + GinkgoT(), + patchGenerator, + &tt, + ) + }) + } +}) diff --git a/pkg/handlers/nutanix/mutation/machinedetails/variables_test.go b/pkg/handlers/nutanix/mutation/machinedetails/variables_test.go new file mode 100644 index 000000000..23d432d92 --- /dev/null +++ b/pkg/handlers/nutanix/mutation/machinedetails/variables_test.go @@ -0,0 +1,132 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package machinedetails + +import ( + "testing" + + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/utils/ptr" + + capxv1 "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/external/github.com/nutanix-cloud-native/cluster-api-provider-nutanix/api/v1beta1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" + nutanixclusterconfig "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/nutanix/clusterconfig" +) + +func TestVariableValidation(t *testing.T) { + testImageName := "fake-image" + testPEClusterName := "fake-pe-cluster" + capitest.ValidateDiscoverVariables( + t, + clusterconfig.MetaVariableName, + ptr.To(v1alpha1.ClusterConfigSpec{Nutanix: &v1alpha1.NutanixSpec{}}.VariableSchema()), + true, + nutanixclusterconfig.NewVariable, + capitest.VariableTestDef{ + Name: "all fields set", + Vals: v1alpha1.ClusterConfigSpec{ + ControlPlane: &v1alpha1.NodeConfigSpec{ + Nutanix: &v1alpha1.NutanixNodeSpec{ + MachineDetails: v1alpha1.NutanixMachineDetails{ + BootType: v1alpha1.NutanixBootType(capxv1.NutanixBootTypeLegacy), + VCPUSockets: 2, + VCPUsPerSocket: 1, + Image: v1alpha1.NutanixResourceIdentifier{ + Type: capxv1.NutanixIdentifierName, + Name: &testImageName, + }, + Cluster: v1alpha1.NutanixResourceIdentifier{ + Type: capxv1.NutanixIdentifierName, + Name: &testPEClusterName, + }, + MemorySize: resource.MustParse("8Gi"), + SystemDiskSize: resource.MustParse("40Gi"), + Subnets: []v1alpha1.NutanixResourceIdentifier{}, + }, + }, + }, + }, + }, + capitest.VariableTestDef{ + Name: "invalid boot type", + Vals: v1alpha1.ClusterConfigSpec{ + ControlPlane: &v1alpha1.NodeConfigSpec{ + Nutanix: &v1alpha1.NutanixNodeSpec{ + MachineDetails: v1alpha1.NutanixMachineDetails{ + BootType: "invalid", + VCPUSockets: 2, + VCPUsPerSocket: 1, + Image: v1alpha1.NutanixResourceIdentifier{ + Type: capxv1.NutanixIdentifierName, + Name: &testImageName, + }, + Cluster: v1alpha1.NutanixResourceIdentifier{ + Type: capxv1.NutanixIdentifierName, + Name: &testPEClusterName, + }, + MemorySize: resource.MustParse("8Gi"), + SystemDiskSize: resource.MustParse("40Gi"), + Subnets: []v1alpha1.NutanixResourceIdentifier{}, + }, + }, + }, + }, + ExpectError: true, + }, + capitest.VariableTestDef{ + Name: "invalid image type", + Vals: v1alpha1.ClusterConfigSpec{ + ControlPlane: &v1alpha1.NodeConfigSpec{ + Nutanix: &v1alpha1.NutanixNodeSpec{ + MachineDetails: v1alpha1.NutanixMachineDetails{ + BootType: v1alpha1.NutanixBootType(capxv1.NutanixBootTypeLegacy), + VCPUSockets: 2, + VCPUsPerSocket: 1, + Image: v1alpha1.NutanixResourceIdentifier{ + Type: "invalid", + Name: &testImageName, + }, + Cluster: v1alpha1.NutanixResourceIdentifier{ + Type: capxv1.NutanixIdentifierName, + Name: &testPEClusterName, + }, + MemorySize: resource.MustParse("8Gi"), + SystemDiskSize: resource.MustParse("40Gi"), + Subnets: []v1alpha1.NutanixResourceIdentifier{}, + }, + }, + }, + }, + ExpectError: true, + }, + capitest.VariableTestDef{ + Name: "invalid cluster type", + Vals: v1alpha1.ClusterConfigSpec{ + ControlPlane: &v1alpha1.NodeConfigSpec{ + Nutanix: &v1alpha1.NutanixNodeSpec{ + MachineDetails: v1alpha1.NutanixMachineDetails{ + BootType: v1alpha1.NutanixBootType(capxv1.NutanixBootTypeLegacy), + VCPUSockets: 2, + VCPUsPerSocket: 1, + Image: v1alpha1.NutanixResourceIdentifier{ + Type: capxv1.NutanixIdentifierName, + Name: &testImageName, + }, + Cluster: v1alpha1.NutanixResourceIdentifier{ + Type: "invalid", + Name: &testPEClusterName, + }, + MemorySize: resource.MustParse("8Gi"), + SystemDiskSize: resource.MustParse("40Gi"), + Subnets: []v1alpha1.NutanixResourceIdentifier{}, + }, + }, + }, + }, + ExpectError: true, + }, + ) +} diff --git a/pkg/handlers/nutanix/mutation/metapatch_handler.go b/pkg/handlers/nutanix/mutation/metapatch_handler.go new file mode 100644 index 000000000..c3bd1be77 --- /dev/null +++ b/pkg/handlers/nutanix/mutation/metapatch_handler.go @@ -0,0 +1,44 @@ +// Copyright 2024 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package mutation + +import ( + "sigs.k8s.io/controller-runtime/pkg/manager" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" + genericmutation "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/nutanix/mutation/controlplaneendpoint" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/nutanix/mutation/machinedetails" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/nutanix/mutation/prismcentralendpoint" +) + +// MetaPatchHandler returns a meta patch handler for mutating CAPX clusters. +func MetaPatchHandler(mgr manager.Manager) handlers.Named { + patchHandlers := append( + []mutation.MetaMutator{ + controlplaneendpoint.NewPatch(), + prismcentralendpoint.NewPatch(), + machinedetails.NewControlPlanePatch(), + }, + genericmutation.MetaMutators(mgr)..., + ) + + return mutation.NewMetaGeneratePatchesHandler( + "nutanixClusterConfigPatch", + patchHandlers..., + ) +} + +// MetaWorkerPatchHandler returns a meta patch handler for mutating CAPA workers. +func MetaWorkerPatchHandler() handlers.Named { + patchHandlers := []mutation.MetaMutator{ + machinedetails.NewWorkerPatch(), + } + + return mutation.NewMetaGeneratePatchesHandler( + "nutanixWorkerConfigPatch", + patchHandlers..., + ) +} diff --git a/pkg/handlers/nutanix/mutation/prismcentralendpoint/inject.go b/pkg/handlers/nutanix/mutation/prismcentralendpoint/inject.go new file mode 100644 index 000000000..0e9350477 --- /dev/null +++ b/pkg/handlers/nutanix/mutation/prismcentralendpoint/inject.go @@ -0,0 +1,138 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package prismcentralendpoint + +import ( + "context" + "encoding/base64" + "fmt" + + "github.com/nutanix-cloud-native/prism-go-client/environment/credentials" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/utils/ptr" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + capxv1 "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/external/github.com/nutanix-cloud-native/cluster-api-provider-nutanix/api/v1beta1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches/selectors" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/variables" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" +) + +const ( + // VariableName is the external patch variable name. + VariableName = "prismCentralEndpoint" +) + +type nutanixPrismCentralEndpoint struct { + variableName string + variableFieldPath []string +} + +func NewPatch() *nutanixPrismCentralEndpoint { + return newNutanixPrismCentralEndpoint( + clusterconfig.MetaVariableName, + v1alpha1.NutanixVariableName, + VariableName, + ) +} + +func newNutanixPrismCentralEndpoint( + variableName string, + variableFieldPath ...string, +) *nutanixPrismCentralEndpoint { + return &nutanixPrismCentralEndpoint{ + variableName: variableName, + variableFieldPath: variableFieldPath, + } +} + +func (h *nutanixPrismCentralEndpoint) Mutate( + ctx context.Context, + obj *unstructured.Unstructured, + vars map[string]apiextensionsv1.JSON, + holderRef runtimehooksv1.HolderReference, + clusterKey client.ObjectKey, +) error { + log := ctrl.LoggerFrom(ctx).WithValues( + "holderRef", holderRef, + ) + + prismCentralEndpointVar, found, err := variables.Get[v1alpha1.NutanixPrismCentralEndpointSpec]( + vars, + h.variableName, + h.variableFieldPath..., + ) + if err != nil { + return err + } + if !found { + log.V(5).Info("Nutanix PrismCentralEndpoint variable not defined") + return nil + } + + log = log.WithValues( + "variableName", + h.variableName, + "variableFieldPath", + h.variableFieldPath, + "variableValue", + prismCentralEndpointVar, + ) + + return patches.MutateIfApplicable( + obj, + vars, + &holderRef, + selectors.InfrastructureCluster(capxv1.GroupVersion.Version, "NutanixClusterTemplate"), + log, + func(obj *capxv1.NutanixClusterTemplate) error { + log.WithValues( + "patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(), + "patchedObjectName", client.ObjectKeyFromObject(obj), + ).Info("setting prismCentralEndpoint in NutanixCluster spec") + + var address string + var port int32 + address, port, err = prismCentralEndpointVar.ParseURL() + if err != nil { + return err + } + prismCentral := &credentials.NutanixPrismEndpoint{ + Address: address, + Port: port, + Insecure: prismCentralEndpointVar.Insecure, + CredentialRef: &credentials.NutanixCredentialReference{ + Kind: credentials.SecretKind, + Name: prismCentralEndpointVar.Credentials.Name, + // Assume the secret is in the same namespace as Cluster + Namespace: clusterKey.Namespace, + }, + } + additionalTrustBundle := ptr.Deref(prismCentralEndpointVar.AdditionalTrustBundle, "") + if additionalTrustBundle != "" { + var decoded []byte + decoded, err = base64.StdEncoding.DecodeString(additionalTrustBundle) + if err != nil { + log.Error(err, "error decoding additional trust bundle") + return fmt.Errorf("error decoding additional trust bundle: %w", err) + } + prismCentral.AdditionalTrustBundle = &credentials.NutanixTrustBundleReference{ + Kind: credentials.NutanixTrustBundleKindString, + Data: string(decoded), + } + // TODO: Consider always setting Insecure to false when AdditionalTrustBundle is set. + // But do it in a webhook and not hidden in this handler. + } + + obj.Spec.Template.Spec.PrismCentral = prismCentral + + return nil + }, + ) +} diff --git a/pkg/handlers/nutanix/mutation/prismcentralendpoint/inject_test.go b/pkg/handlers/nutanix/mutation/prismcentralendpoint/inject_test.go new file mode 100644 index 000000000..ca7b695b2 --- /dev/null +++ b/pkg/handlers/nutanix/mutation/prismcentralendpoint/inject_test.go @@ -0,0 +1,158 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package prismcentralendpoint + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/ptr" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest/request" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" + nutanixclusterconfig "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/nutanix/clusterconfig" +) + +// +//nolint:lll // just a long string +const testCertBundle = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVjekNDQTF1Z0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRUUZBRC4uQWtHQTFVRUJoTUNSMEl4CkV6QVJCZ05WQkFnVENsTnZiV1V0VTNSaGRHVXhGREFTQmdOVkJBb1RDMC4uMEVnVEhSa01UY3dOUVlEClZRUUxFeTVEYkdGemN5QXhJRkIxWW14cFl5QlFjbWx0WVhKNUlFTmxjbi4uWFJwYjI0Z1FYVjBhRzl5CmFYUjVNUlF3RWdZRFZRUURFd3RDWlhOMElFTkJJRXgwWkRBZUZ3MHdNRC4uVFV3TVRaYUZ3MHdNVEF5Ck1EUXhPVFV3TVRaYU1JR0hNUXN3Q1FZRFZRUUdFd0pIUWpFVE1CRUdBMS4uMjl0WlMxVGRHRjBaVEVVCk1CSUdBMVVFQ2hNTFFtVnpkQ0JEUVNCTWRHUXhOekExQmdOVkJBc1RMay4uREVnVUhWaWJHbGpJRkJ5CmFXMWhjbmtnUTJWeWRHbG1hV05oZEdsdmJpQkJkWFJvYjNKcGRIa3hGRC4uQU1UQzBKbGMzUWdRMEVnClRIUmtNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZy4uVHoybXI3U1ppQU1mUXl1CnZCak05T2lKalJhelhCWjFCalA1Q0UvV20vUnI1MDBQUksrTGg5eDVlSi4uL0FOQkUwc1RLMFpzREdNCmFrMm0xZzdvcnVJM2RZM1ZIcUl4RlR6MFRhMWQrTkFqd25MZTRuT2I3Ly4uazA1U2hoQnJKR0JLS3hiCjhuMTA0by81cDhIQXNaUGR6YkZNSXlOakp6Qk0ybzV5NUExM3dpTGl0RS4uZnlZa1F6YXhDdzBBd3psCmtWSGlJeUN1YUY0d2o1NzFwU3prdjZzdis0SURNYlQvWHBDbzhMNndUYS4uc2grZXRMRDZGdFRqWWJiCnJ2WjhSUU0xdGxLZG9NSGcycXhyYUFWKytITkJZbU5XczBkdUVkalViSi4uWEk5VHRuUzRvMUNrajdQCk9mbGppUUlEQVFBQm80SG5NSUhrTUIwR0ExVWREZ1FXQkJROHVyTUNSTC4uNUFrSXA5TkpISnc1VENCCnRBWURWUjBqQklHc01JR3BnQlE4dXJNQ1JMWVlNSFVLVTVBa0lwOU5KSC4uYVNCaWpDQmh6RUxNQWtHCkExVUVCaE1DUjBJeEV6QVJCZ05WQkFnVENsTnZiV1V0VTNSaGRHVXhGRC4uQW9UQzBKbGMzUWdRMEVnClRIUmtNVGN3TlFZRFZRUUxFeTVEYkdGemN5QXhJRkIxWW14cFl5QlFjbS4uRU5sY25ScFptbGpZWFJwCmIyNGdRWFYwYUc5eWFYUjVNUlF3RWdZRFZRUURFd3RDWlhOMElFTkJJRS4uREFNQmdOVkhSTUVCVEFECkFRSC9NQTBHQ1NxR1NJYjNEUUVCQkFVQUE0SUJBUUMxdVlCY3NTbmN3QS4uRENzUWVyNzcyQzJ1Y3BYCnhRVUUvQzBwV1dtNmdEa3dkNUQwRFNNREpScVYvd2VvWjR3QzZCNzNmNS4uYkxoR1lIYVhKZVNENktyClhjb093TGRTYUdtSllzbExLWkIzWklERXAwd1lUR2hndGViNkpGaVR0bi4uc2YyeGRyWWZQQ2lJQjdnCkJNQVY3R3pkYzRWc3BTNmxqckFoYmlpYXdkQmlRbFFtc0JlRno5SmtGNC4uYjNsOEJvR04rcU1hNTZZCkl0OHVuYTJnWTRsMk8vL29uODhyNUlXSmxtMUwwb0E4ZTRmUjJ5ckJIWC4uYWRzR2VGS2t5TnJ3R2kvCjd2UU1mWGRHc1JyWE5HUkduWCt2V0RaMy96V0kwam9EdENrTm5xRXBWbi4uSG9YCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=" + +func TestPrismCentralEndpointPatch(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "Nutanix Prism Central Endpoint mutator suite") +} + +var _ = Describe("Generate Nutanix Prism Central Endpoint patches", func() { + patchGenerator := func() mutation.GeneratePatches { + return mutation.NewMetaGeneratePatchesHandler("", NewPatch()).(mutation.GeneratePatches) + } + + testDefs := []capitest.PatchTestDef{ + { + Name: "unset variable", + }, + { + Name: "all required fields set", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + clusterconfig.MetaVariableName, + v1alpha1.NutanixPrismCentralEndpointSpec{ + URL: "https://prism-central.nutanix.com:9441", + Insecure: true, + Credentials: corev1.LocalObjectReference{ + Name: "credentials", + }, + }, + nutanixclusterconfig.NutanixVariableName, + VariableName, + ), + }, + RequestItem: request.NewNutanixClusterTemplateRequestItem(""), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{ + { + Operation: "replace", + Path: "/spec/template/spec/prismCentral", + ValueMatcher: gomega.SatisfyAll( + gomega.HaveKeyWithValue( + "address", + gomega.BeEquivalentTo("prism-central.nutanix.com"), + ), + gomega.HaveKeyWithValue("port", gomega.BeEquivalentTo(9441)), + gomega.HaveKeyWithValue("insecure", true), + gomega.HaveKey("credentialRef"), + gomega.Not(gomega.HaveKey("additionalTrustBundle")), + ), + }, + }, + }, + { + Name: "all required fields set without port", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + clusterconfig.MetaVariableName, + v1alpha1.NutanixPrismCentralEndpointSpec{ + URL: "https://prism-central.nutanix.com", + Insecure: true, + Credentials: corev1.LocalObjectReference{ + Name: "credentials", + }, + }, + nutanixclusterconfig.NutanixVariableName, + VariableName, + ), + }, + RequestItem: request.NewNutanixClusterTemplateRequestItem(""), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{ + { + Operation: "replace", + Path: "/spec/template/spec/prismCentral", + ValueMatcher: gomega.SatisfyAll( + gomega.HaveKeyWithValue( + "address", + gomega.BeEquivalentTo("prism-central.nutanix.com"), + ), + gomega.HaveKeyWithValue("port", gomega.BeEquivalentTo(v1alpha1.DefaultPrismCentralPort)), + gomega.HaveKeyWithValue("insecure", true), + gomega.HaveKey("credentialRef"), + gomega.Not(gomega.HaveKey("additionalTrustBundle")), + ), + }, + }, + }, + { + Name: "additional trust bundle is set", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + clusterconfig.MetaVariableName, + v1alpha1.NutanixPrismCentralEndpointSpec{ + URL: "https://prism-central.nutanix.com:9441", + Insecure: true, + Credentials: corev1.LocalObjectReference{ + Name: "credentials", + }, + AdditionalTrustBundle: ptr.To(testCertBundle), + }, + nutanixclusterconfig.NutanixVariableName, + VariableName, + ), + }, + RequestItem: request.NewNutanixClusterTemplateRequestItem(""), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{ + { + Operation: "replace", + Path: "/spec/template/spec/prismCentral", + ValueMatcher: gomega.SatisfyAll( + gomega.HaveKeyWithValue( + "address", + gomega.BeEquivalentTo("prism-central.nutanix.com"), + ), + gomega.HaveKeyWithValue("port", gomega.BeEquivalentTo(9441)), + // Assert the insecure field was not modified when additional trust bundle is set. + gomega.HaveKeyWithValue("insecure", true), + gomega.HaveKey("credentialRef"), + gomega.HaveKey("additionalTrustBundle"), + ), + }, + }, + }, + } + + // create test node for each case + for testIdx := range testDefs { + tt := testDefs[testIdx] + It(tt.Name, func() { + capitest.AssertGeneratePatches( + GinkgoT(), + patchGenerator, + &tt, + ) + }) + } +}) diff --git a/pkg/handlers/nutanix/mutation/prismcentralendpoint/variables_test.go b/pkg/handlers/nutanix/mutation/prismcentralendpoint/variables_test.go new file mode 100644 index 000000000..0f8009218 --- /dev/null +++ b/pkg/handlers/nutanix/mutation/prismcentralendpoint/variables_test.go @@ -0,0 +1,161 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package prismcentralendpoint + +import ( + "fmt" + "testing" + + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/ptr" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" + nutanixclusterconfig "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/nutanix/clusterconfig" +) + +func TestVariableValidation(t *testing.T) { + capitest.ValidateDiscoverVariables( + t, + clusterconfig.MetaVariableName, + ptr.To(v1alpha1.ClusterConfigSpec{Nutanix: &v1alpha1.NutanixSpec{}}.VariableSchema()), + true, + nutanixclusterconfig.NewVariable, + capitest.VariableTestDef{ + Name: "valid PC URL", + Vals: v1alpha1.ClusterConfigSpec{ + Nutanix: &v1alpha1.NutanixSpec{ + PrismCentralEndpoint: v1alpha1.NutanixPrismCentralEndpointSpec{ + URL: fmt.Sprintf("https://prism-central.nutanix.com:%d", v1alpha1.DefaultPrismCentralPort), + Insecure: false, + Credentials: corev1.LocalObjectReference{ + Name: "credentials", + }, + }, + // ControlPlaneEndpoint is a required field and must always be set + ControlPlaneEndpoint: clusterv1.APIEndpoint{ + Host: "10.20.100.10", + Port: 6443, + }, + }, + }, + }, + capitest.VariableTestDef{ + Name: "valid PC URL as an IP", + Vals: v1alpha1.ClusterConfigSpec{ + Nutanix: &v1alpha1.NutanixSpec{ + PrismCentralEndpoint: v1alpha1.NutanixPrismCentralEndpointSpec{ + URL: fmt.Sprintf("https://10.0.0.1:%d", v1alpha1.DefaultPrismCentralPort), + Insecure: false, + Credentials: corev1.LocalObjectReference{ + Name: "credentials", + }, + }, + // ControlPlaneEndpoint is a required field and must always be set + ControlPlaneEndpoint: clusterv1.APIEndpoint{ + Host: "10.20.100.10", + Port: 6443, + }, + }, + }, + }, + capitest.VariableTestDef{ + Name: "valid PC URL without a port", + Vals: v1alpha1.ClusterConfigSpec{ + Nutanix: &v1alpha1.NutanixSpec{ + PrismCentralEndpoint: v1alpha1.NutanixPrismCentralEndpointSpec{ + URL: "https://prism-central.nutanix.com", + Insecure: false, + Credentials: corev1.LocalObjectReference{ + Name: "credentials", + }, + }, + // ControlPlaneEndpoint is a required field and must always be set + ControlPlaneEndpoint: clusterv1.APIEndpoint{ + Host: "10.20.100.10", + Port: 6443, + }, + }, + }, + }, + capitest.VariableTestDef{ + Name: "empty PC URL", + Vals: v1alpha1.ClusterConfigSpec{ + Nutanix: &v1alpha1.NutanixSpec{ + PrismCentralEndpoint: v1alpha1.NutanixPrismCentralEndpointSpec{ + Insecure: false, + Credentials: corev1.LocalObjectReference{ + Name: "credentials", + }, + }, + // ControlPlaneEndpoint is a required field and must always be set + ControlPlaneEndpoint: clusterv1.APIEndpoint{ + Host: "10.20.100.10", + Port: 6443, + }, + }, + }, + ExpectError: true, + }, + capitest.VariableTestDef{ + Name: "http is not a valid PC URL", + Vals: v1alpha1.ClusterConfigSpec{ + Nutanix: &v1alpha1.NutanixSpec{ + PrismCentralEndpoint: v1alpha1.NutanixPrismCentralEndpointSpec{ + URL: "http://prism-central.nutanix.com", + Insecure: false, + Credentials: corev1.LocalObjectReference{ + Name: "credentials", + }, + }, + // ControlPlaneEndpoint is a required field and must always be set + ControlPlaneEndpoint: clusterv1.APIEndpoint{ + Host: "10.20.100.10", + Port: 6443, + }, + }, + }, + ExpectError: true, + }, + capitest.VariableTestDef{ + Name: "not a valid PC URL", + Vals: v1alpha1.ClusterConfigSpec{ + Nutanix: &v1alpha1.NutanixSpec{ + PrismCentralEndpoint: v1alpha1.NutanixPrismCentralEndpointSpec{ + URL: "not-a-valid-url", + Insecure: false, + Credentials: corev1.LocalObjectReference{ + Name: "credentials", + }, + }, + // ControlPlaneEndpoint is a required field and must always be set + ControlPlaneEndpoint: clusterv1.APIEndpoint{ + Host: "10.20.100.10", + Port: 6443, + }, + }, + }, + ExpectError: true, + }, + capitest.VariableTestDef{ + Name: "nil PC credentials", + Vals: v1alpha1.ClusterConfigSpec{ + Nutanix: &v1alpha1.NutanixSpec{ + PrismCentralEndpoint: v1alpha1.NutanixPrismCentralEndpointSpec{ + URL: fmt.Sprintf("https://prism-central.nutanix.com:%d", v1alpha1.DefaultPrismCentralPort), + Insecure: false, + }, + // ControlPlaneEndpoint is a required field and must always be set + ControlPlaneEndpoint: clusterv1.APIEndpoint{ + Host: "10.20.100.10", + Port: 6443, + }, + }, + }, + ExpectError: true, + }, + ) +} diff --git a/pkg/handlers/nutanix/workerconfig/variables.go b/pkg/handlers/nutanix/workerconfig/variables.go new file mode 100644 index 000000000..719be0404 --- /dev/null +++ b/pkg/handlers/nutanix/workerconfig/variables.go @@ -0,0 +1,52 @@ +// Copyright 2024 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package workerconfig + +import ( + "context" + + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + commonhandlers "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/workerconfig" +) + +var ( + _ commonhandlers.Named = &nutanixWorkerConfigVariableHandler{} + _ mutation.DiscoverVariables = &nutanixWorkerConfigVariableHandler{} +) + +const ( + // HandlerNameVariable is the name of the variable handler. + HandlerNameVariable = "NutanixWorkerConfigVars" + + // NutanixVariableName is the Nutanix config patch variable name. + NutanixVariableName = "nutanix" +) + +func NewVariable() *nutanixWorkerConfigVariableHandler { + return &nutanixWorkerConfigVariableHandler{} +} + +type nutanixWorkerConfigVariableHandler struct{} + +func (h *nutanixWorkerConfigVariableHandler) Name() string { + return HandlerNameVariable +} + +func (h *nutanixWorkerConfigVariableHandler) DiscoverVariables( + ctx context.Context, + _ *runtimehooksv1.DiscoverVariablesRequest, + resp *runtimehooksv1.DiscoverVariablesResponse, +) { + resp.Variables = append(resp.Variables, clusterv1.ClusterClassVariable{ + Name: workerconfig.MetaVariableName, + Required: false, + Schema: v1alpha1.NodeConfigSpec{Nutanix: &v1alpha1.NutanixNodeSpec{}}.VariableSchema(), + }) + resp.SetStatus(runtimehooksv1.ResponseStatusSuccess) +} diff --git a/pkg/handlers/nutanix/workerconfig/variables_test.go b/pkg/handlers/nutanix/workerconfig/variables_test.go new file mode 100644 index 000000000..aad787ce8 --- /dev/null +++ b/pkg/handlers/nutanix/workerconfig/variables_test.go @@ -0,0 +1,24 @@ +// Copyright 2024 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package workerconfig + +import ( + "testing" + + "k8s.io/utils/ptr" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/workerconfig" +) + +func TestVariableValidation(t *testing.T) { + capitest.ValidateDiscoverVariables( + t, + workerconfig.MetaVariableName, + ptr.To(v1alpha1.NodeConfigSpec{Nutanix: &v1alpha1.NutanixNodeSpec{}}.VariableSchema()), + false, + NewVariable, + ) +} diff --git a/pkg/handlers/options/global.go b/pkg/handlers/options/global.go index e355a4366..6f1de11a9 100644 --- a/pkg/handlers/options/global.go +++ b/pkg/handlers/options/global.go @@ -13,7 +13,8 @@ func NewGlobalOptions() *GlobalOptions { } type GlobalOptions struct { - defaultsNamespace string + defaultsNamespace string + helmAddonsConfigMapName string } func (o *GlobalOptions) AddFlags(flags *pflag.FlagSet) { @@ -23,8 +24,18 @@ func (o *GlobalOptions) AddFlags(flags *pflag.FlagSet) { corev1.NamespaceDefault, "namespace for default configurations", ) + flags.StringVar( + &o.helmAddonsConfigMapName, + "helm-addons-configmap", + "default-helm-addons-config", + "Name of helm addons configmap", + ) } func (o *GlobalOptions) DefaultsNamespace() string { return o.defaultsNamespace } + +func (o *GlobalOptions) HelmAddonsConfigMapName() string { + return o.helmAddonsConfigMapName +} diff --git a/test/e2e/config/cre.yaml b/test/e2e/config/caren.yaml similarity index 97% rename from test/e2e/config/cre.yaml rename to test/e2e/config/caren.yaml index b5ae76a0e..45ac1a554 100644 --- a/test/e2e/config/cre.yaml +++ b/test/e2e/config/caren.yaml @@ -1,7 +1,7 @@ # Copyright 2024 D2iQ, Inc. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -managementClusterName: cre-e2e +managementClusterName: caren-e2e images: - name: ko.local/cluster-api-runtime-extensions-nutanix:${E2E_IMAGE_TAG} @@ -101,12 +101,12 @@ providers: type: "url" contract: v1beta1 files: - - sourcePath: "../data/shared/v1beta1-helm/metadata.yaml" + - sourcePath: "../data/shared/v1beta1-caaph/metadata.yaml" replacements: - old: --metrics-addr=127.0.0.1:8080 new: --metrics-addr=:8080 -- name: cre +- name: caren type: RuntimeExtensionProvider versions: - name: "{go://github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix@v0.5}" @@ -125,7 +125,7 @@ providers: type: "url" contract: v1beta1 files: - - sourcePath: "../data/shared/v1beta1-cre/metadata.yaml" + - sourcePath: "../data/shared/v1beta1-caren/metadata.yaml" replacements: - old: "--v=0" new: "--v=8" diff --git a/test/e2e/data/shared/v1beta1-helm/metadata.yaml b/test/e2e/data/shared/v1beta1-caaph/metadata.yaml similarity index 100% rename from test/e2e/data/shared/v1beta1-helm/metadata.yaml rename to test/e2e/data/shared/v1beta1-caaph/metadata.yaml diff --git a/test/e2e/data/shared/v1beta1-cre/metadata.yaml b/test/e2e/data/shared/v1beta1-caren/metadata.yaml similarity index 100% rename from test/e2e/data/shared/v1beta1-cre/metadata.yaml rename to test/e2e/data/shared/v1beta1-caren/metadata.yaml diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index d41f1c440..a47770158 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -75,7 +75,7 @@ func init() { //nolint:gochecknoinits // Idiomatically used to set up flags. func TestE2E(t *testing.T) { ctrl.SetLogger(klog.Background()) RegisterFailHandler(Fail) - RunSpecs(t, "cre-e2e") + RunSpecs(t, "caren-e2e") } // Using a SynchronizedBeforeSuite for controlling how to create resources shared across ParallelNodes (~ginkgo @@ -238,7 +238,7 @@ func setupBootstrapCluster( } else { // Loading image for already created cluster imagesInput := capibootstrap.LoadImagesToKindClusterInput{ - Name: "cre-e2e", + Name: "caren-e2e", Images: config.Images, } err := capibootstrap.LoadImagesToKindCluster(context.TODO(), imagesInput) diff --git a/test/helpers/environment.go b/test/helpers/environment.go new file mode 100644 index 000000000..a8a69cd38 --- /dev/null +++ b/test/helpers/environment.go @@ -0,0 +1,34 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package helpers + +import ( + "context" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var TestEnv *TestEnvironment + +// Initialize the test environment. BeforeSuite will be only executed if this package is loaded by the test. +var _ = BeforeSuite(func(ctx SpecContext) { + By("Starting test environment") + testEnvConfig := NewTestEnvironmentConfiguration() + var err error + TestEnv, err = testEnvConfig.Build() + if err != nil { + panic(err) + } + By("Starting the manager") + go func() { + defer GinkgoRecover() + Expect(TestEnv.StartManager(ctx)).To(Succeed()) + }() +}, NodeTimeout(60*time.Second)) + +var _ = AfterSuite(func(ctx context.Context) { + Expect(TestEnv.Stop()).To(Succeed()) +}) diff --git a/test/helpers/envtest.go b/test/helpers/envtest.go index caaa1186d..9801cf740 100644 --- a/test/helpers/envtest.go +++ b/test/helpers/envtest.go @@ -130,16 +130,23 @@ func (t *TestEnvironmentConfiguration) Build() (*TestEnvironment, error) { }, nil } +// GetK8sClient returns a “live” k8s client that does will not invoke against controller cache. +// If a test is writeing an object, they are not immediately available to read since controller caches +// are not synchronized yet. +func (t *TestEnvironment) GetK8sClient() (client.Client, error) { + return client.New(t.Manager.GetConfig(), client.Options{Scheme: scheme.Scheme}) +} + // StartManager starts the test controller against the local API server. func (t *TestEnvironment) StartManager(ctx context.Context) error { - ctx, cancel := context.WithCancel(ctx) - t.cancel = cancel return t.Manager.Start(ctx) } // Stop stops the test environment. func (t *TestEnvironment) Stop() error { - t.cancel() + if t.cancel != nil { + t.cancel() + } return t.env.Stop() } diff --git a/webhooks/cluster.go b/webhooks/cluster.go new file mode 100644 index 000000000..0bdea09a7 --- /dev/null +++ b/webhooks/cluster.go @@ -0,0 +1,142 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package webhooks + +import ( + "context" + "fmt" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/validation/field" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/variables" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig" + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/workerconfig" +) + +// +kubebuilder:webhook:verbs=create;update,path=/mutate-cluster-x-k8s-io-v1beta1-cluster,mutating=true,failurePolicy=fail,matchPolicy=Equivalent,groups=cluster.x-k8s.io,resources=clusters,versions=v1beta1,name=default.cluster.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1 + +// Cluster implements a defaulting webhook for Cluster. +type Cluster struct{} + +var _ webhook.CustomDefaulter = &Cluster{} + +// SetupWebhookWithManager sets up Cluster webhooks. +func (webhook *Cluster) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(&clusterv1.Cluster{}). + WithDefaulter(webhook). + Complete() +} + +// Default satisfies the defaulting webhook interface. +func (webhook *Cluster) Default(_ context.Context, obj runtime.Object) error { + // We gather all defaulting errors and return them together. + var allErrs field.ErrorList + + cluster, ok := obj.(*clusterv1.Cluster) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a Cluster but got a %T", obj)) + } + + if cluster.Spec.Topology == nil || + len(cluster.Spec.Topology.Variables) == 0 { + return nil + } + + // Set defaults for 'clusterConfig' variable from spec.topology.variables + clusterConfigVariable, clusterConfigVariableIndex := variables.GetClusterVariableByName( + clusterconfig.MetaVariableName, + cluster.Spec.Topology.Variables, + ) + if clusterConfigVariable != nil { + clusterConfigSpec := &v1alpha1.ClusterConfigSpec{} + err := clusterConfigSpec.FromClusterVariable(clusterConfigVariable) + if err != nil { + return fmt.Errorf("failed to unmarshal ClusterConfigSpec from ClusterVariable: %w", err) + } + errs := defaultClusterConfig(clusterConfigSpec) + if len(errs) > 1 { + allErrs = append(allErrs, errs...) + } + clusterConfigVariable, err = clusterConfigSpec.ToClusterVariable(clusterconfig.MetaVariableName) + if err != nil { + return fmt.Errorf("failed to marshal ClusterConfigSpec to ClusterVariable: %w", err) + } + cluster.Spec.Topology.Variables[clusterConfigVariableIndex] = *clusterConfigVariable + + } + + // Set defaults from 'workerConfig' variable from spec.topology.variables + workerConfigVariable, workerConfigVariableIndex := variables.GetClusterVariableByName( + workerconfig.MetaVariableName, + cluster.Spec.Topology.Variables, + ) + if workerConfigVariable != nil { + workerConfigSpec := &v1alpha1.NodeConfigSpec{} + err := workerConfigSpec.FromClusterVariable(workerConfigVariable) + if err != nil { + return fmt.Errorf("failed to unmarshal NodeConfigSpec from WorkerVariable: %w", err) + } + errs := defaultWorkerConfig(workerConfigSpec) + if len(errs) > 1 { + allErrs = append(allErrs, errs...) + } + workerConfigVariable, err = workerConfigSpec.ToClusterVariable(workerconfig.MetaVariableName) + if err != nil { + return fmt.Errorf("failed to marshal NodeConfigSpec to WorkerVariable: %w", err) + } + cluster.Spec.Topology.Variables[workerConfigVariableIndex] = *workerConfigVariable + } + + // Set defaults for 'workerConfig' variable from spec.topology.workers.machineDeployments.variables.overrides + if cluster.Spec.Topology.Workers != nil { + for i, md := range cluster.Spec.Topology.Workers.MachineDeployments { + mdWorkerConfigVariable, mdWorkerConfigVariableIndex := variables.GetMachineDeploymentVariableByName( + workerconfig.MetaVariableName, + md.Variables, + ) + if mdWorkerConfigVariable != nil { + workerConfigSpec := &v1alpha1.NodeConfigSpec{} + err := workerConfigSpec.FromClusterVariable(mdWorkerConfigVariable) + if err != nil { + return fmt.Errorf("failed to unmarshal NodeConfigSpec from WorkerVariable: %w", err) + } + errs := defaultWorkerConfig(workerConfigSpec) + if len(errs) > 1 { + allErrs = append(allErrs, errs...) + } + mdWorkerConfigVariable, err = workerConfigSpec.ToClusterVariable(workerconfig.MetaVariableName) + if err != nil { + return fmt.Errorf("failed to marshal NodeConfigSpec to WorkerVariable: %w", err) + } + cluster.Spec.Topology.Workers.MachineDeployments[i].Variables.Overrides[mdWorkerConfigVariableIndex] = + *mdWorkerConfigVariable + } + } + } + + if len(allErrs) > 0 { + return apierrors.NewInvalid(clusterv1.GroupVersion.WithKind("Cluster").GroupKind(), cluster.Name, allErrs) + } + + return nil +} + +func defaultClusterConfig(clusterConfig *v1alpha1.ClusterConfigSpec) field.ErrorList { + // Set defaults for clusterConfig variable here. + + return nil +} + +func defaultWorkerConfig(workerConfig *v1alpha1.NodeConfigSpec) field.ErrorList { + // Set defaults for workerConfig variable here. + + return nil +} diff --git a/webhooks/webhooks.go b/webhooks/webhooks.go new file mode 100644 index 000000000..f1390fa51 --- /dev/null +++ b/webhooks/webhooks.go @@ -0,0 +1,26 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package webhooks + +import "github.com/spf13/pflag" + +type Options struct { + WebhookPort int + WebhookCertDir string +} + +func NewOptions() *Options { + return &Options{} +} + +func (s *Options) AddFlags(fs *pflag.FlagSet) { + fs.IntVar(&s.WebhookPort, "webhook-port", s.WebhookPort, "Webhook server port") + + fs.StringVar( + &s.WebhookCertDir, + "webhook-cert-dir", + s.WebhookCertDir, + "Webhook server cert dir", + ) +}