diff --git a/Makefile b/Makefile index f81d2ad40..d2af304cc 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION=v0.0.24 +VERSION=v0.0.25 OUT_DIR=dist YEAR?=$(shell date +"%Y") @@ -6,15 +6,9 @@ CLI_NAME?=cf IMAGE_REPOSITORY?=quay.io IMAGE_NAMESPACE?=codefresh -ARGOCD_INSTALLATION_MANIFESTS_URL="github.com/codefresh-io/cli-v2/manifests/argo-cd?ref=$(VERSION)" -EVENTS_INSTALLATION_MANIFESTS_URL="github.com/codefresh-io/cli-v2/manifests/argo-events?ref=$(VERSION)" -ROLLOUTS_INSTALLATION_MANIFESTS_URL="github.com/codefresh-io/cli-v2/manifests/argo-rollouts?ref=$(VERSION)" -WORKFLOWS_INSTALLATION_MANIFESTS_URL="github.com/codefresh-io/cli-v2/manifests/argo-workflows?ref=$(VERSION)" +RUNTIME_DEF_URL="https://github.com/codefresh-io/cli-v2/manifests/runtime.yaml" -DEV_ARGOCD_INSTALLATION_MANIFESTS_URL="manifests/argo-cd" -DEV_EVENTS_INSTALLATION_MANIFESTS_URL="manifests/argo-events" -DEV_ROLLOUTS_INSTALLATION_MANIFESTS_URL="manifests/argo-rollouts" -DEV_WORKFLOWS_INSTALLATION_MANIFESTS_URL="manifests/argo-workflows" +DEV_RUNTIME_DEF_URL="manifests/runtime.yaml" CLI_SRCS := $(shell find . -name '*.go') @@ -26,10 +20,7 @@ BUILD_DATE=$(shell date -u +'%Y-%m-%dT%H:%M:%SZ') DEV_MODE?=true ifeq (${DEV_MODE},true) - ARGOCD_INSTALLATION_MANIFESTS_URL=${DEV_ARGOCD_INSTALLATION_MANIFESTS_URL} - EVENTS_INSTALLATION_MANIFESTS_URL=${DEV_EVENTS_INSTALLATION_MANIFESTS_URL} - ROLLOUTS_INSTALLATION_MANIFESTS_URL=${DEV_ROLLOUTS_INSTALLATION_MANIFESTS_URL} - WORKFLOWS_INSTALLATION_MANIFESTS_URL=${DEV_WORKFLOWS_INSTALLATION_MANIFESTS_URL} + RUNTIME_DEF_URL=${DEV_RUNTIME_DEF_URL} endif ifndef GOBIN @@ -94,10 +85,7 @@ $(OUT_DIR)/$(CLI_NAME)-%: $(CLI_SRCS) VERSION=$(VERSION) \ GIT_COMMIT=$(GIT_COMMIT) \ OUT_FILE=$(OUT_DIR)/$(CLI_NAME)-$* \ - ARGOCD_INSTALLATION_MANIFESTS_URL=$(ARGOCD_INSTALLATION_MANIFESTS_URL) \ - EVENTS_INSTALLATION_MANIFESTS_URL=$(EVENTS_INSTALLATION_MANIFESTS_URL) \ - ROLLOUTS_INSTALLATION_MANIFESTS_URL=$(ROLLOUTS_INSTALLATION_MANIFESTS_URL) \ - WORKFLOWS_INSTALLATION_MANIFESTS_URL=$(WORKFLOWS_INSTALLATION_MANIFESTS_URL) \ + RUNTIME_DEF_URL=$(RUNTIME_DEF_URL) \ MAIN=./cmd \ ./hack/build.sh @@ -120,6 +108,7 @@ test: .PHONY: codegen codegen: $(GOBIN)/mockery + rm -f ./docs/commands/* go generate ./... go run ./hack/license.go --license ./hack/boilerplate.txt --year $(YEAR) . diff --git a/cmd/commands/config.go b/cmd/commands/config.go index dc2281ef2..31d769eb1 100644 --- a/cmd/commands/config.go +++ b/cmd/commands/config.go @@ -132,9 +132,10 @@ func NewConfigCurrentContextCommand() *cobra.Command { func RunConfigCurrentContext(ctx context.Context) error { cur := cfConfig.GetCurrentContext() if cur.Name == "" { - log.G().Fatal(util.Doc("no currently selected context, use ' config use-context' to select a context")) + log.G(ctx).Fatal(util.Doc("no currently selected context, use ' config use-context' to select a context")) } - log.G().Info(cur.Name) + + log.G(ctx).Info(cur.Name) return nil } @@ -188,6 +189,7 @@ func RunConfigDeleteContext(ctx context.Context, context string) error { if err := cfConfig.DeleteContext(context); err != nil { return err } - log.G().Infof("delete context: %s", context) + + log.G(ctx).Infof("delete context: %s", context) return nil } diff --git a/cmd/commands/runtime.go b/cmd/commands/runtime.go index 8b14d6bc2..a28728e57 100644 --- a/cmd/commands/runtime.go +++ b/cmd/commands/runtime.go @@ -23,10 +23,11 @@ import ( "github.com/codefresh-io/cli-v2/pkg/cdUtils" "github.com/codefresh-io/cli-v2/pkg/eventUtils" "github.com/codefresh-io/cli-v2/pkg/log" + "github.com/codefresh-io/cli-v2/pkg/runtime" "github.com/codefresh-io/cli-v2/pkg/store" "github.com/codefresh-io/cli-v2/pkg/util" - "github.com/juju/ansiterm" + "github.com/Masterminds/semver/v3" appset "github.com/argoproj-labs/applicationset/api/v1alpha1" apcmd "github.com/argoproj-labs/argocd-autopilot/cmd/commands" "github.com/argoproj-labs/argocd-autopilot/pkg/application" @@ -40,6 +41,7 @@ import ( wfv1alpha1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" "github.com/ghodss/yaml" "github.com/go-git/go-billy/v5/memfs" + "github.com/juju/ansiterm" "github.com/spf13/cobra" v1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" @@ -47,21 +49,25 @@ import ( ) type ( - RuntimeCreateOptions struct { + RuntimeInstallOptions struct { RuntimeName string - KubeContext string gsCloneOpts *git.CloneOptions insCloneOpts *git.CloneOptions KubeFactory kube.Factory } - RuntimeDeleteOptions struct { + RuntimeUninstallOptions struct { RuntimeName string - KubeContext string Timeout time.Duration CloneOpts *git.CloneOptions KubeFactory kube.Factory } + + RuntimeUpgradeOptions struct { + RuntimeName string + Version *semver.Version + CloneOpts *git.CloneOptions + } ) func NewRuntimeCommand() *cobra.Command { @@ -75,14 +81,15 @@ func NewRuntimeCommand() *cobra.Command { }, } - cmd.AddCommand(NewRuntimeCreateCommand()) + cmd.AddCommand(NewRuntimeInstallCommand()) cmd.AddCommand(NewRuntimeListCommand()) - cmd.AddCommand(NewRuntimeDeleteCommand()) + cmd.AddCommand(NewRuntimeUninsatllCommand()) + cmd.AddCommand(NewRuntimeUpgradeCommand()) return cmd } -func NewRuntimeCreateCommand() *cobra.Command { +func NewRuntimeInstallCommand() *cobra.Command { var ( f kube.Factory insCloneOpts *git.CloneOptions @@ -90,8 +97,8 @@ func NewRuntimeCreateCommand() *cobra.Command { ) cmd := &cobra.Command{ - Use: "create [runtime_name]", - Short: "Create a new Codefresh runtime", + Use: "install [runtime_name]", + Short: "Install a new Codefresh runtime", Example: util.Doc(` # To run this command you need to create a personal access token for your git provider # and provide it using: @@ -104,7 +111,7 @@ func NewRuntimeCreateCommand() *cobra.Command { # Adds a new runtime - runtime create runtime-name --install-repo gitops_repo + runtime install runtime-name --install-repo gitops_repo `), PreRun: func(_ *cobra.Command, _ []string) { if gsCloneOpts.Auth.Password == "" { @@ -125,9 +132,8 @@ func NewRuntimeCreateCommand() *cobra.Command { log.G(ctx).Fatal("must enter runtime name") } - return RunRuntimeCreate(ctx, &RuntimeCreateOptions{ + return RunRuntimeInstall(ctx, &RuntimeInstallOptions{ RuntimeName: args[0], - KubeContext: cmd.Flag("context").Value.String(), gsCloneOpts: gsCloneOpts, insCloneOpts: insCloneOpts, KubeFactory: f, @@ -151,16 +157,20 @@ func NewRuntimeCreateCommand() *cobra.Command { return cmd } -func RunRuntimeCreate(ctx context.Context, opts *RuntimeCreateOptions) error { - err := apcmd.RunRepoBootstrap(ctx, &apcmd.RepoBootstrapOptions{ - AppSpecifier: store.Get().ArgoCDManifestsURL, +func RunRuntimeInstall(ctx context.Context, opts *RuntimeInstallOptions) error { + rt, err := runtime.Download(nil, opts.RuntimeName) + if err != nil { + return fmt.Errorf("failed to download runtime definition: %w", err) + } + + err = apcmd.RunRepoBootstrap(ctx, &apcmd.RepoBootstrapOptions{ + AppSpecifier: rt.Spec.BootstrapSpecifier, Namespace: opts.RuntimeName, - KubeContext: opts.KubeContext, KubeFactory: opts.KubeFactory, CloneOptions: opts.insCloneOpts, }) if err != nil { - return err + return fmt.Errorf("failed to bootstrap repository: %w", err) } err = apcmd.RunProjectCreate(ctx, &apcmd.ProjectCreateOptions{ @@ -168,19 +178,18 @@ func RunRuntimeCreate(ctx context.Context, opts *RuntimeCreateOptions) error { ProjectName: opts.RuntimeName, }) if err != nil { - return err - } - - if err = createApp(ctx, opts.KubeFactory, opts.insCloneOpts, opts.RuntimeName, "rollouts", store.Get().ArgoRolloutsManifestsURL, application.AppTypeKustomize, opts.RuntimeName, false); err != nil { - return fmt.Errorf("failed to create rollouts application: %w", err) + return fmt.Errorf("failed to create project: %w", err) } - if err = createApp(ctx, opts.KubeFactory, opts.insCloneOpts, opts.RuntimeName, "workflows", store.Get().ArgoWorkflowsManifestsURL, application.AppTypeKustomize, opts.RuntimeName, false); err != nil { - return fmt.Errorf("failed to create workflows application: %w", err) + for _, component := range rt.Spec.Components { + log.G(ctx).Infof("creating component '%s'", component.Name) + if err = component.CreateApp(ctx, opts.KubeFactory, opts.insCloneOpts, opts.RuntimeName); err != nil { + return fmt.Errorf("failed to create '%s' application: %w", component.Name, err) + } } - if err = createApp(ctx, opts.KubeFactory, opts.insCloneOpts, opts.RuntimeName, "events", store.Get().ArgoEventsManifestsURL, application.AppTypeKustomize, opts.RuntimeName, true); err != nil { - return fmt.Errorf("failed to create events application: %w", err) + if err = persistRuntime(ctx, opts.insCloneOpts, rt); err != nil { + return fmt.Errorf("failed to create codefresh-cm: %w", err) } if err = createComponentsReporter(ctx, opts.insCloneOpts, opts); err != nil { @@ -188,7 +197,7 @@ func RunRuntimeCreate(ctx context.Context, opts *RuntimeCreateOptions) error { } if err = createDemoWorkflowTemplate(ctx, opts.gsCloneOpts, store.Get().GitSourceName, opts.RuntimeName); err != nil { - return err + return fmt.Errorf("failed to create demo workflowTemplate: %w", err) } if err = createGitSource(ctx, opts.insCloneOpts, opts.gsCloneOpts, store.Get().GitSourceName, opts.RuntimeName); err != nil { @@ -199,32 +208,32 @@ func RunRuntimeCreate(ctx context.Context, opts *RuntimeCreateOptions) error { } func NewRuntimeListCommand() *cobra.Command { - - cmd := &cobra.Command{ - Use: "list ", - Short: "List all Codefresh runtimes", + Use: "list ", + Short: "List all Codefresh runtimes", Example: util.Doc(` runtime list`), - RunE: func(cmd *cobra.Command, args []string) error { - return listRuntimes(cmd.Context()) + RunE: func(_ *cobra.Command, _ []string) error { + return RunRuntimeList() }, } return cmd } -func listRuntimes(ctx context.Context) error { +func RunRuntimeList() error { runtimes, err := cfConfig.NewClient().ArgoRuntime().List() if err != nil { return err } + tb := ansiterm.NewTabWriter(os.Stdout, 0, 0, 4, ' ', 0) _, err = fmt.Fprintln(tb, "NAME\tNAMESPACE\tCLUSTER\tSTATUS\tVERSION") if err != nil { return err } + for _, rt := range runtimes { status := "N/A" - namespace:= "N/A" + namespace := "N/A" name := "N/A" cluster := "N/A" version := "N/A" @@ -234,7 +243,7 @@ func listRuntimes(ctx context.Context) error { if rt.Namespace != nil { namespace = *rt.Namespace } - if rt.ObjectMeta != nil && rt.ObjectMeta.Name != nil { + if rt.ObjectMeta != nil && rt.ObjectMeta.Name != nil { name = *rt.ObjectMeta.Name } if rt.Cluster != nil { @@ -244,28 +253,29 @@ func listRuntimes(ctx context.Context) error { version = *rt.Version } _, err = fmt.Fprintf(tb, "%s\t%s\t%s\t%s\t%s\n", - name, - namespace, - cluster, - status, - version, - ) - if err != nil { - return err - } + name, + namespace, + cluster, + status, + version, + ) + if err != nil { + return err + } } + return tb.Flush() } -func NewRuntimeDeleteCommand() *cobra.Command { +func NewRuntimeUninsatllCommand() *cobra.Command { var ( f kube.Factory cloneOpts *git.CloneOptions ) cmd := &cobra.Command{ - Use: "delete [runtime_name]", - Short: "Deletes a Codefresh runtime", + Use: "uninstall [runtime_name]", + Short: "Uninstall a Codefresh runtime", Example: util.Doc(` # To run this command you need to create a personal access token for your git provider # and provide it using: @@ -276,9 +286,9 @@ func NewRuntimeDeleteCommand() *cobra.Command { --git-token -# Adds a new runtime +# Deletes a runtime - runtime delete runtime-name --repo gitops_repo + runtime uninstall runtime-name --repo gitops_repo `), PreRun: func(_ *cobra.Command, _ []string) { cloneOpts.Parse() @@ -289,9 +299,8 @@ func NewRuntimeDeleteCommand() *cobra.Command { log.G(ctx).Fatal("must enter runtime name") } - return RunRuntimeDelete(ctx, &RuntimeDeleteOptions{ + return RunRuntimeUninstall(ctx, &RuntimeUninstallOptions{ RuntimeName: args[0], - KubeContext: cmd.Flag("context").Value.String(), Timeout: aputil.MustParseDuration(cmd.Flag("request-timeout").Value.String()), CloneOpts: cloneOpts, KubeFactory: f, @@ -307,38 +316,130 @@ func NewRuntimeDeleteCommand() *cobra.Command { return cmd } -func RunRuntimeDelete(ctx context.Context, opts *RuntimeDeleteOptions) error { +func RunRuntimeUninstall(ctx context.Context, opts *RuntimeUninstallOptions) error { return apcmd.RunRepoUninstall(ctx, &apcmd.RepoUninstallOptions{ Namespace: opts.RuntimeName, - KubeContext: opts.KubeContext, Timeout: opts.Timeout, CloneOptions: opts.CloneOpts, KubeFactory: opts.KubeFactory, }) } -func createApp(ctx context.Context, f kube.Factory, cloneOpts *git.CloneOptions, projectName, appName, appURL, appType, namespace string, wait bool) error { - timeout := time.Duration(0) - if wait { - timeout = store.Get().WaitTimeout - } - - return apcmd.RunAppCreate(ctx, &apcmd.AppCreateOptions{ - CloneOpts: cloneOpts, - AppsCloneOpts: &git.CloneOptions{}, - ProjectName: projectName, - AppOpts: &application.CreateOptions{ - AppName: appName, - AppSpecifier: appURL, - AppType: appType, - DestNamespace: namespace, +func NewRuntimeUpgradeCommand() *cobra.Command { + var ( + cloneOpts *git.CloneOptions + ) + + cmd := &cobra.Command{ + Use: "upgrade [runtime_name]", + Short: "Upgrade a Codefresh runtime", + Example: util.Doc(` +# To run this command you need to create a personal access token for your git provider +# and provide it using: + + export GIT_TOKEN= + +# or with the flag: + + --git-token + +# Upgrade a runtime to version v0.0.30 + + runtime upgrade runtime-name v0.0.30 --repo gitops_repo +`), + PreRun: func(_ *cobra.Command, _ []string) { + cloneOpts.Parse() + }, + RunE: func(cmd *cobra.Command, args []string) error { + var version *semver.Version + ctx := cmd.Context() + if len(args) < 1 { + log.G(ctx).Fatal("must enter runtime name") + } + + if len(args) > 1 { + version = semver.MustParse(args[1]) + } + + return RunRuntimeUpgrade(ctx, &RuntimeUpgradeOptions{ + RuntimeName: args[0], + Version: version, + CloneOpts: cloneOpts, + }) }, - KubeFactory: f, - Timeout: timeout, + } + + cloneOpts = git.AddFlags(cmd, &git.AddFlagsOptions{ + FS: memfs.New(), }) + + return cmd } -func createComponentsReporter(ctx context.Context, cloneOpts *git.CloneOptions, opts *RuntimeCreateOptions) error { +func RunRuntimeUpgrade(ctx context.Context, opts *RuntimeUpgradeOptions) error { + if opts.Version == nil { + opts.Version = store.Get().Version.Version + } + + newRt, err := runtime.Download(opts.Version, opts.RuntimeName) + if err != nil { + return fmt.Errorf("failed to download runtime definition: %w", err) + } + + if newRt.Spec.DefVersion.GreaterThan(store.Get().MaxDefVersion) { + return fmt.Errorf("please upgrade your cli version before upgrading to %s", newRt.Spec.Version) + } + + r, fs, err := opts.CloneOpts.GetRepo(ctx) + if err != nil { + return err + } + + curRt, err := runtime.Load(fs, fs.Join(apstore.Default.BootsrtrapDir, store.Get().RuntimeFilename)) + if err != nil { + return fmt.Errorf("failed to load current runtime definition: %w", err) + } + + if !newRt.Spec.Version.GreaterThan(curRt.Spec.Version) { + return fmt.Errorf("must upgrade to version > %s", curRt.Spec.Version) + } + + newComponents, err := curRt.Upgrade(fs, newRt) + if err != nil { + return fmt.Errorf("failed to upgrade runtime: %w", err) + } + + if _, err = r.Persist(ctx, &git.PushOptions{CommitMsg: fmt.Sprintf("Upgraded to %s", opts.Version)}); err != nil { + return err + } + + for _, component := range newComponents { + log.G(ctx).Infof("Creating app '%s'", component.Name) + if err = component.CreateApp(ctx, nil, opts.CloneOpts, opts.RuntimeName); err != nil { + return fmt.Errorf("failed to create '%s' application: %w", component.Name, err) + } + } + + return nil +} + +func persistRuntime(ctx context.Context, cloneOpts *git.CloneOptions, rt *runtime.Runtime) error { + r, fs, err := cloneOpts.GetRepo(ctx) + if err != nil { + return err + } + + if err = rt.Save(fs, fs.Join(apstore.Default.BootsrtrapDir, store.Get().RuntimeFilename)); err != nil { + return err + } + + _, err = r.Persist(ctx, &git.PushOptions{ + CommitMsg: "Persisted runtime data", + }) + return err +} + +func createComponentsReporter(ctx context.Context, cloneOpts *git.CloneOptions, opts *RuntimeInstallOptions) error { tokenSecret, err := getTokenSecret(opts.RuntimeName) if err != nil { return fmt.Errorf("failed to create codefresh token secret: %w", err) @@ -349,7 +450,12 @@ func createComponentsReporter(ctx context.Context, cloneOpts *git.CloneOptions, } resPath := cloneOpts.FS.Join(apstore.Default.AppsDir, store.Get().ComponentsReporterName, opts.RuntimeName, "resources") - if err := createApp(ctx, opts.KubeFactory, cloneOpts, opts.RuntimeName, store.Get().ComponentsReporterName, cloneOpts.URL()+"/"+resPath, application.AppTypeDirectory, opts.RuntimeName, false); err != nil { + appDef := &runtime.AppDef{ + Name: store.Get().ComponentsReporterName, + Type: application.AppTypeDirectory, + URL: cloneOpts.URL() + "/" + resPath, + } + if err := appDef.CreateApp(ctx, opts.KubeFactory, cloneOpts, opts.RuntimeName); err != nil { return err } @@ -398,8 +504,8 @@ func updateProject(repofs fs.FS, runtimeName string) error { project.ObjectMeta.Labels = make(map[string]string) } - appset.Spec.Template.Labels[store.Get().CFType] = "component" - project.ObjectMeta.Labels[store.Get().CFType] = "runtime" + appset.Spec.Template.Labels[store.Get().LabelKeyCFType] = "component" + project.ObjectMeta.Labels[store.Get().LabelKeyCFType] = "runtime" return repofs.WriteYamls(projPath, project, appset) } @@ -499,7 +605,7 @@ func createEventSource(repofs fs.FS, path, namespace string) error { Namespace: namespace, Selectors: []eventUtils.CreateSelectorOptions{ { - Key: store.Get().CFType, + Key: store.Get().LabelKeyCFType, Operation: "==", Value: store.Get().CFComponentType, }, @@ -512,7 +618,7 @@ func createEventSource(repofs fs.FS, path, namespace string) error { Namespace: namespace, Selectors: []eventUtils.CreateSelectorOptions{ { - Key: store.Get().CFType, + Key: store.Get().LabelKeyCFType, Operation: "==", Value: store.Get().CFRuntimeType, }, @@ -724,8 +830,12 @@ func createGitSource(ctx context.Context, insCloneOpts *git.CloneOptions, gsClon return err } - fullResPath := insFs.Join(insFs.Root(), resPath) - if err = createApp(ctx, nil, insCloneOpts, runtimeName, gsName, insCloneOpts.URL()+fullResPath, application.AppTypeDirectory, runtimeName, false); err != nil { + appDef := &runtime.AppDef{ + Name: gsName, + Type: application.AppTypeDirectory, + URL: insCloneOpts.URL() + insFs.Join(insFs.Root(), resPath), + } + if err = appDef.CreateApp(ctx, nil, insCloneOpts, runtimeName); err != nil { return fmt.Errorf("failed to create git-source: %w", err) } diff --git a/docs/commands/cli-v2_runtime.md b/docs/commands/cli-v2_runtime.md index a6f13c29e..31eff54aa 100644 --- a/docs/commands/cli-v2_runtime.md +++ b/docs/commands/cli-v2_runtime.md @@ -24,7 +24,8 @@ cli-v2 runtime [flags] ### SEE ALSO * [cli-v2](cli-v2.md) - cli-v2 is used for installing and managing codefresh installations using gitops -* [cli-v2 runtime create](cli-v2_runtime_create.md) - Create a new Codefresh runtime -* [cli-v2 runtime delete](cli-v2_runtime_delete.md) - Deletes a Codefresh runtime +* [cli-v2 runtime install](cli-v2_runtime_install.md) - Install a new Codefresh runtime * [cli-v2 runtime list](cli-v2_runtime_list.md) - List all Codefresh runtimes +* [cli-v2 runtime uninstall](cli-v2_runtime_uninstall.md) - Uninstall a Codefresh runtime +* [cli-v2 runtime upgrade](cli-v2_runtime_upgrade.md) - Upgrade a Codefresh runtime diff --git a/docs/commands/cli-v2_runtime_create.md b/docs/commands/cli-v2_runtime_create.md deleted file mode 100644 index 11eba45c5..000000000 --- a/docs/commands/cli-v2_runtime_create.md +++ /dev/null @@ -1,67 +0,0 @@ -## cli-v2 runtime create - -Create a new Codefresh runtime - -``` -cli-v2 runtime create [runtime_name] [flags] -``` - -### Examples - -``` - -# To run this command you need to create a personal access token for your git provider -# and provide it using: - - export INSTALL_GIT_TOKEN= - -# or with the flag: - - --install-git-token - -# Adds a new runtime - - cli-v2 runtime create runtime-name --install-repo gitops_repo - -``` - -### Options - -``` - --as string Username to impersonate for the operation - --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. - --cache-dir string Default cache directory (default "/home/user/.kube/cache") - --certificate-authority string Path to a cert file for the certificate authority - --client-certificate string Path to a client certificate file for TLS - --client-key string Path to a client key file for TLS - --cluster string The name of the kubeconfig cluster to use - --context string The name of the kubeconfig context to use - --git-src-git-token string Your git provider api token [GIT_SRC_GIT_TOKEN] - --git-src-provider string The git provider, one of: github - --git-src-repo string Repository URL [GIT_SRC_GIT_REPO] - -h, --help help for create - --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure - --install-git-token string Your git provider api token [INSTALL_GIT_TOKEN] - --install-provider string The git provider, one of: github - --install-repo string Repository URL [INSTALL_GIT_REPO] - --kubeconfig string Path to the kubeconfig file to use for CLI requests. - -n, --namespace string If present, the namespace scope for this CLI request - -s, --server string The address and port of the Kubernetes API server - --tls-server-name string Server name to use for server certificate validation. If it is not provided, the hostname used to contact the server is used - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use -``` - -### Options inherited from parent commands - -``` - --auth-context string Run the next command using a specific authentication context - --cfconfig string Custom path for authentication contexts config file (default "/home/user") - --insecure Disable certificate validation for TLS connections (e.g. to g.codefresh.io) - --request-timeout duration Request timeout (default 30s) -``` - -### SEE ALSO - -* [cli-v2 runtime](cli-v2_runtime.md) - Manage Codefresh runtimes - diff --git a/docs/commands/cli-v2_runtime_delete.md b/docs/commands/cli-v2_runtime_delete.md deleted file mode 100644 index d395801c2..000000000 --- a/docs/commands/cli-v2_runtime_delete.md +++ /dev/null @@ -1,63 +0,0 @@ -## cli-v2 runtime delete - -Deletes a Codefresh runtime - -``` -cli-v2 runtime delete [runtime_name] [flags] -``` - -### Examples - -``` - -# To run this command you need to create a personal access token for your git provider -# and provide it using: - - export GIT_TOKEN= - -# or with the flag: - - --git-token - -# Adds a new runtime - - cli-v2 runtime delete runtime-name --repo gitops_repo - -``` - -### Options - -``` - --as string Username to impersonate for the operation - --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. - --cache-dir string Default cache directory (default "/home/user/.kube/cache") - --certificate-authority string Path to a cert file for the certificate authority - --client-certificate string Path to a client certificate file for TLS - --client-key string Path to a client key file for TLS - --cluster string The name of the kubeconfig cluster to use - --context string The name of the kubeconfig context to use - -t, --git-token string Your git provider api token [GIT_TOKEN] - -h, --help help for delete - --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure - --kubeconfig string Path to the kubeconfig file to use for CLI requests. - -n, --namespace string If present, the namespace scope for this CLI request - --repo string Repository URL [GIT_REPO] - -s, --server string The address and port of the Kubernetes API server - --tls-server-name string Server name to use for server certificate validation. If it is not provided, the hostname used to contact the server is used - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use -``` - -### Options inherited from parent commands - -``` - --auth-context string Run the next command using a specific authentication context - --cfconfig string Custom path for authentication contexts config file (default "/home/user") - --insecure Disable certificate validation for TLS connections (e.g. to g.codefresh.io) - --request-timeout duration Request timeout (default 30s) -``` - -### SEE ALSO - -* [cli-v2 runtime](cli-v2_runtime.md) - Manage Codefresh runtimes - diff --git a/docs/commands/cli-v2_runtime_install.md b/docs/commands/cli-v2_runtime_install.md new file mode 100644 index 000000000..c12cece73 --- /dev/null +++ b/docs/commands/cli-v2_runtime_install.md @@ -0,0 +1,54 @@ +## cli-v2 runtime install + +Install a new Codefresh runtime + +``` +cli-v2 runtime install [runtime_name] [flags] +``` + +### Examples + +``` + +# To run this command you need to create a personal access token for your git provider +# and provide it using: + + export INSTALL_GIT_TOKEN= + +# or with the flag: + + --install-git-token + +# Adds a new runtime + + cli-v2 runtime install runtime-name --install-repo gitops_repo + +``` + +### Options + +``` + --git-src-git-token string Your git provider api token [GIT_SRC_GIT_TOKEN] + --git-src-provider string The git provider, one of: gitea|github + --git-src-repo string Repository URL [GIT_SRC_GIT_REPO] + -h, --help help for install + --install-git-token string Your git provider api token [INSTALL_GIT_TOKEN] + --install-provider string The git provider, one of: gitea|github + --install-repo string Repository URL [INSTALL_GIT_REPO] + --kubeconfig string Path to the kubeconfig file to use for CLI requests. + -n, --namespace string If present, the namespace scope for this CLI request +``` + +### Options inherited from parent commands + +``` + --auth-context string Run the next command using a specific authentication context + --cfconfig string Custom path for authentication contexts config file (default "/home/user") + --insecure Disable certificate validation for TLS connections (e.g. to g.codefresh.io) + --request-timeout duration Request timeout (default 30s) +``` + +### SEE ALSO + +* [cli-v2 runtime](cli-v2_runtime.md) - Manage Codefresh runtimes + diff --git a/docs/commands/cli-v2_runtime_uninstall.md b/docs/commands/cli-v2_runtime_uninstall.md new file mode 100644 index 000000000..e21497bea --- /dev/null +++ b/docs/commands/cli-v2_runtime_uninstall.md @@ -0,0 +1,50 @@ +## cli-v2 runtime uninstall + +Uninstall a Codefresh runtime + +``` +cli-v2 runtime uninstall [runtime_name] [flags] +``` + +### Examples + +``` + +# To run this command you need to create a personal access token for your git provider +# and provide it using: + + export GIT_TOKEN= + +# or with the flag: + + --git-token + +# Deletes a runtime + + cli-v2 runtime uninstall runtime-name --repo gitops_repo + +``` + +### Options + +``` + -t, --git-token string Your git provider api token [GIT_TOKEN] + -h, --help help for uninstall + --kubeconfig string Path to the kubeconfig file to use for CLI requests. + -n, --namespace string If present, the namespace scope for this CLI request + --repo string Repository URL [GIT_REPO] +``` + +### Options inherited from parent commands + +``` + --auth-context string Run the next command using a specific authentication context + --cfconfig string Custom path for authentication contexts config file (default "/home/user") + --insecure Disable certificate validation for TLS connections (e.g. to g.codefresh.io) + --request-timeout duration Request timeout (default 30s) +``` + +### SEE ALSO + +* [cli-v2 runtime](cli-v2_runtime.md) - Manage Codefresh runtimes + diff --git a/docs/commands/cli-v2_runtime_upgrade.md b/docs/commands/cli-v2_runtime_upgrade.md new file mode 100644 index 000000000..b1e2b6eb2 --- /dev/null +++ b/docs/commands/cli-v2_runtime_upgrade.md @@ -0,0 +1,48 @@ +## cli-v2 runtime upgrade + +Upgrade a Codefresh runtime + +``` +cli-v2 runtime upgrade [runtime_name] [flags] +``` + +### Examples + +``` + +# To run this command you need to create a personal access token for your git provider +# and provide it using: + + export GIT_TOKEN= + +# or with the flag: + + --git-token + +# Upgrade a runtime to version v0.0.30 + + cli-v2 runtime upgrade runtime-name v0.0.30 --repo gitops_repo + +``` + +### Options + +``` + -t, --git-token string Your git provider api token [GIT_TOKEN] + -h, --help help for upgrade + --repo string Repository URL [GIT_REPO] +``` + +### Options inherited from parent commands + +``` + --auth-context string Run the next command using a specific authentication context + --cfconfig string Custom path for authentication contexts config file (default "/home/user") + --insecure Disable certificate validation for TLS connections (e.g. to g.codefresh.io) + --request-timeout duration Request timeout (default 30s) +``` + +### SEE ALSO + +* [cli-v2 runtime](cli-v2_runtime.md) - Manage Codefresh runtimes + diff --git a/docs/releases/release_notes.md b/docs/releases/release_notes.md index 6b848e7b0..fd060240a 100644 --- a/docs/releases/release_notes.md +++ b/docs/releases/release_notes.md @@ -1,7 +1,7 @@ ### Installed Applications: * Argo CD [v2.0.4](https://github.com/argoproj/argo-cd/releases/tag/v2.0.4) * Argo CD ApplicationSet Controller [2c62537a8e5a](https://github.com/argoproj-labs/applicationset/commit/2c62537a8e5a3d5aecad87b843870789b74bdf89) -* Argo Events [d403c441bc1d](https://github.com/argoproj/argo-events/commit/d403c441bc1d4032daff4e54b496f9342cc5cd57) +* Argo Events [v1.4.0](https://github.com/argoproj/argo-events/releases/tag/v1.4.0) * Argo Rollouts [v1.0.2](https://github.com/argoproj/argo-rollouts/releases/tag/v1.0.2) * Argo Workflows [v3.1.1](https://github.com/argoproj/argo-workflows/releases/tag/v3.1.1) diff --git a/go.mod b/go.mod index e21daa906..dad86b8e0 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,9 @@ module github.com/codefresh-io/cli-v2 go 1.16 require ( + github.com/Masterminds/semver/v3 v3.1.1 github.com/argoproj-labs/applicationset v0.1.0 - github.com/argoproj-labs/argocd-autopilot v0.2.9 + github.com/argoproj-labs/argocd-autopilot v0.2.11 github.com/argoproj/argo-cd/v2 v2.0.3 github.com/argoproj/argo-events v1.3.1 github.com/argoproj/argo-workflows/v3 v3.1.0 @@ -23,6 +24,7 @@ require ( github.com/stretchr/testify v1.7.0 k8s.io/api v0.21.1 k8s.io/apimachinery v0.21.1 + sigs.k8s.io/kustomize/api v0.8.8 ) replace ( diff --git a/go.sum b/go.sum index ba8e64b0a..3c2aa3b4b 100644 --- a/go.sum +++ b/go.sum @@ -34,6 +34,8 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +code.gitea.io/sdk/gitea v0.14.1 h1:NaRluse+dAxVM5RmHC7Xktfas5a8WWmcnUBlJLhJycA= +code.gitea.io/sdk/gitea v0.14.1/go.mod h1:89WiyOX1KEcvjP66sRHdu0RafojGo60bT9UqW17VbWs= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-amqp-common-go/v3 v3.0.1/go.mod h1:PBIGdzcO1teYoufTKMcGibdKaYZv4avS+O6LNIp8bq0= @@ -99,6 +101,7 @@ github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YH github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig/v3 v3.2.0/go.mod h1:tWhwTbUTndesPNeF0C900vKoq283u6zp4APT9vaF3SI= @@ -164,8 +167,8 @@ github.com/ardielle/ardielle-go v1.5.2/go.mod h1:I4hy1n795cUhaVt/ojz83SNVCYIGsAF github.com/ardielle/ardielle-tools v1.5.4/go.mod h1:oZN+JRMnqGiIhrzkRN9l26Cej9dEx4jeNG6A+AdkShk= github.com/argoproj-labs/applicationset v0.0.0-20210614145856-2c62537a8e5a h1:8Nm2KtOu/G7NtoAgucj4TkX8rghzwgFJGXNrAmuz7gc= github.com/argoproj-labs/applicationset v0.0.0-20210614145856-2c62537a8e5a/go.mod h1:5rxggh8ymYXedQDIYylNzIHe6jdshDNasIBCVJuR1iU= -github.com/argoproj-labs/argocd-autopilot v0.2.9 h1:zYStiQq1cCT1EkfwEpdMEVfSkj+7ZEe1CwilmXGoldQ= -github.com/argoproj-labs/argocd-autopilot v0.2.9/go.mod h1:o3HQ3wBzSFlLILnFDFWMkHetHTSjwwC30runsLvwVp8= +github.com/argoproj-labs/argocd-autopilot v0.2.11 h1:ysxOnVuRtvEe0Qj9apPMHcYi77oxTeu9LuZVDcOupl4= +github.com/argoproj-labs/argocd-autopilot v0.2.11/go.mod h1:o+e85vsYHC7FqUgxgQMAcIOmlRmVvcAwCw/fPXPz3xY= github.com/argoproj/argo-cd v1.8.1/go.mod h1:Vfl7OGgBC83dVWgq58wU6UR3kG864h0dtHEIQ8xqw4s= github.com/argoproj/argo-cd v1.8.7 h1:CkIu8p/gcTY/fOZWM2tHuSCIAV2HggXjJftrT1IIT3k= github.com/argoproj/argo-cd v1.8.7/go.mod h1:tqFZW5Lr9KBCDsvOaE5Fh8M1eJ1ThvR58pyyLv8Zqvs= @@ -701,6 +704,8 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= diff --git a/manifests/argo-events/kustomization.yaml b/manifests/argo-events/kustomization.yaml index 98d895ae3..22e37ac52 100644 --- a/manifests/argo-events/kustomization.yaml +++ b/manifests/argo-events/kustomization.yaml @@ -1,6 +1,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - # events from master to get the secureHeaders for now, should move to next release when available - - https://raw.githubusercontent.com/argoproj/argo-events/master/manifests/install.yaml?ref=d403c441bc1d + - https://raw.githubusercontent.com/argoproj/argo-events/v1.4.0/manifests/install.yaml - eventbus.yaml diff --git a/manifests/runtime.yaml b/manifests/runtime.yaml new file mode 100644 index 000000000..e6e62336c --- /dev/null +++ b/manifests/runtime.yaml @@ -0,0 +1,20 @@ +apiVersion: codefresh.io/v1alpha1 +kind: Runtime +metadata: + name: "{{ name }}" + namespace: "{{ namespace }}" +spec: + defVersion: 1.0.0 + version: 0.0.25 + bootstrapSpecifier: github.com/codefresh-io/cli-v2/manifests/argo-cd + components: + - name: events + type: kustomize + url: github.com/codefresh-io/cli-v2/manifests/argo-events + wait: true + - name: rollouts + type: kustomize + url: github.com/codefresh-io/cli-v2/manifests/argo-rollouts + - name: workflows + type: kustomize + url: github.com/codefresh-io/cli-v2/manifests/argo-workflows diff --git a/pkg/cdUtils/cdUtils.go b/pkg/cdUtils/cdUtils.go index 5b6b68258..ef323791d 100644 --- a/pkg/cdUtils/cdUtils.go +++ b/pkg/cdUtils/cdUtils.go @@ -45,8 +45,8 @@ func CreateApp(opts *CreateAppOptions) *cdv1alpha1.Application { app := &cdv1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: cdv1alpha1.ApplicationSchemaGroupVersionKind.Kind, APIVersion: cdv1alpha1.ApplicationSchemaGroupVersionKind.GroupVersion().String(), + Kind: cdv1alpha1.ApplicationSchemaGroupVersionKind.Kind, }, ObjectMeta: metav1.ObjectMeta{ Namespace: opts.Namespace, @@ -55,7 +55,7 @@ func CreateApp(opts *CreateAppOptions) *cdv1alpha1.Application { "argocd.argoproj.io/sync-wave": fmt.Sprintf("%d", opts.SyncWave), }, Labels: map[string]string{ - "app.kubernetes.io/managed-by": store.Get().BinaryName, + apstore.Default.LabelKeyAppManagedBy: store.Get().BinaryName, "app.kubernetes.io/name": opts.Name, }, Finalizers: []string{ diff --git a/pkg/eventUtils/eventUtils.go b/pkg/eventUtils/eventUtils.go index d5913253a..acda19025 100644 --- a/pkg/eventUtils/eventUtils.go +++ b/pkg/eventUtils/eventUtils.go @@ -17,6 +17,7 @@ package eventUtils import ( "github.com/codefresh-io/cli-v2/pkg/store" + apstore "github.com/argoproj-labs/argocd-autopilot/pkg/store" apicommon "github.com/argoproj/argo-events/pkg/apis/common" eventsourcereg "github.com/argoproj/argo-events/pkg/apis/eventsource" eventsourcev1alpha1 "github.com/argoproj/argo-events/pkg/apis/eventsource/v1alpha1" @@ -94,6 +95,9 @@ func CreateEventSource(opts *CreateEventSourceOptions) *eventsourcev1alpha1.Even ObjectMeta: metav1.ObjectMeta{ Name: opts.Name, Namespace: opts.Namespace, + Labels: map[string]string{ + apstore.Default.LabelKeyAppManagedBy: store.Get().BinaryName, + }, }, Spec: eventsourcev1alpha1.EventSourceSpec{ Template: &eventsourcev1alpha1.Template{ @@ -169,6 +173,9 @@ func CreateSensor(opts *CreateSensorOptions) *sensorsv1alpha1.Sensor { ObjectMeta: metav1.ObjectMeta{ Name: opts.Name, Namespace: opts.Namespace, + Labels: map[string]string{ + apstore.Default.LabelKeyAppManagedBy: store.Get().BinaryName, + }, }, Spec: sensorsv1alpha1.SensorSpec{ EventBusName: opts.EventBusName, diff --git a/pkg/runtime/runtime.go b/pkg/runtime/runtime.go new file mode 100644 index 000000000..228d78f8b --- /dev/null +++ b/pkg/runtime/runtime.go @@ -0,0 +1,269 @@ +// Copyright 2021 The Codefresh Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( + "context" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strings" + "time" + + "github.com/codefresh-io/cli-v2/pkg/log" + "github.com/codefresh-io/cli-v2/pkg/store" + + "github.com/Masterminds/semver/v3" + apcmd "github.com/argoproj-labs/argocd-autopilot/cmd/commands" + "github.com/argoproj-labs/argocd-autopilot/pkg/application" + "github.com/argoproj-labs/argocd-autopilot/pkg/fs" + "github.com/argoproj-labs/argocd-autopilot/pkg/git" + "github.com/argoproj-labs/argocd-autopilot/pkg/kube" + apstore "github.com/argoproj-labs/argocd-autopilot/pkg/store" + "github.com/ghodss/yaml" + billyUtils "github.com/go-git/go-billy/v5/util" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + kusttypes "sigs.k8s.io/kustomize/api/types" +) + +type ( + Runtime struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata"` + + Spec RuntimeSpec `json:"spec"` + } + + RuntimeSpec struct { + DefVersion *semver.Version `json:"defVersion"` + Version *semver.Version `json:"version"` + BootstrapSpecifier string `json:"bootstrapSpecifier"` + Components []AppDef `json:"components"` + } + + AppDef struct { + Name string `json:"name"` + Type string `json:"type"` + URL string `json:"url"` + Wait bool `json:"wait"` + } +) + +func Download(version *semver.Version, name string) (*Runtime, error) { + var ( + body []byte + err error + ) + + if strings.HasPrefix(store.RuntimeDefURL, "http") { + urlObj, err := url.Parse(store.RuntimeDefURL) + if err != nil { + return nil, fmt.Errorf("failed parsing url: %w", err) + } + + if urlObj.Query().Get("ref") == "" { + urlObj.Query().Set("ref", version.String()) + } + + res, err := http.Get(urlObj.String()) + if err != nil { + return nil, fmt.Errorf("failed to download runtime definition: %w", err) + } + + defer res.Body.Close() + body, err = ioutil.ReadAll(res.Body) + if err != nil { + return nil, fmt.Errorf("failed to read runtime definition data: %w", err) + } + } else { + body, err = ioutil.ReadFile(store.RuntimeDefURL) + if err != nil { + return nil, fmt.Errorf("failed to read runtime definition data: %w", err) + } + } + + runtime := &Runtime{} + err = yaml.Unmarshal(body, runtime) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal runtime definition data: %w", err) + } + + runtime.Name = name + runtime.Namespace = name + return runtime, nil +} + +func Load(fs fs.FS, filename string) (*Runtime, error) { + cm := &v1.ConfigMap{} + if err := fs.ReadYamls(filename, cm); err != nil { + return nil, err + } + + data := cm.Data["runtime"] + runtime := &Runtime{} + return runtime, yaml.Unmarshal([]byte(data), runtime) +} + +func (r *Runtime) Save(fs fs.FS, filename string) error { + data, err := yaml.Marshal(r) + if err != nil { + return err + } + + cm := v1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "codefresh-cm", + Namespace: r.Namespace, + Labels: map[string]string{ + apstore.Default.LabelKeyAppManagedBy: store.Get().BinaryName, + store.Get().LabelKeyCFType: "runtimeDef", + }, + }, + Data: map[string]string{ + "runtime": string(data), + }, + } + + return fs.WriteYamls(filename, cm) +} + +func (r *Runtime) Upgrade(fs fs.FS, newRt *Runtime) ([]AppDef, error) { + newComponents, err := r.Spec.upgrade(fs, &newRt.Spec) + if err != nil { + return nil, err + } + + if err := newRt.Save(fs, fs.Join(apstore.Default.BootsrtrapDir, store.Get().RuntimeFilename)); err != nil { + return nil, fmt.Errorf("failed to save runtime definition: %w", err) + } + + return newComponents, nil +} + +func (r *RuntimeSpec) upgrade(fs fs.FS, newRt *RuntimeSpec) ([]AppDef, error) { + log.G().Infof("Upgrading bootstrap specifier") + argocdFilename := fs.Join(apstore.Default.BootsrtrapDir, apstore.Default.ArgoCDName, "kustomization.yaml") + if err := updateKustomization(fs, argocdFilename, r.fullSpecifier(), newRt.fullSpecifier()); err != nil { + return nil, fmt.Errorf("failed to upgrade bootstrap specifier: %w", err) + } + + newComponents := make([]AppDef, 0) + for _, newComponent := range newRt.Components { + curComponent := r.component(newComponent.Name) + if curComponent != nil { + log.G().Infof("Upgrading '%s'", newComponent.Name) + curURL := buildFullURL(curComponent.URL, r.Version) + newURL := buildFullURL(newComponent.URL, r.Version) + baseFilename := fs.Join(apstore.Default.AppsDir, curComponent.Name, apstore.Default.BaseDir, "kustomization.yaml") + if err := updateKustomization(fs, baseFilename, curURL, newURL); err != nil { + return nil, fmt.Errorf("failed to upgrade app '%s': %w", curComponent.Name, err) + } + } else { + log.G().Debugf("marking '%s' to be created later", newComponent.Name) + newComponents = append(newComponents, newComponent) + } + } + + for _, curComponent := range r.Components { + newComponent := newRt.component(curComponent.Name) + if newComponent == nil { + log.G().Infof("Deleting '%s'", curComponent.Name) + if err := curComponent.delete(fs); err != nil { + return nil, fmt.Errorf("failed to delete app '%s': %w", curComponent.Name, err) + } + } + } + + return newComponents, nil +} + +func (a *RuntimeSpec) component(name string) *AppDef { + for _, c := range a.Components { + if c.Name == name { + return &c + } + } + + return nil +} + +func (r *RuntimeSpec) fullSpecifier() string { + return buildFullURL(r.BootstrapSpecifier, r.DefVersion) +} + +func (a *AppDef) CreateApp(ctx context.Context, f kube.Factory, cloneOpts *git.CloneOptions, projectName string) error { + timeout := time.Duration(0) + if a.Wait { + timeout = store.Get().WaitTimeout + } + + return apcmd.RunAppCreate(ctx, &apcmd.AppCreateOptions{ + CloneOpts: cloneOpts, + AppsCloneOpts: &git.CloneOptions{}, + ProjectName: projectName, + AppOpts: &application.CreateOptions{ + AppName: a.Name, + AppSpecifier: a.URL, + AppType: a.Type, + DestNamespace: projectName, + }, + KubeFactory: f, + Timeout: timeout, + }) +} + +func (a *AppDef) delete(fs fs.FS) error { + return billyUtils.RemoveAll(fs, fs.Join(apstore.Default.AppsDir, a.Name)) +} + +func updateKustomization(fs fs.FS, filename, fromURL, toURL string) error { + kust := &kusttypes.Kustomization{} + if err := fs.ReadYamls(filename, kust); err != nil { + return fmt.Errorf("failed reading kustomization from '%s': %w", filename, err) + } + + found := false + for i, res := range kust.Resources { + if res == fromURL { + kust.Resources[i] = toURL + break + } + } + + if !found { + if len(kust.Resources) == 1 { + kust.Resources[0] = toURL + } else { + return fmt.Errorf("base kustomization does not contain expected resource '%s'", fromURL) + } + } + + return fs.WriteYamls(filename, kust) +} + +func buildFullURL(urlString string, version *semver.Version) string { + urlObj, _ := url.Parse(urlString) + if urlObj.Query().Get("ref") == "" { + urlObj.Query().Add("ref", version.String()) + } + + return urlObj.String() +} diff --git a/pkg/store/store.go b/pkg/store/store.go index 18f0190af..cf42ce5b8 100644 --- a/pkg/store/store.go +++ b/pkg/store/store.go @@ -18,23 +18,23 @@ import ( "fmt" "runtime" "time" + + "github.com/Masterminds/semver/v3" ) var s Store var ( - binaryName = "cli-v2" - version = "v99.99.99" - buildDate = "" - gitCommit = "" - ArgoCDManifestsURL = "manifests/argo-cd" - ArgoEventsManifestsURL = "manifests/argo-events" - ArgoRolloutsManifestsURL = "manifests/argo-rollouts" - ArgoWorkflowsManifestsURL = "manifests/argo-workflows" + binaryName = "cli-v2" + version = "v99.99.99" + buildDate = "" + gitCommit = "" + maxDefVersion = "1.0.0" + RuntimeDefURL = "manifests/runtime.yaml" ) type Version struct { - Version string + Version *semver.Version BuildDate string GitCommit string GoVersion string @@ -43,24 +43,24 @@ type Version struct { } type Store struct { - ArgoCDManifestsURL string - ArgoEventsManifestsURL string - ArgoRolloutsManifestsURL string - ArgoWorkflowsManifestsURL string - BinaryName string - CFComponentType string - CFRuntimeType string - CFTokenSecret string - CFTokenSecretKey string - CFType string - ComponentsReporterName string - ComponentsReporterSA string - DefaultAPI string - EventBusName string - EventReportingEndpoint string - GitSourceName string - Version Version - WaitTimeout time.Duration + BinaryName string + CFComponentType string + CFRuntimeType string + CFTokenSecret string + CFTokenSecretKey string + ComponentsReporterName string + ComponentsReporterSA string + ComponentsReporterURL string + DefaultAPI string + EventBusName string + EventReportingEndpoint string + GitSourceName string + LabelKeyCFType string + MaxDefVersion *semver.Version + RuntimeDefURL string + RuntimeFilename string + Version Version + WaitTimeout time.Duration } // Get returns the global store @@ -69,28 +69,27 @@ func Get() *Store { } func init() { - s.ArgoCDManifestsURL = ArgoCDManifestsURL - s.ArgoEventsManifestsURL = ArgoEventsManifestsURL - s.ArgoRolloutsManifestsURL = ArgoRolloutsManifestsURL - s.ArgoWorkflowsManifestsURL = ArgoWorkflowsManifestsURL s.BinaryName = binaryName s.CFComponentType = "component" s.CFRuntimeType = "runtime" s.CFTokenSecret = "codefresh-token" s.CFTokenSecretKey = "token" - s.CFType = "codefresh.io/type" s.ComponentsReporterName = "components-reporter" s.ComponentsReporterSA = "components-reporter-sa" s.DefaultAPI = "https://g.codefresh.io" s.EventBusName = "codefresh-eventbus" s.EventReportingEndpoint = "/argo/api/events" s.GitSourceName = "default-git-source" + s.LabelKeyCFType = "codefresh.io/type" + s.MaxDefVersion = semver.MustParse(maxDefVersion) + s.RuntimeDefURL = RuntimeDefURL + s.RuntimeFilename = "runtime.yaml" s.WaitTimeout = 5 * time.Minute initVersion() } func initVersion() { - s.Version.Version = version + s.Version.Version = semver.MustParse(version) s.Version.BuildDate = buildDate s.Version.GitCommit = gitCommit s.Version.GoVersion = runtime.Version()