Skip to content

Commit 6ecd1b4

Browse files
committed
Disallow 1 worker node with exception
1 worker node is allowed only when SNO is configured on platforms None or External. Add validation disallowing 1 worker node for other scenarios.
1 parent d7dc751 commit 6ecd1b4

File tree

2 files changed

+93
-5
lines changed

2 files changed

+93
-5
lines changed

pkg/types/validation/installconfig.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"k8s.io/apimachinery/pkg/util/sets"
1919
"k8s.io/apimachinery/pkg/util/validation/field"
2020
utilsnet "k8s.io/utils/net"
21+
"k8s.io/utils/ptr"
2122

2223
configv1 "github.com/openshift/api/config/v1"
2324
"github.com/openshift/api/features"
@@ -797,6 +798,7 @@ func validateComputeEdge(platform *types.Platform, pName string, fldPath *field.
797798

798799
func validateCompute(platform *types.Platform, control *types.MachinePool, pools []types.MachinePool, fldPath *field.Path) field.ErrorList {
799800
allErrs := field.ErrorList{}
801+
controlReplicas, computeReplicas := int64(0), int64(0)
800802
// Multi Arch is enabled by default for AWS and GCP, these are also the only
801803
// two valid platforms for multi arch installations.
802804
isMultiArchEnabled := platform.AWS != nil || platform.GCP != nil
@@ -805,6 +807,7 @@ func validateCompute(platform *types.Platform, control *types.MachinePool, pools
805807
poolFldPath := fldPath.Index(i)
806808
switch p.Name {
807809
case types.MachinePoolComputeRoleName:
810+
computeReplicas += ptr.Deref(p.Replicas, 0)
808811
case types.MachinePoolEdgeRoleName:
809812
allErrs = append(allErrs, validateComputeEdge(platform, p.Name, poolFldPath, poolFldPath)...)
810813
default:
@@ -824,6 +827,16 @@ func validateCompute(platform *types.Platform, control *types.MachinePool, pools
824827
allErrs = append(allErrs, field.Invalid(poolFldPath.Child("fencing"), p.Fencing, "fencing is only valid for control plane"))
825828
}
826829
}
830+
831+
if control != nil {
832+
controlReplicas = ptr.Deref(control.Replicas, 3)
833+
}
834+
835+
// Validate that exactly 1 worker node is allowed only when a single control plane node is provisioned on platforms None or External
836+
if computeReplicas == 1 && controlReplicas != 1 && !(platform.None != nil || platform.External != nil) {
837+
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"))
838+
}
839+
827840
return allErrs
828841
}
829842

pkg/types/validation/installconfig_test.go

Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"github.com/stretchr/testify/assert"
1010
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1111
"k8s.io/apimachinery/pkg/util/validation/field"
12-
"k8s.io/utils/pointer"
1312
utilsslice "k8s.io/utils/strings/slices"
1413

1514
configv1 "github.com/openshift/api/config/v1"
@@ -31,6 +30,12 @@ import (
3130

3231
const TechPreviewNoUpgrade = "TechPreviewNoUpgrade"
3332

33+
func validControlPlane() *types.MachinePool {
34+
mp := validMachinePool("master")
35+
mp.Replicas = ptr.To[int64](3)
36+
return mp
37+
}
38+
3439
func validInstallConfig() *types.InstallConfig {
3540
return &types.InstallConfig{
3641
TypeMeta: metav1.TypeMeta{
@@ -41,8 +46,8 @@ func validInstallConfig() *types.InstallConfig {
4146
},
4247
BaseDomain: "test-domain",
4348
Networking: validIPv4NetworkingConfig(),
44-
ControlPlane: validMachinePool("master"),
45-
Compute: []types.MachinePool{*validMachinePool("worker")},
49+
ControlPlane: validControlPlane(),
50+
Compute: []types.MachinePool{func() types.MachinePool { p := *validMachinePool("worker"); p.Replicas = ptr.To[int64](0); return p }()},
4651
Platform: types.Platform{
4752
AWS: validAWSPlatform(),
4853
},
@@ -801,7 +806,7 @@ func TestValidateInstallConfig(t *testing.T) {
801806
name: "control plane with 0 replicas",
802807
installConfig: func() *types.InstallConfig {
803808
c := validInstallConfig()
804-
c.ControlPlane.Replicas = pointer.Int64Ptr(0)
809+
c.ControlPlane.Replicas = ptr.To[int64](0)
805810
return c
806811
}(),
807812
expectedError: `^controlPlane.replicas: Invalid value: 0: number of control plane replicas must be positive$`,
@@ -850,7 +855,69 @@ func TestValidateInstallConfig(t *testing.T) {
850855
c.Compute = []types.MachinePool{
851856
func() types.MachinePool {
852857
p := *validMachinePool("worker")
853-
p.Replicas = pointer.Int64Ptr(0)
858+
p.Replicas = ptr.To[int64](0)
859+
return p
860+
}(),
861+
}
862+
return c
863+
}(),
864+
},
865+
{
866+
name: "one compute replica, one control plane, AWS",
867+
installConfig: func() *types.InstallConfig {
868+
c := validInstallConfig()
869+
c.Compute = []types.MachinePool{
870+
func() types.MachinePool {
871+
p := *validMachinePool("worker")
872+
p.Replicas = ptr.To[int64](1)
873+
return p
874+
}(),
875+
}
876+
return c
877+
}(),
878+
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`,
879+
},
880+
{
881+
name: "one compute replicas, 3 control plane, AWS",
882+
installConfig: func() *types.InstallConfig {
883+
c := validInstallConfig()
884+
c.ControlPlane.Replicas = ptr.To[int64](3)
885+
c.Compute = []types.MachinePool{
886+
func() types.MachinePool {
887+
p := *validMachinePool("worker")
888+
p.Replicas = ptr.To[int64](1)
889+
return p
890+
}(),
891+
}
892+
return c
893+
}(),
894+
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`,
895+
},
896+
{
897+
name: "one compute replicas, one control plane, None/External",
898+
installConfig: func() *types.InstallConfig {
899+
c := validInstallConfig()
900+
c.Platform = types.Platform{
901+
None: &none.Platform{},
902+
}
903+
c.Compute = []types.MachinePool{
904+
func() types.MachinePool {
905+
p := *validMachinePool("worker")
906+
p.Replicas = ptr.To[int64](1)
907+
return p
908+
}(),
909+
}
910+
return c
911+
}(),
912+
},
913+
{
914+
name: "two compute replicas allowed",
915+
installConfig: func() *types.InstallConfig {
916+
c := validInstallConfig()
917+
c.Compute = []types.MachinePool{
918+
func() types.MachinePool {
919+
p := *validMachinePool("worker")
920+
p.Replicas = ptr.To[int64](2)
854921
return p
855922
}(),
856923
}
@@ -910,6 +977,7 @@ func TestValidateInstallConfig(t *testing.T) {
910977
name: "valid baremetal platform",
911978
installConfig: func() *types.InstallConfig {
912979
c := validInstallConfig()
980+
c.ControlPlane.Replicas = ptr.To[int64](1)
913981
c.Capabilities = &types.Capabilities{BaselineCapabilitySet: "v4.11"}
914982
c.Capabilities.AdditionalEnabledCapabilities = append(c.Capabilities.AdditionalEnabledCapabilities, configv1.ClusterVersionCapabilityIngress, configv1.ClusterVersionCapabilityCloudCredential, configv1.ClusterVersionCapabilityCloudControllerManager, configv1.ClusterVersionCapabilityOperatorLifecycleManager)
915983
c.Platform = types.Platform{
@@ -1672,6 +1740,7 @@ func TestValidateInstallConfig(t *testing.T) {
16721740
name: "invalidly set cloud credentials mode",
16731741
installConfig: func() *types.InstallConfig {
16741742
c := validInstallConfig()
1743+
c.ControlPlane.Replicas = ptr.To[int64](1)
16751744
c.Platform = types.Platform{BareMetal: validBareMetalPlatform()}
16761745
c.Capabilities = &types.Capabilities{BaselineCapabilitySet: configv1.ClusterVersionCapabilitySetCurrent}
16771746
c.CredentialsMode = types.PassthroughCredentialsMode
@@ -1859,6 +1928,7 @@ func TestValidateInstallConfig(t *testing.T) {
18591928
name: "apivip_v4_not_in_machinenetwork_cidr_usermanaged_loadbalancer",
18601929
installConfig: func() *types.InstallConfig {
18611930
c := validInstallConfig()
1931+
c.ControlPlane.Replicas = ptr.To[int64](1)
18621932
c.FeatureSet = configv1.TechPreviewNoUpgrade
18631933
c.Networking.MachineNetwork = []types.MachineNetworkEntry{
18641934
{CIDR: *ipnet.MustParseCIDR("10.0.0.0/16")},
@@ -2160,6 +2230,7 @@ func TestValidateInstallConfig(t *testing.T) {
21602230
name: "identical_apivip_ingressvip_usermanaged_loadbalancer",
21612231
installConfig: func() *types.InstallConfig {
21622232
c := validInstallConfig()
2233+
c.ControlPlane.Replicas = ptr.To[int64](1)
21632234
c.FeatureSet = configv1.TechPreviewNoUpgrade
21642235
c.Platform = types.Platform{
21652236
BareMetal: validBareMetalPlatform(),
@@ -2548,6 +2619,7 @@ func TestValidateInstallConfig(t *testing.T) {
25482619
c := validInstallConfig()
25492620
c.BareMetal = validBareMetalPlatform()
25502621
c.AWS = nil
2622+
c.ControlPlane.Replicas = ptr.To[int64](1)
25512623
c.Capabilities = &types.Capabilities{
25522624
BaselineCapabilitySet: configv1.ClusterVersionCapabilitySetCurrent,
25532625
}
@@ -2560,6 +2632,7 @@ func TestValidateInstallConfig(t *testing.T) {
25602632
c := validInstallConfig()
25612633
c.BareMetal = validBareMetalPlatform()
25622634
c.AWS = nil
2635+
c.ControlPlane.Replicas = ptr.To[int64](1)
25632636
c.Capabilities = &types.Capabilities{
25642637
BaselineCapabilitySet: configv1.ClusterVersionCapabilitySetNone,
25652638
AdditionalEnabledCapabilities: []configv1.ClusterVersionCapability{configv1.ClusterVersionCapabilityBaremetal, configv1.ClusterVersionCapabilityMachineAPI, configv1.ClusterVersionCapabilityIngress},
@@ -2584,6 +2657,7 @@ func TestValidateInstallConfig(t *testing.T) {
25842657
c := validInstallConfig()
25852658
c.Platform.AWS = nil
25862659
c.Platform.None = &none.Platform{}
2660+
c.ControlPlane.Replicas = ptr.To[int64](1)
25872661
c.Capabilities = &types.Capabilities{
25882662
BaselineCapabilitySet: configv1.ClusterVersionCapabilitySetNone,
25892663
AdditionalEnabledCapabilities: []configv1.ClusterVersionCapability{configv1.ClusterVersionCapabilityIngress},
@@ -2596,6 +2670,7 @@ func TestValidateInstallConfig(t *testing.T) {
25962670
installConfig: func() *types.InstallConfig {
25972671
c := validInstallConfig()
25982672
c.Platform.AWS = nil
2673+
c.ControlPlane.Replicas = ptr.To[int64](1)
25992674
c.Platform.BareMetal = validBareMetalPlatform()
26002675
c.Capabilities = &types.Capabilities{
26012676
BaselineCapabilitySet: configv1.ClusterVersionCapabilitySetNone,

0 commit comments

Comments
 (0)