From 37022bf0c8b8445c581c510b2483a1feeb6958ac Mon Sep 17 00:00:00 2001 From: decentral1se Date: Wed, 13 Oct 2021 00:24:23 +0200 Subject: [PATCH] feat: make deploy only deploy See https://git.coopcloud.tech/coop-cloud/organising/issues/127. --- cli/app/deploy.go | 51 ++++++++++++++++++++++++++++++++++++--- pkg/client/stack/stack.go | 32 ++++++++++++++++++++++++ pkg/recipe/recipe.go | 49 ++++++++++++++++++++++++++++++++++--- 3 files changed, 125 insertions(+), 7 deletions(-) diff --git a/cli/app/deploy.go b/cli/app/deploy.go index 1476c958..42ba1399 100644 --- a/cli/app/deploy.go +++ b/cli/app/deploy.go @@ -6,9 +6,11 @@ import ( abraFormatter "coopcloud.tech/abra/cli/formatter" "coopcloud.tech/abra/cli/internal" + "coopcloud.tech/abra/pkg/catalogue" "coopcloud.tech/abra/pkg/client" stack "coopcloud.tech/abra/pkg/client/stack" "coopcloud.tech/abra/pkg/config" + "coopcloud.tech/abra/pkg/recipe" "github.com/AlecAivazis/survey/v2" "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" @@ -18,14 +20,55 @@ var appDeployCommand = &cli.Command{ Name: "deploy", Aliases: []string{"d"}, Usage: "Deploy an app", + Flags: []cli.Flag{ + internal.ForceFlag, + }, + Description: ` +This command deploys a new instance of an app. It does not support changing the +version of an existing deployed app, for this you need to look at the "abra app +upgrade " command. You may pass "--force" to re-deploy the same version. +`, Action: func(c *cli.Context) error { app := internal.ValidateApp(c) + stackName := app.StackName() cl, err := client.New(app.Server) if err != nil { logrus.Fatal(err) } + logrus.Debugf("checking whether '%s' is already deployed", stackName) + + isDeployed, deployedVersion, err := stack.IsDeployed(c.Context, cl, stackName) + if err != nil { + logrus.Fatal(err) + } + + if isDeployed { + if internal.Force { + logrus.Infof("continuing with deployment due to '--force/-f' being set") + } else { + logrus.Fatalf("'%s' is already deployed", stackName) + } + } + + version := deployedVersion + if version == "" { + versions, err := catalogue.GetRecipeCatalogueVersions(app.Type) + if err != nil { + logrus.Fatal(err) + } + if len(versions) > 0 { + version = versions[len(versions)-1] + logrus.Infof("choosing '%s' as version to deploy", version) + recipe.EnsureVersion(app.Type, version) + } else { + version = "latest commit" + logrus.Warning("no versions detected, using latest commit") + recipe.EnsureLatest(app.Type) + } + } + abraShPath := fmt.Sprintf("%s/%s/%s", config.APPS_DIR, app.Type, "abra.sh") abraShEnv, err := config.ReadAbraShEnvVars(abraShPath) if err != nil { @@ -41,7 +84,7 @@ var appDeployCommand = &cli.Command{ } deployOpts := stack.Deploy{ Composefiles: composeFiles, - Namespace: app.StackName(), + Namespace: stackName, Prune: false, ResolveImage: stack.ResolveImageAlways, } @@ -50,7 +93,7 @@ var appDeployCommand = &cli.Command{ logrus.Fatal(err) } - if err := DeployOverview(app); err != nil { + if err := DeployOverview(app, version); err != nil { logrus.Fatal(err) } @@ -75,7 +118,7 @@ var appDeployCommand = &cli.Command{ } // DeployOverview shows a deployment overview -func DeployOverview(app config.App) error { +func DeployOverview(app config.App, version string) error { tableCol := []string{"server", "compose", "domain", "stack", "version"} table := abraFormatter.CreateTable(tableCol) @@ -84,7 +127,7 @@ func DeployOverview(app config.App) error { deployConfig = strings.Join(strings.Split(composeFiles, ":"), "\n") } - table.Append([]string{app.Server, deployConfig, app.Domain, app.StackName(), "unknown"}) + table.Append([]string{app.Server, deployConfig, app.Domain, app.StackName(), version}) table.Render() response := false diff --git a/pkg/client/stack/stack.go b/pkg/client/stack/stack.go index 5eac34c5..49392db8 100644 --- a/pkg/client/stack/stack.go +++ b/pkg/client/stack/stack.go @@ -2,6 +2,7 @@ package stack import ( "context" + "fmt" "strings" abraClient "coopcloud.tech/abra/pkg/client" @@ -92,6 +93,37 @@ func GetAllDeployedServices(contextName string) StackStatus { return StackStatus{services, nil} } +// IsDeployed chekcks whether an appp is deployed or not. +func IsDeployed(ctx context.Context, cl *dockerclient.Client, stackName string) (bool, string, error) { + version := "" + isDeployed := false + + filter := filters.NewArgs() + filter.Add("label", fmt.Sprintf("%s=%s", convert.LabelNamespace, stackName)) + + services, err := cl.ServiceList(ctx, types.ServiceListOptions{Filters: filter}) + if err != nil { + return false, version, err + } + + if len(services) > 0 { + for _, service := range services { + labelKey := fmt.Sprintf("coop-cloud.%s.version", stackName) + if deployedVersion, ok := service.Spec.Labels[labelKey]; ok { + version = deployedVersion + break + } + } + + logrus.Debugf("'%s' has been detected as deployed with version '%s'", stackName, version) + + return true, version, nil + } + + logrus.Debugf("'%s' has been detected as not deployed", stackName) + return isDeployed, version, nil +} + // pruneServices removes services that are no longer referenced in the source func pruneServices(ctx context.Context, cl *dockerclient.Client, namespace convert.Namespace, services map[string]struct{}) { oldServices, err := GetStackServices(ctx, cl, namespace.Name()) diff --git a/pkg/recipe/recipe.go b/pkg/recipe/recipe.go index bee90cf1..e987e898 100644 --- a/pkg/recipe/recipe.go +++ b/pkg/recipe/recipe.go @@ -119,10 +119,10 @@ func EnsureVersion(recipeName, version string) error { return nil } - logrus.Debugf("read '%s' as tags for recipe '%s'", tags, recipeName) - + var parsedTags []string var tagRef plumbing.ReferenceName if err := tags.ForEach(func(ref *plumbing.Reference) (err error) { + parsedTags = append(parsedTags, ref.Name().Short()) if ref.Name().Short() == version { tagRef = ref.Name() } @@ -131,6 +131,8 @@ func EnsureVersion(recipeName, version string) error { return err } + logrus.Debugf("read '%s' as tags for recipe '%s'", strings.Join(parsedTags, ", "), recipeName) + if tagRef.String() == "" { return fmt.Errorf("%s is not available?", version) } @@ -145,7 +147,48 @@ func EnsureVersion(recipeName, version string) error { return err } - logrus.Debugf("successfully checked '%s' out to '%s' in '%s'", recipeName, tagRef, recipeDir) + logrus.Debugf("successfully checked '%s' out to '%s' in '%s'", recipeName, tagRef.Short(), recipeDir) + + return nil +} + +// EnsureLatest makes sure the latest commit is checkout on for a local recipe repository. +func EnsureLatest(recipeName string) error { + recipeDir := path.Join(config.ABRA_DIR, "apps", recipeName) + + logrus.Debugf("attempting to open git repository in '%s'", recipeDir) + + repo, err := git.PlainOpen(recipeDir) + if err != nil { + return err + } + + worktree, err := repo.Worktree() + if err != nil { + return err + } + + branch := "master" + if _, err := repo.Branch("master"); err != nil { + if _, err := repo.Branch("main"); err != nil { + logrus.Debugf("failed to select branch in '%s'", path.Join(config.APPS_DIR, recipeName)) + return err + } + branch = "main" + } + + refName := fmt.Sprintf("refs/heads/%s", branch) + checkOutOpts := &git.CheckoutOptions{ + Create: false, + Force: true, + Keep: false, + Branch: plumbing.ReferenceName(refName), + } + + if err := worktree.Checkout(checkOutOpts); err != nil { + logrus.Debugf("failed to check out '%s' in '%s'", branch, recipeDir) + return err + } return nil }