Skip to content

Commit 51e87d5

Browse files
committed
Improve eksctl-anywhere cluster config generation with parameters for bare metal and vSphere
Sample command for bare metal is: eksctl anywhere generate clusterconfig <cluster name> \ -p tinkerbell \ -m params_tinkerbell.yaml Sample params_tinkerbell.yaml has content: """ managementClusterName: <management cluster name> podsCidrBlocks: - 192.168.64.0/18 servicesCidrBlocks: - 10.96.0.0/12 kubernetesVersion: 1.26 cpCount: 1 workerCount: 2 cpEndpointHost: <control plane endpoint host ip> tinkerbellIP: <tinkerbellIP> adminIP: <admin machine ip> osFamily: ubuntu osImageURL: <osImageURL of K8s 1.26> hardwareCSV: <hardware CSV file> sshAuthorizedKeyFile: <sshKey.pub file> tinkerbellTemplateConfigTemplateFile: tinkerbellTemplateConfigTemplateUbuntu.yaml """ managementClusterName is optional, the default value is <cluster name> tinkerbellTemplateConfigTemplateFile is for advanced use cases, the default ("") is ok to use Sample command for vSphere is: eksctl anywhere generate clusterconfig <cluster name> -p vsphere -m params_vsphere.yaml Sample params_vsphere.yaml has content: """ managementClusterName: <management cluster name> podsCidrBlocks: - 192.168.192.0/18 servicesCidrBlocks: - 10.96.192.0/18 cpCount: 2 etcdCount: 3 workerCount: 3 cpEndpointHost: <control plane endpoint host ip> kubernetesVersion: 1.28 datacenter: <vDatacenter> insecure: true network: <vCenterNetwork> server: <serverIP> thumbprint: <thumprint> datastore: <vDatastore> folder: <folder> cpDiskGiB: 0 cpMemoryMiB: 0 cpNumCPUs: 0 etcdDiskGiB: 0 etcdMemoryMiB: 0 etcdNumCPUs: 0 workerDiskGiB: 256 workerMemoryMiB: 65536 workerNumCPUs: 16 osFamily: "ubuntu" resourcePool: <resource pool> template: <template name of OS> sshAuthorizedKeyFile: <sshKey.pub> """ managementClusterName is optional, the default value is <cluster name>
1 parent 797d0c7 commit 51e87d5

13 files changed

+634
-48
lines changed

cmd/eksctl-anywhere/cmd/generateclusterconfig.go

+240-22
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package cmd
22

