forked from toolshed/abra
		
	Compare commits
	
		
			31 Commits
		
	
	
		
			0.4.0-alph
			...
			0.4.0-alph
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						69a7d37fb7
	
				 | 
					
					
						|||
| 
						
						
							
						
						87649cbbd0
	
				 | 
					
					
						|||
| 
						
						
							
						
						4b7ec6384c
	
				 | 
					
					
						|||
| 
						
						
							
						
						b22b63c2ba
	
				 | 
					
					
						|||
| 
						
						
							
						
						d9f3a11265
	
				 | 
					
					
						|||
| 
						
						
							
						
						d7cf11b876
	
				 | 
					
					
						|||
| 
						
						
							
						
						d7e1b2947a
	
				 | 
					
					
						|||
| 
						
						
							
						
						1b37d2d5f5
	
				 | 
					
					
						|||
| 
						
						
							
						
						74dfb12fd6
	
				 | 
					
					
						|||
| 
						
						
							
						
						49ccf2d204
	
				 | 
					
					
						|||
| 
						
						
							
						
						76adc45431
	
				 | 
					
					
						|||
| 
						
						
							
						
						e38a0078f3
	
				 | 
					
					
						|||
| 
						
						
							
						
						25b44dc54e
	
				 | 
					
					
						|||
| 
						
						
							
						
						0c2f6fb676
	
				 | 
					
					
						|||
| 
						
						
							
						
						10e4a8b97f
	
				 | 
					
					
						|||
| 
						
						
							
						
						eed2756784
	
				 | 
					
					
						|||
| 
						
						
							
						
						b61b8f0d2a
	
				 | 
					
					
						|||
| 
						
						
							
						
						763e7b5bff
	
				 | 
					
					
						|||
| 
						
						
							
						
						d5ab9aedbf
	
				 | 
					
					
						|||
| 
						
						
							
						
						2ebb00c9d4
	
				 | 
					
					
						|||
| 
						
						
							
						
						6d76b3646a
	
				 | 
					
					
						|||
| 
						
						
							
						
						636dc82258
	
				 | 
					
					
						|||
| 
						
						
							
						
						66d5453248
	
				 | 
					
					
						|||
| 
						
						
							
						
						ba9abcb0d7
	
				 | 
					
					
						|||
| 
						
						
							
						
						a1cbf21f61
	
				 | 
					
					
						|||
| 
						
						
							
						
						bd1da39374
	
				 | 
					
					
						|||
| 
						
						
							
						
						8b90519bc9
	
				 | 
					
					
						|||
| 
						
						
							
						
						65feda7f1d
	
				 | 
					
					
						|||
| 
						
						
							
						
						64e223a810
	
				 | 
					
					
						|||
| 
						
						
							
						
						379e01d855
	
				 | 
					
					
						|||
| 
						
						
							
						
						a421c0dca5
	
				 | 
					
					
						
