Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/build-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ jobs:
- "capx-feature-test"
- "nutanix-feature-test"
- "nutanix-storage-test"
- "projects"
- "scaling"
- "kubernetes-upgrade"
- "gpu"
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,13 @@ jobs:
NUTANIX_USER: ${{ secrets.NUTANIX_USER }}
NUTANIX_PASSWORD: ${{ secrets.NUTANIX_PASSWORD }}
NUTANIX_API_KEY: ${{ secrets.NUTANIX_API_KEY }}
NUTANIX_PROJECT_SCOPE_USER: ${{ secrets.NUTANIX_PROJECT_SCOPE_USER }}
NUTANIX_PROJECT_SCOPE_PASSWORD: ${{ secrets.NUTANIX_PROJECT_SCOPE_PASSWORD }}
NUTANIX_ENDPOINT: ${{ secrets.NUTANIX_ENDPOINT }}
NUTANIX_GPU_PASSTHROUGH_NAME: '${{ vars.NUTANIX_GPU_PASSTHROUGH_NAME }}'
NUTANIX_GPU_VIRTUAL_NAME: '${{ vars.NUTANIX_GPU_VIRTUAL_NAME }}'
NUTANIX_GPU_PHYSICAL_PROFILE_NAME: '${{ vars.NUTANIX_GPU_PHYSICAL_PROFILE_NAME }}'
NUTANIX_GPU_VIRTUAL_PROFILE_NAME: '${{ vars.NUTANIX_GPU_VIRTUAL_PROFILE_NAME }}'
NUTANIX_PRISM_ELEMENT_CLUSTER_IP: ${{ secrets.NUTANIX_PRISM_ELEMENT_CLUSTER_IP }}
NUTANIX_PRISM_ELEMENT_CLUSTER_USERNAME: ${{ secrets.NUTANIX_PRISM_ELEMENT_CLUSTER_USERNAME }}
NUTANIX_PRISM_ELEMENT_CLUSTER_PASSWORD: ${{ secrets.NUTANIX_PRISM_ELEMENT_CLUSTER_PASSWORD }}
Expand Down
20 changes: 13 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ CNI_PATH_CILIUM ?= "${E2E_DIR}/data/cni/cilium/cilium.yaml"
CNI_PATH_CILIUM_NO_KUBEPROXY ?= "${E2E_DIR}/data/cni/cilium/cilium-no-kubeproxy.yaml"
CNI_PATH_FLANNEL ?= "${E2E_DIR}/data/cni/flannel/flannel.yaml"
CNI_PATH_KINDNET ?= "${E2E_DIR}/data/cni/kindnet/kindnet.yaml"
CCM_VERSION ?= 0.7.0-alpha.1
CCM_VERSION ?= 0.8.0-dev.1
CCM_REPO ?= ghcr.io/nutanix-cloud-native/cloud-provider-nutanix/controller
# Auto-select credential type: API key takes precedence when set.
NUTANIX_CREDENTIALS_TYPE ?= $(if $(strip $(NUTANIX_API_KEY)),api_key,basic_auth)
NUTANIX_CREDENTIALS_OVERLAY ?= $(subst _,-,$(NUTANIX_CREDENTIALS_TYPE))
Expand Down Expand Up @@ -131,7 +132,7 @@ define ginkgo_option
endef

.PHONY: all
all: build
all: mocks generate fmt vet lint build

##@ General

Expand Down Expand Up @@ -225,12 +226,12 @@ update-kindnet-cni: ## Updates the kindnet CNI manifests
@curl -sL https://github.com/kubernetes-sigs/cluster-api/raw/main/test/e2e/data/cni/kindnet/kindnet.yaml -o $(CNI_PATH_KINDNET)

