1
1
package cmd
2
2
3
3
import (
4
+ "encoding/csv"
4
5
"fmt"
5
6
"log"
7
+ "os"
6
8
"strings"
7
9
8
10
"github.com/spf13/cobra"
@@ -50,6 +52,7 @@ func preRunGenerateClusterConfig(cmd *cobra.Command, args []string) {
50
52
func init () {
51
53
generateCmd .AddCommand (generateClusterConfigCmd )
52
54
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)" )
53
56
err := generateClusterConfigCmd .MarkFlagRequired ("provider" )
54
57
if err != nil {
55
58
log .Fatalf ("marking flag as required: %v" , err )
@@ -61,6 +64,30 @@ func generateClusterConfig(clusterName string) error {
61
64
var datacenterYaml []byte
62
65
var machineGroupYaml [][]byte
63
66
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
+
64
91
switch strings .ToLower (viper .GetString ("provider" )) {
65
92
case constants .DockerProviderName :
66
93
datacenterConfig := v1alpha1 .NewDockerDatacenterConfigGenerate (clusterName )
@@ -77,25 +104,76 @@ func generateClusterConfig(clusterName string) error {
77
104
}
78
105
datacenterYaml = dcyaml
79
106
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 )
82
131
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
+ }
83
151
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 ),
87
155
v1alpha1 .WorkerNodeConfigName (constants .DefaultWorkerNodeGroupName ),
88
156
)
89
157
dcyaml , err := yaml .Marshal (datacenterConfig )
90
158
if err != nil {
91
159
return fmt .Errorf ("generating cluster yaml: %v" , err )
92
160
}
93
161
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
94
172
// need to default control plane config name to something different from the cluster name based on assumption
95
173
// 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 )
99
177
clusterConfigOpts = append (clusterConfigOpts ,
100
178
v1alpha1 .WithCPMachineGroupRef (cpMachineConfig ),
101
179
v1alpha1 .WithWorkerMachineGroupRef (workerMachineConfig ),
@@ -183,35 +261,167 @@ func generateClusterConfig(clusterName string) error {
183
261
}
184
262
machineGroupYaml = append (machineGroupYaml , cpMcYaml , workerMcYaml , etcdMcYaml )
185
263
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 )
188
294
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
+
189
358
clusterConfigOpts = append (clusterConfigOpts ,
190
- v1alpha1 .ControlPlaneConfigCount (1 ),
191
- v1alpha1 .WorkerNodeConfigCount (1 ),
192
- v1alpha1 .WorkerNodeConfigName (constants .DefaultWorkerNodeGroupName ),
359
+ v1alpha1 .ControlPlaneConfigCount (cpCount ),
193
360
)
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
+ }
194
368
dcyaml , err := yaml .Marshal (datacenterConfig )
195
369
if err != nil {
196
370
return fmt .Errorf ("generating cluster yaml: %v" , err )
197
371
}
198
372
datacenterYaml = dcyaml
199
373
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 )
202
384
clusterConfigOpts = append (clusterConfigOpts ,
203
385
v1alpha1 .WithCPMachineGroupRef (cpMachineConfig ),
204
- v1alpha1 .WithWorkerMachineGroupRef (workerMachineConfig ),
205
386
)
206
387
cpMcYaml , err := yaml .Marshal (cpMachineConfig )
207
388
if err != nil {
208
389
return fmt .Errorf ("generating cluster yaml: %v" , err )
209
390
}
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 )
213
424
}
214
- machineGroupYaml = append (machineGroupYaml , cpMcYaml , workerMcYaml )
215
425
case constants .NutanixProviderName :
216
426
datacenterConfig := v1alpha1 .NewNutanixDatacenterConfigGenerate (clusterName )
217
427
dcYaml , err := yaml .Marshal (datacenterConfig )
@@ -257,7 +467,8 @@ func generateClusterConfig(clusterName string) error {
257
467
default :
258
468
return fmt .Errorf ("not a valid provider" )
259
469
}
260
- config := v1alpha1 .NewClusterGenerate (clusterName , clusterConfigOpts ... )
470
+
471
+ config := v1alpha1 .NewClusterGenerate (clusterName , managementClusterName , kubernetesVersion , podsCidrBlocks , servicesCidrBlocks , clusterConfigOpts ... )
261
472
262
473
configMarshal , err := yaml .Marshal (config )
263
474
if err != nil {
@@ -272,6 +483,13 @@ func generateClusterConfig(clusterName string) error {
272
483
resources = append (resources , machineGroupYaml ... )
273
484
}
274
485
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
+
276
494
return nil
277
495
}
0 commit comments