@@ -45,6 +45,7 @@ type RegistryConfig struct {
4545// OpenStackNodeImage represents the structure of the OpenStackNodeImages.
4646type OpenStackNodeImage struct {
4747 URL string `yaml:"url"`
48+ ImageDir string `yaml:"imageDir,omitempty"`
4849 CreateOpts struct {
4950 Name string `yaml:"name"`
5051 DiskFormat string `yaml:"disk_format"` //nolint:tagliatelle // The `DiskFormat` field in this struct corresponds to the `disk_format` YAML tag
@@ -72,7 +73,9 @@ https://github.com/SovereignCloudStack/csctl
7273}
7374
7475func main () {
75- if len (os .Args ) != 4 {
76+ numArgs := 5
77+ if len (os .Args ) != numArgs {
78+ fmt .Printf ("Wrong number of arguments. Expected %d got %d\n " , numArgs , len (os .Args ))
7679 usage ()
7780 os .Exit (1 )
7881 }
@@ -81,13 +84,18 @@ func main() {
8184 os .Exit (1 )
8285 }
8386 clusterStackPath := os .Args [2 ]
84- configFilePath := filepath .Join (clusterStackPath , "node-images" , "config.yaml" )
85- config , err := csctlclusterstack .GetCsctlConfig (clusterStackPath )
87+ csctlConfig , err := csctlclusterstack .GetCsctlConfig (clusterStackPath )
8688 if err != nil {
8789 fmt .Println (err .Error ())
8890 os .Exit (1 )
8991 }
90- if config .Config .Provider .Type != provider {
92+ configFilePath := filepath .Join (clusterStackPath , "node-images" )
93+ config , err := GetConfig (configFilePath )
94+ if err != nil {
95+ fmt .Println (err .Error ())
96+ os .Exit (1 )
97+ }
98+ if csctlConfig .Config .Provider .Type != provider {
9199 fmt .Printf ("Wrong provider in %s. Expected %s\n " , clusterStackPath , provider )
92100 os .Exit (1 )
93101 }
@@ -97,8 +105,8 @@ func main() {
97105 fmt .Println (err .Error ())
98106 os .Exit (1 )
99107 }
100- method := config .Config .Provider .Config . Method
101- switch strings . ToLower ( method ) {
108+ method := csctlConfig .Config .Provider .Config [ "method" ]
109+ switch method {
102110 case "get" :
103111 // Copy config.yaml to releaseDir as node-images.yaml
104112 dest := filepath .Join (releaseDir , "node-images.yaml" )
@@ -108,66 +116,67 @@ func main() {
108116 }
109117 fmt .Println ("config.yaml copied to releaseDir as node-images.yaml successfully!" )
110118 case "build" :
111- if len (config .Config .Provider .Config .Images ) > 0 {
112- for _ , image := range config .Config .Provider .Config .Images {
113- // Construct the path to the image folder
114- packerImagePath := filepath .Join (clusterStackPath , "node-images" , * image )
115-
116- if _ , err := os .Stat (packerImagePath ); err == nil {
117- fmt .Println ("Running packer build..." )
118- // Warning: variables like build_name and output_directory must exist in packer ariables file like in example
119- // #nosec G204
120- cmd := exec .Command ("packer" , "build" , "-var" , "build_name=" + * image , "-var" , "output_directory=" + outputDirectory , packerImagePath )
121- cmd .Stdout = os .Stdout
122- cmd .Stderr = os .Stderr
123- if err := cmd .Run (); err != nil {
124- fmt .Printf ("Error running packer build: %v\n " , err )
125- os .Exit (1 )
126- }
127- fmt .Println ("Packer build completed successfully." )
128-
129- // Todo: Use --node-image-registry flag to pass path to registry.yaml
130- registryConfigPath := filepath .Join (clusterStackPath , "node-images" , "registry.yaml" )
131-
132- // Get the current working directory
133- currentDir , err := os .Getwd ()
134- if err != nil {
135- fmt .Printf ("Error getting current working directory: %v\n " , err )
136- os .Exit (1 )
137- }
138-
139- // Path to the image created by the packer
140- // Warning: name of the image created by packer should have same name as the name of the image folder in node-images
141- ouputImagePath := filepath .Join (currentDir , outputDirectory , * image )
142-
143- // Push the built image to S3
144- if err := pushToS3 (ouputImagePath , * image , registryConfigPath ); err != nil {
145- fmt .Printf ("Error pushing image to S3: %v\n " , err )
146- os .Exit (1 )
147- }
148-
149- // TODO: Add check if config.yaml openStackNodeImages have same length as images list in csctl.yaml
150- // Update URL in config.yaml if it is necessary
151- if err := updateURLNodeImages (configFilePath , registryConfigPath , * image ); err != nil {
152- fmt .Printf ("Error updating URL in config.yaml: %v\n " , err )
153- os .Exit (1 )
154- }
155- // Copy config.yaml to releaseDir as node-images.yaml
156- dest := filepath .Join (releaseDir , "node-images.yaml" )
157- if err := copyFile (configFilePath , dest ); err != nil {
158- fmt .Printf ("Error copying config.yaml to releaseDir: %v\n " , err )
159- os .Exit (1 )
160- }
161- fmt .Println ("config.yaml copied to releaseDir as node-images.yaml successfully!" )
162- } else {
163- fmt .Printf ("Image folder %s does not exist\n " , packerImagePath )
164- }
119+ for _ , image := range config .OpenStackNodeImages {
120+ if image .ImageDir == "" {
121+ fmt .Printf ("No images to build, image directory is not defined in config.yaml file" )
122+ os .Exit (1 )
123+ }
124+
125+ // Construct the path to the image folder
126+ packerImagePath := filepath .Join (clusterStackPath , "node-images" , image .ImageDir )
127+
128+ if _ , err := os .Stat (packerImagePath ); err != nil {
129+ fmt .Printf ("Image folder %s does not exist\n " , packerImagePath )
130+ os .Exit (1 )
131+ }
132+ fmt .Println ("Running packer build..." )
133+ // Warning: variables like build_name and output_directory must exist in packer ariables file like in example
134+ // #nosec G204
135+ cmd := exec .Command ("packer" , "build" , "-var" , "build_name=" + image .ImageDir , "-var" , "output_directory=" + outputDirectory , packerImagePath )
136+ cmd .Stdout = os .Stdout
137+ cmd .Stderr = os .Stderr
138+ if err := cmd .Run (); err != nil {
139+ fmt .Printf ("Error running packer build: %v\n " , err )
140+ os .Exit (1 )
141+ }
142+ fmt .Println ("Packer build completed successfully." )
143+
144+ // Todo: Use --node-image-registry flag to pass path to registry.yaml
145+ registryConfigPath := filepath .Join (clusterStackPath , "node-images" , "registry.yaml" )
146+
147+ // Get the current working directory
148+ currentDir , err := os .Getwd ()
149+ if err != nil {
150+ fmt .Printf ("Error getting current working directory: %v\n " , err )
151+ os .Exit (1 )
152+ }
153+
154+ // Path to the image created by the packer
155+ // Warning: name of the image created by packer should have same name as the name of the image folder in node-images
156+ ouputImagePath := filepath .Join (currentDir , outputDirectory , image .ImageDir )
157+
158+ // Push the built image to S3
159+ if err := pushToS3 (ouputImagePath , image .ImageDir , registryConfigPath ); err != nil {
160+ fmt .Printf ("Error pushing image to S3: %v\n " , err )
161+ os .Exit (1 )
162+ }
163+
164+ // Update URL in config.yaml if it is necessary
165+ if err := updateURLNodeImages (configFilePath , registryConfigPath , image .ImageDir ); err != nil {
166+ fmt .Printf ("Error updating URL in config.yaml: %v\n " , err )
167+ os .Exit (1 )
168+ }
169+ // Copy config.yaml to releaseDir as node-images.yaml
170+ dest := filepath .Join (releaseDir , "node-images.yaml" )
171+ if err := copyFile (configFilePath , dest ); err != nil {
172+ fmt .Printf ("Error copying config.yaml to releaseDir: %v\n " , err )
173+ os .Exit (1 )
165174 }
166- } else {
167- fmt .Println ("No images to build" )
175+ fmt .Println ("config.yaml copied to releaseDir as node-images.yaml successfully!" )
168176 }
169177 default :
170178 fmt .Println ("Unknown method:" , method )
179+ os .Exit (1 )
171180 }
172181}
173182
@@ -186,6 +195,10 @@ func pushToS3(filePath, fileName, registryConfigPath string) error {
186195 return fmt .Errorf ("error decoding registry config file: %w" , err )
187196 }
188197
198+ // Remove "http://" or "https://" from the endpoint if present cause Endpoint cannot have fully qualified paths in minioClient.
199+ registryConfig .Config .Endpoint = strings .TrimPrefix (registryConfig .Config .Endpoint , "http://" )
200+ registryConfig .Config .Endpoint = strings .TrimPrefix (registryConfig .Config .Endpoint , "https://" )
201+
189202 // Initialize Minio client
190203 minioClient , err := minio .New (registryConfig .Config .Endpoint , & minio.Options {
191204 Creds : credentials .NewStaticV4 (registryConfig .Config .AccessKey , registryConfig .Config .SecretKey , "" ),
@@ -292,3 +305,48 @@ func copyFile(src, dest string) error {
292305
293306 return nil
294307}
308+
309+ // GetConfig returns CsctlConfig.
310+ func GetConfig (path string ) (NodeImages , error ) {
311+ configPath := filepath .Join (path , "config.yaml" )
312+ configFileData , err := os .ReadFile (filepath .Clean (configPath ))
313+ if err != nil {
314+ return NodeImages {}, fmt .Errorf ("failed to read csctl config: %w" , err )
315+ }
316+
317+ nd := NodeImages {}
318+ if err := yaml .Unmarshal (configFileData , & nd ); err != nil {
319+ return NodeImages {}, fmt .Errorf ("failed to unmarshal config yaml: %w" , err )
320+ }
321+
322+ if nd .APIVersion == "" {
323+ return NodeImages {}, fmt .Errorf ("api version must not be empty" )
324+ }
325+
326+ if len (nd .OpenStackNodeImages ) == 0 {
327+ return NodeImages {}, fmt .Errorf ("at least one node image needs to exist in OpenStackNodeImages list" )
328+ }
329+
330+ // Ensure all fields in OpenStackNodeImages are defined
331+ for _ , image := range nd .OpenStackNodeImages {
332+ switch {
333+ case image .CreateOpts == (struct {
334+ Name string `yaml:"name"`
335+ DiskFormat string `yaml:"disk_format"` //nolint:tagliatelle // The `DiskFormat` field in this struct corresponds to the `disk_format` YAML tag
336+ ContainerFormat string `yaml:"container_format"` //nolint:tagliatelle // The `DiskFormat` field in this struct corresponds to the `disk_format` YAML tag
337+ Visibility string `yaml:"visibility"`
338+ }{}):
339+ return NodeImages {}, fmt .Errorf ("field CreateOpts must not be empty" )
340+ case image .CreateOpts .Name == "" :
341+ return NodeImages {}, fmt .Errorf ("field 'name' in CreateOpts must be defined" )
342+ case image .CreateOpts .DiskFormat == "" :
343+ return NodeImages {}, fmt .Errorf ("field 'disk_format' in CreateOpts must be defined" )
344+ case image .CreateOpts .ContainerFormat == "" :
345+ return NodeImages {}, fmt .Errorf ("field 'container_format' in CreateOpts must be defined" )
346+ case image .CreateOpts .Visibility == "" :
347+ return NodeImages {}, fmt .Errorf ("field 'visibility' in CreateOpts must be defined" )
348+ }
349+ }
350+
351+ return nd , nil
352+ }
0 commit comments