.PHONY: update-ccm
update-ccm: ## Updates the Nutanix CCM tag in all the template manifests to CCM_VERSION
@echo "Updating Nutanix CCM tag to $(CCM_VERSION)"
@find $(TEMPLATES_DIR) -type f -name "*.yaml" -exec sed -i '' 's|CCM_TAG=[^}]*|CCM_TAG=$(CCM_VERSION)|g' {} +
@find $(NUTANIX_E2E_TEMPLATES) -type f -name "*.yaml" -exec sed -i '' 's|CCM_TAG=[^}]*|CCM_TAG=$(CCM_VERSION)|g' {} +
update-ccm: ## Updates the Nutanix CCM repo/tag in all the template manifests to CCM_REPO/CCM_VERSION
@echo "Updating Nutanix CCM image to $(CCM_REPO):$(CCM_VERSION)"
@find $(TEMPLATES_DIR) -type f -name "*.yaml" -exec sed -i '' 's|CCM_REPO=[^}]*|CCM_REPO=$(CCM_REPO)|g; s|CCM_TAG=[^}]*|CCM_TAG=$(CCM_VERSION)|g' {} +
@find $(NUTANIX_E2E_TEMPLATES) -type f -name "*.yaml" -exec sed -i '' 's|CCM_REPO=[^}]*|CCM_REPO=$(CCM_REPO)|g; s|CCM_TAG=[^}]*|CCM_TAG=$(CCM_VERSION)|g' {} +
@sed -i '' 's|CCM_TAG: ".*"|CCM_TAG: "$(CCM_VERSION)"|g' $(E2E_DIR)/config/nutanix.yaml
@echo "Updated CCM tag to $(CCM_VERSION) in templates and E2E config"
@echo "Updated CCM image to $(CCM_REPO):$(CCM_VERSION) in templates and E2E config"

.PHONY: update-cni-manifests ## Updates all the CNI manifests to latest variants from upstream
update-cni-manifests: update-calico-cni update-cilium-cni update-flannel-cni update-kindnet-cni ## Updates all the CNI manifests to latest variants from upstream
Expand Down Expand Up @@ -307,7 +308,9 @@ cluster-e2e-templates-v1beta1: ## Generate cluster templates for v1beta1
kustomize build $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-no-nutanix-cluster --load-restrictor LoadRestrictionsNone > $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-no-nutanix-cluster.yaml
kustomize build $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-additional-categories --load-restrictor LoadRestrictionsNone > $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-additional-categories.yaml
kustomize build $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-no-nmt --load-restrictor LoadRestrictionsNone > $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-no-nmt.yaml
kustomize build $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-no-nmt-project-scoped-user --load-restrictor LoadRestrictionsNone > $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-no-nmt-project-scoped-user.yaml
kustomize build $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-project --load-restrictor LoadRestrictionsNone > $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-project.yaml
kustomize build $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-project-scoped-user --load-restrictor LoadRestrictionsNone > $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-project-scoped-user.yaml
kustomize build $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-upgrades --load-restrictor LoadRestrictionsNone > $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-upgrades.yaml
kustomize build $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-md-remediation --load-restrictor LoadRestrictionsNone > $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-md-remediation.yaml
kustomize build $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-md-taints --load-restrictor LoadRestrictionsNone > $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-md-taints.yaml
Expand All @@ -321,6 +324,7 @@ cluster-e2e-templates-v1beta1: ## Generate cluster templates for v1beta1
kustomize build $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-topology-autoscaler --load-restrictor LoadRestrictionsNone > $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-topology-autoscaler.yaml
kustomize build $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-image-lookup --load-restrictor LoadRestrictionsNone > $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-image-lookup.yaml
kustomize build $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-failure-domains-migration --load-restrictor LoadRestrictionsNone > $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-failure-domains-migration.yaml
kustomize build $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-vmprofile --load-restrictor LoadRestrictionsNone > $(NUTANIX_E2E_TEMPLATES)/v1beta1/cluster-template-vmprofile.yaml

cluster-e2e-templates-no-kubeproxy: ##Generate cluster templates without kubeproxy
# v1beta1
Expand Down Expand Up @@ -391,6 +395,8 @@ mocks: ## Generate mocks for the project
mockgen -destination=mocks/converged/ovas.go -package=mockconverged github.com/nutanix-cloud-native/prism-go-client/converged Ovas
mockgen -destination=mocks/converged/protection_policies.go -package=mockconverged github.com/nutanix-cloud-native/prism-go-client/converged ProtectionPolicies
mockgen -destination=mocks/converged/recovery_plans.go -package=mockconverged github.com/nutanix-cloud-native/prism-go-client/converged RecoveryPlans
mockgen -destination=mocks/converged/projects.go -package=mockconverged github.com/nutanix-cloud-native/prism-go-client/converged Projects
mockgen -destination=mocks/converged/resource_groups.go -package=mockconverged github.com/nutanix-cloud-native/prism-go-client/converged ResourceGroups

# Disable VCS stamping for `go list` / `go test` so unit tests run in partial checkouts and CI sandboxes.
GOTESTPKGS = $(shell GOFLAGS=-buildvcs=false go list ./... | grep -v /mocks | grep -v /templates)
Expand Down
4 changes: 3 additions & 1 deletion api/v1beta1/conditions.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ const (
)

