From c616907b715e89d11bd38b51e1b0a441f33db2e6 Mon Sep 17 00:00:00 2001 From: decentral1se Date: Tue, 5 Oct 2021 10:28:09 +0200 Subject: [PATCH] feat: teach recipe sync to understand new versions Closes https://git.coopcloud.tech/coop-cloud/organising/issues/177. --- cli/recipe/sync.go | 82 +++++++++++++++++++++++++++++--------------- pkg/recipe/recipe.go | 27 +++++++++++++++ 2 files changed, 81 insertions(+), 28 deletions(-) diff --git a/cli/recipe/sync.go b/cli/recipe/sync.go index 1e23e3ef..05cdc3af 100644 --- a/cli/recipe/sync.go +++ b/cli/recipe/sync.go @@ -4,62 +4,88 @@ import ( "fmt" "coopcloud.tech/abra/cli/internal" - "coopcloud.tech/abra/pkg/client" - "github.com/docker/distribution/reference" + "coopcloud.tech/abra/pkg/catalogue" + "github.com/AlecAivazis/survey/v2" "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" ) var recipeSyncCommand = &cli.Command{ Name: "sync", - Usage: "Generate new recipe labels", + Usage: "Ensure recipe version labels are up-to-date", Aliases: []string{"s"}, Description: ` -This command will generate labels for each service which correspond to the -following format: +This command will generate labels for the main recipe service (i.e. the service +named "app", by convention) which corresponds to the following format: - coop-cloud.${STACK_NAME}.${SERVICE_NAME}.version=${IMAGE_TAG}-${IMAGE_DIGEST} + coop-cloud.${STACK_NAME}.version=${RECIPE_TAG} -The configuration will be updated on the local file system. These -labels are consumed by abra in other command invocations and used to determine -the versioning metadata of up-and-running containers are. +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. `, ArgsUsage: "", + BashComplete: func(c *cli.Context) { + catl, err := catalogue.ReadRecipeCatalogue() + if err != nil { + logrus.Warn(err) + } + if c.NArg() > 0 { + return + } + for name := range catl { + fmt.Println(name) + } + }, Action: func(c *cli.Context) error { recipe := internal.ValidateRecipe(c) + mainService := "app" + + var services []string hasAppService := false for _, service := range recipe.Config.Services { + services = append(services, service.Name) if service.Name == "app" { hasAppService = true + logrus.Debugf("detected app service in '%s'", recipe.Name) } } if !hasAppService { - logrus.Fatal(fmt.Sprintf("no 'app' service defined in '%s'", recipe.Name)) + logrus.Warnf("no 'app' service defined in '%s'", recipe.Name) + var chosenService string + prompt := &survey.Select{ + Message: fmt.Sprintf("what is the main service name for '%s'?", recipe.Name), + Options: services, + } + if err := survey.AskOne(prompt, &chosenService); err != nil { + logrus.Fatal(err) + } + mainService = chosenService } - for _, service := range recipe.Config.Services { - img, err := reference.ParseNormalizedNamed(service.Image) - if err != nil { - logrus.Fatal(err) - } - logrus.Debugf("detected image '%s' for service '%s'", img, service.Name) + logrus.Debugf("selecting '%s' as the service to sync version labels", mainService) - digest, err := client.GetTagDigest(img) - if err != nil { - logrus.Fatal(err) - } - logrus.Debugf("retrieved digest '%s' for '%s'", digest, img) - - tag := img.(reference.NamedTagged).Tag() - label := fmt.Sprintf("coop-cloud.${STACK_NAME}.%s.version=%s-%s", service.Name, tag, digest) - if err := recipe.UpdateLabel(service.Name, label); err != nil { - logrus.Fatal(err) - } - logrus.Debugf("added label '%s' to service '%s'", label, service.Name) + 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) + if err := recipe.UpdateLabel(mainService, label); err != nil { + logrus.Fatal(err) + } + + logrus.Infof("added label '%s' to service '%s'", label, mainService) + return nil }, } diff --git a/pkg/recipe/recipe.go b/pkg/recipe/recipe.go index 2b4025eb..bee90cf1 100644 --- a/pkg/recipe/recipe.go +++ b/pkg/recipe/recipe.go @@ -41,6 +41,33 @@ func (r Recipe) UpdateTag(image, tag string) error { return nil } +// Tags list the recipe tags +func (r Recipe) Tags() ([]string, error) { + var tags []string + + recipeDir := path.Join(config.ABRA_DIR, "apps", r.Name) + repo, err := git.PlainOpen(recipeDir) + if err != nil { + return tags, err + } + + gitTags, err := repo.Tags() + if err != nil { + return tags, err + } + + if err := gitTags.ForEach(func(ref *plumbing.Reference) (err error) { + tags = append(tags, strings.TrimPrefix(string(ref.Name()), "refs/tags/")) + return nil + }); err != nil { + return tags, err + } + + logrus.Debugf("detected '%s' as tags for recipe '%s'", strings.Join(tags, ", "), r.Name) + + return tags, nil +} + // Get retrieves a recipe. func Get(recipeName string) (Recipe, error) { if err := EnsureExists(recipeName); err != nil {