Skip to content
This repository was archived by the owner on Dec 16, 2025. It is now read-only.

Commit abf0d67

Browse files
Move imageDir to the config.yaml file
Signed-off-by: michal.gubricky <michal.gubricky@dnation.cloud>
1 parent d1b6f75 commit abf0d67

File tree

3 files changed

+124
-65
lines changed
  • example/cluster-stacks/openstack/ferrol/node-images
  • vendor/github.com/SovereignCloudStack/csctl/pkg/clusterstack

3 files changed

+124
-65
lines changed

example/cluster-stacks/openstack/ferrol/node-images/config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
apiVersion: openstack.infrastructure.clusterstack.x-k8s.io/v1alpha1
22
openStackNodeImages:
33
- url: https://swift.services.a.regiocloud.tech/swift/v1/AUTH_b182637428444b9aa302bb8d5a5a418c/openstack-k8s-capi-images/ubuntu-2204-kube-v1.27/ubuntu-2204-kube-v1.27.8.qcow2
4+
# imageDir: <image-directory-in-node-images-folder> # define only if you choose the build method
45
createOpts:
56
name: ubuntu-capi-image-v1.27.8
67
disk_format: qcow2

main.go

Lines changed: 120 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ type RegistryConfig struct {
4545
// OpenStackNodeImage represents the structure of the OpenStackNodeImages.
4646
type 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

7475
func 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+
}

vendor/github.com/SovereignCloudStack/csctl/pkg/clusterstack/config.go

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)