-
Notifications
You must be signed in to change notification settings - Fork 1.4k
OCPBUGS-56954: Disallow 1 worker node with exception #10042
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -18,6 +18,7 @@ import ( | |||||||||
| "k8s.io/apimachinery/pkg/util/sets" | ||||||||||
| "k8s.io/apimachinery/pkg/util/validation/field" | ||||||||||
| utilsnet "k8s.io/utils/net" | ||||||||||
| "k8s.io/utils/ptr" | ||||||||||
|
|
||||||||||
| configv1 "github.com/openshift/api/config/v1" | ||||||||||
| "github.com/openshift/api/features" | ||||||||||
|
|
@@ -797,6 +798,7 @@ func validateComputeEdge(platform *types.Platform, pName string, fldPath *field. | |||||||||
|
|
||||||||||
| func validateCompute(platform *types.Platform, control *types.MachinePool, pools []types.MachinePool, fldPath *field.Path) field.ErrorList { | ||||||||||
| allErrs := field.ErrorList{} | ||||||||||
| controlReplicas, computeReplicas := int64(0), int64(0) | ||||||||||
| // Multi Arch is enabled by default for AWS and GCP, these are also the only | ||||||||||
| // two valid platforms for multi arch installations. | ||||||||||
| isMultiArchEnabled := platform.AWS != nil || platform.GCP != nil | ||||||||||
|
|
@@ -805,6 +807,7 @@ func validateCompute(platform *types.Platform, control *types.MachinePool, pools | |||||||||
| poolFldPath := fldPath.Index(i) | ||||||||||
| switch p.Name { | ||||||||||
| case types.MachinePoolComputeRoleName: | ||||||||||
| computeReplicas += ptr.Deref(p.Replicas, 0) | ||||||||||
| case types.MachinePoolEdgeRoleName: | ||||||||||
| allErrs = append(allErrs, validateComputeEdge(platform, p.Name, poolFldPath, poolFldPath)...) | ||||||||||
| default: | ||||||||||
|
|
@@ -824,6 +827,16 @@ func validateCompute(platform *types.Platform, control *types.MachinePool, pools | |||||||||
| allErrs = append(allErrs, field.Invalid(poolFldPath.Child("fencing"), p.Fencing, "fencing is only valid for control plane")) | ||||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| if control != nil { | ||||||||||
| controlReplicas = ptr.Deref(control.Replicas, 3) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // Validate that exactly 1 worker node is allowed only when a single control plane node is provisioned on platforms None or External | ||||||||||
| if computeReplicas == 1 && controlReplicas != 1 && !(platform.None != nil || platform.External != nil) { | ||||||||||
| allErrs = append(allErrs, field.Invalid(fldPath, computeReplicas, "exactly 1 worker node is not allowed for this platform and configuration. Use 0 workers for single-node clusters or 2 workers for multi-node clusters")) | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we say
Suggested change
in this error msg?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 as users might mistake it to exactly 2. We should probably use the correct field path:
Suggested change
|
||||||||||
| } | ||||||||||
|
|
||||||||||
| return allErrs | ||||||||||
| } | ||||||||||
|
|
||||||||||
|
|
||||||||||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -9,7 +9,6 @@ import ( | |||||||
| "github.com/stretchr/testify/assert" | ||||||||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||||
| "k8s.io/apimachinery/pkg/util/validation/field" | ||||||||
| "k8s.io/utils/pointer" | ||||||||
| utilsslice "k8s.io/utils/strings/slices" | ||||||||
|
|
||||||||
| configv1 "github.com/openshift/api/config/v1" | ||||||||
|
|
@@ -31,6 +30,12 @@ import ( | |||||||
|
|
||||||||
| const TechPreviewNoUpgrade = "TechPreviewNoUpgrade" | ||||||||
|
|
||||||||
| func validControlPlane() *types.MachinePool { | ||||||||
| mp := validMachinePool("master") | ||||||||
| mp.Replicas = ptr.Int64(3) | ||||||||
| return mp | ||||||||
| } | ||||||||
|
|
||||||||
| func validInstallConfig() *types.InstallConfig { | ||||||||
| return &types.InstallConfig{ | ||||||||
| TypeMeta: metav1.TypeMeta{ | ||||||||
|
|
@@ -41,8 +46,8 @@ func validInstallConfig() *types.InstallConfig { | |||||||
| }, | ||||||||
| BaseDomain: "test-domain", | ||||||||
| Networking: validIPv4NetworkingConfig(), | ||||||||
| ControlPlane: validMachinePool("master"), | ||||||||
| Compute: []types.MachinePool{*validMachinePool("worker")}, | ||||||||
| ControlPlane: validControlPlane(), | ||||||||
| Compute: []types.MachinePool{func() types.MachinePool { p := *validMachinePool("worker"); p.Replicas = ptr.Int64(0); return p }()}, | ||||||||
| Platform: types.Platform{ | ||||||||
| AWS: validAWSPlatform(), | ||||||||
| }, | ||||||||
|
|
@@ -801,7 +806,7 @@ func TestValidateInstallConfig(t *testing.T) { | |||||||
| name: "control plane with 0 replicas", | ||||||||
| installConfig: func() *types.InstallConfig { | ||||||||
| c := validInstallConfig() | ||||||||
| c.ControlPlane.Replicas = pointer.Int64Ptr(0) | ||||||||
| c.ControlPlane.Replicas = ptr.Int64(0) | ||||||||
| return c | ||||||||
| }(), | ||||||||
| expectedError: `^controlPlane.replicas: Invalid value: 0: number of control plane replicas must be positive$`, | ||||||||
|
|
@@ -850,7 +855,69 @@ func TestValidateInstallConfig(t *testing.T) { | |||||||
| c.Compute = []types.MachinePool{ | ||||||||
| func() types.MachinePool { | ||||||||
| p := *validMachinePool("worker") | ||||||||
| p.Replicas = pointer.Int64Ptr(0) | ||||||||
| p.Replicas = ptr.Int64(0) | ||||||||
| return p | ||||||||
| }(), | ||||||||
| } | ||||||||
| return c | ||||||||
| }(), | ||||||||
| }, | ||||||||
| { | ||||||||
| name: "one compute replica, one control plane, AWS", | ||||||||
| installConfig: func() *types.InstallConfig { | ||||||||
| c := validInstallConfig() | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
The default now is |
||||||||
| c.Compute = []types.MachinePool{ | ||||||||
| func() types.MachinePool { | ||||||||
| p := *validMachinePool("worker") | ||||||||
| p.Replicas = ptr.Int64(1) | ||||||||
| return p | ||||||||
| }(), | ||||||||
| } | ||||||||
| return c | ||||||||
| }(), | ||||||||
| expectedError: `compute: Invalid value: 1: exactly 1 worker node is not allowed for this platform and configuration. Use 0 workers for single-node clusters or 2 workers for multi-node clusters`, | ||||||||
| }, | ||||||||
| { | ||||||||
| name: "one compute replicas, 3 control plane, AWS", | ||||||||
| installConfig: func() *types.InstallConfig { | ||||||||
| c := validInstallConfig() | ||||||||
| c.ControlPlane.Replicas = ptr.Int64(3) | ||||||||
| c.Compute = []types.MachinePool{ | ||||||||
| func() types.MachinePool { | ||||||||
| p := *validMachinePool("worker") | ||||||||
| p.Replicas = ptr.Int64(1) | ||||||||
| return p | ||||||||
| }(), | ||||||||
| } | ||||||||
| return c | ||||||||
| }(), | ||||||||
| expectedError: `compute: Invalid value: 1: exactly 1 worker node is not allowed for this platform and configuration. Use 0 workers for single-node clusters or 2 workers for multi-node clusters`, | ||||||||
| }, | ||||||||
| { | ||||||||
| name: "one compute replicas, one control plane, None/External", | ||||||||
| installConfig: func() *types.InstallConfig { | ||||||||
| c := validInstallConfig() | ||||||||
| c.Platform = types.Platform{ | ||||||||
| None: &none.Platform{}, | ||||||||
| } | ||||||||
| c.Compute = []types.MachinePool{ | ||||||||
| func() types.MachinePool { | ||||||||
| p := *validMachinePool("worker") | ||||||||
| p.Replicas = ptr.Int64(1) | ||||||||
| return p | ||||||||
| }(), | ||||||||
| } | ||||||||
| return c | ||||||||
| }(), | ||||||||
| }, | ||||||||
| { | ||||||||
| name: "two compute replicas allowed", | ||||||||
| installConfig: func() *types.InstallConfig { | ||||||||
| c := validInstallConfig() | ||||||||
| c.Compute = []types.MachinePool{ | ||||||||
| func() types.MachinePool { | ||||||||
| p := *validMachinePool("worker") | ||||||||
| p.Replicas = ptr.Int64(2) | ||||||||
| return p | ||||||||
| }(), | ||||||||
| } | ||||||||
|
|
@@ -910,6 +977,7 @@ func TestValidateInstallConfig(t *testing.T) { | |||||||
| name: "valid baremetal platform", | ||||||||
| installConfig: func() *types.InstallConfig { | ||||||||
| c := validInstallConfig() | ||||||||
| c.ControlPlane.Replicas = ptr.Int64(1) | ||||||||
| c.Capabilities = &types.Capabilities{BaselineCapabilitySet: "v4.11"} | ||||||||
| c.Capabilities.AdditionalEnabledCapabilities = append(c.Capabilities.AdditionalEnabledCapabilities, configv1.ClusterVersionCapabilityIngress, configv1.ClusterVersionCapabilityCloudCredential, configv1.ClusterVersionCapabilityCloudControllerManager, configv1.ClusterVersionCapabilityOperatorLifecycleManager) | ||||||||
| c.Platform = types.Platform{ | ||||||||
|
|
@@ -1672,6 +1740,7 @@ func TestValidateInstallConfig(t *testing.T) { | |||||||
| name: "invalidly set cloud credentials mode", | ||||||||
| installConfig: func() *types.InstallConfig { | ||||||||
| c := validInstallConfig() | ||||||||
| c.ControlPlane.Replicas = ptr.Int64(1) | ||||||||
| c.Platform = types.Platform{BareMetal: validBareMetalPlatform()} | ||||||||
| c.Capabilities = &types.Capabilities{BaselineCapabilitySet: configv1.ClusterVersionCapabilitySetCurrent} | ||||||||
| c.CredentialsMode = types.PassthroughCredentialsMode | ||||||||
|
|
@@ -1859,6 +1928,7 @@ func TestValidateInstallConfig(t *testing.T) { | |||||||
| name: "apivip_v4_not_in_machinenetwork_cidr_usermanaged_loadbalancer", | ||||||||
| installConfig: func() *types.InstallConfig { | ||||||||
| c := validInstallConfig() | ||||||||
| c.ControlPlane.Replicas = ptr.Int64(1) | ||||||||
| c.FeatureSet = configv1.TechPreviewNoUpgrade | ||||||||
| c.Networking.MachineNetwork = []types.MachineNetworkEntry{ | ||||||||
| {CIDR: *ipnet.MustParseCIDR("10.0.0.0/16")}, | ||||||||
|
|
@@ -2160,6 +2230,7 @@ func TestValidateInstallConfig(t *testing.T) { | |||||||
| name: "identical_apivip_ingressvip_usermanaged_loadbalancer", | ||||||||
| installConfig: func() *types.InstallConfig { | ||||||||
| c := validInstallConfig() | ||||||||
| c.ControlPlane.Replicas = ptr.Int64(1) | ||||||||
| c.FeatureSet = configv1.TechPreviewNoUpgrade | ||||||||
| c.Platform = types.Platform{ | ||||||||
| BareMetal: validBareMetalPlatform(), | ||||||||
|
|
@@ -2548,6 +2619,7 @@ func TestValidateInstallConfig(t *testing.T) { | |||||||
| c := validInstallConfig() | ||||||||
| c.BareMetal = validBareMetalPlatform() | ||||||||
| c.AWS = nil | ||||||||
| c.ControlPlane.Replicas = ptr.Int64(1) | ||||||||
| c.Capabilities = &types.Capabilities{ | ||||||||
| BaselineCapabilitySet: configv1.ClusterVersionCapabilitySetCurrent, | ||||||||
| } | ||||||||
|
|
@@ -2560,6 +2632,7 @@ func TestValidateInstallConfig(t *testing.T) { | |||||||
| c := validInstallConfig() | ||||||||
| c.BareMetal = validBareMetalPlatform() | ||||||||
| c.AWS = nil | ||||||||
| c.ControlPlane.Replicas = ptr.Int64(1) | ||||||||
| c.Capabilities = &types.Capabilities{ | ||||||||
| BaselineCapabilitySet: configv1.ClusterVersionCapabilitySetNone, | ||||||||
| AdditionalEnabledCapabilities: []configv1.ClusterVersionCapability{configv1.ClusterVersionCapabilityBaremetal, configv1.ClusterVersionCapabilityMachineAPI, configv1.ClusterVersionCapabilityIngress}, | ||||||||
|
|
@@ -2584,6 +2657,7 @@ func TestValidateInstallConfig(t *testing.T) { | |||||||
| c := validInstallConfig() | ||||||||
| c.Platform.AWS = nil | ||||||||
| c.Platform.None = &none.Platform{} | ||||||||
| c.ControlPlane.Replicas = ptr.Int64(1) | ||||||||
| c.Capabilities = &types.Capabilities{ | ||||||||
| BaselineCapabilitySet: configv1.ClusterVersionCapabilitySetNone, | ||||||||
| AdditionalEnabledCapabilities: []configv1.ClusterVersionCapability{configv1.ClusterVersionCapabilityIngress}, | ||||||||
|
|
@@ -2596,6 +2670,7 @@ func TestValidateInstallConfig(t *testing.T) { | |||||||
| installConfig: func() *types.InstallConfig { | ||||||||
| c := validInstallConfig() | ||||||||
| c.Platform.AWS = nil | ||||||||
| c.ControlPlane.Replicas = ptr.Int64(1) | ||||||||
| c.Platform.BareMetal = validBareMetalPlatform() | ||||||||
| c.Capabilities = &types.Capabilities{ | ||||||||
| BaselineCapabilitySet: configv1.ClusterVersionCapabilitySetNone, | ||||||||
|
|
||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding the comment, it hints that only SNO and platform none/external is allowed. But from #10042 (comment) and official doc, either SNO or none/external platform is allowed, which also matches the current code implementation.
I guess the comment is incorrect?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: It seems a bit more verbose, but improves readability? WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1+1 is actually not a valid topology on day 1. Assisted is not capable of creating an SNO with worker nodes.
But 3+1 is actually a valid topology, the bug report notwithstanding.