@ -39,13 +39,13 @@ var appRemoveCommand = &cli.Command{
 | 
			
		||||
		if !internal.Force {
 | 
			
		||||
			response := false
 | 
			
		||||
			prompt := &survey.Confirm{
 | 
			
		||||
				Message: fmt.Sprintf("about to delete %s, are you sure?", app.Name),
 | 
			
		||||
				Message: fmt.Sprintf("about to remove %s, are you sure?", app.Name),
 | 
			
		||||
			}
 | 
			
		||||
			if err := survey.AskOne(prompt, &response); err != nil {
 | 
			
		||||
				logrus.Fatal(err)
 | 
			
		||||
			}
 | 
			
		||||
			if !response {
 | 
			
		||||
				logrus.Fatal("user aborted app removal")
 | 
			
		||||
				logrus.Fatal("aborting as requested")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -54,7 +54,6 @@ var appRemoveCommand = &cli.Command{
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !internal.Force {
 | 
			
		||||
		isDeployed, _, err := stack.IsDeployed(c.Context, cl, app.StackName())
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
@ -62,10 +61,9 @@ var appRemoveCommand = &cli.Command{
 | 
			
		||||
		if isDeployed {
 | 
			
		||||
			logrus.Fatalf("%s is still deployed. Run \"abra app undeploy %s \" or pass --force", app.Name, app.Name)
 | 
			
		||||
		}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fs := filters.NewArgs()
 | 
			
		||||
		fs.Add("name", app.Name)
 | 
			
		||||
		fs.Add("name", app.StackName())
 | 
			
		||||
		secretList, err := cl.SecretList(c.Context, types.SecretListOptions{Filters: fs})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
@ -81,6 +79,7 @@ var appRemoveCommand = &cli.Command{
 | 
			
		||||
 | 
			
		||||
		if len(secrets) > 0 {
 | 
			
		||||
			var secretNamesToRemove []string
 | 
			
		||||
 | 
			
		||||
			if !internal.Force {
 | 
			
		||||
				secretsPrompt := &survey.MultiSelect{
 | 
			
		||||
					Message: "which secrets do you want to remove?",
 | 
			
		||||
@ -142,8 +141,10 @@ var appRemoveCommand = &cli.Command{
 | 
			
		||||
				logrus.Info("no volumes were removed")
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			if Volumes {
 | 
			
		||||
				logrus.Info("no volumes to remove")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		err = os.Remove(app.Path)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@ import (
 | 
			
		||||
var allSecrets bool
 | 
			
		||||
var allSecretsFlag = &cli.BoolFlag{
 | 
			
		||||
	Name:        "all",
 | 
			
		||||
	Aliases:     []string{"A"},
 | 
			
		||||
	Aliases:     []string{"a"},
 | 
			
		||||
	Value:       false,
 | 
			
		||||
	Destination: &allSecrets,
 | 
			
		||||
	Usage:       "Generate all secrets",
 | 
			
		||||
@ -32,6 +32,7 @@ var appSecretGenerateCommand = &cli.Command{
 | 
			
		||||
	Usage:        "Generate secrets",
 | 
			
		||||
	ArgsUsage:    "<secret> <version>",
 | 
			
		||||
	Flags:        []cli.Flag{allSecretsFlag, internal.PassFlag},
 | 
			
		||||
	BashComplete: autocomplete.AppNameComplete,
 | 
			
		||||
	Action: func(c *cli.Context) error {
 | 
			
		||||
		app := internal.ValidateApp(c)
 | 
			
		||||
 | 
			
		||||
@ -100,6 +101,7 @@ var appSecretInsertCommand = &cli.Command{
 | 
			
		||||
	Usage:        "Insert secret",
 | 
			
		||||
	Flags:        []cli.Flag{internal.PassFlag},
 | 
			
		||||
	ArgsUsage:    "<app> <secret-name> <version> <data>",
 | 
			
		||||
	BashComplete: autocomplete.AppNameComplete,
 | 
			
		||||
	Description: `
 | 
			
		||||
This command inserts a secret into an app environment.
 | 
			
		||||
 | 
			
		||||
@ -144,6 +146,7 @@ var appSecretRmCommand = &cli.Command{
 | 
			
		||||
	Aliases:      []string{"rm"},
 | 
			
		||||
	Flags:        []cli.Flag{allSecretsFlag, internal.PassFlag},
 | 
			
		||||
	ArgsUsage:    "<app> <secret-name>",
 | 
			
		||||
	BashComplete: autocomplete.AppNameComplete,
 | 
			
		||||
	Description: `
 | 
			
		||||
This command removes a secret from an app environment.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -113,7 +113,7 @@ recipes.
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if len(availableUpgrades) == 0 && !internal.Force {
 | 
			
		||||
				logrus.Info("no available upgrades, you're on latest ✌️")
 | 
			
		||||
				logrus.Infof("no available upgrades, you're on latest (%s) ✌️", deployedVersion)
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,6 @@
 | 
			
		||||
package app
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"coopcloud.tech/abra/cli/internal"
 | 
			
		||||
	"coopcloud.tech/abra/pkg/autocomplete"
 | 
			
		||||
	"coopcloud.tech/abra/pkg/client"
 | 
			
		||||
@ -22,9 +20,8 @@ func getImagePath(image string) (string, error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	path := reference.Path(img)
 | 
			
		||||
	if strings.Contains(path, "library") {
 | 
			
		||||
		path = strings.Split(path, "/")[1]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	path = recipe.StripTagMeta(path)
 | 
			
		||||
 | 
			
		||||
	logrus.Debugf("parsed %s from %s", path, image)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -24,9 +24,11 @@ import (
 | 
			
		||||
func DeployAction(c *cli.Context) error {
 | 
			
		||||
	app := ValidateApp(c)
 | 
			
		||||
 | 
			
		||||
	if !Chaos {
 | 
			
		||||
		if err := recipe.EnsureUpToDate(app.Type); err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r, err := recipe.Get(app.Type)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@ -58,7 +60,7 @@ func DeployAction(c *cli.Context) error {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	version := deployedVersion
 | 
			
		||||
	if version == "" && !Chaos {
 | 
			
		||||
	if version == "unknown" && !Chaos {
 | 
			
		||||
		catl, err := recipe.ReadRecipeCatalogue()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
@ -86,14 +88,14 @@ func DeployAction(c *cli.Context) error {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if version == "" && !Chaos {
 | 
			
		||||
	if version == "unknown" && !Chaos {
 | 
			
		||||
		logrus.Debugf("choosing %s as version to deploy", version)
 | 
			
		||||
		if err := recipe.EnsureVersion(app.Type, version); err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if version != "" && !Chaos {
 | 
			
		||||
	if version != "unknown" && !Chaos {
 | 
			
		||||
		if err := recipe.EnsureVersion(app.Type, version); err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
@ -221,7 +223,7 @@ func NewVersionOverview(app config.App, currentVersion, newVersion, releaseNotes
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if releaseNotes != "" {
 | 
			
		||||
	if releaseNotes != "" && newVersion != "" {
 | 
			
		||||
		fmt.Println()
 | 
			
		||||
		fmt.Println(fmt.Sprintf("%s release notes:\n\n%s", newVersion, releaseNotes))
 | 
			
		||||
	} else {
 | 
			
		||||
@ -250,6 +252,10 @@ func NewVersionOverview(app config.App, currentVersion, newVersion, releaseNotes
 | 
			
		||||
 | 
			
		||||
// GetReleaseNotes prints release notes for a recipe version
 | 
			
		||||
func GetReleaseNotes(recipeName, version string) (string, error) {
 | 
			
		||||
	if version == "" {
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fpath := path.Join(config.RECIPES_DIR, recipeName, "release", version)
 | 
			
		||||
 | 
			
		||||
	if _, err := os.Stat(fpath); !os.IsNotExist(err) {
 | 
			
		||||
 | 
			
		||||
@ -163,9 +163,9 @@ func NewAction(c *cli.Context) error {
 | 
			
		||||
		NewAppServer = "local"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tableCol := []string{"Name", "Domain", "Type", "Server"}
 | 
			
		||||
	tableCol := []string{"server", "type", "domain", "app name"}
 | 
			
		||||
	table := formatter.CreateTable(tableCol)
 | 
			
		||||
	table.Append([]string{sanitisedAppName, Domain, recipe.Name, NewAppServer})
 | 
			
		||||
	table.Append([]string{NewAppServer, recipe.Name, Domain, NewAppName})
 | 
			
		||||
 | 
			
		||||
	fmt.Println("")
 | 
			
		||||
	fmt.Println(fmt.Sprintf("A new %s app has been created! Here is an overview:", recipe.Name))
 | 
			
		||||
@ -173,10 +173,10 @@ func NewAction(c *cli.Context) error {
 | 
			
		||||
	table.Render()
 | 
			
		||||
	fmt.Println("")
 | 
			
		||||
	fmt.Println("You can configure this app by running the following:")
 | 
			
		||||
	fmt.Println(fmt.Sprintf("\n    abra app config %s", sanitisedAppName))
 | 
			
		||||
	fmt.Println(fmt.Sprintf("\n    abra app config %s", NewAppName))
 | 
			
		||||
	fmt.Println("")
 | 
			
		||||
	fmt.Println("You can deploy this app by running the following:")
 | 
			
		||||
	fmt.Println(fmt.Sprintf("\n    abra app deploy %s", sanitisedAppName))
 | 
			
		||||
	fmt.Println(fmt.Sprintf("\n    abra app deploy %s", NewAppName))
 | 
			
		||||
	fmt.Println("")
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
 | 
			
		||||
@ -2,9 +2,9 @@ package internal
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"coopcloud.tech/abra/pkg/recipe"
 | 
			
		||||
	recipePkg "coopcloud.tech/abra/pkg/recipe"
 | 
			
		||||
	"github.com/AlecAivazis/survey/v2"
 | 
			
		||||
	"github.com/docker/distribution/reference"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
@ -94,9 +94,7 @@ func GetMainAppImage(recipe recipe.Recipe) (string, error) {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			path = reference.Path(img)
 | 
			
		||||
			if strings.Contains(path, "library") {
 | 
			
		||||
				path = strings.Split(path, "/")[1]
 | 
			
		||||
			}
 | 
			
		||||
			path = recipePkg.StripTagMeta(path)
 | 
			
		||||
 | 
			
		||||
			return path, nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -127,6 +127,7 @@ your SSH keys configured on your account.
 | 
			
		||||
func getImageVersions(recipe recipe.Recipe) (map[string]string, error) {
 | 
			
		||||
	var services = make(map[string]string)
 | 
			
		||||
 | 
			
		||||
	missingTag := false
 | 
			
		||||
	for _, service := range recipe.Config.Services {
 | 
			
		||||
		if service.Image == "" {
 | 
			
		||||
			continue
 | 
			
		||||
@ -138,21 +139,27 @@ func getImageVersions(recipe recipe.Recipe) (map[string]string, error) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		path := reference.Path(img)
 | 
			
		||||
		if strings.Contains(path, "library") {
 | 
			
		||||
			path = strings.Split(path, "/")[1]
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		path = recipePkg.StripTagMeta(path)
 | 
			
		||||
 | 
			
		||||
		var tag string
 | 
			
		||||
		switch img.(type) {
 | 
			
		||||
		case reference.NamedTagged:
 | 
			
		||||
			tag = img.(reference.NamedTagged).Tag()
 | 
			
		||||
		case reference.Named:
 | 
			
		||||
			return services, fmt.Errorf("%s service is missing image tag?", path)
 | 
			
		||||
			if service.Name == "app" {
 | 
			
		||||
				missingTag = true
 | 
			
		||||
			}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		services[path] = tag
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if missingTag {
 | 
			
		||||
		return services, fmt.Errorf("app service is missing image tag?")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return services, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -115,23 +115,26 @@ You may invoke this command in "wizard" mode and be prompted for input:
 | 
			
		||||
			}
 | 
			
		||||
			logrus.Debugf("retrieved %s from remote registry for %s", regVersions, image)
 | 
			
		||||
 | 
			
		||||
			if strings.Contains(image, "library") {
 | 
			
		||||
				// ParseNormalizedNamed prepends 'library' to images like nginx:<tag>,
 | 
			
		||||
				// postgres:<tag>, i.e. images which do not have a username in the
 | 
			
		||||
				// first position of the string
 | 
			
		||||
				image = strings.Split(image, "/")[1]
 | 
			
		||||
			}
 | 
			
		||||
			semverLikeTag := true
 | 
			
		||||
			image = recipePkg.StripTagMeta(image)
 | 
			
		||||
 | 
			
		||||
			switch img.(type) {
 | 
			
		||||
			case reference.NamedTagged:
 | 
			
		||||
				if !tagcmp.IsParsable(img.(reference.NamedTagged).Tag()) {
 | 
			
		||||
					logrus.Debugf("%s not considered semver-like", img.(reference.NamedTagged).Tag())
 | 
			
		||||
				semverLikeTag = false
 | 
			
		||||
				}
 | 
			
		||||
			default:
 | 
			
		||||
				logrus.Warnf("unable to read tag for image %s, is it missing? skipping upgrade for %s", image, service.Name)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			tag, err := tagcmp.Parse(img.(reference.NamedTagged).Tag())
 | 
			
		||||
			if err != nil && semverLikeTag {
 | 
			
		||||
				logrus.Fatal(err)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				logrus.Warnf("unable to parse %s, error was: %s, skipping upgrade for %s", image, err.Error(), service.Name)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			logrus.Debugf("parsed %s for %s", tag, service.Name)
 | 
			
		||||
 | 
			
		||||
			var compatible []tagcmp.Tag
 | 
			
		||||
			for _, regVersion := range regVersions {
 | 
			
		||||
				other, err := tagcmp.Parse(regVersion.Name)
 | 
			
		||||
@ -148,7 +151,7 @@ You may invoke this command in "wizard" mode and be prompted for input:
 | 
			
		||||
 | 
			
		||||
			sort.Sort(tagcmp.ByTagDesc(compatible))
 | 
			
		||||
 | 
			
		||||
			if len(compatible) == 0 && semverLikeTag {
 | 
			
		||||
			if len(compatible) == 0 {
 | 
			
		||||
				logrus.Info(fmt.Sprintf("no new versions available for %s, %s is the latest", image, tag))
 | 
			
		||||
				continue // skip on to the next tag and don't update any compose files
 | 
			
		||||
			}
 | 
			
		||||
@ -188,13 +191,13 @@ You may invoke this command in "wizard" mode and be prompted for input:
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					if contains {
 | 
			
		||||
						logrus.Infof("Upgrading service %s from %s to %s (pinned tag: %s)", service.Name, tag.String(), upgradeTag, pinnedTagString)
 | 
			
		||||
						logrus.Infof("upgrading service %s from %s to %s (pinned tag: %s)", service.Name, tag.String(), upgradeTag, pinnedTagString)
 | 
			
		||||
					} else {
 | 
			
		||||
						logrus.Infof("service %s, image %s pinned to %s, no compatible upgrade found", service.Name, servicePins[service.Name].image, pinnedTagString)
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					logrus.Fatalf("Service %s is at version %s, but pinned to %s, please correct your compose.yml file manually!", service.Name, tag.String(), pinnedTag.String())
 | 
			
		||||
					logrus.Fatalf("service %s is at version %s, but pinned to %s, please correct your compose.yml file manually!", service.Name, tag.String(), pinnedTag.String())
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
@ -211,7 +214,7 @@ You may invoke this command in "wizard" mode and be prompted for input:
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					if upgradeTag == "" {
 | 
			
		||||
						logrus.Warnf("not upgrading from %s to %s for %s, because the upgrade type is more serious than what user wants.", tag.String(), compatible[0].String(), image)
 | 
			
		||||
						logrus.Warnf("not upgrading from %s to %s for %s, because the upgrade type is more serious than what user wants", tag.String(), compatible[0].String(), image)
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
@ -220,7 +223,7 @@ You may invoke this command in "wizard" mode and be prompted for input:
 | 
			
		||||
						tag := img.(reference.NamedTagged).Tag()
 | 
			
		||||
						logrus.Warning(fmt.Sprintf("unable to determine versioning semantics of %s, listing all tags", tag))
 | 
			
		||||
						msg = fmt.Sprintf("upgrade to which tag? (service: %s, tag: %s)", service.Name, tag)
 | 
			
		||||
						compatibleStrings = []string{}
 | 
			
		||||
						compatibleStrings = []string{"skip"}
 | 
			
		||||
						for _, regVersion := range regVersions {
 | 
			
		||||
							compatibleStrings = append(compatibleStrings, regVersion.Name)
 | 
			
		||||
						}
 | 
			
		||||
@ -238,10 +241,13 @@ You may invoke this command in "wizard" mode and be prompted for input:
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if upgradeTag != "skip" {
 | 
			
		||||
				if err := recipe.UpdateTag(image, upgradeTag); err != nil {
 | 
			
		||||
				ok, err := recipe.UpdateTag(image, upgradeTag)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					logrus.Fatal(err)
 | 
			
		||||
				}
 | 
			
		||||
				if ok {
 | 
			
		||||
					logrus.Infof("tag upgraded from %s to %s for %s", tag.String(), upgradeTag, image)
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				logrus.Warnf("not upgrading %s, skipping as requested", image)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@ -16,10 +16,10 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// UpdateTag updates an image tag in-place on file system local compose files.
 | 
			
		||||
func UpdateTag(pattern, image, tag, recipeName string) error {
 | 
			
		||||
func UpdateTag(pattern, image, tag, recipeName string) (bool, error) {
 | 
			
		||||
	composeFiles, err := filepath.Glob(pattern)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logrus.Debugf("considering %s config(s) for tag update", strings.Join(composeFiles, ", "))
 | 
			
		||||
@ -30,12 +30,12 @@ func UpdateTag(pattern, image, tag, recipeName string) error {
 | 
			
		||||
		envSamplePath := path.Join(config.RECIPES_DIR, recipeName, ".env.sample")
 | 
			
		||||
		sampleEnv, err := config.ReadEnv(envSamplePath)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
			return false, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		compose, err := loader.LoadComposefile(opts, sampleEnv)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
			return false, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, service := range compose.Services {
 | 
			
		||||
@ -45,24 +45,26 @@ func UpdateTag(pattern, image, tag, recipeName string) error {
 | 
			
		||||
 | 
			
		||||
			img, _ := reference.ParseNormalizedNamed(service.Image)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
				return false, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var composeTag string
 | 
			
		||||
			switch img.(type) {
 | 
			
		||||
			case reference.NamedTagged:
 | 
			
		||||
				composeTag = img.(reference.NamedTagged).Tag()
 | 
			
		||||
			default:
 | 
			
		||||
				// unable to parse, typically image missing tag
 | 
			
		||||
				return false, nil
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			composeImage := reference.Path(img)
 | 
			
		||||
			if strings.Contains(composeImage, "library") {
 | 
			
		||||
				// ParseNormalizedNamed prepends 'library' to images like nginx:<tag>,
 | 
			
		||||
				// postgres:<tag>, i.e. images which do not have a username in the
 | 
			
		||||
				// first position of the string
 | 
			
		||||
				composeImage = strings.Split(composeImage, "/")[1]
 | 
			
		||||
			}
 | 
			
		||||
			composeTag := img.(reference.NamedTagged).Tag()
 | 
			
		||||
 | 
			
		||||
			logrus.Debugf("parsed %s from %s", composeTag, service.Image)
 | 
			
		||||
 | 
			
		||||
			if image == composeImage {
 | 
			
		||||
				bytes, err := ioutil.ReadFile(composeFile)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
					return false, err
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				old := fmt.Sprintf("%s:%s", composeImage, composeTag)
 | 
			
		||||
@ -72,13 +74,13 @@ func UpdateTag(pattern, image, tag, recipeName string) error {
 | 
			
		||||
				logrus.Debugf("updating %s to %s in %s", old, new, compose.Filename)
 | 
			
		||||
 | 
			
		||||
				if err := ioutil.WriteFile(compose.Filename, []byte(replacedBytes), 0764); err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
					return true, err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
	return false, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateLabel updates a label in-place on file system local compose files.
 | 
			
		||||
 | 
			
		||||
@ -163,12 +163,17 @@ func (r Recipe) UpdateLabel(pattern, serviceName, label string) error {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateTag updates a recipe tag
 | 
			
		||||
func (r Recipe) UpdateTag(image, tag string) error {
 | 
			
		||||
func (r Recipe) UpdateTag(image, tag string) (bool, error) {
 | 
			
		||||
	pattern := fmt.Sprintf("%s/%s/compose**yml", config.RECIPES_DIR, r.Name)
 | 
			
		||||
	if err := compose.UpdateTag(pattern, image, tag, r.Name); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
 | 
			
		||||
	image = StripTagMeta(image)
 | 
			
		||||
 | 
			
		||||
	ok, err := compose.UpdateTag(pattern, image, tag, r.Name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
 | 
			
		||||
	return ok, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Tags list the recipe tags
 | 
			
		||||
@ -973,9 +978,8 @@ func GetRecipeVersions(recipeName, registryUsername, registryPassword string) (R
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			path := reference.Path(img)
 | 
			
		||||
			if strings.Contains(path, "library") {
 | 
			
		||||
				path = strings.Split(path, "/")[1]
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			path = StripTagMeta(path)
 | 
			
		||||
 | 
			
		||||
			var tag string
 | 
			
		||||
			switch img.(type) {
 | 
			
		||||
@ -1041,3 +1045,22 @@ func GetRecipeCatalogueVersions(recipeName string, catl RecipeCatalogue) ([]stri
 | 
			
		||||
 | 
			
		||||
	return versions, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StripTagMeta strips front-matter image tag data that we don't need for parsing.
 | 
			
		||||
func StripTagMeta(image string) string {
 | 
			
		||||
	originalImage := image
 | 
			
		||||
 | 
			
		||||
	if strings.Contains(image, "docker.io") {
 | 
			
		||||
		image = strings.Split(image, "/")[1]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if strings.Contains(image, "library") {
 | 
			
		||||
		image = strings.Split(image, "/")[1]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if originalImage != image {
 | 
			
		||||
		logrus.Debugf("stripped %s to %s for parsing", originalImage, image)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return image
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -479,24 +479,28 @@ func WaitOnService(ctx context.Context, cl *dockerclient.Client, serviceID, appN
 | 
			
		||||
 | 
			
		||||
	go io.Copy(ioutil.Discard, pipeReader)
 | 
			
		||||
 | 
			
		||||
	timeout := 30 * time.Second
 | 
			
		||||
	timeout := 50 * time.Second
 | 
			
		||||
 | 
			
		||||
	select {
 | 
			
		||||
	case err := <-errChan:
 | 
			
		||||
		return err
 | 
			
		||||
	case <-time.After(timeout):
 | 
			
		||||
		return fmt.Errorf(fmt.Sprintf(`
 | 
			
		||||
%s has still not converged (%s second timeout reached)
 | 
			
		||||
%s has not converged (%s second timeout reached).
 | 
			
		||||
 | 
			
		||||
This does not necessarily mean your deployment has failed, it may just be that
 | 
			
		||||
the app is taking longer to deploy based on your server resources or network
 | 
			
		||||
latency. Please run the following the inspect the logs of your deployed app:
 | 
			
		||||
latency.
 | 
			
		||||
 | 
			
		||||
You can track latest deployment status with:
 | 
			
		||||
 | 
			
		||||
    abra app ps --watch %s
 | 
			
		||||
 | 
			
		||||
And inspect the logs with:
 | 
			
		||||
 | 
			
		||||
    abra app logs %s
 | 
			
		||||
 | 
			
		||||
If a service is failing to even start (run "abra app ps %s" to see what
 | 
			
		||||
services are running) there could be a few things. The follow command will
 | 
			
		||||
try to smoke those out for you:
 | 
			
		||||
If a service is failing to even start, try smoke out the error with:
 | 
			
		||||
 | 
			
		||||
    abra app errors --watch %s
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
ABRA_VERSION="0.3.0-alpha"
 | 
			
		||||
ABRA_RELEASE_URL="https://git.coopcloud.tech/api/v1/repos/coop-cloud/abra/releases/tags/$ABRA_VERSION"
 | 
			
		||||
RC_VERSION="0.4.0-alpha-rc1"
 | 
			
		||||
RC_VERSION="0.4.0-alpha-rc4"
 | 
			
		||||
RC_VERSION_URL="https://git.coopcloud.tech/api/v1/repos/coop-cloud/abra/releases/tags/$RC_VERSION"
 | 
			
		||||
 | 
			
		||||
for arg in "$@"; do
 | 
			
		||||
@ -58,7 +58,7 @@ function install_abra_release {
 | 
			
		||||
  checksum=$(echo "$checksums" | grep "$FILENAME" - | sed -En 's/([0-9a-f]{64})\s+'"$FILENAME"'.*/\1/p')
 | 
			
		||||
 | 
			
		||||
  echo "downloading $ABRA_VERSION $PLATFORM binary release for abra..."
 | 
			
		||||
  wget -q --show-progress "$release_url" -O "$HOME/.local/bin/.abra-download"
 | 
			
		||||
  wget -q "$release_url" -O "$HOME/.local/bin/.abra-download"
 | 
			
		||||
  localsum=$(sha256sum $HOME/.local/bin/.abra-download | sed -En 's/([0-9a-f]{64})\s+.*/\1/p')
 | 
			
		||||
  echo "checking if checksums match..."
 | 
			
		||||
  if [[ "$localsum" != "$checksum" ]]; then
 | 
			
		||||
 | 
			
		||||
@ -61,9 +61,9 @@ $ABRA autocomplete zsh
 | 
			
		||||
# ========================================================================
 | 
			
		||||
# record command
 | 
			
		||||
# ========================================================================
 | 
			
		||||
$ABRA record new -p gandi -t A -n e2e -v 192.157.2.21 coopcloud.tech
 | 
			
		||||
$ABRA record list -p gandi coopcloud.tech | grep -q e2e
 | 
			
		||||
$ABRA -n record rm -p gandi -t A -n e2e coopcloud.tech
 | 
			
		||||
$ABRA record new -p gandi -t A -n int-core -v 192.157.2.21 coopcloud.tech
 | 
			
		||||
$ABRA record list -p gandi coopcloud.tech | grep -q int-core
 | 
			
		||||
$ABRA -n record rm -p gandi -t A -n int-core coopcloud.tech
 | 
			
		||||
 | 
			
		||||
# ========================================================================
 | 
			
		||||
# catalogue command
 | 
			
		||||
@ -86,11 +86,11 @@ $ABRA recipe lint gitea
 | 
			
		||||
# ========================================================================
 | 
			
		||||
# server command
 | 
			
		||||
# ========================================================================
 | 
			
		||||
$ABRA -n server new -p hetzner-cloud --hn e2e
 | 
			
		||||
$ABRA -n server new -p hetzner-cloud --hn int-core
 | 
			
		||||
 | 
			
		||||
$ABRA server ls | grep -q e2e
 | 
			
		||||
$ABRA server ls | grep -q int-core
 | 
			
		||||
 | 
			
		||||
$ABRA -n server rm -s -p hetzner-cloud --hn e2e
 | 
			
		||||
$ABRA -n server rm -s -p hetzner-cloud --hn int-core
 | 
			
		||||
 | 
			
		||||
# ========================================================================
 | 
			
		||||
# app command
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,8 @@ wire up for testing in an automated way.
 | 
			
		||||
## deploy, upgrade, rollback
 | 
			
		||||
 | 
			
		||||
- `abra app deploy <app>`
 | 
			
		||||
- `abra app deploy --force <app>`
 | 
			
		||||
- `abra app deploy --chaos <app>`
 | 
			
		||||
- `abra app upgrade <app>`
 | 
			
		||||
- `abra app rollback <app>`
 | 
			
		||||
 | 
			
		||||
@ -33,6 +35,10 @@ wire up for testing in an automated way.
 | 
			
		||||
 | 
			
		||||
### easy mode
 | 
			
		||||
 | 
			
		||||
- `abra app ls -t <recipe>`
 | 
			
		||||
- `abra app ls -s <server>`
 | 
			
		||||
- `abra app ls -s <server> -t <recipe>`
 | 
			
		||||
- `abra app ls -s <server> -t <recipe> -S`
 | 
			
		||||
- `abra app config <app>`
 | 
			
		||||
- `abra app check <app>`
 | 
			
		||||
- `abra app ps <app>`
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user