33
import (
4+
"encoding/csv"
45
"fmt"
56
"log"
7+
"os"
68
"strings"
79

810
"github.com/spf13/cobra"
@@ -50,6 +52,7 @@ func preRunGenerateClusterConfig(cmd *cobra.Command, args []string) {
5052
func init() {
5153
generateCmd.AddCommand(generateClusterConfigCmd)
5254
generateClusterConfigCmd.Flags().StringP("provider", "p", "", fmt.Sprintf("Provider to use (%s)", strings.Join(constants.SupportedProviders, " or ")))
55+
generateClusterConfigCmd.Flags().StringP("paramsFile", "m", "", "parameters file (vsphere or tinkerbell)")
5356
err := generateClusterConfigCmd.MarkFlagRequired("provider")
5457
if err != nil {
5558
log.Fatalf("marking flag as required: %v", err)
@@ -61,6 +64,30 @@ func generateClusterConfig(clusterName string) error {
6164
var datacenterYaml []byte
6265
var machineGroupYaml [][]byte
6366
var clusterConfigOpts []v1alpha1.ClusterGenerateOpt
67+
var kubernetesVersion string
68+
var tinkerbellTemplateConfigTemplate string
69+
var podsCidrBlocks []string
70+
var servicesCidrBlocks []string
71+
var paramsData []byte
72+
var err error
73+
74+
// use cluster name as the default management cluster name.
75+
managementClusterName := clusterName
76+
77+
if viper.IsSet("paramsFile") {
78+
switch strings.ToLower(viper.GetString("provider")) {
79+
case constants.VSphereProviderName:
80+
case constants.TinkerbellProviderName:
81+
paramsFile := viper.GetString("paramsFile")
82+
paramsData, err = os.ReadFile(paramsFile)
83+
if err != nil {
84+
return fmt.Errorf("reading paramsFile: %v", err)
85+
}
86+
default:
87+
return fmt.Errorf("parameter file is only supported for vsphere and tinkerbell")
88+
}
89+
}
90+
6491
switch strings.ToLower(viper.GetString("provider")) {
6592
case constants.DockerProviderName:
6693
datacenterConfig := v1alpha1.NewDockerDatacenterConfigGenerate(clusterName)
@@ -77,25 +104,76 @@ func generateClusterConfig(clusterName string) error {
77104
}
78105
datacenterYaml = dcyaml
79106
case constants.VSphereProviderName:
80-
clusterConfigOpts = append(clusterConfigOpts, v1alpha1.WithClusterEndpoint())
81-
datacenterConfig := v1alpha1.NewVSphereDatacenterConfigGenerate(clusterName)
107+
var vSphereParams v1alpha1.VSphereClusterConfigParams
108+
err = yaml.Unmarshal(paramsData, &vSphereParams)
109+
if err != nil {
110+
return fmt.Errorf("unmarshal vSphereParams: %v", err)
111+
}
112+
113+
if vSphereParams.ManagementClusterName != "" {
114+
// override the management cluster name with that from parameter file.
115+
managementClusterName = vSphereParams.ManagementClusterName
116+
}
117+
118+
// set podsCidrBlocks and servicesCidrBlocks to the values from parameter file.
119+
podsCidrBlocks = vSphereParams.PodsCidrBlocks
120+
servicesCidrBlocks = vSphereParams.ServicesCidrBlocks
121+
122+
if vSphereParams.CPEndpointHost != "" {
123+
// add control plane endpoint config with host from parameter file.
124+
clusterConfigOpts = append(clusterConfigOpts, v1alpha1.WithClusterEndpointHost(vSphereParams.CPEndpointHost))
125+
} else {
126+
clusterConfigOpts = append(clusterConfigOpts, v1alpha1.WithClusterEndpoint())
127+
}
128+
129+
// create datacenter config with values from parameter file
130+
datacenterConfig := v1alpha1.NewVSphereDatacenterConfigGenerate(clusterName, vSphereParams.Datacenter, vSphereParams.Network, vSphereParams.Server, vSphereParams.Thumbprint, vSphereParams.Insecure)
82131
clusterConfigOpts = append(clusterConfigOpts, v1alpha1.WithDatacenterRef(datacenterConfig))
132+
// default counts of CP nodes, Etcd nodes and worker nodes.
133+
cpCount := 2
134+
etcdCount := 3
135+
workerCount := 2
136+
137+
if vSphereParams.CPCount != 0 {
138+
// override counts of CP nodes with value from parameter file.
139+
cpCount = vSphereParams.CPCount
140+
}
141+
142+
if vSphereParams.EtcdCount != 0 {
143+
// override counts of Etcd nodes with value from parameter file.
144+
etcdCount = vSphereParams.EtcdCount
145+
}
146+
147+
if vSphereParams.WorkerCount != 0 {
148+
// override counts of Worker nodes with value from parameter file.
149+
workerCount = vSphereParams.WorkerCount
150+
}
83151
clusterConfigOpts = append(clusterConfigOpts,
84-
v1alpha1.ControlPlaneConfigCount(2),
85-
v1alpha1.ExternalETCDConfigCount(3),
86-
v1alpha1.WorkerNodeConfigCount(2),
152+
v1alpha1.ControlPlaneConfigCount(cpCount),
153+
v1alpha1.ExternalETCDConfigCount(etcdCount),
154+
v1alpha1.WorkerNodeConfigCount(workerCount),
87155
v1alpha1.WorkerNodeConfigName(constants.DefaultWorkerNodeGroupName),
88156
)
89157
dcyaml, err := yaml.Marshal(datacenterConfig)
90158
if err != nil {
91159
return fmt.Errorf("generating cluster yaml: %v", err)
92160
}
93161
datacenterYaml = dcyaml
162+
var sshAuthorizedKey string
163+
if vSphereParams.SSHAuthorizedKeyFile != "" {
164+
b, err := os.ReadFile(vSphereParams.SSHAuthorizedKeyFile)
165+
if err != nil {
166+
return fmt.Errorf("open sshAuthorizedKeyFile file: %v", err)
167+
}
168+
sshAuthorizedKey = string(b)
169+
}
170+
171+
kubernetesVersion = vSphereParams.KubernetesVersion
94172
// need to default control plane config name to something different from the cluster name based on assumption
95173
// in controller code
96-
cpMachineConfig := v1alpha1.NewVSphereMachineConfigGenerate(providers.GetControlPlaneNodeName(clusterName))
97-
workerMachineConfig := v1alpha1.NewVSphereMachineConfigGenerate(clusterName)
98-
etcdMachineConfig := v1alpha1.NewVSphereMachineConfigGenerate(providers.GetEtcdNodeName(clusterName))
174+
cpMachineConfig := v1alpha1.NewVSphereMachineConfigGenerate(providers.GetControlPlaneNodeName(clusterName), vSphereParams.Datastore, vSphereParams.Folder, vSphereParams.ResourcePool, vSphereParams.Template, sshAuthorizedKey, vSphereParams.OSFamily, vSphereParams.CPDiskGiB, vSphereParams.CPNumCPUs, vSphereParams.CPMemoryMiB)
175+
workerMachineConfig := v1alpha1.NewVSphereMachineConfigGenerate(clusterName, vSphereParams.Datastore, vSphereParams.Folder, vSphereParams.ResourcePool, vSphereParams.Template, sshAuthorizedKey, vSphereParams.OSFamily, vSphereParams.WorkerDiskGiB, vSphereParams.WorkerNumCPUs, vSphereParams.WorkerMemoryMiB)
176+
etcdMachineConfig := v1alpha1.NewVSphereMachineConfigGenerate(providers.GetEtcdNodeName(clusterName), vSphereParams.Datastore, vSphereParams.Folder, vSphereParams.ResourcePool, vSphereParams.Template, sshAuthorizedKey, vSphereParams.OSFamily, vSphereParams.EtcdDiskGiB, vSphereParams.EtcdNumCPUs, vSphereParams.EtcdMemoryMiB)
99177
clusterConfigOpts = append(clusterConfigOpts,
100178
v1alpha1.WithCPMachineGroupRef(cpMachineConfig),
101179
v1alpha1.WithWorkerMachineGroupRef(workerMachineConfig),
@@ -183,35 +261,167 @@ func generateClusterConfig(clusterName string) error {
183261
}
184262
machineGroupYaml = append(machineGroupYaml, cpMcYaml, workerMcYaml, etcdMcYaml)
185263
case constants.TinkerbellProviderName:
186-
clusterConfigOpts = append(clusterConfigOpts, v1alpha1.WithClusterEndpoint())
187-
datacenterConfig := v1alpha1.NewTinkerbellDatacenterConfigGenerate(clusterName)
264+
var tinkerbellParams v1alpha1.TinkerbellClusterConfigParams
265+
err = yaml.Unmarshal(paramsData, &tinkerbellParams)
266+
if err != nil {
267+
return fmt.Errorf("unmarshal tinkerbellParams: %v", err)
268+
}
269+
270+
if tinkerbellParams.ManagementClusterName != "" {
271+
// override the management cluster name with that from parameter file.
272+
managementClusterName = tinkerbellParams.ManagementClusterName
273+
}
274+
275+
// set podsCidrBlocks and servicesCidrBlocks to the values from parameter file.
276+
podsCidrBlocks = tinkerbellParams.PodsCidrBlocks
277+
servicesCidrBlocks = tinkerbellParams.ServicesCidrBlocks
278+
279+
if tinkerbellParams.CPEndpointHost != "" {
280+
// add control plane endpoint config with host from parameter file.
281+
clusterConfigOpts = append(clusterConfigOpts, v1alpha1.WithClusterEndpointHost(tinkerbellParams.CPEndpointHost))
282+
} else {
283+
clusterConfigOpts = append(clusterConfigOpts, v1alpha1.WithClusterEndpoint())
284+
}
285+
286+
kubernetesVersion = tinkerbellParams.KubernetesVersion
287+
288+
adminIP := tinkerbellParams.AdminIP
289+
tinkerbellIP := tinkerbellParams.TinkerbellIP
290+
osImageURL := tinkerbellParams.OSImageURL
291+
292+
// create datacenter config with values from parameter file
293+
datacenterConfig := v1alpha1.NewTinkerbellDatacenterConfigGenerate(clusterName, tinkerbellIP, osImageURL)
188294
clusterConfigOpts = append(clusterConfigOpts, v1alpha1.WithDatacenterRef(datacenterConfig))
295+
// default counts of CP nodes, Etcd nodes and worker nodes.
296+
cpCount := 1
297+
workerCount := 1
298+
if tinkerbellParams.HardwareCSV != "" {
299+
// parse hardware.csv file to get counts of CP/worker nodes
300+
f, err := os.Open(tinkerbellParams.HardwareCSV)
301+
if err != nil {
302+
return fmt.Errorf("open hardware file: %v", err)
303+
}
304+
defer f.Close()
305+
csvReader := csv.NewReader(f)
306+
data, err := csvReader.ReadAll()
307+
if err != nil {
308+
return fmt.Errorf("read hardware file: %v", err)
309+
}
310+
macIndex := -1
311+
ipIndex := -1
312+
labelsIndex := -1
313+
cpCount = 0
314+
workerCount = 0
315+
for i, line := range data {
316+
if i == 0 {
317+
// from the header (first line), find the index of
318+
// MAC, IP, labels.
319+
for j, field := range line {
320+
if strings.EqualFold(field, "mac") {
321+
macIndex = j
322+
} else if strings.EqualFold(field, "ip_address") {
323+
ipIndex = j
324+
} else if strings.EqualFold(field, "labels") {
325+
labelsIndex = j
326+
}
327+
}
328+
if macIndex == -1 {
329+
return fmt.Errorf("no mac header found in hardware file")
330+
}
331+
if ipIndex == -1 {
332+
return fmt.Errorf("no ip header found in hardware file")
333+
}
334+
if labelsIndex == -1 {
335+
return fmt.Errorf("no labels header found in hardware file")
336+
}
337+
} else {
338+
// for rest lines, increase counts of CP nodes and worker nodes.
339+
if strings.ToLower(line[labelsIndex]) == "type=cp" {
340+
cpCount = cpCount + 1
341+
} else {
342+
workerCount = workerCount + 1
343+
}
344+
}
345+
}
346+
}
347+
348+
if tinkerbellParams.CPCount != 0 {
349+
// override counts of CP nodes with value from parameter file.
350+
cpCount = tinkerbellParams.CPCount
351+
}
352+
353+
if tinkerbellParams.WorkerCount != 0 {
354+
// override counts of Worker nodes with value from parameter file.
355+
workerCount = tinkerbellParams.WorkerCount
356+
}
357+
189358
clusterConfigOpts = append(clusterConfigOpts,
190-
v1alpha1.ControlPlaneConfigCount(1),
191-
v1alpha1.WorkerNodeConfigCount(1),
192-
v1alpha1.WorkerNodeConfigName(constants.DefaultWorkerNodeGroupName),
359+
v1alpha1.ControlPlaneConfigCount(cpCount),
193360
)
361+
if workerCount > 0 {
362+
// only generate worker cluster when worker count > 0.
363+
clusterConfigOpts = append(clusterConfigOpts,
364+
v1alpha1.WorkerNodeConfigCount(workerCount),
365+
v1alpha1.WorkerNodeConfigName(constants.DefaultWorkerNodeGroupName),
366+
)
367+
}
194368
dcyaml, err := yaml.Marshal(datacenterConfig)
195369
if err != nil {
196370
return fmt.Errorf("generating cluster yaml: %v", err)
197371
}
198372
datacenterYaml = dcyaml
199373

200-
cpMachineConfig := v1alpha1.NewTinkerbellMachineConfigGenerate(providers.GetControlPlaneNodeName(clusterName))
201-
workerMachineConfig := v1alpha1.NewTinkerbellMachineConfigGenerate(clusterName)
374+
var sshAuthorizedKey string
375+
if tinkerbellParams.SSHAuthorizedKeyFile != "" {
376+
b, err := os.ReadFile(tinkerbellParams.SSHAuthorizedKeyFile)
377+
if err != nil {
378+
return fmt.Errorf("open sshAuthorizedKeyFile file: %v", err)
379+
}
380+
sshAuthorizedKey = string(b)
381+
}
382+
383+
cpMachineConfig := v1alpha1.NewTinkerbellMachineConfigGenerate(clusterName, providers.GetControlPlaneNodeName(clusterName), "cp", sshAuthorizedKey, tinkerbellParams.OSFamily)
202384
clusterConfigOpts = append(clusterConfigOpts,
203385
v1alpha1.WithCPMachineGroupRef(cpMachineConfig),
204-
v1alpha1.WithWorkerMachineGroupRef(workerMachineConfig),
205386
)
206387
cpMcYaml, err := yaml.Marshal(cpMachineConfig)
207388
if err != nil {
208389
return fmt.Errorf("generating cluster yaml: %v", err)
209390
}
210-
workerMcYaml, err := yaml.Marshal(workerMachineConfig)
211-
if err != nil {
212-
return fmt.Errorf("generating cluster yaml: %v", err)
391+
machineGroupYaml = append(machineGroupYaml, cpMcYaml)
392+
393+
if workerCount > 0 {
394+
workerMachineConfig := v1alpha1.NewTinkerbellMachineConfigGenerate(clusterName, clusterName, "worker", sshAuthorizedKey, tinkerbellParams.OSFamily)
395+
// only generate worker machine group reference when worker count > 0.
396+
clusterConfigOpts = append(clusterConfigOpts,
397+
v1alpha1.WithWorkerMachineGroupRef(workerMachineConfig),
398+
)
399+
// only generate worker machine config YAML when worker count > 0.
400+
workerMcYaml, err := yaml.Marshal(workerMachineConfig)
401+
if err != nil {
402+
return fmt.Errorf("generating cluster yaml: %v", err)
403+
}
404+
machineGroupYaml = append(machineGroupYaml, workerMcYaml)
405+
}
406+
407+
if viper.IsSet("paramsFile") {
408+
if tinkerbellParams.TinkerbellTemplateConfigTemplateFile != "" {
409+
b, err := os.ReadFile(tinkerbellParams.TinkerbellTemplateConfigTemplateFile)
410+
if err != nil {
411+
return fmt.Errorf("open tinkerbellTemplateConfigTemplateFile file: %v", err)
412+
}
413+
tinkerbellTemplateConfigTemplate = string(b)
414+
} else if tinkerbellParams.OSFamily == v1alpha1.Ubuntu {
415+
tinkerbellTemplateConfigTemplate = GetDefaultTinkerbellTemplateConfigTemplateUbuntu()
416+
} else if tinkerbellParams.OSFamily == v1alpha1.Bottlerocket {
417+
tinkerbellTemplateConfigTemplate = GetDefaultTinkerbellTemplateConfigTemplateBottlerocket()
418+
}
419+
420+
tinkerbellTemplateConfigTemplate = strings.Replace(tinkerbellTemplateConfigTemplate, "$$NAME", clusterName, -1)
421+
tinkerbellTemplateConfigTemplate = strings.Replace(tinkerbellTemplateConfigTemplate, "$$IMG_URL", osImageURL, -1)
422+
tinkerbellTemplateConfigTemplate = strings.Replace(tinkerbellTemplateConfigTemplate, "$$ADMIN_IP", adminIP, -1)
423+
tinkerbellTemplateConfigTemplate = strings.Replace(tinkerbellTemplateConfigTemplate, "$$TINKERBELL_IP", tinkerbellIP, -1)
213424
}
214-
machineGroupYaml = append(machineGroupYaml, cpMcYaml, workerMcYaml)
215425
case constants.NutanixProviderName:
216426
datacenterConfig := v1alpha1.NewNutanixDatacenterConfigGenerate(clusterName)
217427
dcYaml, err := yaml.Marshal(datacenterConfig)
@@ -257,7 +467,8 @@ func generateClusterConfig(clusterName string) error {
257467
default:
258468
return fmt.Errorf("not a valid provider")
259469
}
260-
config := v1alpha1.NewClusterGenerate(clusterName, clusterConfigOpts...)
470+
471+
config := v1alpha1.NewClusterGenerate(clusterName, managementClusterName, kubernetesVersion, podsCidrBlocks, servicesCidrBlocks, clusterConfigOpts...)
261472

262473
configMarshal, err := yaml.Marshal(config)
263474
if err != nil {
@@ -272,6 +483,13 @@ func generateClusterConfig(clusterName string) error {
272483
resources = append(resources, machineGroupYaml...)
273484
}
274485

275-
fmt.Println(string(templater.AppendYamlResources(resources...)))
486+
fmt.Print(string(templater.AppendYamlResources(resources...)))
487+
488+
if tinkerbellTemplateConfigTemplate != "" {
489+
fmt.Println(tinkerbellTemplateConfigTemplate)
490+
} else {
491+
fmt.Println("")
492+
}
493+
276494
return nil
277495
}

0 commit comments

Comments
 (0)