const (
// ClusterCategoryCreatedCondition indicates the status of the category linked to the NutanixCluster
// ClusterCategoryCreatedCondition indicates the status of the default CAPX
// categories: set True by the machine reconciler on first successful
// create/get, flipped False by the cluster reconciler on delete.
ClusterCategoryCreatedCondition capiv1beta1.ConditionType = "ClusterCategoryCreated"

ClusterCategoryCreationFailed = "ClusterCategoryCreationFailed"
Expand Down
17 changes: 16 additions & 1 deletion api/v1beta1/nutanix_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ const (
// NutanixGPUIdentifierDeviceID is a resource identifier identifying a GPU using device ID.
NutanixGPUIdentifierDeviceID NutanixGPUIdentifierType = "deviceID"

// NutanixGPUIdentifierProfile is a resource identifier identifying a GPU by an AHV
// GPU profile. Profile-based GPU assignment is only supported on PC 7.6 or later.
NutanixGPUIdentifierProfile NutanixGPUIdentifierType = "profile"

// ObsoleteDefaultCAPICategoryPrefix is the obsolete default category prefix used for CAPI clusters.
ObsoleteDefaultCAPICategoryPrefix = "kubernetes-io-cluster-"

Expand Down Expand Up @@ -145,10 +149,15 @@ type NutanixCategoryIdentifier struct {
Value string `json:"value,omitempty"`
}

// NutanixGPU identifies a GPU to attach to a VM, either by device (name/deviceID) or
// by an AHV GPU profile.
// +kubebuilder:validation:XValidation:rule="self.type == 'name' ? has(self.name) : !has(self.name)",message="'name' must be set when type is 'name', and forbidden otherwise"
// +kubebuilder:validation:XValidation:rule="self.type == 'deviceID' ? has(self.deviceID) : !has(self.deviceID)",message="'deviceID' must be set when type is 'deviceID', and forbidden otherwise"
// +kubebuilder:validation:XValidation:rule="self.type == 'profile' ? has(self.profile) : !has(self.profile)",message="'profile' must be set when type is 'profile', and forbidden otherwise"
type NutanixGPU struct {
// Type is the identifier type to use for this resource.
// +kubebuilder:validation:Required
// +kubebuilder:validation:Enum:=deviceID;name
// +kubebuilder:validation:Enum:=deviceID;name;profile
Type NutanixGPUIdentifierType `json:"type"`

// deviceID is the id of the GPU entity.
Expand All @@ -157,5 +166,11 @@ type NutanixGPU struct {

// name is the GPU name
// +optional
// +kubebuilder:validation:MinLength=1
Name *string `json:"name,omitempty"`

// profile identifies the AHV GPU profile to assign by name or UUID. Profile-based GPU
// assignment uses the named GPU profile APIs and is only supported on PC 7.6 or later.
// +optional
Profile *NutanixResourceIdentifier `json:"profile,omitempty"`
}
38 changes: 30 additions & 8 deletions api/v1beta1/nutanixmachine_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ type NutanixImageLookup struct {
// NutanixMachineSpec defines the desired state of NutanixMachine
// +kubebuilder:validation:XValidation:rule="has(self.image) != has(self.imageLookup)",message="Either 'image' or 'imageLookup' must be set, but not both"
// +kubebuilder:validation:XValidation:rule="has(self.subnet) && size(self.subnet) > 1 ? self.subnet.all(x, self.subnet.exists_one(y, x == y)) : true",message="each subnet must be unique"
// NOTE: For non-pointer scalar fields (e.g. int32/string aliases), CEL has(...) can
// evaluate as present due zero-value decoding in CRDs. Use value checks instead.
// +kubebuilder:validation:XValidation:rule="has(self.vmProfile) ? ((!has(self.vcpusPerSocket) || self.vcpusPerSocket == 0) && (!has(self.vcpuSockets) || self.vcpuSockets == 0) && (!has(self.memorySize) || self.memorySize == \"\" || self.memorySize == \"0\") && (!has(self.bootType) || self.bootType == \"\") && (!has(self.gpus) || size(self.gpus) == 0) && (!has(self.dataDisks) || size(self.dataDisks) == 0)) : true",message="When 'vmProfile' is set, 'vcpusPerSocket', 'vcpuSockets', 'memorySize', 'bootType', 'gpus', and 'dataDisks' must not be set"
// +kubebuilder:validation:XValidation:rule="!has(self.vmProfile) ? (has(self.vcpusPerSocket) && self.vcpusPerSocket > 0 && has(self.vcpuSockets) && self.vcpuSockets > 0 && has(self.memorySize) && self.memorySize != \"\" && self.memorySize != \"0\") : true",message="When 'vmProfile' is not set, 'vcpusPerSocket', 'vcpuSockets', and 'memorySize' must be set"
type NutanixMachineSpec struct {
// SPEC FIELDS - desired state of NutanixMachine
// Important: Run "make" to regenerate code after modifying this file
Expand All @@ -107,17 +111,20 @@ type NutanixMachineSpec struct {
// +optional
ProviderID string `json:"providerID,omitempty"`
// vcpusPerSocket is the number of vCPUs per socket of the VM
// +kubebuilder:validation:Required
// Required when VMProfile is not set, mutually exclusive with VMProfile
// +kubebuilder:validation:Optional
// +kubebuilder:validation:Minimum=1
VCPUsPerSocket int32 `json:"vcpusPerSocket"`
// vcpuSockets is the number of vCPU sockets of the VM
// +kubebuilder:validation:Required
VCPUsPerSocket int32 `json:"vcpusPerSocket,omitzero"`
// vCPUsockets is the number of vCPU sockets of the VM
// Required when VMProfile is not set, mutually exclusive with VMProfile
// +kubebuilder:validation:Optional
// +kubebuilder:validation:Minimum=1
VCPUSockets int32 `json:"vcpuSockets"`
VCPUSockets int32 `json:"vcpuSockets,omitzero"`
// memorySize is the memory size (in Quantity format) of the VM
// The minimum memorySize is 2Gi bytes
// +kubebuilder:validation:Required
MemorySize resource.Quantity `json:"memorySize"`
// Required when VMProfile is not set, mutually exclusive with VMProfile
// +kubebuilder:validation:Optional
MemorySize resource.Quantity `json:"memorySize,omitempty"`
// image is to identify the nutanix machine 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.
Expand Down Expand Up @@ -147,15 +154,18 @@ type NutanixMachineSpec struct {
// +optional
Project *NutanixResourceIdentifier `json:"project,omitempty"`
// Defines the boot type of the virtual machine. Only supports UEFI and Legacy
// Mutually exclusive with vmProfile. When vmProfile is set, bootType must not be set.
// +kubebuilder:validation:Optional
// +kubebuilder:validation:Enum:=legacy;uefi
BootType NutanixBootType `json:"bootType,omitempty"`
// +optional
BootType NutanixBootType `json:"bootType,omitzero"`
// systemDiskSize is size (in Quantity format) of the system disk of the VM
// The minimum systemDiskSize is 20Gi bytes
// +kubebuilder:validation:Required
SystemDiskSize resource.Quantity `json:"systemDiskSize"`

// dataDisks hold the list of data disks to be attached to the VM
// Mutually exclusive with VMProfile. When VMProfile is set, dataDisks must not be set.
// +kubebuilder:validation:Optional
DataDisks []NutanixMachineVMDisk `json:"dataDisks,omitempty"`

Expand All @@ -164,8 +174,20 @@ type NutanixMachineSpec struct {
// +optional
BootstrapRef *corev1.ObjectReference `json:"bootstrapRef,omitempty"`
// List of GPU devices that need to be added to the machines.
// Mutually exclusive with VMProfile. When VMProfile is set, GPUs must not be set.
// +kubebuilder:validation:Optional
// +kubebuilder:validation:MaxItems=32
GPUs []NutanixGPU `json:"gpus,omitempty"`

// VMProfile is to identify the VM profile in Prism Central (PC)
// The VM profile identifier (uuid or name) can be obtained from the Prism Central console
// or using the prism_central API.
// When VMProfile is set, it provides CPU, memory, and bootType configuration.
// vcpusPerSocket, vcpuSockets, memorySize, bootType, and GPUs are mutually exclusive with VMProfile;
// dataDisks is currently mutually exclusive with VMProfile (support for this combination may be added later).
// +kubebuilder:validation:Optional
// +optional
VMProfile *NutanixResourceIdentifier `json:"vmProfile,omitempty"`
}

// NutanixMachineVMDisk defines the disk configuration for a NutanixMachine
Expand Down
10 changes: 10 additions & 0 deletions api/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading