forked from toolshed/abra
		
	feat: show proposed secret version changes during deploy
This commit is contained in:
		@ -2,12 +2,11 @@ package deploy
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	appPkg "coopcloud.tech/abra/pkg/app"
 | 
			
		||||
	"coopcloud.tech/abra/pkg/client"
 | 
			
		||||
	"coopcloud.tech/abra/pkg/envfile"
 | 
			
		||||
	"coopcloud.tech/abra/pkg/formatter"
 | 
			
		||||
	"coopcloud.tech/abra/pkg/i18n"
 | 
			
		||||
@ -36,6 +35,61 @@ func MergeAbraShEnv(recipe recipe.Recipe, env envfile.AppEnv) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetEntityNameAndVersion parses a full config name like `app_example_com_someconf_v1` to extract name and version, ("someconf", "v1")
 | 
			
		||||
func GetEntityNameAndVersion(fullName string, stackName string) (string, string, error) {
 | 
			
		||||
	name := strings.TrimPrefix(fullName, stackName+"_")
 | 
			
		||||
	if lastUnderscore := strings.LastIndex(name, "_"); lastUnderscore != -1 {
 | 
			
		||||
		return name[0:lastUnderscore], name[lastUnderscore+1:], nil
 | 
			
		||||
	}
 | 
			
		||||
	return "", "", errors.New(i18n.G("can't parse version from '%s'", fullName))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetSecretsForStack(cl *dockerClient.Client, app appPkg.App) (map[string]string, error) {
 | 
			
		||||
	filters, err := app.Filters(false, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// List all services in the stack
 | 
			
		||||
	// NOTE: we could do cl.SecretList, but we want to know which secrets are actually attached
 | 
			
		||||
	services, err := cl.ServiceList(context.Background(), swarm.ServiceListOptions{
 | 
			
		||||
		Filters: filters,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	secrets := make(map[string]string)
 | 
			
		||||
 | 
			
		||||
	for _, service := range services {
 | 
			
		||||
		if service.Spec.TaskTemplate.ContainerSpec.Secrets != nil {
 | 
			
		||||
			for _, secretRef := range service.Spec.TaskTemplate.ContainerSpec.Secrets {
 | 
			
		||||
				secretName := secretRef.SecretName
 | 
			
		||||
				if secretName == "" {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				secretBaseName, secretVersion, err := GetEntityNameAndVersion(secretName, app.StackName())
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					log.Warn(err)
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				existingSecretVersion, exists := secrets[secretBaseName]
 | 
			
		||||
				if !exists {
 | 
			
		||||
					// First time seeing this, add to map
 | 
			
		||||
					secrets[secretBaseName] = secretVersion
 | 
			
		||||
				} else {
 | 
			
		||||
					// Just make sure the versions are the same..
 | 
			
		||||
					if existingSecretVersion != secretVersion {
 | 
			
		||||
						log.Warnf(i18n.G("different versions for secret '%s', '%s' and %s'", secretBaseName, existingSecretVersion, secretVersion))
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return secrets, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetConfigsForStack retrieves all Docker configs attached to services in a given stack.
 | 
			
		||||
func GetConfigsForStack(cl *dockerClient.Client, app appPkg.App) (map[string]string, error) {
 | 
			
		||||
	filters, err := app.Filters(false, false)
 | 
			
		||||
@ -60,7 +114,7 @@ func GetConfigsForStack(cl *dockerClient.Client, app appPkg.App) (map[string]str
 | 
			
		||||
				if configName == "" {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				configBaseName, configVersion, err := client.GetConfigNameAndVersion(configName, app.StackName())
 | 
			
		||||
				configBaseName, configVersion, err := GetEntityNameAndVersion(configName, app.StackName())
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					log.Warn(err)
 | 
			
		||||
					continue
 | 
			
		||||
@ -129,21 +183,41 @@ func GetImagesForStack(cl *dockerClient.Client, app appPkg.App) (map[string]stri
 | 
			
		||||
	return images, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GatherSecretsForDeploy(cl *dockerClient.Client, app appPkg.App) ([]string, error) {
 | 
			
		||||
	secStats, err := secret.PollSecretsStatus(cl, app)
 | 
			
		||||
func GatherSecretsForDeploy(cl *dockerClient.Client, app appPkg.App, showUnchanged bool) ([]string, error) {
 | 
			
		||||
	// Get current secrets from existing deployment
 | 
			
		||||
	currentSecrets, err := GetSecretsForStack(cl, app)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var secretInfo []string
 | 
			
		||||
	log.Debugf("current secrets: %v", currentSecrets)
 | 
			
		||||
 | 
			
		||||
	newSecrets, err := secret.PollSecretsStatus(cl, app)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Sort secrets to ensure reproducible output
 | 
			
		||||
	sort.Slice(secStats, func(i, j int) bool {
 | 
			
		||||
		return secStats[i].LocalName < secStats[j].LocalName
 | 
			
		||||
	sort.Slice(newSecrets, func(i, j int) bool {
 | 
			
		||||
		return newSecrets[i].LocalName < newSecrets[j].LocalName
 | 
			
		||||
	})
 | 
			
		||||
	for _, secStat := range secStats {
 | 
			
		||||
		secretInfo = append(secretInfo, fmt.Sprintf("%s: %s", secStat.LocalName, secStat.Version))
 | 
			
		||||
 | 
			
		||||
	var secretInfo []string
 | 
			
		||||
 | 
			
		||||
	for _, newSecret := range newSecrets {
 | 
			
		||||
		if currentVersion, exists := currentSecrets[newSecret.LocalName]; exists {
 | 
			
		||||
			if currentVersion == newSecret.Version {
 | 
			
		||||
				if showUnchanged {
 | 
			
		||||
					secretInfo = append(secretInfo, i18n.G("%s: %s (unchanged)", newSecret.LocalName, newSecret.Version))
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				secretInfo = append(secretInfo, i18n.G("%s: %s → %s", newSecret.LocalName, currentVersion, newSecret.Version))
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			secretInfo = append(secretInfo, i18n.G("%s: %s (new)", newSecret.LocalName, newSecret.Version))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return secretInfo, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -200,13 +274,14 @@ func GatherImagesForDeploy(cl *dockerClient.Client, app appPkg.App, compose *com
 | 
			
		||||
 | 
			
		||||
	for _, service := range compose.Services {
 | 
			
		||||
		imageParsed, err := reference.ParseNormalizedNamed(service.Image)
 | 
			
		||||
		imageBaseName := reference.Path(imageParsed)
 | 
			
		||||
		imageTag := imageParsed.(reference.NamedTagged).Tag()
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Warn(err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		imageBaseName := reference.Path(imageParsed)
 | 
			
		||||
		imageTag := imageParsed.(reference.NamedTagged).Tag()
 | 
			
		||||
 | 
			
		||||
		existingImageVersion, ok := newImages[imageBaseName]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			// First time seeing this, add to map
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user