From dde8afcd4340c968ee9e3f3d456eedc0f1032834 Mon Sep 17 00:00:00 2001 From: decentral1se Date: Fri, 8 Oct 2021 09:51:47 +0200 Subject: [PATCH] feat: support version/upgrade listing Closes https://git.coopcloud.tech/coop-cloud/organising/issues/130. --- cli/app/list.go | 37 ++++++++++++++++++++++++++---- cli/app/remove.go | 2 +- cli/recipe/sync.go | 47 ++++++++++++++++++-------------------- pkg/catalogue/catalogue.go | 20 ++++++++++++++++ pkg/compose/compose.go | 2 +- pkg/config/app.go | 15 +++++++++--- 6 files changed, 89 insertions(+), 34 deletions(-) diff --git a/cli/app/list.go b/cli/app/list.go index eb91f74e..185090b5 100644 --- a/cli/app/list.go +++ b/cli/app/list.go @@ -2,8 +2,10 @@ package app import ( "sort" + "strings" abraFormatter "coopcloud.tech/abra/cli/formatter" + "coopcloud.tech/abra/pkg/catalogue" "coopcloud.tech/abra/pkg/config" "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" @@ -65,10 +67,10 @@ can take some time. } sort.Sort(config.ByServerAndType(apps)) - statuses := map[string]string{} + statuses := make(map[string]map[string]string) tableCol := []string{"Server", "Type", "Domain"} if status { - tableCol = append(tableCol, "Status") + tableCol = append(tableCol, "Status", "Version", "Updates") statuses, err = config.GetAppStatuses(appFiles) if err != nil { logrus.Fatal(err) @@ -84,11 +86,38 @@ can take some time. // If type flag is set, check for it, if not, Type == "" tableRow = []string{app.Server, app.Type, app.Domain} if status { - if status, ok := statuses[app.StackName()]; ok { - tableRow = append(tableRow, status) + stackName := app.StackName() + if app.Env["STACK_NAME"] != "" { + stackName = app.Env["STACK_NAME"] + } + + var version string + if status, ok := statuses[stackName]; ok { + version = status["version"] + tableRow = append(tableRow, status["status"], version) } else { tableRow = append(tableRow, "unknown") } + + var newUpdates []string + updates, err := catalogue.GetRecipeCatalogueVersions(app.Type) + if err != nil { + logrus.Fatal(err) + } + for _, update := range updates { + if update != version { + newUpdates = append(newUpdates, update) + } + } + if len(newUpdates) == 0 { + tableRow = append(tableRow, "none, on latest") + } else { + // FIXME: jeezus golang why do you not have a list reverse function + for i, j := 0, len(newUpdates)-1; i < j; i, j = i+1, j-1 { + newUpdates[i], newUpdates[j] = newUpdates[j], newUpdates[i] + } + tableRow = append(tableRow, strings.Join(newUpdates, "\n")) + } } } table.Append(tableRow) diff --git a/cli/app/remove.go b/cli/app/remove.go index ef237a39..f9fddb7f 100644 --- a/cli/app/remove.go +++ b/cli/app/remove.go @@ -63,7 +63,7 @@ var appRemoveCommand = &cli.Command{ if err != nil { logrus.Fatal(err) } - if statuses[app.Name] == "deployed" { + if statuses[app.Name]["status"] == "deployed" { logrus.Fatalf("'%s' is still deployed. Run \"abra app %s undeploy\" or pass --force", app.Name, app.Name) } } diff --git a/cli/recipe/sync.go b/cli/recipe/sync.go index 05cdc3af..a0c98ec6 100644 --- a/cli/recipe/sync.go +++ b/cli/recipe/sync.go @@ -1,6 +1,7 @@ package recipe import ( + "errors" "fmt" "coopcloud.tech/abra/cli/internal" @@ -11,20 +12,21 @@ import ( ) var recipeSyncCommand = &cli.Command{ - Name: "sync", - Usage: "Ensure recipe version labels are up-to-date", - Aliases: []string{"s"}, + Name: "sync", + Usage: "Ensure recipe version labels are up-to-date", + Aliases: []string{"s"}, + ArgsUsage: " ", Description: ` -This command will generate labels for the main recipe service (i.e. the service -named "app", by convention) which corresponds to the following format: +This command will generate labels for the main recipe service (i.e. by +convention, typically the service named "app") which corresponds to the +following format: - coop-cloud.${STACK_NAME}.version=${RECIPE_TAG} + coop-cloud.${STACK_NAME}.version= -The ${RECIPE_TAG} is determined by the recipe maintainer and is retrieved by -this command by asking for the list of git tags on the local git repository. -The configuration will be updated on the local file system. +The is determined by the recipe maintainer and is specified on the +command-line. The configuration will be updated on the local file +system. `, - ArgsUsage: "", BashComplete: func(c *cli.Context) { catl, err := catalogue.ReadRecipeCatalogue() if err != nil { @@ -38,10 +40,17 @@ The configuration will be updated on the local file system. } }, Action: func(c *cli.Context) error { + if c.Args().Len() != 2 { + internal.ShowSubcommandHelpAndError(c, errors.New("missing / arguments?")) + } + recipe := internal.ValidateRecipe(c) - mainService := "app" + // TODO: validate with tagcmp when new commits come in + // See https://git.coopcloud.tech/coop-cloud/abra/pulls/109 + nextTag := c.Args().Get(1) + mainService := "app" var services []string hasAppService := false for _, service := range recipe.Config.Services { @@ -67,24 +76,12 @@ The configuration will be updated on the local file system. logrus.Debugf("selecting '%s' as the service to sync version labels", mainService) - tags, err := recipe.Tags() - if err != nil { - logrus.Fatal(err) - } - - if len(tags) == 0 { - logrus.Fatalf("no tags detected for '%s'", recipe.Name) - } - - latestTag := tags[len(tags)-1] - logrus.Infof("choosing '%s' as latest tag for recipe '%s'", latestTag, recipe.Name) - - label := fmt.Sprintf("coop-cloud.${STACK_NAME}.version=%s", latestTag) + label := fmt.Sprintf("coop-cloud.${STACK_NAME}.version=%s", nextTag) if err := recipe.UpdateLabel(mainService, label); err != nil { logrus.Fatal(err) } - logrus.Infof("added label '%s' to service '%s'", label, mainService) + logrus.Infof("synced label '%s' to service '%s'", label, mainService) return nil }, diff --git a/pkg/catalogue/catalogue.go b/pkg/catalogue/catalogue.go index 1b379327..09be2e4f 100644 --- a/pkg/catalogue/catalogue.go +++ b/pkg/catalogue/catalogue.go @@ -480,3 +480,23 @@ func GetRecipeVersions(recipeName string) (RecipeVersions, error) { return versions, nil } + +// GetRecipeCatalogueVersions list the recipe versions listed in the recipe catalogue. +func GetRecipeCatalogueVersions(recipeName string) ([]string, error) { + var versions []string + + catl, err := ReadRecipeCatalogue() + if err != nil { + return versions, err + } + + if recipeMeta, exists := catl[recipeName]; exists { + for _, versionMeta := range recipeMeta.Versions { + for tag := range versionMeta { + versions = append(versions, tag) + } + } + } + + return versions, nil +} diff --git a/pkg/compose/compose.go b/pkg/compose/compose.go index 84e09e5f..c7b9efc5 100644 --- a/pkg/compose/compose.go +++ b/pkg/compose/compose.go @@ -124,7 +124,7 @@ func UpdateLabel(pattern, serviceName, label, recipeName string) error { return err } - old := fmt.Sprintf("coop-cloud.${STACK_NAME}.%s.version=%s", service.Name, value) + old := fmt.Sprintf("coop-cloud.${STACK_NAME}.version=%s", value) replacedBytes := strings.Replace(string(bytes), old, label, -1) logrus.Debugf("updating '%s' to '%s' in '%s'", old, label, compose.Filename) diff --git a/pkg/config/app.go b/pkg/config/app.go index d17152f1..e183f242 100644 --- a/pkg/config/app.go +++ b/pkg/config/app.go @@ -276,8 +276,8 @@ func SanitiseAppName(name string) string { } // GetAppStatuses queries servers to check the deployment status of given apps -func GetAppStatuses(appFiles AppFiles) (map[string]string, error) { - statuses := map[string]string{} +func GetAppStatuses(appFiles AppFiles) (map[string]map[string]string, error) { + statuses := make(map[string]map[string]string) var unique []string servers := make(map[string]struct{}) @@ -299,11 +299,20 @@ func GetAppStatuses(appFiles AppFiles) (map[string]string, error) { for range servers { status := <-ch + result := make(map[string]string) for _, service := range status.Services { name := service.Spec.Labels[convert.LabelNamespace] + if _, ok := statuses[name]; !ok { - statuses[name] = "deployed" + result["status"] = "deployed" } + + labelKey := fmt.Sprintf("coop-cloud.%s.version", name) + if version, ok := service.Spec.Labels[labelKey]; ok { + result["version"] = version + } + + statuses[name] = result } }