Skip to content

Commit 7acb610

Browse files
committed
Allow running without worker nodes
1 parent 56a48af commit 7acb610

File tree

6 files changed

+49
-17
lines changed

6 files changed

+49
-17
lines changed

cmd/kops/create_cluster.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ func NewCmdCreateCluster(f *util.Factory, out io.Writer) *cobra.Command {
175175
sshPublicKey := ""
176176
associatePublicIP := false
177177
encryptEtcdStorage := false
178+
nodeCount := int32(0)
178179

179180
cmd := &cobra.Command{
180181
Use: "cluster [CLUSTER]",
@@ -194,6 +195,10 @@ func NewCmdCreateCluster(f *util.Factory, out io.Writer) *cobra.Command {
194195
options.EncryptEtcdStorage = &encryptEtcdStorage
195196
}
196197

198+
if cmd.Flag("node-count").Changed {
199+
options.NodeCount = &nodeCount
200+
}
201+
197202
if sshPublicKey != "" {
198203
options.SSHPublicKeys, err = loadSSHPublicKeys(sshPublicKey)
199204
if err != nil {
@@ -268,7 +273,7 @@ func NewCmdCreateCluster(f *util.Factory, out io.Writer) *cobra.Command {
268273
cmd.Flags().Int32Var(&options.ControlPlaneCount, "master-count", options.ControlPlaneCount, "Number of control-plane nodes. Defaults to one control-plane node per control-plane-zone")
269274
cmd.Flags().MarkDeprecated("master-count", "use --control-plane-count instead")
270275
cmd.Flags().Int32Var(&options.ControlPlaneCount, "control-plane-count", options.ControlPlaneCount, "Number of control-plane nodes. Defaults to one control-plane node per control-plane-zone")
271-
cmd.Flags().Int32Var(&options.NodeCount, "node-count", options.NodeCount, "Total number of worker nodes. Defaults to one node per zone")
276+
cmd.Flags().Int32Var(&nodeCount, "node-count", 0, "Total number of worker nodes. Defaults to one node per zone")
272277

273278
cmd.Flags().StringVar(&options.Image, "image", options.Image, "Machine image for all instances")
274279
cmd.RegisterFlagCompletionFunc("image", completeInstanceImage)

pkg/apis/kops/validation/legacy.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -274,10 +274,6 @@ func DeepValidate(c *kops.Cluster, groups []*kops.InstanceGroup, strict bool, vf
274274
return fmt.Errorf("must configure at least one ControlPlane InstanceGroup")
275275
}
276276

277-
if nodeGroupCount == 0 {
278-
return fmt.Errorf("must configure at least one Node InstanceGroup")
279-
}
280-
281277
for _, g := range groups {
282278
errs := CrossValidateInstanceGroup(g, c, cloud, strict)
283279

upup/models/cloudup/resources/addons/coredns.addons.k8s.io/k8s-1.12.yaml.template

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ spec:
138138
- key: "CriticalAddonsOnly"
139139
operator: "Exists"
140140
{{- end }}
141-
{{- if KarpenterEnabled }}
141+
{{- if or IsControlPlaneOnly KarpenterEnabled }}
142142
- key: node-role.kubernetes.io/master
143143
operator: Exists
144144
- key: node-role.kubernetes.io/control-plane

upup/pkg/fi/cloudup/deepvalidate_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@ func TestDeepValidate_NoNodeZones(t *testing.T) {
4444
c := buildDefaultCluster(t)
4545
var groups []*kopsapi.InstanceGroup
4646
groups = append(groups, buildMinimalMasterInstanceGroup("subnet-us-test-1a"))
47-
expectErrorFromDeepValidate(t, c, groups, "must configure at least one Node InstanceGroup")
47+
err := validation.DeepValidate(c, groups, true, vfs.Context, nil)
48+
if err != nil {
49+
t.Fatalf("Expected no error from DeepValidate, got %v", err)
50+
}
4851
}
4952

5053
func TestDeepValidate_NoMasterZones(t *testing.T) {

upup/pkg/fi/cloudup/new_cluster.go

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ type NewClusterOptions struct {
134134

135135
// NodeCount is the number of nodes to create. Defaults to leaving the count unspecified
136136
// on the InstanceGroup, which results in a count of 2.
137-
NodeCount int32
137+
NodeCount *int32
138138
// Bastion enables the creation of a Bastion instance.
139139
Bastion bool
140140
// BastionLoadBalancerType is the bastion loadbalancer type to use; "public" or "internal".
@@ -1040,9 +1040,14 @@ func setupNodes(opt *NewClusterOptions, cluster *api.Cluster, zoneToSubnetsMap m
10401040
var nodes []*api.InstanceGroup
10411041

10421042
if featureflag.AWSSingleNodesInstanceGroup.Enabled() && cloudProvider == api.CloudProviderAWS && len(opt.SubnetIDs) == 0 {
1043-
nodeCount := opt.NodeCount
1044-
if nodeCount == 0 {
1043+
var nodeCount int32
1044+
if opt.NodeCount == nil {
10451045
nodeCount = 1
1046+
} else {
1047+
nodeCount = fi.ValueOf(opt.NodeCount)
1048+
if nodeCount == 0 {
1049+
return nil, nil
1050+
}
10461051
}
10471052

10481053
g := &api.InstanceGroup{}
@@ -1078,19 +1083,24 @@ func setupNodes(opt *NewClusterOptions, cluster *api.Cluster, zoneToSubnetsMap m
10781083

10791084
// The node count is the number of zones unless explicitly set
10801085
// We then divvy up amongst the zones
1081-
numZones := len(opt.Zones)
1082-
nodeCount := opt.NodeCount
1083-
if nodeCount == 0 {
1084-
// If node count is not specified, default to the number of zones
1085-
nodeCount = int32(numZones)
1086+
numZones := int32(len(opt.Zones))
1087+
var nodeCount int32
1088+
if opt.NodeCount == nil {
1089+
// If the node count is not specified, default to the number of zones
1090+
nodeCount = numZones
1091+
} else {
1092+
nodeCount = fi.ValueOf(opt.NodeCount)
1093+
if nodeCount == 0 {
1094+
return nil, nil
1095+
}
10861096
}
10871097

10881098
countPerIG := nodeCount / int32(numZones)
1089-
remainder := int(nodeCount) % numZones
1099+
remainder := nodeCount % numZones
10901100

10911101
for i, zone := range opt.Zones {
10921102
count := countPerIG
1093-
if i < remainder {
1103+
if i < int(remainder) {
10941104
count++
10951105
}
10961106

upup/pkg/fi/cloudup/template_functions.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,8 @@ func (tf *TemplateFunctions) AddTo(dest template.FuncMap, secretStore fi.SecretS
399399

400400
dest["ParseTaint"] = util.ParseTaint
401401

402+
dest["IsControlPlaneOnly"] = tf.IsControlPlaneOnly
403+
402404
dest["KarpenterEnabled"] = func() bool {
403405
return cluster.Spec.Karpenter != nil && cluster.Spec.Karpenter.Enabled
404406
}
@@ -511,6 +513,22 @@ func (tf *TemplateFunctions) HasHighlyAvailableControlPlane() bool {
511513
return false
512514
}
513515

516+
// IsControlPlaneOnly returns true if the cluster has only control plane node(s). False otherwise.
517+
func (tf *TemplateFunctions) IsControlPlaneOnly() bool {
518+
var cp, wn int
519+
for _, ig := range tf.InstanceGroups {
520+
switch ig.Spec.Role {
521+
case kops.InstanceGroupRoleControlPlane:
522+
cp++
523+
case kops.InstanceGroupRoleNode:
524+
wn++
525+
default:
526+
// Ignore Bastion and APIServer
527+
}
528+
}
529+
return cp > 0 && wn == 0
530+
}
531+
514532
// CloudControllerConfigArgv returns the args to external cloud controller
515533
func (tf *TemplateFunctions) CloudControllerConfigArgv() ([]string, error) {
516534
cluster := tf.Cluster

0 commit comments

Comments
 (0)