From c1b03bcbd743c0656c0a9b763ea4c8ba21c9b2d4 Mon Sep 17 00:00:00 2001 From: p4u1 Date: Mon, 8 Jul 2024 12:31:39 +0200 Subject: [PATCH] refactor(recipe): load load compoes config where its used --- cli/app/deploy.go | 19 ++--- cli/app/new.go | 2 +- cli/app/rollback.go | 17 ++--- cli/app/upgrade.go | 10 +-- cli/catalogue/catalogue.go | 2 +- cli/internal/recipe.go | 8 +- cli/recipe/lint.go | 4 +- cli/recipe/release.go | 18 +++-- cli/recipe/sync.go | 4 +- cli/recipe/upgrade.go | 7 +- cli/updater/updater.go | 4 +- pkg/lint/recipe.go | 102 +++++++++++++++++-------- pkg/recipe/compose.go | 53 ++++++++++++- pkg/recipe/git.go | 89 ++++++++++++++++++++++ pkg/recipe/recipe.go | 147 +------------------------------------ pkg/recipe/recipe_test.go | 7 +- 16 files changed, 263 insertions(+), 230 deletions(-) diff --git a/cli/app/deploy.go b/cli/app/deploy.go index b272918b..10744056 100644 --- a/cli/app/deploy.go +++ b/cli/app/deploy.go @@ -58,13 +58,8 @@ recipes. log.Fatal("cannot use and --chaos together") } - r2 := recipe.Get2(app.Recipe) - if err := r2.Ensure(internal.Chaos, internal.Offline); err != nil { - log.Fatal(err) - } - - r, err := recipe.Get(app.Recipe, internal.Offline) - if err != nil { + r := recipe.Get2(app.Recipe) + if err := r.Ensure(internal.Chaos, internal.Offline); err != nil { log.Fatal(err) } @@ -92,7 +87,7 @@ recipes. if specificVersion != "" { version = specificVersion log.Debugf("choosing %s as version to deploy", version) - if err := r2.EnsureVersion(version); err != nil { + if err := r.EnsureVersion(version); err != nil { log.Fatal(err) } } @@ -128,7 +123,7 @@ recipes. if len(versions) == 0 && !internal.Chaos { log.Warn("no published versions in catalogue, trying local recipe repository") - recipeVersions, err := recipe.GetRecipeVersions(app.Recipe, internal.Offline) + recipeVersions, err := r.GetRecipeVersions(internal.Offline) if err != nil { log.Warn(err) } @@ -142,7 +137,7 @@ recipes. if len(versions) > 0 && !internal.Chaos { version = versions[len(versions)-1] log.Debugf("choosing %s as version to deploy", version) - if err := r2.EnsureVersion(version); err != nil { + if err := r.EnsureVersion(version); err != nil { log.Fatal(err) } } else { @@ -158,7 +153,7 @@ recipes. if internal.Chaos { log.Warnf("chaos mode engaged") var err error - version, err = r2.ChaosVersion() + version, err = r.ChaosVersion() if err != nil { log.Fatal(err) } @@ -173,7 +168,7 @@ recipes. app.Env[k] = v } - composeFiles, err := r2.GetComposeFiles(app.Env) + composeFiles, err := r.GetComposeFiles(app.Env) if err != nil { log.Fatal(err) } diff --git a/cli/app/new.go b/cli/app/new.go index 0453650d..7ee1fde5 100644 --- a/cli/app/new.go +++ b/cli/app/new.go @@ -80,7 +80,7 @@ var appNewCommand = cli.Command{ if c.Args().Get(1) == "" { var version string - recipeVersions, err := recipePkg.GetRecipeVersions(recipe.Name, internal.Offline) + recipeVersions, err := r.GetRecipeVersions(internal.Offline) if err != nil { log.Fatal(err) } diff --git a/cli/app/rollback.go b/cli/app/rollback.go index afe1b23e..da4ba251 100644 --- a/cli/app/rollback.go +++ b/cli/app/rollback.go @@ -58,13 +58,8 @@ recipes. log.Fatal("cannot use and --chaos together") } - r2 := recipe.Get2(app.Recipe) - if err := r2.Ensure(internal.Chaos, internal.Offline); err != nil { - log.Fatal(err) - } - - r, err := recipe.Get(app.Recipe, internal.Offline) - if err != nil { + r := recipe.Get2(app.Recipe) + if err := r.Ensure(internal.Chaos, internal.Offline); err != nil { log.Fatal(err) } @@ -100,7 +95,7 @@ recipes. if len(versions) == 0 && !internal.Chaos { log.Warn("no published versions in catalogue, trying local recipe repository") - recipeVersions, err := recipe.GetRecipeVersions(app.Recipe, internal.Offline) + recipeVersions, err := r.GetRecipeVersions(internal.Offline) if err != nil { log.Warn(err) } @@ -170,7 +165,7 @@ recipes. } if !internal.Chaos { - if err := r2.EnsureVersion(chosenDowngrade); err != nil { + if err := r.EnsureVersion(chosenDowngrade); err != nil { log.Fatal(err) } } @@ -178,7 +173,7 @@ recipes. if internal.Chaos { log.Warn("chaos mode engaged") var err error - chosenDowngrade, err = r2.ChaosVersion() + chosenDowngrade, err = r.ChaosVersion() if err != nil { log.Fatal(err) } @@ -193,7 +188,7 @@ recipes. app.Env[k] = v } - composeFiles, err := r2.GetComposeFiles(app.Env) + composeFiles, err := r.GetComposeFiles(app.Env) if err != nil { log.Fatal(err) } diff --git a/cli/app/upgrade.go b/cli/app/upgrade.go index 5f2a129c..6a8e4712 100644 --- a/cli/app/upgrade.go +++ b/cli/app/upgrade.go @@ -69,12 +69,8 @@ recipes. log.Fatal(err) } - recipe, err := recipePkg.Get(app.Recipe, internal.Offline) - if err != nil { - log.Fatal(err) - } - - if err := lint.LintForErrors(recipe); err != nil { + r2 := recipePkg.Get2(app.Recipe) + if err := lint.LintForErrors(r2); err != nil { log.Fatal(err) } @@ -106,7 +102,7 @@ recipes. if len(versions) == 0 && !internal.Chaos { log.Warn("no published versions in catalogue, trying local recipe repository") - recipeVersions, err := recipePkg.GetRecipeVersions(app.Recipe, internal.Offline) + recipeVersions, err := r2.GetRecipeVersions(internal.Offline) if err != nil { log.Warn(err) } diff --git a/cli/catalogue/catalogue.go b/cli/catalogue/catalogue.go index ae319c93..c5bf3c3b 100644 --- a/cli/catalogue/catalogue.go +++ b/cli/catalogue/catalogue.go @@ -99,7 +99,7 @@ keys configured on your account. continue } - versions, err := recipe.GetRecipeVersions(recipeMeta.Name, internal.Offline) + versions, err := r.GetRecipeVersions(internal.Offline) if err != nil { log.Warn(err) } diff --git a/cli/internal/recipe.go b/cli/internal/recipe.go index 9c87cce2..5e8fb887 100644 --- a/cli/internal/recipe.go +++ b/cli/internal/recipe.go @@ -85,10 +85,14 @@ func SetBumpType(bumpType string) { } // GetMainAppImage retrieves the main 'app' image name -func GetMainAppImage(recipe recipe.Recipe) (string, error) { +func GetMainAppImage(recipe recipe.Recipe2) (string, error) { var path string - for _, service := range recipe.Config.Services { + config, err := recipe.GetComposeConfig(nil) + if err != nil { + return "", err + } + for _, service := range config.Services { if service.Name == "app" { img, err := reference.ParseNormalizedNamed(service.Image) if err != nil { diff --git a/cli/recipe/lint.go b/cli/recipe/lint.go index 079a0a53..cc73a8a3 100644 --- a/cli/recipe/lint.go +++ b/cli/recipe/lint.go @@ -47,7 +47,7 @@ var recipeLintCommand = cli.Command{ } skipped := false - if rule.Skip(recipe) { + if rule.Skip(r) { skipped = true } @@ -58,7 +58,7 @@ var recipeLintCommand = cli.Command{ satisfied := false if !skipped { - ok, err := rule.Function(recipe) + ok, err := rule.Function(r) if err != nil { log.Warn(err) } diff --git a/cli/recipe/release.go b/cli/recipe/release.go index 1c2b4d92..f639d9f0 100644 --- a/cli/recipe/release.go +++ b/cli/recipe/release.go @@ -65,12 +65,12 @@ your SSH keys configured on your account. recipe := internal.ValidateRecipe(c) r := recipePkg.Get2(recipe.Name) - imagesTmp, err := getImageVersions(recipe) + imagesTmp, err := getImageVersions(r) if err != nil { log.Fatal(err) } - mainApp, err := internal.GetMainAppImage(recipe) + mainApp, err := internal.GetMainAppImage(r) if err != nil { log.Fatal(err) } @@ -104,7 +104,7 @@ your SSH keys configured on your account. if tagString == "" && (!internal.Major && !internal.Minor && !internal.Patch) { var err error - tagString, err = getLabelVersion(recipe, false) + tagString, err = getLabelVersion(r, false) if err != nil { log.Fatal(err) } @@ -143,11 +143,15 @@ your SSH keys configured on your account. } // getImageVersions retrieves image versions for a recipe -func getImageVersions(recipe recipe.Recipe) (map[string]string, error) { +func getImageVersions(recipe recipe.Recipe2) (map[string]string, error) { services := make(map[string]string) + config, err := recipe.GetComposeConfig(nil) + if err != nil { + return nil, err + } missingTag := false - for _, service := range recipe.Config.Services { + for _, service := range config.Services { if service.Image == "" { continue } @@ -526,8 +530,8 @@ func cleanUpTag(tag, recipeName string) error { return nil } -func getLabelVersion(recipe recipe.Recipe, prompt bool) (string, error) { - initTag, err := recipePkg.GetVersionLabelLocal(recipe) +func getLabelVersion(recipe recipe.Recipe2, prompt bool) (string, error) { + initTag, err := recipe.GetVersionLabelLocal() if err != nil { return "", err } diff --git a/cli/recipe/sync.go b/cli/recipe/sync.go index 565650f8..1d2e2897 100644 --- a/cli/recipe/sync.go +++ b/cli/recipe/sync.go @@ -47,12 +47,12 @@ local file system. recipe := internal.ValidateRecipe(c) r := recipePkg.Get2(recipe.Name) - mainApp, err := internal.GetMainAppImage(recipe) + mainApp, err := internal.GetMainAppImage(r) if err != nil { log.Fatal(err) } - imagesTmp, err := getImageVersions(recipe) + imagesTmp, err := getImageVersions(r) if err != nil { log.Fatal(err) } diff --git a/cli/recipe/upgrade.go b/cli/recipe/upgrade.go index 05d44f2c..f49ec182 100644 --- a/cli/recipe/upgrade.go +++ b/cli/recipe/upgrade.go @@ -130,7 +130,12 @@ You may invoke this command in "wizard" mode and be prompted for input: log.Debugf("did not find versions file for %s", recipe.Name) } - for _, service := range recipe.Config.Services { + config, err := r.GetComposeConfig(nil) + if err != nil { + log.Fatal(err) + } + + for _, service := range config.Services { img, err := reference.ParseNormalizedNamed(service.Image) if err != nil { log.Fatal(err) diff --git a/cli/updater/updater.go b/cli/updater/updater.go index 8565f5d5..04906247 100644 --- a/cli/updater/updater.go +++ b/cli/updater/updater.go @@ -331,9 +331,7 @@ func processRecipeRepoVersion(r recipe.Recipe2, version string) error { return err } - if r, err := recipe.Get(r.Name, internal.Offline); err != nil { - return err - } else if err := lint.LintForErrors(r); err != nil { + if err := lint.LintForErrors(r); err != nil { return err } diff --git a/pkg/lint/recipe.go b/pkg/lint/recipe.go index 85e7783c..d4d366cb 100644 --- a/pkg/lint/recipe.go +++ b/pkg/lint/recipe.go @@ -19,13 +19,13 @@ import ( var Warn = "warn" var Critical = "critical" -type LintFunction func(recipe.Recipe) (bool, error) +type LintFunction func(recipe.Recipe2) (bool, error) // SkipFunction determines whether the LintFunction is run or not. It should // not take the lint rule level into account because some rules are always an // error but may depend on some additional context of the recipe configuration. // This function aims to cover those additional cases. -type SkipFunction func(recipe.Recipe) (bool, error) +type SkipFunction func(recipe.Recipe2) (bool, error) // LintRule is a linting rule which helps a recipe maintainer avoid common // problems in their recipe configurations. We aim to highlight things that @@ -42,7 +42,7 @@ type LintRule struct { } // Skip implements the SkipFunction for the lint rule. -func (l LintRule) Skip(recipe recipe.Recipe) bool { +func (l LintRule) Skip(recipe recipe.Recipe2) bool { if l.SkipCondition != nil { ok, err := l.SkipCondition(recipe) if err != nil { @@ -173,7 +173,7 @@ var LintRules = map[string][]LintRule{ // LintForErrors lints specifically for errors and not other levels. This is // used in code paths such as "app deploy" to avoid nasty surprises but not for // the typical linting commands, which do handle other levels. -func LintForErrors(recipe recipe.Recipe) error { +func LintForErrors(recipe recipe.Recipe2) error { log.Debugf("linting for critical errors in %s configs", recipe.Name) for level := range LintRules { @@ -201,15 +201,19 @@ func LintForErrors(recipe recipe.Recipe) error { return nil } -func LintComposeVersion(recipe recipe.Recipe) (bool, error) { - if recipe.Config.Version == "3.8" { +func LintComposeVersion(recipe recipe.Recipe2) (bool, error) { + config, err := recipe.GetComposeConfig(nil) + if err != nil { + return false, err + } + if config.Version == "3.8" { return true, nil } return true, nil } -func LintEnvConfigPresent(r recipe.Recipe) (bool, error) { +func LintEnvConfigPresent(r recipe.Recipe2) (bool, error) { r2 := recipe.Get2(r.Name) if _, err := os.Stat(r2.SampleEnvPath); !os.IsNotExist(err) { return true, nil @@ -218,8 +222,12 @@ func LintEnvConfigPresent(r recipe.Recipe) (bool, error) { return false, nil } -func LintAppService(recipe recipe.Recipe) (bool, error) { - for _, service := range recipe.Config.Services { +func LintAppService(recipe recipe.Recipe2) (bool, error) { + config, err := recipe.GetComposeConfig(nil) + if err != nil { + return false, err + } + for _, service := range config.Services { if service.Name == "app" { return true, nil } @@ -232,7 +240,7 @@ func LintAppService(recipe recipe.Recipe) (bool, error) { // confirms that there is no "DOMAIN=..." in the .env.sample configuration of // the recipe. This typically means that no domain is required to deploy and // therefore no matching traefik deploy label will be present. -func LintTraefikEnabledSkipCondition(r recipe.Recipe) (bool, error) { +func LintTraefikEnabledSkipCondition(r recipe.Recipe2) (bool, error) { r2 := recipe.Get2(r.Name) sampleEnv, err := r2.SampleEnv() if err != nil { @@ -246,8 +254,12 @@ func LintTraefikEnabledSkipCondition(r recipe.Recipe) (bool, error) { return false, nil } -func LintTraefikEnabled(recipe recipe.Recipe) (bool, error) { - for _, service := range recipe.Config.Services { +func LintTraefikEnabled(recipe recipe.Recipe2) (bool, error) { + config, err := recipe.GetComposeConfig(nil) + if err != nil { + return false, err + } + for _, service := range config.Services { for label := range service.Deploy.Labels { if label == "traefik.enable" { if service.Deploy.Labels[label] == "true" { @@ -260,8 +272,12 @@ func LintTraefikEnabled(recipe recipe.Recipe) (bool, error) { return false, nil } -func LintHealthchecks(recipe recipe.Recipe) (bool, error) { - for _, service := range recipe.Config.Services { +func LintHealthchecks(recipe recipe.Recipe2) (bool, error) { + config, err := recipe.GetComposeConfig(nil) + if err != nil { + return false, err + } + for _, service := range config.Services { if service.HealthCheck == nil { return false, nil } @@ -270,8 +286,12 @@ func LintHealthchecks(recipe recipe.Recipe) (bool, error) { return true, nil } -func LintAllImagesTagged(recipe recipe.Recipe) (bool, error) { - for _, service := range recipe.Config.Services { +func LintAllImagesTagged(recipe recipe.Recipe2) (bool, error) { + config, err := recipe.GetComposeConfig(nil) + if err != nil { + return false, err + } + for _, service := range config.Services { img, err := reference.ParseNormalizedNamed(service.Image) if err != nil { return false, err @@ -284,8 +304,12 @@ func LintAllImagesTagged(recipe recipe.Recipe) (bool, error) { return true, nil } -func LintNoUnstableTags(recipe recipe.Recipe) (bool, error) { - for _, service := range recipe.Config.Services { +func LintNoUnstableTags(recipe recipe.Recipe2) (bool, error) { + config, err := recipe.GetComposeConfig(nil) + if err != nil { + return false, err + } + for _, service := range config.Services { img, err := reference.ParseNormalizedNamed(service.Image) if err != nil { return false, err @@ -307,8 +331,12 @@ func LintNoUnstableTags(recipe recipe.Recipe) (bool, error) { return true, nil } -func LintSemverLikeTags(recipe recipe.Recipe) (bool, error) { - for _, service := range recipe.Config.Services { +func LintSemverLikeTags(recipe recipe.Recipe2) (bool, error) { + config, err := recipe.GetComposeConfig(nil) + if err != nil { + return false, err + } + for _, service := range config.Services { img, err := reference.ParseNormalizedNamed(service.Image) if err != nil { return false, err @@ -330,8 +358,12 @@ func LintSemverLikeTags(recipe recipe.Recipe) (bool, error) { return true, nil } -func LintImagePresent(recipe recipe.Recipe) (bool, error) { - for _, service := range recipe.Config.Services { +func LintImagePresent(recipe recipe.Recipe2) (bool, error) { + config, err := recipe.GetComposeConfig(nil) + if err != nil { + return false, err + } + for _, service := range config.Services { if service.Image == "" { return false, nil } @@ -339,7 +371,7 @@ func LintImagePresent(recipe recipe.Recipe) (bool, error) { return true, nil } -func LintHasPublishedVersion(recipe recipe.Recipe) (bool, error) { +func LintHasPublishedVersion(recipe recipe.Recipe2) (bool, error) { catl, err := recipePkg.ReadRecipeCatalogue(false) if err != nil { log.Fatal(err) @@ -357,7 +389,7 @@ func LintHasPublishedVersion(recipe recipe.Recipe) (bool, error) { return true, nil } -func LintMetadataFilledIn(r recipe.Recipe) (bool, error) { +func LintMetadataFilledIn(r recipe.Recipe2) (bool, error) { r2 := recipe.Get2(r.Name) features, category, err := recipe.GetRecipeFeaturesAndCategory(r2) if err != nil { @@ -379,10 +411,14 @@ func LintMetadataFilledIn(r recipe.Recipe) (bool, error) { return true, nil } -func LintAbraShVendors(recipe recipe.Recipe) (bool, error) { - for _, service := range recipe.Config.Services { +func LintAbraShVendors(recipe recipe.Recipe2) (bool, error) { + config, err := recipe.GetComposeConfig(nil) + if err != nil { + return false, err + } + for _, service := range config.Services { if len(service.Configs) > 0 { - abraSh := path.Join(config.RECIPES_DIR, recipe.Name, "abra.sh") + abraSh := path.Join(recipe.Dir, "abra.sh") if _, err := os.Stat(abraSh); err != nil { if os.IsNotExist(err) { return false, err @@ -394,7 +430,7 @@ func LintAbraShVendors(recipe recipe.Recipe) (bool, error) { return true, nil } -func LintHasRecipeRepo(recipe recipe.Recipe) (bool, error) { +func LintHasRecipeRepo(recipe recipe.Recipe2) (bool, error) { url := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, recipe.Name) res, err := http.Get(url) @@ -409,8 +445,12 @@ func LintHasRecipeRepo(recipe recipe.Recipe) (bool, error) { return true, nil } -func LintSecretLengths(recipe recipe.Recipe) (bool, error) { - for name := range recipe.Config.Secrets { +func LintSecretLengths(recipe recipe.Recipe2) (bool, error) { + config, err := recipe.GetComposeConfig(nil) + if err != nil { + return false, err + } + for name := range config.Secrets { if len(name) > 12 { return false, fmt.Errorf("secret %s is longer than 12 characters", name) } @@ -419,7 +459,7 @@ func LintSecretLengths(recipe recipe.Recipe) (bool, error) { return true, nil } -func LintValidTags(recipe recipe.Recipe) (bool, error) { +func LintValidTags(recipe recipe.Recipe2) (bool, error) { recipeDir := path.Join(config.RECIPES_DIR, recipe.Name) repo, err := git.PlainOpen(recipeDir) diff --git a/pkg/recipe/compose.go b/pkg/recipe/compose.go index 6e7c9264..5312263a 100644 --- a/pkg/recipe/compose.go +++ b/pkg/recipe/compose.go @@ -59,6 +59,55 @@ func (r Recipe2) GetComposeFiles(appEnv map[string]string) ([]string, error) { return composeFiles, nil } +func (r Recipe2) GetComposeConfig(env map[string]string) (*composetypes.Config, error) { + pattern := fmt.Sprintf("%s/compose**yml", r.Dir) + composeFiles, err := filepath.Glob(pattern) + if err != nil { + return nil, err + } + + if len(composeFiles) == 0 { + return nil, fmt.Errorf("%s is missing a compose.yml or compose.*.yml file?", r.Name) + } + + if env == nil { + env, err = r.SampleEnv() + if err != nil { + return nil, err + } + } + + opts := stack.Deploy{Composefiles: composeFiles} + config, err := loader.LoadComposefile(opts, env) + if err != nil { + return nil, err + } + return config, nil +} + +// GetVersionLabelLocal retrieves the version label on the local recipe config +func (r Recipe2) GetVersionLabelLocal() (string, error) { + var label string + config, err := r.GetComposeConfig(nil) + if err != nil { + return "", err + } + + for _, service := range config.Services { + for label, value := range service.Deploy.Labels { + if strings.HasPrefix(label, "coop-cloud") && strings.Contains(label, "version") { + return value, nil + } + } + } + + if label == "" { + return label, fmt.Errorf("%s has no version label? try running \"abra recipe sync %s\" first?", r.Name, r.Name) + } + + return label, nil +} + // UpdateTag updates an image tag in-place on file system local compose files. func (r Recipe2) UpdateTag(image, tag string) (bool, error) { fullPattern := fmt.Sprintf("%s/compose**yml", r.Dir) @@ -119,7 +168,7 @@ func (r Recipe2) UpdateTag(image, tag string) (bool, error) { log.Debugf("updating %s to %s in %s", old, new, compose.Filename) - if err := os.WriteFile(compose.Filename, []byte(replacedBytes), 0764); err != nil { + if err := os.WriteFile(compose.Filename, []byte(replacedBytes), 0o764); err != nil { return false, err } } @@ -185,7 +234,7 @@ func (r Recipe2) UpdateLabel(pattern, serviceName, label string) error { log.Debugf("updating %s to %s in %s", old, label, compose.Filename) - if err := ioutil.WriteFile(compose.Filename, []byte(replacedBytes), 0764); err != nil { + if err := ioutil.WriteFile(compose.Filename, []byte(replacedBytes), 0o764); err != nil { return err } diff --git a/pkg/recipe/git.go b/pkg/recipe/git.go index 9df862e4..d41fa498 100644 --- a/pkg/recipe/git.go +++ b/pkg/recipe/git.go @@ -10,6 +10,7 @@ import ( "coopcloud.tech/abra/pkg/formatter" gitPkg "coopcloud.tech/abra/pkg/git" "coopcloud.tech/abra/pkg/log" + "github.com/distribution/reference" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" ) @@ -288,3 +289,91 @@ func (r Recipe2) Tags() ([]string, error) { return tags, nil } + +// GetRecipeVersions retrieves all recipe versions. +func (r Recipe2) GetRecipeVersions(offline bool) (RecipeVersions, error) { + versions := RecipeVersions{} + log.Debugf("attempting to open git repository in %s", r.Dir) + + repo, err := git.PlainOpen(r.Dir) + if err != nil { + return versions, err + } + + worktree, err := repo.Worktree() + if err != nil { + return versions, err + } + + gitTags, err := repo.Tags() + if err != nil { + return versions, err + } + + if err := gitTags.ForEach(func(ref *plumbing.Reference) (err error) { + tag := strings.TrimPrefix(string(ref.Name()), "refs/tags/") + + log.Debugf("processing %s for %s", tag, r.Name) + + checkOutOpts := &git.CheckoutOptions{ + Create: false, + Force: true, + Branch: plumbing.ReferenceName(ref.Name()), + } + if err := worktree.Checkout(checkOutOpts); err != nil { + log.Debugf("failed to check out %s in %s", tag, r.Dir) + return err + } + + log.Debugf("successfully checked out %s in %s", ref.Name(), r.Dir) + + config, err := r.GetComposeConfig(nil) + if err != nil { + return err + } + + versionMeta := make(map[string]ServiceMeta) + for _, service := range config.Services { + + img, err := reference.ParseNormalizedNamed(service.Image) + if err != nil { + return err + } + + path := reference.Path(img) + + path = formatter.StripTagMeta(path) + + var tag string + switch img.(type) { + case reference.NamedTagged: + tag = img.(reference.NamedTagged).Tag() + case reference.Named: + log.Warnf("%s service is missing image tag?", path) + continue + } + + versionMeta[service.Name] = ServiceMeta{ + Image: path, + Tag: tag, + } + } + + versions = append(versions, map[string]map[string]ServiceMeta{tag: versionMeta}) + + return nil + }); err != nil { + return versions, err + } + + _, err = gitPkg.CheckoutDefaultBranch(repo, r.Dir) + if err != nil { + return versions, err + } + + sortRecipeVersions(versions) + + log.Debugf("collected %s for %s", versions, r.Dir) + + return versions, nil +} diff --git a/pkg/recipe/recipe.go b/pkg/recipe/recipe.go index 46998d29..4affadd8 100644 --- a/pkg/recipe/recipe.go +++ b/pkg/recipe/recipe.go @@ -6,7 +6,6 @@ import ( "io/ioutil" "os" "path" - "path/filepath" "slices" "sort" "strconv" @@ -18,14 +17,8 @@ import ( gitPkg "coopcloud.tech/abra/pkg/git" "coopcloud.tech/abra/pkg/limit" "coopcloud.tech/abra/pkg/log" - "coopcloud.tech/abra/pkg/upstream/stack" - loader "coopcloud.tech/abra/pkg/upstream/stack" "coopcloud.tech/abra/pkg/web" "coopcloud.tech/tagcmp" - "github.com/distribution/reference" - composetypes "github.com/docker/cli/cli/compose/types" - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/plumbing" ) // RecipeCatalogueURL is the only current recipe catalogue available. @@ -131,9 +124,8 @@ type Features struct { // Recipe represents a recipe. type Recipe struct { - Name string - Config *composetypes.Config - Meta RecipeMeta + Name string + Meta RecipeMeta } // Get retrieves a recipe. @@ -143,27 +135,6 @@ func Get(recipeName string, offline bool) (Recipe, error) { return Recipe{}, err } - pattern := fmt.Sprintf("%s/%s/compose**yml", config.RECIPES_DIR, r.Name) - composeFiles, err := filepath.Glob(pattern) - if err != nil { - return Recipe{}, err - } - - if len(composeFiles) == 0 { - return Recipe{}, fmt.Errorf("%s is missing a compose.yml or compose.*.yml file?", r.Name) - } - - sampleEnv, err := r.SampleEnv() - if err != nil { - return Recipe{}, err - } - - opts := stack.Deploy{Composefiles: composeFiles} - config, err := loader.LoadComposefile(opts, sampleEnv) - if err != nil { - return Recipe{}, err - } - meta, err := GetRecipeMeta(r.Name, offline) if err != nil { switch err.(type) { @@ -175,9 +146,8 @@ func Get(recipeName string, offline bool) (Recipe, error) { } return Recipe{ - Name: recipeName, - Config: config, - Meta: meta, + Name: recipeName, + Meta: meta, }, nil } @@ -216,25 +186,6 @@ func GetRecipesLocal() ([]string, error) { return recipes, nil } -// GetVersionLabelLocal retrieves the version label on the local recipe config -func GetVersionLabelLocal(recipe Recipe) (string, error) { - var label string - - for _, service := range recipe.Config.Services { - for label, value := range service.Deploy.Labels { - if strings.HasPrefix(label, "coop-cloud") && strings.Contains(label, "version") { - return value, nil - } - } - } - - if label == "" { - return label, fmt.Errorf("%s has no version label? try running \"abra recipe sync %s\" first?", recipe.Name, recipe.Name) - } - - return label, nil -} - func GetRecipeFeaturesAndCategory(r Recipe2) (Features, string, error) { feat := Features{} @@ -579,96 +530,6 @@ func ReadReposMetadata() (RepoCatalogue, error) { return reposMeta, nil } -// GetRecipeVersions retrieves all recipe versions. -func GetRecipeVersions(recipeName string, offline bool) (RecipeVersions, error) { - versions := RecipeVersions{} - recipeDir := path.Join(config.RECIPES_DIR, recipeName) - - log.Debugf("attempting to open git repository in %s", recipeDir) - - repo, err := git.PlainOpen(recipeDir) - if err != nil { - return versions, err - } - - worktree, err := repo.Worktree() - if err != nil { - return versions, err - } - - gitTags, err := repo.Tags() - if err != nil { - return versions, err - } - - if err := gitTags.ForEach(func(ref *plumbing.Reference) (err error) { - tag := strings.TrimPrefix(string(ref.Name()), "refs/tags/") - - log.Debugf("processing %s for %s", tag, recipeName) - - checkOutOpts := &git.CheckoutOptions{ - Create: false, - Force: true, - Branch: plumbing.ReferenceName(ref.Name()), - } - if err := worktree.Checkout(checkOutOpts); err != nil { - log.Debugf("failed to check out %s in %s", tag, recipeDir) - return err - } - - log.Debugf("successfully checked out %s in %s", ref.Name(), recipeDir) - - recipe, err := Get(recipeName, offline) - if err != nil { - return err - } - - versionMeta := make(map[string]ServiceMeta) - for _, service := range recipe.Config.Services { - - img, err := reference.ParseNormalizedNamed(service.Image) - if err != nil { - return err - } - - path := reference.Path(img) - - path = formatter.StripTagMeta(path) - - var tag string - switch img.(type) { - case reference.NamedTagged: - tag = img.(reference.NamedTagged).Tag() - case reference.Named: - log.Warnf("%s service is missing image tag?", path) - continue - } - - versionMeta[service.Name] = ServiceMeta{ - Image: path, - Tag: tag, - } - } - - versions = append(versions, map[string]map[string]ServiceMeta{tag: versionMeta}) - - return nil - }); err != nil { - return versions, err - } - - _, err = gitPkg.CheckoutDefaultBranch(repo, recipeDir) - if err != nil { - return versions, err - } - - sortRecipeVersions(versions) - - log.Debugf("collected %s for %s", versions, recipeName) - - return versions, nil -} - // sortRecipeVersions sorts the recipe semver versions func sortRecipeVersions(versions RecipeVersions) { sort.Slice(versions, func(i, j int) bool { diff --git a/pkg/recipe/recipe_test.go b/pkg/recipe/recipe_test.go index 01b0f093..08e23a07 100644 --- a/pkg/recipe/recipe_test.go +++ b/pkg/recipe/recipe_test.go @@ -14,13 +14,10 @@ func TestGetVersionLabelLocalDoesNotUseTimeoutLabel(t *testing.T) { t.Fatal(err) } - r, err := Get("traefik", offline) - if err != nil { - t.Fatal(err) - } + r := Get2("traefik") for i := 1; i < 1000; i++ { - label, err := GetVersionLabelLocal(r) + label, err := r.GetVersionLabelLocal() if err != nil { t.Fatal(err) }