Skip to content

Commit 84f5a09

Browse files
committed
✨ Support calling provider plugins.
Signed-off-by: Thomas Guettler <[email protected]>
1 parent 052e8e1 commit 84f5a09

File tree

8 files changed

+153
-10
lines changed

8 files changed

+153
-10
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ temp
1717
# build and release
1818
dist
1919
csctl
20+
csctl-docker
2021
tmp/
2122
releases/

Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ clean: ## cleans the csctl binary
5959
.PHONY: build
6060
build: # build the csctl binary
6161
go build -ldflags "$(LDFLAGS)" -o csctl main.go
62+
go build -o csctl-docker csctldocker/csctldocker_main.go
6263

6364
.PHONY: lint-golang
6465
lint-golang: ## Lint Golang codebase

csctldocker/csctldocker_main.go

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Package main provides a dummy plugin for csctl. You can use that code
18+
// to create a real csctl plugin.
19+
// You can implement the "create-node-images" command to create node images during
20+
// a `csctl create` call.
21+
package main
22+
23+
import (
24+
"fmt"
25+
"os"
26+
27+
csctlclusterstack "github.com/SovereignCloudStack/csctl/pkg/clusterstack"
28+
)
29+
30+
const provider = "docker"
31+
32+
func usage() {
33+
fmt.Printf(`%s create-node-images cluster-stack-directory cluster-stack-release-directory
34+
This command is a csctl plugin.
35+
36+
https://github.com/SovereignCloudStack/csctl
37+
`, os.Args[0])
38+
}
39+
40+
func main() {
41+
if len(os.Args) != 4 {
42+
usage()
43+
os.Exit(1)
44+
}
45+
if os.Args[1] != "create-node-images" {
46+
usage()
47+
os.Exit(1)
48+
}
49+
clusterStackPath := os.Args[2]
50+
config, err := csctlclusterstack.GetCsctlConfig(clusterStackPath)
51+
if err != nil {
52+
fmt.Println(err.Error())
53+
os.Exit(1)
54+
}
55+
if config.Config.Provider.Type != provider {
56+
fmt.Printf("Wrong provider in %s. Expected %s\n", clusterStackPath, provider)
57+
os.Exit(1)
58+
}
59+
releaseDir := os.Args[3]
60+
_, err = os.Stat(releaseDir)
61+
if err != nil {
62+
fmt.Println(err.Error())
63+
os.Exit(1)
64+
}
65+
fmt.Printf("clusterStackPath: %s\n", clusterStackPath)
66+
fmt.Printf("releaseDir: %s\n", releaseDir)
67+
fmt.Println(".... pretending to do heavy work (creating node images) ...")
68+
}

docs/design.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ help print a overview of available flags etc.
133133

