forked from toolshed/abra
		
	feat: teach recipe sync to understand new versions
Closes coop-cloud/organising#177.
This commit is contained in:
		@ -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 <recipe> 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 <recipe> configuration will be updated on the local file system.
 | 
			
		||||
`,
 | 
			
		||||
	ArgsUsage: "<recipe>",
 | 
			
		||||
	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
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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 {
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user