pass all errors to the Command function

This commit is contained in:
Moritz 2023-02-07 18:15:09 +01:00
parent 9564673a10
commit 5a7fe9971a
1 changed files with 85 additions and 56 deletions

View File

@ -63,9 +63,15 @@ var Notify = cli.Command{
} }
for _, stackInfo := range stacks { for _, stackInfo := range stacks {
stackName := stackInfo.Name stackName := stackInfo.Name
recipeName := getLabel(cl, stackName, "recipe") recipeName, err := getLabel(cl, stackName, "recipe")
if err != nil {
logrus.Fatal(err)
}
if recipeName != "" { if recipeName != "" {
getLatestUpgrade(cl, stackName, recipeName) _, err = getLatestUpgrade(cl, stackName, recipeName)
if err != nil {
logrus.Fatal(err)
}
} }
} }
return nil return nil
@ -120,47 +126,51 @@ var UpgradeApp = cli.Command{
}, },
} }
func getLabel(cl *dockerclient.Client, stackName string, label string) string {
// getLabel reads docker label in the format coop-cloud.${STACK_NAME}.${LABEL} // getLabel reads docker label in the format coop-cloud.${STACK_NAME}.${LABEL}
func getLabel(cl *dockerclient.Client, stackName string, label string) (string, error) {
filter := filters.NewArgs() filter := filters.NewArgs()
filter.Add("label", fmt.Sprintf("%s=%s", convert.LabelNamespace, stackName)) filter.Add("label", fmt.Sprintf("%s=%s", convert.LabelNamespace, stackName))
services, err := cl.ServiceList(context.Background(), types.ServiceListOptions{Filters: filter}) services, err := cl.ServiceList(context.Background(), types.ServiceListOptions{Filters: filter})
if err != nil { if err != nil {
logrus.Fatal(err) return "", err
} }
for _, service := range services { for _, service := range services {
labelKey := fmt.Sprintf("coop-cloud.%s.%s", stackName, label) labelKey := fmt.Sprintf("coop-cloud.%s.%s", stackName, label)
if labelValue, ok := service.Spec.Labels[labelKey]; ok { if labelValue, ok := service.Spec.Labels[labelKey]; ok {
return labelValue return labelValue, nil
} }
} }
logrus.Debugf("no %s label found for %s", label, stackName) logrus.Debugf("no %s label found for %s", label, stackName)
return "" return "", nil
} }
func getBoolLabel(cl *dockerclient.Client, stackName string, label string) bool {
lableValue := getLabel(cl, stackName, label)
// getBoolLabel reads a boolean docker label // getBoolLabel reads a boolean docker label
func getBoolLabel(cl *dockerclient.Client, stackName string, label string) (bool, error) {
lableValue, err := getLabel(cl, stackName, label)
if err != nil {
return false, err
}
if lableValue != "" { if lableValue != "" {
value, err := strconv.ParseBool(lableValue) value, err := strconv.ParseBool(lableValue)
if err != nil { if err != nil {
logrus.Fatal(err) return false, err
} }
return value return value, nil
} }
return false logrus.Debugf("Boolean label %s could not be found for %s, set default to false.", label, stackName)
return false, nil
} }
func getEnv(cl *dockerclient.Client, stackName string) config.AppEnv {
// getEnv reads Env variables from docker services // getEnv reads Env variables from docker services
func getEnv(cl *dockerclient.Client, stackName string) (config.AppEnv, error) {
envMap := make(map[string]string) envMap := make(map[string]string)
filter := filters.NewArgs() filter := filters.NewArgs()
filter.Add("label", fmt.Sprintf("%s=%s", convert.LabelNamespace, stackName)) filter.Add("label", fmt.Sprintf("%s=%s", convert.LabelNamespace, stackName))
services, err := cl.ServiceList(context.Background(), types.ServiceListOptions{Filters: filter}) services, err := cl.ServiceList(context.Background(), types.ServiceListOptions{Filters: filter})
if err != nil { if err != nil {
logrus.Fatal(err) return nil, err
} }
for _, service := range services { for _, service := range services {
envList := service.Spec.TaskTemplate.ContainerSpec.Env envList := service.Spec.TaskTemplate.ContainerSpec.Env
@ -176,17 +186,23 @@ func getEnv(cl *dockerclient.Client, stackName string) config.AppEnv {
envMap[k] = v envMap[k] = v
} }
} }
return envMap return envMap, nil
} }
func getLatestUpgrade(cl *dockerclient.Client, stackName string, recipeName string) string {
deployedVersion := getDeployedVersion(cl, stackName, recipeName)
availableUpgrades := getAvailableUpgrades(cl, stackName, recipeName, deployedVersion)
// getLatestUpgrade returns the latest available version for an app regarding to the --major flag // getLatestUpgrade returns the latest available version for an app regarding to the --major flag
// if it is newer than the currently deployed version // if it is newer than the currently deployed version
func getLatestUpgrade(cl *dockerclient.Client, stackName string, recipeName string) (string, error) {
deployedVersion, err := getDeployedVersion(cl, stackName, recipeName)
if err != nil {
return "", err
}
availableUpgrades, err := getAvailableUpgrades(cl, stackName, recipeName, deployedVersion)
if err != nil {
return "", err
}
if len(availableUpgrades) == 0 { if len(availableUpgrades) == 0 {
logrus.Debugf("no available upgrades for %s", stackName) logrus.Debugf("no available upgrades for %s", stackName)
return "" return "", nil
} }
// Uncomment to select the next version instead of the last version // Uncomment to select the next version instead of the last version
// availableUpgrades = internal.ReverseStringList(availableUpgrades) // availableUpgrades = internal.ReverseStringList(availableUpgrades)
@ -195,42 +211,42 @@ func getLatestUpgrade(cl *dockerclient.Client, stackName string, recipeName stri
chosenUpgrade = availableUpgrades[len(availableUpgrades)-1] chosenUpgrade = availableUpgrades[len(availableUpgrades)-1]
logrus.Infof("%s (%s) can be upgraded from version %s to %s", stackName, recipeName, deployedVersion, chosenUpgrade) logrus.Infof("%s (%s) can be upgraded from version %s to %s", stackName, recipeName, deployedVersion, chosenUpgrade)
} }
return chosenUpgrade return chosenUpgrade, nil
} }
func getDeployedVersion(cl *dockerclient.Client, stackName string, recipeName string) string {
// getDeployedVersion returns the currently deployed version of an app // getDeployedVersion returns the currently deployed version of an app
func getDeployedVersion(cl *dockerclient.Client, stackName string, recipeName string) (string, error) {
logrus.Debugf("Retrieve deployed version whether %s is already deployed", stackName) logrus.Debugf("Retrieve deployed version whether %s is already deployed", stackName)
isDeployed, deployedVersion, err := stack.IsDeployed(context.Background(), cl, stackName) isDeployed, deployedVersion, err := stack.IsDeployed(context.Background(), cl, stackName)
if err != nil { if err != nil {
logrus.Fatal(err) return "", err
} }
if !isDeployed { if !isDeployed {
logrus.Fatalf("%s is not deployed?", stackName) return "", fmt.Errorf("%s is not deployed?", stackName)
} }
if deployedVersion == "unknown" { if deployedVersion == "unknown" {
logrus.Fatalf("failed to determine deployed version of %s", stackName) return "", fmt.Errorf("failed to determine deployed version of %s", stackName)
} }
return deployedVersion return deployedVersion, nil
} }
// getAvailableUpgrades returns all available versions of an app that are newer than // getAvailableUpgrades returns all available versions of an app that are newer than
// the deployed version. It only includes major steps if the --major flag is set. // the deployed version. It only includes major steps if the --major flag is set.
func getAvailableUpgrades(cl *dockerclient.Client, stackName string, recipeName string, func getAvailableUpgrades(cl *dockerclient.Client, stackName string, recipeName string,
deployedVersion string) []string { deployedVersion string) ([]string, error) {
catl, err := recipe.ReadRecipeCatalogue() catl, err := recipe.ReadRecipeCatalogue()
if err != nil { if err != nil {
logrus.Fatal(err) return nil, err
} }
versions, err := recipe.GetRecipeCatalogueVersions(recipeName, catl) versions, err := recipe.GetRecipeCatalogueVersions(recipeName, catl)
if err != nil { if err != nil {
logrus.Fatal(err) return nil, err
} }
if len(versions) == 0 { if len(versions) == 0 {
logrus.Fatalf("no published releases for %s in the recipe catalogue?", recipeName) return nil, fmt.Errorf("no published releases for %s in the recipe catalogue?", recipeName)
} }
var availableUpgrades []string var availableUpgrades []string
@ -238,15 +254,15 @@ func getAvailableUpgrades(cl *dockerclient.Client, stackName string, recipeName
for _, version := range versions { for _, version := range versions {
parsedDeployedVersion, err := tagcmp.Parse(deployedVersion) parsedDeployedVersion, err := tagcmp.Parse(deployedVersion)
if err != nil { if err != nil {
logrus.Fatal(err) return nil, err
} }
parsedVersion, err := tagcmp.Parse(version) parsedVersion, err := tagcmp.Parse(version)
if err != nil { if err != nil {
logrus.Fatal(err) return nil, err
} }
versionDelta, err := parsedDeployedVersion.UpgradeDelta(parsedVersion) versionDelta, err := parsedDeployedVersion.UpgradeDelta(parsedVersion)
if err != nil { if err != nil {
logrus.Fatal(err) return nil, err
} }
if 0 < versionDelta.UpgradeType() && (versionDelta.UpgradeType() < 4 || majorUpdate) { if 0 < versionDelta.UpgradeType() && (versionDelta.UpgradeType() < 4 || majorUpdate) {
availableUpgrades = append(availableUpgrades, version) availableUpgrades = append(availableUpgrades, version)
@ -254,87 +270,100 @@ func getAvailableUpgrades(cl *dockerclient.Client, stackName string, recipeName
} }
logrus.Debugf("Available updates for %s: %s", stackName, availableUpgrades) logrus.Debugf("Available updates for %s: %s", stackName, availableUpgrades)
return availableUpgrades return availableUpgrades, nil
} }
func processRecipeRepoVersion(recipeName string, version string) {
// processRecipeRepoVersion clones, pulls, checks out the version and lints the recipe repository // processRecipeRepoVersion clones, pulls, checks out the version and lints the recipe repository
func processRecipeRepoVersion(recipeName string, version string) error {
if err := recipe.EnsureExists(recipeName); err != nil { if err := recipe.EnsureExists(recipeName); err != nil {
logrus.Fatal(err) return err
} }
if err := recipe.EnsureUpToDate(recipeName); err != nil { if err := recipe.EnsureUpToDate(recipeName); err != nil {
logrus.Fatal(err) return err
} }
if err := recipe.EnsureVersion(recipeName, version); err != nil { if err := recipe.EnsureVersion(recipeName, version); err != nil {
logrus.Fatal(err) return err
} }
if r, err := recipe.Get(recipeName); err != nil { if r, err := recipe.Get(recipeName); err != nil {
logrus.Fatal(err) return err
} else if err := lint.LintForErrors(r); err != nil { } else if err := lint.LintForErrors(r); err != nil {
logrus.Fatal(err) return err
} }
return nil
} }
func mergeAbraShEnv(recipeName string, env config.AppEnv) {
// mergeAbraShEnv merges abra.sh env's into the app env's // mergeAbraShEnv merges abra.sh env's into the app env's
func mergeAbraShEnv(recipeName string, env config.AppEnv) error {
abraShPath := fmt.Sprintf("%s/%s/%s", config.RECIPES_DIR, recipeName, "abra.sh") abraShPath := fmt.Sprintf("%s/%s/%s", config.RECIPES_DIR, recipeName, "abra.sh")
abraShEnv, err := config.ReadAbraShEnvVars(abraShPath) abraShEnv, err := config.ReadAbraShEnvVars(abraShPath)
if err != nil { if err != nil {
logrus.Fatal(err) return err
} }
for k, v := range abraShEnv { for k, v := range abraShEnv {
logrus.Debugf("read v:%s k: %s", v, k) logrus.Debugf("read v:%s k: %s", v, k)
env[k] = v env[k] = v
} }
return nil
} }
func createDeployConfig(recipeName string, stackName string, env config.AppEnv) (*composetypes.Config, stack.Deploy) {
// createDeployConfig merges and enriches the compose config for the deployment // createDeployConfig merges and enriches the compose config for the deployment
func createDeployConfig(recipeName string, stackName string, env config.AppEnv) (*composetypes.Config, stack.Deploy, error) {
// Workaround, is there a better way? // Workaround, is there a better way?
env["STACK_NAME"] = stackName env["STACK_NAME"] = stackName
composeFiles, err := config.GetAppComposeFiles(recipeName, env)
if err != nil {
logrus.Fatal(err)
}
deployOpts := stack.Deploy{ deployOpts := stack.Deploy{
Composefiles: composeFiles,
Namespace: stackName, Namespace: stackName,
Prune: false, Prune: false,
ResolveImage: stack.ResolveImageAlways, ResolveImage: stack.ResolveImageAlways,
} }
composeFiles, err := config.GetAppComposeFiles(recipeName, env)
if err != nil {
return nil, deployOpts, err
}
deployOpts.Composefiles = composeFiles
compose, err := config.GetAppComposeConfig(stackName, deployOpts, env) compose, err := config.GetAppComposeConfig(stackName, deployOpts, env)
if err != nil { if err != nil {
logrus.Fatal(err) return nil, deployOpts, err
} }
config.ExposeAllEnv(stackName, compose, env) config.ExposeAllEnv(stackName, compose, env)
// after the upgrade the deployment won't be in chaos state anymore // after the upgrade the deployment won't be in chaos state anymore
config.SetChaosLabel(compose, stackName, false) config.SetChaosLabel(compose, stackName, false)
config.SetRecipeLabel(compose, stackName, recipeName) config.SetRecipeLabel(compose, stackName, recipeName)
config.SetUpdateLabel(compose, stackName, env) config.SetUpdateLabel(compose, stackName, env)
return compose, deployOpts return compose, deployOpts, nil
}
} }
func upgrade(cl *dockerclient.Client, stackName string, recipeName string, upgradeVersion string) {
// upgrade performs all necessary steps to upgrade an app // upgrade performs all necessary steps to upgrade an app
func upgrade(cl *dockerclient.Client, stackName string, recipeName string, upgradeVersion string) error {
env, err := getEnv(cl, stackName)
if err != nil {
return err
}
app := config.App{ app := config.App{
Name: stackName, Name: stackName,
Recipe: recipeName, Recipe: recipeName,
Env: getEnv(cl, stackName),
Server: SERVER, Server: SERVER,
Env: env,
} }
processRecipeRepoVersion(recipeName, upgradeVersion) if err = processRecipeRepoVersion(recipeName, upgradeVersion); err != nil {
return err
}
mergeAbraShEnv(recipeName, app.Env) if err = mergeAbraShEnv(recipeName, app.Env); err != nil {
return err
}
compose, deployOpts := createDeployConfig(recipeName, stackName, app.Env) compose, deployOpts, err := createDeployConfig(recipeName, stackName, app.Env)
if err != nil {
return err
}
logrus.Infof("Upgrade %s (%s) to version %s", stackName, recipeName, upgradeVersion) logrus.Infof("Upgrade %s (%s) to version %s", stackName, recipeName, upgradeVersion)
if err := stack.RunDeploy(cl, deployOpts, compose, stackName, true); err != nil { err = stack.RunDeploy(cl, deployOpts, compose, stackName, true)
logrus.Fatal(err) return err
}
} }
func newAbraApp(version, commit string) *cli.App { func newAbraApp(version, commit string) *cli.App {