11package cmd
22
33import (
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) {
5052func 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