Skip to content

Commit

Permalink
feat(create): Support OCI remote and deduplicate code (#162)
Browse files Browse the repository at this point in the history
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 <[email protected]>
Co-authored-by: Jan Schoone <[email protected]>
  • Loading branch information
DEiselt and jschoone authored Oct 23, 2024
1 parent 8b5d6b4 commit 5bf6153
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 389 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ require (
golang.org/x/mod v0.16.0
golang.org/x/oauth2 v0.18.0
gopkg.in/src-d/go-git.v4 v4.13.1
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
helm.sh/helm/v3 v3.14.4
oras.land/oras-go/v2 v2.5.0
Expand Down Expand Up @@ -142,6 +141,7 @@ require (
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/api v0.29.0 // indirect
k8s.io/apiextensions-apiserver v0.29.0 // indirect
k8s.io/apimachinery v0.29.0 // indirect
Expand Down
100 changes: 93 additions & 7 deletions pkg/cmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ import (
"os"
"path/filepath"

"github.com/SovereignCloudStack/csctl/pkg/assetsclient"
"github.com/SovereignCloudStack/csctl/pkg/assetsclient/github"
"github.com/SovereignCloudStack/csctl/pkg/assetsclient/oci"
"github.com/SovereignCloudStack/csctl/pkg/clusterstack"
"github.com/SovereignCloudStack/csctl/pkg/hash"
"github.com/SovereignCloudStack/csctl/pkg/providerplugin"
Expand All @@ -47,7 +49,9 @@ var (
note - Hash mode takes the last hash of the git commit.`
example = `csctl create tests/cluster-stacks/docker/ferrol -m hash (for hash mode)
csctl create tests/cluster-stacks/docker/ferrol -m hash --github-release github-release/ (for stable mode)`
csctl create tests/cluster-stacks/docker/ferrol -m hash github-release/ (for stable mode)
csctl create --publish --remote oci tests/cluster-stacks/docker/ferrol (publish to OCI repository)`
)

var (
Expand All @@ -57,6 +61,8 @@ var (
clusterStackVersion string
clusterAddonVersion string
nodeImageVersion string
remote string
publish bool
)

// CreateOptions contains config for creating a release.
Expand All @@ -69,6 +75,7 @@ type CreateOptions struct {
CurrentReleaseHash hash.ReleaseHash
LatestReleaseHash hash.ReleaseHash
NodeImageRegistry string
releaseName string
}

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

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

gc, err := github.NewFactory().NewClient(ctx)
var remoteFactory assetsclient.Factory

// using switch here in case more will be added in the future (aws?)
switch remote {
case "github":
remoteFactory = github.NewFactory()
case "oci":
remoteFactory = oci.NewFactory()
}

ac, err := remoteFactory.NewClient(ctx)
if err != nil {
return nil, fmt.Errorf("failed to create new github client: %w", err)
return nil, fmt.Errorf("failed to create new asset client: %w", err)
}

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

Expand Down Expand Up @@ -185,6 +204,12 @@ func GetCreateOptions(ctx context.Context, clusterStackPath string) (*CreateOpti
if err != nil {
return nil, fmt.Errorf("failed to get cluster stack release directory name: %w", err)
}

createOption.releaseName, err = clusterstack.GetClusterStackReleaseDirectoryName(createOption.Metadata, createOption.Config)
if err != nil {
return nil, fmt.Errorf("failed to get cluster stack release name: %w", err)
}

// Release directory name `release/docker-ferrol-1-27-v1`
createOption.ClusterStackReleaseDir = filepath.Join(outputDirectory, releaseDirName)

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

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

func (c *CreateOptions) generateRelease() error {
func (c *CreateOptions) generateRelease(ctx context.Context) error {
if err := os.MkdirAll(c.ClusterStackReleaseDir, os.ModePerm); err != nil {
return fmt.Errorf("failed to create output directory: %w", err)
}
Expand Down Expand Up @@ -323,6 +348,32 @@ func (c *CreateOptions) generateRelease() error {
if err != nil {
return fmt.Errorf("providerplugin.CreateNodeImages() failed: %w", err)
}

if publish {
if remote != "oci" {
return fmt.Errorf("not pushing assets. --publish is only implemented for remote OCI")
}

ociClient, err := oci.NewClient()
if err != nil {
return fmt.Errorf("failed to create new oci client: %w", err)
}

var hashAnnotation string
if len(c.CurrentReleaseHash.ClusterStack) >= 7 {
hashAnnotation = c.CurrentReleaseHash.ClusterStack[:7]
}

annotations := map[string]string{
"kubernetesVersion": c.Metadata.Versions.Kubernetes,
"hash": hashAnnotation,
}

if err := pushReleaseAssets(ctx, ociClient, c.ClusterStackReleaseDir, c.releaseName, annotations); err != nil {
return fmt.Errorf("failed to push release assets to the oci registry: %w", err)
}
}

return nil
}

Expand Down Expand Up @@ -382,3 +433,38 @@ func cleanTmpDirectory() error {

return nil
}

func pushReleaseAssets(ctx context.Context, pusher assetsclient.Pusher, clusterStackReleasePath, releaseName string, annotations map[string]string) error {
releaseAssets := []assetsclient.ReleaseAsset{}

ociclient, err := oci.NewClient()
if err != nil {
return fmt.Errorf("error creating oci client: %w", err)
}

if ociclient.FoundRelease(ctx, releaseName) {
fmt.Printf("release tag \"%s\" found in oci registry. aborting push\n", releaseName)
return nil
}

files, err := os.ReadDir(clusterStackReleasePath)
if err != nil {
return fmt.Errorf("failed to read directory %s: %w", clusterStackReleasePath, err)
}

for _, file := range files {
if file.Type().IsRegular() {
releaseAssets = append(releaseAssets, assetsclient.ReleaseAsset{
FileName: file.Name(),
MediaType: getMediaType(file.Name()),
})
}
}

if err := pusher.PushReleaseAssets(ctx, releaseAssets, releaseName, clusterStackReleasePath, clusterStackArtifactType, annotations); err != nil {
return fmt.Errorf("failed to push release assets to oci registry: %w", err)
}

fmt.Printf("successfully pushed clusterstack release: %s:%s \n", ociclient.Repository.Reference.String(), releaseName)
return nil
}
Loading

0 comments on commit 5bf6153

Please sign in to comment.