Skip to content

Commit 5bf6153

Browse files
DEiseltjschoone
andauthoredOct 23, 2024··
feat(create): Support OCI remote and deduplicate code (#162)
added: allow switch for assetclient in create command added: `--publish` flag for create command removed: unused code changes from last commit changed: moved `pushReleaseAssets` function to `create.go` removed: publish command and `publish.go` file changed: help text format chore: go mod tidy chore(create): reactivate validateHash fix: make linter happy Signed-off-by: Danny Eiselt <eiselt@b1-systems.de> Co-authored-by: Jan Schoone <6106846+jschoone@users.noreply.github.com>
1 parent 8b5d6b4 commit 5bf6153

File tree

4 files changed

+94
-389
lines changed

4 files changed

+94
-389
lines changed
 

‎go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ require (
1111
golang.org/x/mod v0.16.0
1212
golang.org/x/oauth2 v0.18.0
1313
gopkg.in/src-d/go-git.v4 v4.13.1
14-
gopkg.in/yaml.v2 v2.4.0
1514
gopkg.in/yaml.v3 v3.0.1
1615
helm.sh/helm/v3 v3.14.4
1716
oras.land/oras-go/v2 v2.5.0
@@ -142,6 +141,7 @@ require (
142141
gopkg.in/inf.v0 v0.9.1 // indirect
143142
gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect
144143
gopkg.in/warnings.v0 v0.1.2 // indirect
144+
gopkg.in/yaml.v2 v2.4.0 // indirect
145145
k8s.io/api v0.29.0 // indirect
146146
k8s.io/apiextensions-apiserver v0.29.0 // indirect
147147
k8s.io/apimachinery v0.29.0 // indirect

‎pkg/cmd/create.go

+93-7
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ import (
2323
"os"
2424
"path/filepath"
2525

26+
"github.com/SovereignCloudStack/csctl/pkg/assetsclient"
2627
"github.com/SovereignCloudStack/csctl/pkg/assetsclient/github"
28+
"github.com/SovereignCloudStack/csctl/pkg/assetsclient/oci"
2729
"github.com/SovereignCloudStack/csctl/pkg/clusterstack"
2830
"github.com/SovereignCloudStack/csctl/pkg/hash"
2931
"github.com/SovereignCloudStack/csctl/pkg/providerplugin"
@@ -47,7 +49,9 @@ var (
4749
note - Hash mode takes the last hash of the git commit.`
4850
example = `csctl create tests/cluster-stacks/docker/ferrol -m hash (for hash mode)
4951
50-
csctl create tests/cluster-stacks/docker/ferrol -m hash --github-release github-release/ (for stable mode)`
52+
csctl create tests/cluster-stacks/docker/ferrol -m hash github-release/ (for stable mode)
53+
54+
csctl create --publish --remote oci tests/cluster-stacks/docker/ferrol (publish to OCI repository)`
5155
)
5256

5357
var (
@@ -57,6 +61,8 @@ var (
5761
clusterStackVersion string
5862
clusterAddonVersion string
5963
nodeImageVersion string
64+
remote string
65+
publish bool
6066
)
6167

6268
// CreateOptions contains config for creating a release.
@@ -69,6 +75,7 @@ type CreateOptions struct {
6975
CurrentReleaseHash hash.ReleaseHash
7076
LatestReleaseHash hash.ReleaseHash
7177
NodeImageRegistry string
78+
releaseName string
7279
}
7380

7481
// createCmd represents the create command.
@@ -88,6 +95,8 @@ func init() {
8895
createCmd.Flags().StringVar(&clusterStackVersion, "cluster-stack-version", "", "It is used to specify the semver version for the cluster stack in the custom mode")
8996
createCmd.Flags().StringVar(&clusterAddonVersion, "cluster-addon-version", "", "It is used to specify the semver version for the cluster addon in the custom mode")
9097
createCmd.Flags().StringVar(&nodeImageVersion, "node-image-version", "", "It is used to specify the semver version for the node images in the custom mode")
98+
createCmd.Flags().StringVar(&remote, "remote", "github", "Which remote repository to use and thus which credentials are required. Currently supported are 'github' and 'oci'.")
99+
createCmd.Flags().BoolVar(&publish, "publish", false, "Publish release after creation is done. This is only implemented for OCI currently.")
91100
}
92101

93102
// GetCreateOptions create a Create Option for create command.
@@ -129,12 +138,22 @@ func GetCreateOptions(ctx context.Context, clusterStackPath string) (*CreateOpti
129138
case stableMode:
130139
createOption.Metadata = &clusterstack.MetaData{}
131140

132-
gc, err := github.NewFactory().NewClient(ctx)
141+
var remoteFactory assetsclient.Factory
142+
143+
// using switch here in case more will be added in the future (aws?)
144+
switch remote {
145+
case "github":
146+
remoteFactory = github.NewFactory()
147+
case "oci":
148+
remoteFactory = oci.NewFactory()
149+
}
150+
151+
ac, err := remoteFactory.NewClient(ctx)
133152
if err != nil {
134-
return nil, fmt.Errorf("failed to create new github client: %w", err)
153+
return nil, fmt.Errorf("failed to create new asset client: %w", err)
135154
}
136155

137-
latestRepoRelease, err := getLatestReleaseFromRemoteRepository(ctx, mode, config, gc)
156+
latestRepoRelease, err := getLatestReleaseFromRemoteRepository(ctx, mode, config, ac)
138157
if err != nil {
139158
return nil, fmt.Errorf("failed to get latest release form remote repository: %w", err)
140159
}
@@ -147,7 +166,7 @@ func GetCreateOptions(ctx context.Context, clusterStackPath string) (*CreateOpti
147166
createOption.Metadata.Versions.Components.ClusterAddon = "v1"
148167
createOption.Metadata.Versions.Components.NodeImage = "v1"
149168
} else {
150-
if err := downloadReleaseAssets(ctx, latestRepoRelease, "./.tmp/release/", gc); err != nil {
169+
if err := downloadReleaseAssets(ctx, latestRepoRelease, "./.tmp/release/", ac); err != nil {
151170
return nil, fmt.Errorf("failed to download release asset: %w", err)
152171
}
153172

@@ -185,6 +204,12 @@ func GetCreateOptions(ctx context.Context, clusterStackPath string) (*CreateOpti
185204
if err != nil {
186205
return nil, fmt.Errorf("failed to get cluster stack release directory name: %w", err)
187206
}
207+
208+
createOption.releaseName, err = clusterstack.GetClusterStackReleaseDirectoryName(createOption.Metadata, createOption.Config)
209+
if err != nil {
210+
return nil, fmt.Errorf("failed to get cluster stack release name: %w", err)
211+
}
212+
188213
// Release directory name `release/docker-ferrol-1-27-v1`
189214
createOption.ClusterStackReleaseDir = filepath.Join(outputDirectory, releaseDirName)
190215

@@ -215,7 +240,7 @@ func createAction(cmd *cobra.Command, args []string) error {
215240
return fmt.Errorf("failed to validate with latest release hash: %w", err)
216241
}
217242

218-
if err := createOpts.generateRelease(); err != nil {
243+
if err := createOpts.generateRelease(cmd.Context()); err != nil {
219244
return fmt.Errorf("failed to generate release: %w", err)
220245
}
221246
fmt.Printf("Created %s\n", createOpts.ClusterStackReleaseDir)
@@ -234,7 +259,7 @@ func (c *CreateOptions) validateHash() error {
234259
return nil
235260
}
236261

237-
func (c *CreateOptions) generateRelease() error {
262+
func (c *CreateOptions) generateRelease(ctx context.Context) error {
238263
if err := os.MkdirAll(c.ClusterStackReleaseDir, os.ModePerm); err != nil {
239264
return fmt.Errorf("failed to create output directory: %w", err)
240265
}
@@ -323,6 +348,32 @@ func (c *CreateOptions) generateRelease() error {
323348
if err != nil {
324349
return fmt.Errorf("providerplugin.CreateNodeImages() failed: %w", err)
325350
}
351+
352+
if publish {
353+
if remote != "oci" {
354+
return fmt.Errorf("not pushing assets. --publish is only implemented for remote OCI")
355+
}
356+
357+
ociClient, err := oci.NewClient()
358+
if err != nil {
359+
return fmt.Errorf("failed to create new oci client: %w", err)
360+
}
361+
362+
var hashAnnotation string
363+
if len(c.CurrentReleaseHash.ClusterStack) >= 7 {
364+
hashAnnotation = c.CurrentReleaseHash.ClusterStack[:7]
365+
}
366+
367+
annotations := map[string]string{
368+
"kubernetesVersion": c.Metadata.Versions.Kubernetes,
369+
"hash": hashAnnotation,
370+
}
371+
372+
if err := pushReleaseAssets(ctx, ociClient, c.ClusterStackReleaseDir, c.releaseName, annotations); err != nil {
373+
return fmt.Errorf("failed to push release assets to the oci registry: %w", err)
374+
}
375+
}
376+
326377
return nil
327378
}
328379

@@ -382,3 +433,38 @@ func cleanTmpDirectory() error {
382433

383434
return nil
384435
}
436+
437+
func pushReleaseAssets(ctx context.Context, pusher assetsclient.Pusher, clusterStackReleasePath, releaseName string, annotations map[string]string) error {
438+
releaseAssets := []assetsclient.ReleaseAsset{}
439+
440+
ociclient, err := oci.NewClient()
441+
if err != nil {
442+
return fmt.Errorf("error creating oci client: %w", err)
443+
}
444+
445+
if ociclient.FoundRelease(ctx, releaseName) {
446+
fmt.Printf("release tag \"%s\" found in oci registry. aborting push\n", releaseName)
447+
return nil
448+
}
449+
450+
files, err := os.ReadDir(clusterStackReleasePath)
451+
if err != nil {
452+
return fmt.Errorf("failed to read directory %s: %w", clusterStackReleasePath, err)
453+
}
454+
455+
for _, file := range files {
456+
if file.Type().IsRegular() {
457+
releaseAssets = append(releaseAssets, assetsclient.ReleaseAsset{
458+
FileName: file.Name(),
459+
MediaType: getMediaType(file.Name()),
460+
})
461+
}
462+
}
463+
464+
if err := pusher.PushReleaseAssets(ctx, releaseAssets, releaseName, clusterStackReleasePath, clusterStackArtifactType, annotations); err != nil {
465+
return fmt.Errorf("failed to push release assets to oci registry: %w", err)
466+
}
467+
468+
fmt.Printf("successfully pushed clusterstack release: %s:%s \n", ociclient.Repository.Reference.String(), releaseName)
469+
return nil
470+
}

‎pkg/cmd/publish.go

-380
This file was deleted.

‎pkg/cmd/root.go

-1
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,4 @@ func Execute() {
4343
func init() {
4444
rootCmd.AddCommand(createCmd)
4545
rootCmd.AddCommand(versionCmd)
46-
rootCmd.AddCommand(publishCmd)
4746
}

0 commit comments

Comments
 (0)
Please sign in to comment.