134134
subcommands for provider:
135135
install Installs a cluster-stack-release-provider at a version
136-
installed Lists installed csmtcl release providers
137-
remove removes a csmtcl release provider at a version `csmtcl provider remove docker <version>`
136+
installed Lists installed csctl release providers
137+
remove removes a csctl release provider at a version `csctl provider remove docker <version>`
138138
```
139139

140140
## Modes

pkg/clusterstack/config.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ type CsctlConfig struct {
3737
KubernetesVersion string `yaml:"kubernetesVersion"`
3838
ClusterStackName string `yaml:"clusterStackName"`
3939
Provider struct {
40-
Type string `yaml:"type"`
41-
APIVersion string `yaml:"apiVersion"`
42-
Config struct{} `yaml:"config"`
40+
Type string `yaml:"type"`
41+
APIVersion string `yaml:"apiVersion"`
42+
Config map[string]interface{} `yaml:"config"`
4343
} `yaml:"provider"`
4444
} `yaml:"config"`
4545
}

pkg/cmd/create.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/SovereignCloudStack/csctl/pkg/github"
2828
"github.com/SovereignCloudStack/csctl/pkg/github/client"
2929
"github.com/SovereignCloudStack/csctl/pkg/hash"
30+
"github.com/SovereignCloudStack/csctl/pkg/providerplugin"
3031
"github.com/SovereignCloudStack/csctl/pkg/template"
3132
"github.com/spf13/cobra"
3233
"gopkg.in/yaml.v3"
@@ -91,6 +92,11 @@ func GetCreateOptions(ctx context.Context, clusterStackPath string) (*CreateOpti
9192
createOption.ClusterStackPath = clusterStackPath
9293
createOption.Config = config
9394

95+
_, _, err = providerplugin.GetProviderExecutable(&config)
96+
if err != nil {
97+
return createOption, err
98+
}
99+
94100
currentHash, err := hash.GetHash(clusterStackPath)
95101
if err != nil {
96102
return nil, fmt.Errorf("failed to get hash: %w", err)
@@ -109,7 +115,7 @@ func GetCreateOptions(ctx context.Context, clusterStackPath string) (*CreateOpti
109115
return nil, fmt.Errorf("failed to create new github client: %w", err)
110116
}
111117

112-
// update the metadata kubernetes version with the csmctl.yaml config
118+
// update the metadata kubernetes version with the csctl.yaml config
113119
createOption.Metadata.Versions.Kubernetes = config.Config.KubernetesVersion
114120

115121
latestRepoRelease, err := github.GetLatestReleaseFromRemoteRepository(ctx, mode, &config, gc)
@@ -189,7 +195,7 @@ func (c *CreateOptions) generateRelease() error {
189195
if err := os.MkdirAll(c.ClusterStackReleaseDir, os.ModePerm); err != nil {
190196
return fmt.Errorf("failed to create output directory: %w", err)
191197
}
192-
198+
fmt.Printf("Creating output in %s\n", c.ClusterStackReleaseDir)
193199
// Write the current hash
194200
hashJSONData, err := json.MarshalIndent(c.CurrentReleaseHash, "", " ")
195201
if err != nil {
@@ -244,5 +250,5 @@ func (c *CreateOptions) generateRelease() error {
244250
return fmt.Errorf("failed to write metadata: %w", err)
245251
}
246252

247-
return nil
253+
return providerplugin.CreateNodeImages(&c.Config, c.ClusterStackPath, c.ClusterStackReleaseDir)
248254
}

pkg/cmd/version.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ var (
3131

3232
var versionCmd = &cobra.Command{
3333
Use: "version",
34-
Short: "prints the latest version of csmctl",
34+
Short: "prints the latest version of csctl",
3535
Run: printVersion,
3636
SilenceUsage: true,
3737
}
3838

3939
func printVersion(_ *cobra.Command, _ []string) {
40-
fmt.Println("csmctl version:", Version)
40+
fmt.Println("csctl version:", Version)
4141
fmt.Println("commit:", Commit)
4242
}

pkg/providerplugin/providerplugin.go

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Package providerplugin implements calling the provider specific csctl plugin.
18+
package providerplugin
19+
20+
import (
21+
"fmt"
22+
"os"
23+
"os/exec"
24+
"path/filepath"
25+
26+
"github.com/SovereignCloudStack/csctl/pkg/clusterstack"
27+
)
28+
29+
// GetProviderExecutable returns the path to the provider plugin (like "csctl-docker").
30+
// If there is not "config" for the provider in csctl.yaml, then needed is false and path is the empty string.
31+
func GetProviderExecutable(config *clusterstack.CsctlConfig) (needed bool, path string, err error) {
32+
if len(config.Config.Provider.Config) == 0 {
33+
return false, "", nil
34+
}
35+
pluginName := "csctl-" + config.Config.Provider.Type
36+
_, err = os.Stat(pluginName)
37+
if err == nil {
38+
path, err := filepath.Abs(pluginName)
39+
if err != nil {
40+
return false, "", err
41+
}
42+
return true, path, nil
43+
}
44+
path, err = exec.LookPath(pluginName)
45+
if err != nil {
46+
return false, "", fmt.Errorf("could not find plugin %s in $PATH or current working directory", pluginName)
47+
}
48+
return true, path, nil
49+
}
50+
51+
// CreateNodeImages calls the provider plugin command to create nodes images.
52+
func CreateNodeImages(config *clusterstack.CsctlConfig, clusterStackPath, clusterStackReleaseDir string) error {
53+
needed, path, err := GetProviderExecutable(config)
54+
if err != nil {
55+
return err
56+
}
57+
if !needed {
58+
fmt.Printf("No provider specifig configuration in csctl.yaml. No need to call a plugin for provider %q\n", config.Config.Provider.Type)
59+
return nil
60+
}
61+
args := []string{"create-node-images", clusterStackPath, clusterStackReleaseDir}
62+
fmt.Printf("Calling Provider Plugin: %s\n", path)
63+
cmd := exec.Command(path, args...) // #nosec G204
64+
cmd.Stdout = os.Stdout
65+
cmd.Stderr = os.Stderr
66+
return cmd.Run()
67+
}

0 commit comments

Comments
 (0)