package deploy import ( "context" "fmt" "sort" "strings" appPkg "coopcloud.tech/abra/pkg/app" "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/secret" "github.com/docker/docker/api/types/swarm" composetypes "github.com/docker/cli/cli/compose/types" dockerClient "github.com/docker/docker/client" ) // GetConfigNamesForStack retrieves all Docker configs attached to services in a given stack. func GetConfigNamesForStack(cl *dockerClient.Client, app appPkg.App) ([]string, error) { filters, err := app.Filters(false, false) if err != nil { return nil, err } // List all services in the stack services, err := cl.ServiceList(context.Background(), swarm.ServiceListOptions{ Filters: filters, }) if err != nil { return nil, err } // Collect unique config names from all services configNames := make(map[string]bool) for _, service := range services { if service.Spec.TaskTemplate.ContainerSpec != nil { for _, configRef := range service.Spec.TaskTemplate.ContainerSpec.Configs { if configRef.ConfigName != "" { configNames[configRef.ConfigName] = true } } } } // Convert map to slice result := make([]string, 0, len(configNames)) for name := range configNames { result = append(result, name) } return result, nil } func GatherSecretsForDeploy(cl *dockerClient.Client, app appPkg.App) ([]string, error) { secStats, err := secret.PollSecretsStatus(cl, app) if err != nil { return nil, err } var secretInfo []string // Sort secrets to ensure reproducible output sort.Slice(secStats, func(i, j int) bool { return secStats[i].LocalName < secStats[j].LocalName }) for _, secStat := range secStats { secretInfo = append(secretInfo, fmt.Sprintf("%s: %s", secStat.LocalName, secStat.Version)) } return secretInfo, nil } func GatherConfigsForDeploy(cl *dockerClient.Client, app appPkg.App, compose *composetypes.Config, abraShEnv map[string]string) ([]string, error) { // Get current configs from existing deployment currentConfigNames, err := GetConfigNamesForStack(cl, app) if err != nil { return nil, err } log.Infof("Config names: %v", currentConfigNames) // Create map of current config base names to versions currentConfigs := make(map[string]string) for _, configName := range currentConfigNames { baseName, version := client.GetConfigNameAndVersion(configName, app.StackName()) currentConfigs[baseName] = version } log.Infof("Configs: %v", currentConfigs) // Get new configs from the compose specification newConfigs := compose.Configs var configInfo []string for configName := range newConfigs { log.Debugf("Searching abra.sh for version for %s", configName) versionKey := strings.ToUpper(configName) + "_VERSION" newVersion, exists := abraShEnv[versionKey] if !exists { log.Warnf("No version found for config %s", configName) configInfo = append(configInfo, fmt.Sprintf("%s: ? (missing version)", configName)) continue } if currentVersion, exists := currentConfigs[configName]; exists { if currentVersion == newVersion { configInfo = append(configInfo, fmt.Sprintf("%s: %s (unchanged)", configName, newVersion)) } else { configInfo = append(configInfo, fmt.Sprintf("%s: %s → %s", configName, currentVersion, newVersion)) } } else { configInfo = append(configInfo, fmt.Sprintf("%s: %s (new)", configName, newVersion)) } } return configInfo, nil }