Show image differences in pre-deploy overview
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
@ -3,7 +3,6 @@ package app
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
@ -202,10 +201,9 @@ checkout as-is. Recipe commit hashes are also supported as values for
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Gather images
|
// Gather images
|
||||||
|
imageInfo, err := deploy.GatherImagesForDeploy(cl, app, compose)
|
||||||
var imageInfo []string
|
if err != nil {
|
||||||
for _, service := range compose.Services {
|
log.Fatal(err)
|
||||||
imageInfo = append(imageInfo, fmt.Sprintf("%s: %s", service.Name, service.Image))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show deploy overview
|
// Show deploy overview
|
||||||
|
@ -39,10 +39,11 @@ func RemoveConfigs(cl *client.Client, ctx context.Context, configNames []string,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetConfigNameAndVersion(fullName string, stackName string) (string, string) {
|
func GetConfigNameAndVersion(fullName string, stackName string) (string, string, error) {
|
||||||
name := strings.TrimPrefix(fullName, stackName + "_")
|
name := strings.TrimPrefix(fullName, stackName + "_")
|
||||||
if lastUnderscore := strings.LastIndex(name, "_"); lastUnderscore != -1 {
|
if lastUnderscore := strings.LastIndex(name, "_"); lastUnderscore != -1 {
|
||||||
return name[0:lastUnderscore], name[lastUnderscore+1:]
|
return name[0:lastUnderscore], name[lastUnderscore+1:], nil
|
||||||
|
} else {
|
||||||
|
return "", "", errors.New(i18n.G("can't parse version from config '%s'", fullName))
|
||||||
}
|
}
|
||||||
return name, ""
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,9 @@ package deploy
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -16,8 +18,8 @@ import (
|
|||||||
dockerClient "github.com/docker/docker/client"
|
dockerClient "github.com/docker/docker/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetConfigNamesForStack retrieves all Docker configs attached to services in a given stack.
|
// GetConfigsForStack retrieves all Docker configs attached to services in a given stack.
|
||||||
func GetConfigNamesForStack(cl *dockerClient.Client, app appPkg.App) ([]string, error) {
|
func GetConfigsForStack(cl *dockerClient.Client, app appPkg.App) (map[string]string, error) {
|
||||||
filters, err := app.Filters(false, false)
|
filters, err := app.Filters(false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -31,25 +33,92 @@ func GetConfigNamesForStack(cl *dockerClient.Client, app appPkg.App) ([]string,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect unique config names from all services
|
// Collect unique config names with versions
|
||||||
configNames := make(map[string]bool)
|
configs := make(map[string]string)
|
||||||
for _, service := range services {
|
for _, service := range services {
|
||||||
if service.Spec.TaskTemplate.ContainerSpec != nil {
|
if service.Spec.TaskTemplate.ContainerSpec != nil {
|
||||||
for _, configRef := range service.Spec.TaskTemplate.ContainerSpec.Configs {
|
for _, configRef := range service.Spec.TaskTemplate.ContainerSpec.Configs {
|
||||||
if configRef.ConfigName != "" {
|
configName := configRef.ConfigName
|
||||||
configNames[configRef.ConfigName] = true
|
if configName == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
configBaseName, configVersion, err := client.GetConfigNameAndVersion(configName, app.StackName())
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
existingConfigVersion, ok := configs[configBaseName]
|
||||||
|
if !ok {
|
||||||
|
// First time seeing this, add to map
|
||||||
|
configs[configBaseName] = configVersion
|
||||||
|
} else {
|
||||||
|
// Just make sure the versions are the same..
|
||||||
|
if existingConfigVersion != configVersion {
|
||||||
|
log.Warnf("different versions for config '%s', '%s' and %s'", configBaseName, existingConfigVersion, configVersion)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert map to slice
|
return configs, nil
|
||||||
result := make([]string, 0, len(configNames))
|
}
|
||||||
for name := range configNames {
|
|
||||||
result = append(result, name)
|
func GetImageNameAndTag(imageName string) (string, string, error) {
|
||||||
|
imageParts := regexp.MustCompile("^([^:]*):([^@]*)@?").FindSubmatch([]byte(imageName))
|
||||||
|
|
||||||
|
if len(imageParts) == 0 {
|
||||||
|
return "", "", errors.New("can't determine image version for image '%s'")
|
||||||
|
}
|
||||||
|
|
||||||
|
imageBaseName := string(imageParts[1])
|
||||||
|
imageTag := string(imageParts[2])
|
||||||
|
|
||||||
|
return imageBaseName, imageTag, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetImagesForStack retrieves all Docker images for services in a given stack.
|
||||||
|
func GetImagesForStack(cl *dockerClient.Client, app appPkg.App) (map[string]string, error) {
|
||||||
|
filters, err := app.Filters(false, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
// List all services in the stack
|
||||||
|
services, err := cl.ServiceList(context.Background(), swarm.ServiceListOptions{
|
||||||
|
Filters: filters,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect unique image names with versions
|
||||||
|
images := make(map[string]string)
|
||||||
|
for _, service := range services {
|
||||||
|
if service.Spec.TaskTemplate.ContainerSpec != nil {
|
||||||
|
imageName := service.Spec.TaskTemplate.ContainerSpec.Image
|
||||||
|
|
||||||
|
imageBaseName, imageTag, err := GetImageNameAndTag(imageName)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
existingImageVersion, ok := images[imageBaseName]
|
||||||
|
if !ok {
|
||||||
|
// First time seeing this, add to map
|
||||||
|
images[imageBaseName] = imageTag
|
||||||
|
} else {
|
||||||
|
// Just make sure the versions are the same..
|
||||||
|
if existingImageVersion != imageTag {
|
||||||
|
log.Warnf("different versions for image '%s', '%s' and %s'", imageBaseName, existingImageVersion, imageTag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return images, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GatherSecretsForDeploy(cl *dockerClient.Client, app appPkg.App) ([]string, error) {
|
func GatherSecretsForDeploy(cl *dockerClient.Client, app appPkg.App) ([]string, error) {
|
||||||
@ -73,21 +142,12 @@ func GatherSecretsForDeploy(cl *dockerClient.Client, app appPkg.App) ([]string,
|
|||||||
|
|
||||||
func GatherConfigsForDeploy(cl *dockerClient.Client, app appPkg.App, compose *composetypes.Config, abraShEnv map[string]string) ([]string, error) {
|
func GatherConfigsForDeploy(cl *dockerClient.Client, app appPkg.App, compose *composetypes.Config, abraShEnv map[string]string) ([]string, error) {
|
||||||
// Get current configs from existing deployment
|
// Get current configs from existing deployment
|
||||||
currentConfigNames, err := GetConfigNamesForStack(cl, app)
|
currentConfigs, err := GetConfigsForStack(cl, app)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Config names: %v", currentConfigNames)
|
log.Debugf("Deployed config names: %v", currentConfigs)
|
||||||
|
|
||||||
// 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
|
// Get new configs from the compose specification
|
||||||
newConfigs := compose.Configs
|
newConfigs := compose.Configs
|
||||||
@ -116,3 +176,51 @@ func GatherConfigsForDeploy(cl *dockerClient.Client, app appPkg.App, compose *co
|
|||||||
|
|
||||||
return configInfo, nil
|
return configInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GatherImagesForDeploy(cl *dockerClient.Client, app appPkg.App, compose *composetypes.Config) ([]string, error){
|
||||||
|
|
||||||
|
// Get current images from existing deployment
|
||||||
|
currentImages, err := GetImagesForStack(cl, app)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("Deployed images: %v", currentImages)
|
||||||
|
|
||||||
|
// Proposed new images from the compose files
|
||||||
|
newImages := make(map[string]string)
|
||||||
|
|
||||||
|
for _, service := range compose.Services {
|
||||||
|
imageBaseName, imageTag, err := GetImageNameAndTag(service.Image)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
existingImageVersion, ok := newImages[imageBaseName]
|
||||||
|
if !ok {
|
||||||
|
// First time seeing this, add to map
|
||||||
|
newImages[imageBaseName] = imageTag
|
||||||
|
} else {
|
||||||
|
// Just make sure the versions are the same..
|
||||||
|
if existingImageVersion != imageTag {
|
||||||
|
log.Warnf("different versions for image '%s', '%s' and %s'", imageBaseName, existingImageVersion, imageTag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Infof("Proposed images: %v", newImages)
|
||||||
|
|
||||||
|
var imageInfo []string
|
||||||
|
for newImageName, newImageVersion := range newImages {
|
||||||
|
if currentVersion, exists := currentImages[newImageName]; exists {
|
||||||
|
if currentVersion == newImageVersion {
|
||||||
|
imageInfo = append(imageInfo, fmt.Sprintf("%s: %s (unchanged)", newImageName, newImageVersion))
|
||||||
|
} else {
|
||||||
|
imageInfo = append(imageInfo, fmt.Sprintf("%s: %s → %s", newImageName, currentVersion, newImageVersion))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
imageInfo = append(imageInfo, fmt.Sprintf("%s: %s (new)", newImageName, newImageVersion))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return imageInfo, nil
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user