From 75fa733182fa6a2dffe489588e9beb018ab39f0d Mon Sep 17 00:00:00 2001 From: ehila Date: Tue, 1 Jul 2025 07:36:15 -0400 Subject: [PATCH] feat: add arbiter role support to ABI added support for arbiter installs to ABI flow, we currently do not support installing TechPreview featureSet with agent based install, this includes adding that capability for overriding featureSet to be passed to the assisted service. Signed-off-by: ehila --- .../testdata/add-nodes-overrides.txt | 1 + .../assets/with_additional_ntp_sources.txt | 1 + .../image/manifests/default_manifests.txt | 1 + .../configurations/from-ztp-manifests.txt | 1 + .../configurations/interactive.txt | 1 + .../local/bin/start-cluster-installation.sh | 3 +- .../assisted-service.env.template | 3 +- .../start-cluster/start-cluster.env.template | 1 + pkg/asset/agent/agentconfig/agenthosts.go | 9 +- .../agent/agentconfig/agenthosts_test.go | 24 +++-- pkg/asset/agent/image/ignition.go | 9 +- pkg/asset/agent/image/ignition_test.go | 5 +- pkg/asset/agent/installconfig.go | 22 +++-- .../agent/manifests/agentclusterinstall.go | 21 ++++ .../manifests/agentclusterinstall_test.go | 95 +++++++++++++++++++ pkg/asset/agent/manifests/nmstateconfig.go | 16 +++- pkg/asset/agent/manifests/util_test.go | 29 ++++-- 17 files changed, 209 insertions(+), 33 deletions(-) diff --git a/cmd/node-joiner/testdata/add-nodes-overrides.txt b/cmd/node-joiner/testdata/add-nodes-overrides.txt index 0b8de8f516..1ba80eced7 100644 --- a/cmd/node-joiner/testdata/add-nodes-overrides.txt +++ b/cmd/node-joiner/testdata/add-nodes-overrides.txt @@ -50,6 +50,7 @@ spec: status: agentLabelSelector: {} bootArtifacts: + discoveryIgnitionURL: "" initrd: "" ipxeScript: "" kernel: "" diff --git a/cmd/openshift-install/testdata/agent/image/assets/with_additional_ntp_sources.txt b/cmd/openshift-install/testdata/agent/image/assets/with_additional_ntp_sources.txt index 9e2ca55bdb..e3c4dff0de 100644 --- a/cmd/openshift-install/testdata/agent/image/assets/with_additional_ntp_sources.txt +++ b/cmd/openshift-install/testdata/agent/image/assets/with_additional_ntp_sources.txt @@ -74,6 +74,7 @@ spec: status: agentLabelSelector: {} bootArtifacts: + discoveryIgnitionURL: "" initrd: "" ipxeScript: "" kernel: "" diff --git a/cmd/openshift-install/testdata/agent/image/manifests/default_manifests.txt b/cmd/openshift-install/testdata/agent/image/manifests/default_manifests.txt index 4275248113..bda0098689 100644 --- a/cmd/openshift-install/testdata/agent/image/manifests/default_manifests.txt +++ b/cmd/openshift-install/testdata/agent/image/manifests/default_manifests.txt @@ -142,6 +142,7 @@ spec: status: agentLabelSelector: {} bootArtifacts: + discoveryIgnitionURL: "" initrd: "" ipxeScript: "" kernel: "" diff --git a/cmd/openshift-install/testdata/agent/unconfigured-ignition/configurations/from-ztp-manifests.txt b/cmd/openshift-install/testdata/agent/unconfigured-ignition/configurations/from-ztp-manifests.txt index 809fd67a0f..662863802c 100644 --- a/cmd/openshift-install/testdata/agent/unconfigured-ignition/configurations/from-ztp-manifests.txt +++ b/cmd/openshift-install/testdata/agent/unconfigured-ignition/configurations/from-ztp-manifests.txt @@ -81,6 +81,7 @@ spec: status: agentLabelSelector: {} bootArtifacts: + discoveryIgnitionURL: "" initrd: "" ipxeScript: "" kernel: "" diff --git a/cmd/openshift-install/testdata/agent/unconfigured-ignition/configurations/interactive.txt b/cmd/openshift-install/testdata/agent/unconfigured-ignition/configurations/interactive.txt index 1c8fec2803..f4990fad8c 100644 --- a/cmd/openshift-install/testdata/agent/unconfigured-ignition/configurations/interactive.txt +++ b/cmd/openshift-install/testdata/agent/unconfigured-ignition/configurations/interactive.txt @@ -82,6 +82,7 @@ spec: status: agentLabelSelector: {} bootArtifacts: + discoveryIgnitionURL: "" initrd: "" ipxeScript: "" kernel: "" diff --git a/data/data/agent/files/usr/local/bin/start-cluster-installation.sh b/data/data/agent/files/usr/local/bin/start-cluster-installation.sh index cc614c3cf1..7df66bacb4 100644 --- a/data/data/agent/files/usr/local/bin/start-cluster-installation.sh +++ b/data/data/agent/files/usr/local/bin/start-cluster-installation.sh @@ -18,8 +18,9 @@ done printf '\nInfra env id is %s\n' "${INFRA_ENV_ID}" 1>&2 -total_required_nodes=$(( REQUIRED_MASTER_NODES + REQUIRED_WORKER_NODES )) +total_required_nodes=$(( REQUIRED_MASTER_NODES + REQUIRED_ARBITER_NODES + REQUIRED_WORKER_NODES )) echo "Number of required master nodes: ${REQUIRED_MASTER_NODES}" 1>&2 +echo "Number of required arbiter nodes: ${REQUIRED_ARBITER_NODES}" 1>&2 echo "Number of required worker nodes: ${REQUIRED_WORKER_NODES}" 1>&2 echo "Total number of required nodes: ${total_required_nodes}" 1>&2 diff --git a/data/data/agent/files/usr/local/share/assisted-service/assisted-service.env.template b/data/data/agent/files/usr/local/share/assisted-service/assisted-service.env.template index 491f95c41c..973c02cb8d 100644 --- a/data/data/agent/files/usr/local/share/assisted-service/assisted-service.env.template +++ b/data/data/agent/files/usr/local/share/assisted-service/assisted-service.env.template @@ -9,9 +9,10 @@ DISK_ENCRYPTION_SUPPORT=true DUMMY_IGNITION=false ENABLE_SINGLE_NODE_DNSMASQ=true EPHEMERAL_INSTALLER_CLUSTER_TLS_CERTS_OVERRIDE_DIR=/opt/agent/tls -HW_VALIDATOR_REQUIREMENTS=[{"version":"default","master":{"cpu_cores":4,"ram_mib":16384,"disk_size_gb":100,"installation_disk_speed_threshold_ms":10,"network_latency_threshold_ms":100,"packet_loss_percentage":0},"worker":{"cpu_cores":2,"ram_mib":8192,"disk_size_gb":100,"installation_disk_speed_threshold_ms":10,"network_latency_threshold_ms":1000,"packet_loss_percentage":10},"sno":{"cpu_cores":8,"ram_mib":16384,"disk_size_gb":100,"installation_disk_speed_threshold_ms":10}}] +HW_VALIDATOR_REQUIREMENTS=[{"version":"default","master":{"cpu_cores":4,"ram_mib":16384,"disk_size_gb":100,"installation_disk_speed_threshold_ms":10,"network_latency_threshold_ms":100,"packet_loss_percentage":0},"arbiter":{"cpu_cores":2,"ram_mib":8192,"disk_size_gb":50,"installation_disk_speed_threshold_ms":10,"network_latency_threshold_ms":1000,"packet_loss_percentage":0},"worker":{"cpu_cores":2,"ram_mib":8192,"disk_size_gb":100,"installation_disk_speed_threshold_ms":10,"network_latency_threshold_ms":1000,"packet_loss_percentage":10},"sno":{"cpu_cores":8,"ram_mib":16384,"disk_size_gb":100,"installation_disk_speed_threshold_ms":10}}] INSTALL_INVOKER=agent-installer IPV6_SUPPORT=true +TNA_CLUSTERS_SUPPORT=true LOG_LEVEL=debug NTP_DEFAULT_SERVER= PUBLIC_CONTAINER_REGISTRIES={{.PublicContainerRegistries}} diff --git a/data/data/agent/files/usr/local/share/start-cluster/start-cluster.env.template b/data/data/agent/files/usr/local/share/start-cluster/start-cluster.env.template index 285553d508..2cda3fca8e 100644 --- a/data/data/agent/files/usr/local/share/start-cluster/start-cluster.env.template +++ b/data/data/agent/files/usr/local/share/start-cluster/start-cluster.env.template @@ -1,2 +1,3 @@ REQUIRED_MASTER_NODES={{.ControlPlaneAgents}} +REQUIRED_ARBITER_NODES={{.ArbiterAgents}} REQUIRED_WORKER_NODES={{.WorkerAgents}} diff --git a/pkg/asset/agent/agentconfig/agenthosts.go b/pkg/asset/agent/agentconfig/agenthosts.go index 62ac429a7b..d92d975630 100644 --- a/pkg/asset/agent/agentconfig/agenthosts.go +++ b/pkg/asset/agent/agentconfig/agenthosts.go @@ -26,8 +26,9 @@ var ( ) const ( - masterRole string = "master" - workerRole string = "worker" + masterRole string = "master" + workerRole string = "worker" + arbiterRole string = "arbiter" ) type nmStateInterface struct { @@ -193,9 +194,9 @@ func (a *AgentHosts) validateHostRootDeviceHints(hostPath *field.Path, host agen func (a *AgentHosts) validateRoles(hostPath *field.Path, host agent.Host) field.ErrorList { var allErrs field.ErrorList - if len(host.Role) > 0 && host.Role != masterRole && host.Role != workerRole { + if len(host.Role) > 0 && host.Role != masterRole && host.Role != arbiterRole && host.Role != workerRole { allErrs = append(allErrs, field.NotSupported(hostPath.Child("role"), host.Role, - []string{masterRole, workerRole})) + []string{masterRole, workerRole, arbiterRole})) } return allErrs diff --git a/pkg/asset/agent/agentconfig/agenthosts_test.go b/pkg/asset/agent/agentconfig/agenthosts_test.go index a7d4bc9f58..3b5a0d2e04 100644 --- a/pkg/asset/agent/agentconfig/agenthosts_test.go +++ b/pkg/asset/agent/agentconfig/agenthosts_test.go @@ -171,7 +171,7 @@ func TestAgentHosts_Generate(t *testing.T) { &workflow.AgentWorkflow{Workflow: workflow.AgentWorkflowTypeInstall}, &joiner.AddNodesConfig{}, getInstallConfigSingleHost(), - getAgentConfigMultiHost(), + getAgentConfigMultiHost("worker"), }, expectedConfig: agentHosts().hosts( agentHost().name("test").role("master").interfaces(iface("enp3s1", "28:d2:44:d2:b2:1a")).deviceHint().networkConfig(agentNetworkConfigOne), @@ -240,7 +240,7 @@ func TestAgentHosts_Generate(t *testing.T) { getInstallConfigSingleHost(), getAgentConfigInvalidHostRole(), }, - expectedError: "invalid Hosts configuration: hosts[0].role: Unsupported value: \"invalid-role\": supported values: \"master\", \"worker\"", + expectedError: "invalid Hosts configuration: hosts[0].role: Unsupported value: \"invalid-role\": supported values: \"master\", \"worker\", \"arbiter\"", expectedConfig: nil, }, { @@ -332,6 +332,18 @@ func TestAgentHosts_Generate(t *testing.T) { agentHost().name("test").role("master").interfaces(iface("enp3s1", "28:d2:44:d2:b2:1a")).deviceHint().networkConfig(agentNetworkConfigEmbeddedRendezvousIPOne), agentHost().name("test-2").role("worker").interfaces(iface("enp3s1", "28:d2:44:d2:b2:1b")).networkConfig(agentNetworkConfigEmbeddedRendezvousIPTwo)), }, + { + name: "multi-host-from-agent-config-with-arbiter", + dependencies: []asset.Asset{ + &workflow.AgentWorkflow{Workflow: workflow.AgentWorkflowTypeInstall}, + &joiner.AddNodesConfig{}, + getInstallConfigSingleHost(), + getAgentConfigMultiHost("arbiter"), + }, + expectedConfig: agentHosts().hosts( + agentHost().name("test").role("master").interfaces(iface("enp3s1", "28:d2:44:d2:b2:1a")).deviceHint().networkConfig(agentNetworkConfigOne), + agentHost().name("test-2").role("arbiter").interfaces(iface("enp3s1", "28:d2:44:d2:b2:1b")).networkConfig(agentNetworkConfigTwo)), + }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { @@ -439,12 +451,12 @@ func getAgentConfigSingleHost() *AgentConfig { return a } -func getAgentConfigMultiHost() *AgentConfig { +func getAgentConfigMultiHost(role string) *AgentConfig { a := getAgentConfigSingleHost() a.Config.Hosts[0].NetworkConfig.Raw = []byte(agentNetworkConfigOne) host := agent.Host{ Hostname: "test-2", - Role: "worker", + Role: role, Interfaces: []*aiv1beta1.Interface{ { Name: "enp3s1", @@ -460,7 +472,7 @@ func getAgentConfigMultiHost() *AgentConfig { } func getAgentConfigMultiHostEmbeddedRendezvousIP() *AgentConfig { - a := getAgentConfigMultiHost() + a := getAgentConfigMultiHost("worker") a.Config.RendezvousIP = "192.168.111.1" a.Config.Hosts[0].NetworkConfig.Raw = []byte(agentNetworkConfigEmbeddedRendezvousIPOne) a.Config.Hosts[1].NetworkConfig.Raw = []byte(agentNetworkConfigEmbeddedRendezvousIPTwo) @@ -536,7 +548,7 @@ func getAgentConfigMissingInterfaces() *AgentConfig { } func getAgentConfigInvalidRendezvousIP() *AgentConfig { - a := getAgentConfigMultiHost() + a := getAgentConfigMultiHost("worker") a.Config.RendezvousIP = "192.168.111.81" return a } diff --git a/pkg/asset/agent/image/ignition.go b/pkg/asset/agent/image/ignition.go index 565cb151af..dddb4c362d 100644 --- a/pkg/asset/agent/image/ignition.go +++ b/pkg/asset/agent/image/ignition.go @@ -65,6 +65,7 @@ type agentTemplateData struct { ServiceProtocol string PullSecret string ControlPlaneAgents int + ArbiterAgents int WorkerAgents int ReleaseImages string ReleaseImage string @@ -155,6 +156,7 @@ func (a *Ignition) Generate(ctx context.Context, dependencies asset.Parents) err clusterName := "" imageTypeISO := "full-iso" numMasters := 0 + numArbiters := 0 numWorkers := 0 enabledServices := getDefaultEnabledServices() openshiftVersion := "" @@ -178,6 +180,7 @@ func (a *Ignition) Generate(ctx context.Context, dependencies asset.Parents) err } // Fetch the required number of master and worker nodes. numMasters = agentManifests.AgentClusterInstall.Spec.ProvisionRequirements.ControlPlaneAgents + numArbiters = agentManifests.AgentClusterInstall.Spec.ProvisionRequirements.ArbiterAgents numWorkers = agentManifests.AgentClusterInstall.Spec.ProvisionRequirements.WorkerAgents // Enable specific install services enabledServices = append(enabledServices, "start-cluster-installation.service") @@ -202,6 +205,7 @@ func (a *Ignition) Generate(ctx context.Context, dependencies asset.Parents) err // is supported, so forcing the expected number of masters to zero, and assuming implcitly // that all the hosts defined are workers. numMasters = 0 + numArbiters = 0 numWorkers = len(addNodesConfig.Config.Hosts) // Enable add-nodes specific services @@ -290,7 +294,7 @@ func (a *Ignition) Generate(ctx context.Context, dependencies asset.Parents) err authConfig.AuthTokenExpiry, caBundleMount, len(registriesConfig.MirrorConfig) > 0, - numMasters, numWorkers, + numMasters, numArbiters, numWorkers, osImage, infraEnv.Spec.Proxy, ) @@ -413,13 +417,14 @@ func addBootstrapScripts(config *igntypes.Config, releaseImage string) (err erro func getTemplateData(name, pullSecret, releaseImageList, releaseImage, releaseImageMirror, publicContainerRegistries, imageTypeISO, infraEnvID, publicKey, authType, agentAuthToken, userAuthToken, watcherAuthToken, tokenExpiry, caBundleMount string, haveMirrorConfig bool, - numMasters, numWorkers int, + numMasters, numArbiters, numWorkers int, osImage *models.OsImage, proxy *v1beta1.Proxy) *agentTemplateData { return &agentTemplateData{ ServiceProtocol: "http", PullSecret: pullSecret, ControlPlaneAgents: numMasters, + ArbiterAgents: numArbiters, WorkerAgents: numWorkers, ReleaseImages: releaseImageList, ReleaseImage: releaseImage, diff --git a/pkg/asset/agent/image/ignition_test.go b/pkg/asset/agent/image/ignition_test.go index 504e9800d2..b09500ec80 100644 --- a/pkg/asset/agent/image/ignition_test.go +++ b/pkg/asset/agent/image/ignition_test.go @@ -62,6 +62,7 @@ func TestIgnition_getTemplateData(t *testing.T) { ProvisionRequirements: hiveext.ProvisionRequirements{ ControlPlaneAgents: 3, WorkerAgents: 5, + ArbiterAgents: 1, }, }, } @@ -96,11 +97,12 @@ func TestIgnition_getTemplateData(t *testing.T) { agentAuthToken := "agentAuthToken" userAuthToken := "userAuthToken" watcherAuthToken := "watcherAuthToken" - templateData := getTemplateData(clusterName, pullSecret, releaseImageList, releaseImage, releaseImageMirror, publicContainerRegistries, "minimal-iso", infraEnvID, publicKey, gencrypto.AuthType, agentAuthToken, userAuthToken, watcherAuthToken, "", "", haveMirrorConfig, agentClusterInstall.Spec.ProvisionRequirements.ControlPlaneAgents, agentClusterInstall.Spec.ProvisionRequirements.WorkerAgents, osImage, proxy) + templateData := getTemplateData(clusterName, pullSecret, releaseImageList, releaseImage, releaseImageMirror, publicContainerRegistries, "minimal-iso", infraEnvID, publicKey, gencrypto.AuthType, agentAuthToken, userAuthToken, watcherAuthToken, "", "", haveMirrorConfig, agentClusterInstall.Spec.ProvisionRequirements.ControlPlaneAgents, agentClusterInstall.Spec.ProvisionRequirements.ArbiterAgents, agentClusterInstall.Spec.ProvisionRequirements.WorkerAgents, osImage, proxy) assert.Equal(t, clusterName, templateData.ClusterName) assert.Equal(t, "http", templateData.ServiceProtocol) assert.Equal(t, pullSecret, templateData.PullSecret) assert.Equal(t, agentClusterInstall.Spec.ProvisionRequirements.ControlPlaneAgents, templateData.ControlPlaneAgents) + assert.Equal(t, agentClusterInstall.Spec.ProvisionRequirements.ArbiterAgents, templateData.ArbiterAgents) assert.Equal(t, agentClusterInstall.Spec.ProvisionRequirements.WorkerAgents, templateData.WorkerAgents) assert.Equal(t, releaseImageList, templateData.ReleaseImages) assert.Equal(t, releaseImage, templateData.ReleaseImage) @@ -649,6 +651,7 @@ func buildIgnitionAssetDefaultDependencies(t *testing.T) []asset.Asset { ProvisionRequirements: hiveext.ProvisionRequirements{ ControlPlaneAgents: 3, WorkerAgents: 5, + ArbiterAgents: 1, }, }, }, diff --git a/pkg/asset/agent/installconfig.go b/pkg/asset/agent/installconfig.go index 2abbadf781..69da3df708 100644 --- a/pkg/asset/agent/installconfig.go +++ b/pkg/asset/agent/installconfig.go @@ -88,14 +88,10 @@ func (a *OptionalInstallConfig) validateInstallConfig(ctx context.Context, insta allErrs = append(allErrs, err...) } - if installConfig.FeatureSet != configv1.Default { - allErrs = append(allErrs, field.NotSupported(field.NewPath("featureSet"), installConfig.FeatureSet, []string{string(configv1.Default)})) - } - warnUnusedConfig(installConfig) - numMasters, numWorkers := GetReplicaCount(installConfig) - logrus.Infof(fmt.Sprintf("Configuration has %d master replicas and %d worker replicas", numMasters, numWorkers)) + numMasters, numArbiters, numWorkers := GetReplicaCount(installConfig) + logrus.Infof("Configuration has %d master replicas, %d arbiter replicas, and %d worker replicas", numMasters, numArbiters, numWorkers) if err := a.validateControlPlaneConfiguration(installConfig); err != nil { allErrs = append(allErrs, err...) @@ -258,6 +254,10 @@ func (a *OptionalInstallConfig) validateSNOConfiguration(installConfig *types.In fieldPath = field.NewPath("compute", "replicas") allErrs = append(allErrs, field.Forbidden(fieldPath, fmt.Sprintf("Total number of compute replicas must be 0 when controlPlane.replicas is 1 for platform %s or %s. Found %v", none.Name, external.Name, workers))) } + if installConfig.Arbiter != nil && installConfig.Arbiter.Replicas != nil && *installConfig.Arbiter.Replicas > 0 { + fieldPath = field.NewPath("arbiter", "replicas") + allErrs = append(allErrs, field.Forbidden(fieldPath, fmt.Sprintf("Total number of arbiter replicas must be 0 when controlPlane.replicas is 1 for Single Node Openshift (SNO) cluster. Found %d", *installConfig.Arbiter.Replicas))) + } } return allErrs } @@ -518,12 +518,16 @@ func warnUnusedConfig(installConfig *types.InstallConfig) { } } -// GetReplicaCount gets the configured master and worker replicas. -func GetReplicaCount(installConfig *types.InstallConfig) (numMasters, numWorkers int64) { +// GetReplicaCount gets the configured master, arbiter and worker replicas. +func GetReplicaCount(installConfig *types.InstallConfig) (numMasters, numArbiters, numWorkers int64) { numRequiredMasters := int64(0) if installConfig.ControlPlane != nil && installConfig.ControlPlane.Replicas != nil { numRequiredMasters += *installConfig.ControlPlane.Replicas } + numRequiredArbiters := int64(0) + if installConfig.Arbiter != nil && installConfig.Arbiter.Replicas != nil { + numRequiredArbiters += *installConfig.Arbiter.Replicas + } numRequiredWorkers := int64(0) for _, worker := range installConfig.Compute { @@ -532,5 +536,5 @@ func GetReplicaCount(installConfig *types.InstallConfig) (numMasters, numWorkers } } - return numRequiredMasters, numRequiredWorkers + return numRequiredMasters, numRequiredArbiters, numRequiredWorkers } diff --git a/pkg/asset/agent/manifests/agentclusterinstall.go b/pkg/asset/agent/manifests/agentclusterinstall.go index 2b392d7e1f..e33680f44d 100644 --- a/pkg/asset/agent/manifests/agentclusterinstall.go +++ b/pkg/asset/agent/manifests/agentclusterinstall.go @@ -18,6 +18,7 @@ import ( "k8s.io/apimachinery/pkg/util/validation/field" "sigs.k8s.io/yaml" + configv1 "github.com/openshift/api/config/v1" operv1 "github.com/openshift/api/operator/v1" hiveext "github.com/openshift/assisted-service/api/hiveextension/v1beta1" aiv1beta1 "github.com/openshift/assisted-service/api/v1beta1" @@ -122,6 +123,10 @@ type agentClusterInstallInstallConfigOverrides struct { CPUPartitioning types.CPUPartitioningMode `json:"cpuPartitioningMode,omitempty"` // Allow override of AdditionalTrustBundlePolicy AdditionalTrustBundlePolicy types.PolicyType `json:"additionalTrustBundlePolicy,omitempty"` + // Allow override of FeatureSet + FeatureSet configv1.FeatureSet `json:"featureSet,omitempty"` + // Allow override of FeatureGates + FeatureGates []string `json:"featureGates,omitempty"` } var _ asset.WritableAsset = (*AgentClusterInstall)(nil) @@ -167,6 +172,11 @@ func (a *AgentClusterInstall) Generate(_ context.Context, dependencies asset.Par numberOfWorkers = numberOfWorkers + int(*compute.Replicas) } + numberOfArbiters := 0 + if installConfig.Config.IsArbiterEnabled() { + numberOfArbiters = int(*installConfig.Config.Arbiter.Replicas) + } + clusterNetwork := []hiveext.ClusterNetworkEntry{} for _, cn := range installConfig.Config.Networking.ClusterNetwork { entry := hiveext.ClusterNetworkEntry{ @@ -213,6 +223,7 @@ func (a *AgentClusterInstall) Generate(_ context.Context, dependencies asset.Par SSHPublicKey: strings.Trim(installConfig.Config.SSHKey, "|\n\t"), ProvisionRequirements: hiveext.ProvisionRequirements{ ControlPlaneAgents: int(*installConfig.Config.ControlPlane.Replicas), + ArbiterAgents: numberOfArbiters, WorkerAgents: numberOfWorkers, }, PlatformType: agent.HivePlatformType(installConfig.Config.Platform), @@ -237,6 +248,16 @@ func (a *AgentClusterInstall) Generate(_ context.Context, dependencies asset.Par icOverrides.FIPS = installConfig.Config.FIPS } + if len(installConfig.Config.FeatureSet) > 0 { + icOverridden = true + icOverrides.FeatureSet = installConfig.Config.FeatureSet + } + + if len(installConfig.Config.FeatureGates) > 0 { + icOverridden = true + icOverrides.FeatureGates = installConfig.Config.FeatureGates + } + if installConfig.Config.Proxy != nil { rendezvousIP := "" if agentConfig.Config != nil { diff --git a/pkg/asset/agent/manifests/agentclusterinstall_test.go b/pkg/asset/agent/manifests/agentclusterinstall_test.go index a9ea2317a4..5585fc92ec 100644 --- a/pkg/asset/agent/manifests/agentclusterinstall_test.go +++ b/pkg/asset/agent/manifests/agentclusterinstall_test.go @@ -150,6 +150,11 @@ func TestAgentClusterInstall_Generate(t *testing.T) { installConfigOverrides: `{"additionalTrustBundlePolicy":"Always"}`, }) + installConfigWithArbiter := getValidOptionalInstallConfigArbiter() + goodArbiterACI := getGoodACI() + goodArbiterACI.Spec.ProvisionRequirements.ArbiterAgents = 1 + goodArbiterACI.Spec.ProvisionRequirements.WorkerAgents = 0 + cases := []struct { name string dependencies []asset.Asset @@ -306,6 +311,16 @@ func TestAgentClusterInstall_Generate(t *testing.T) { }, expectedConfig: goodTrustBundlePolicyACI, }, + { + name: "valid configuration with ArbiterAgents", + dependencies: []asset.Asset{ + &workflow.AgentWorkflow{Workflow: workflow.AgentWorkflowTypeInstall}, + installConfigWithArbiter, + &agentconfig.AgentHosts{}, + &agentconfig.AgentConfig{}, + }, + expectedConfig: goodArbiterACI, + }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { @@ -422,6 +437,86 @@ spec: }, expectedError: "", }, + { + name: "valid-config-file-with-arbiter", + data: ` +metadata: + name: test-agent-cluster-install + namespace: cluster0 +spec: + apiVIP: 192.168.111.5 + ingressVIP: 192.168.111.4 + diskEncryption: + enableOn: workers + mode: tpmv2 + platformType: BareMetal + clusterDeploymentRef: + name: ostest + imageSetRef: + name: openshift-v4.10.0 + networking: + machineNetwork: + - cidr: 10.10.11.0/24 + clusterNetwork: + - cidr: 10.128.0.0/14 + hostPrefix: 23 + serviceNetwork: + - 172.30.0.0/16 + networkType: OVNKubernetes + provisionRequirements: + controlPlaneAgents: 3 + workerAgents: 2 + arbiterAgents: 1 + sshPublicKey: | + ssh-rsa AAAAmyKey`, + expectedFound: true, + expectedConfig: &hiveext.AgentClusterInstall{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-agent-cluster-install", + Namespace: "cluster0", + }, + Spec: hiveext.AgentClusterInstallSpec{ + APIVIP: "192.168.111.5", + IngressVIP: "192.168.111.4", + DiskEncryption: &hiveext.DiskEncryption{ + EnableOn: swag.String("workers"), + Mode: swag.String("tpmv2"), + }, + PlatformType: hiveext.BareMetalPlatformType, + ClusterDeploymentRef: corev1.LocalObjectReference{ + Name: "ostest", + }, + ImageSetRef: &hivev1.ClusterImageSetReference{ + Name: "openshift-v4.10.0", + }, + Networking: hiveext.Networking{ + MachineNetwork: []hiveext.MachineNetworkEntry{ + { + CIDR: "10.10.11.0/24", + }, + }, + ClusterNetwork: []hiveext.ClusterNetworkEntry{ + { + CIDR: "10.128.0.0/14", + HostPrefix: 23, + }, + }, + ServiceNetwork: []string{ + "172.30.0.0/16", + }, + NetworkType: "OVNKubernetes", + UserManagedNetworking: swag.Bool(false), + }, + ProvisionRequirements: hiveext.ProvisionRequirements{ + ControlPlaneAgents: 3, + WorkerAgents: 2, + ArbiterAgents: 1, + }, + SSHPublicKey: "ssh-rsa AAAAmyKey", + }, + }, + expectedError: "", + }, { name: "valid-config-file-external-oci-platform", data: ` diff --git a/pkg/asset/agent/manifests/nmstateconfig.go b/pkg/asset/agent/manifests/nmstateconfig.go index 8429d9479e..9f634bdd28 100644 --- a/pkg/asset/agent/manifests/nmstateconfig.go +++ b/pkg/asset/agent/manifests/nmstateconfig.go @@ -259,7 +259,7 @@ func GetNodeZeroIP(hosts []agenttype.Host, nmStateConfigs []*aiv1beta1.NMStateCo // Select first the configs from the hosts, if defined // Skip worker hosts (or without an explicit role assigned) for _, host := range hosts { - if host.Role != "master" { + if host.Role != "master" && host.Role != "arbiter" { continue } rawConfigs = append(rawConfigs, host.NetworkConfig.Raw) @@ -361,10 +361,11 @@ func buildMacInterfaceMap(nmStateConfig aiv1beta1.NMStateConfig) models.MacInter } func validateHostCount(installConfig *types.InstallConfig, agentHosts *agentconfig.AgentHosts) error { - numRequiredMasters, numRequiredWorkers := agent.GetReplicaCount(installConfig) + numRequiredMasters, numRequiredArbiters, numRequiredWorkers := agent.GetReplicaCount(installConfig) numMasters := int64(0) numWorkers := int64(0) + numArbiters := int64(0) // Check for hosts explicitly defined for _, host := range agentHosts.Hosts { switch host.Role { @@ -372,6 +373,8 @@ func validateHostCount(installConfig *types.InstallConfig, agentHosts *agentconf numMasters++ case "worker": numWorkers++ + case "arbiter": + numArbiters++ } } @@ -380,6 +383,8 @@ func validateHostCount(installConfig *types.InstallConfig, agentHosts *agentconf if host.Role == "" { if numMasters < numRequiredMasters { numMasters++ + } else if numArbiters < numRequiredArbiters { + numArbiters++ } else { numWorkers++ } @@ -393,6 +398,13 @@ func validateHostCount(installConfig *types.InstallConfig, agentHosts *agentconf return fmt.Errorf("the number of master hosts defined (%v) exceeds the configured ControlPlane replicas (%v)", numMasters, numRequiredMasters) } + if numArbiters != 0 && numArbiters < numRequiredArbiters { + logrus.Warnf("not enough arbiter hosts defined (%v) to support all the configured Arbiter replicas (%v)", numArbiters, numRequiredArbiters) + } + if numArbiters > numRequiredArbiters { + return fmt.Errorf("the number of arbiter hosts defined (%v) exceeds the configured Arbiter replicas (%v)", numArbiters, numRequiredArbiters) + } + if numWorkers != 0 && numWorkers < numRequiredWorkers { logrus.Warnf("not enough worker hosts defined (%v) to support all the configured Compute replicas (%v)", numWorkers, numRequiredWorkers) } diff --git a/pkg/asset/agent/manifests/util_test.go b/pkg/asset/agent/manifests/util_test.go index 7d09350d89..939b034e15 100644 --- a/pkg/asset/agent/manifests/util_test.go +++ b/pkg/asset/agent/manifests/util_test.go @@ -7,7 +7,7 @@ import ( "github.com/go-openapi/swag" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" "sigs.k8s.io/yaml" hiveext "github.com/openshift/assisted-service/api/hiveextension/v1beta1" @@ -84,17 +84,17 @@ func getValidOptionalInstallConfig() *agent.OptionalInstallConfig { SSHKey: testSSHKey, ControlPlane: &types.MachinePool{ Name: "master", - Replicas: pointer.Int64Ptr(3), + Replicas: ptr.To(int64(3)), Platform: types.MachinePoolPlatform{}, }, Compute: []types.MachinePool{ { Name: "worker-machine-pool-1", - Replicas: pointer.Int64Ptr(2), + Replicas: ptr.To(int64(2)), }, { Name: "worker-machine-pool-2", - Replicas: pointer.Int64Ptr(3), + Replicas: ptr.To(int64(3)), }, }, Networking: &types.Networking{ @@ -145,17 +145,17 @@ func getValidOptionalInstallConfigDualStack() *agent.OptionalInstallConfig { SSHKey: testSSHKey, ControlPlane: &types.MachinePool{ Name: "master", - Replicas: pointer.Int64Ptr(3), + Replicas: ptr.To(int64(3)), Platform: types.MachinePoolPlatform{}, }, Compute: []types.MachinePool{ { Name: "worker-machine-pool-1", - Replicas: pointer.Int64Ptr(2), + Replicas: ptr.To(int64(2)), }, { Name: "worker-machine-pool-2", - Replicas: pointer.Int64Ptr(3), + Replicas: ptr.To(int64(3)), }, }, Networking: &types.Networking{ @@ -200,6 +200,21 @@ func getValidOptionalInstallConfigDualStackDualVIPs() *agent.OptionalInstallConf return installConfig } +func getValidOptionalInstallConfigArbiter() *agent.OptionalInstallConfig { + installConfig := getValidOptionalInstallConfig() + installConfig.Config.Compute = []types.MachinePool{ + { + Name: "workers", + Replicas: ptr.To(int64(0)), + }, + } + installConfig.Config.Arbiter = &types.MachinePool{ + Name: "arbiter", + Replicas: ptr.To(int64(1)), + } + return installConfig +} + // getProxyValidOptionalInstallConfig returns a valid optional install config for proxied installation func getProxyValidOptionalInstallConfig() *agent.OptionalInstallConfig { validIC := getValidOptionalInstallConfig()