forked from toolshed/abra
		
	refactor: don't reinvent the wheel
This commit is contained in:
		| @ -2,19 +2,19 @@ package deploy | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"errors" |  | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"regexp" |  | ||||||
| 	"sort" | 	"sort" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	appPkg "coopcloud.tech/abra/pkg/app" | 	appPkg "coopcloud.tech/abra/pkg/app" | ||||||
| 	"coopcloud.tech/abra/pkg/client" | 	"coopcloud.tech/abra/pkg/client" | ||||||
| 	"coopcloud.tech/abra/pkg/envfile" | 	"coopcloud.tech/abra/pkg/envfile" | ||||||
|  | 	"coopcloud.tech/abra/pkg/formatter" | ||||||
| 	"coopcloud.tech/abra/pkg/log" | 	"coopcloud.tech/abra/pkg/log" | ||||||
| 	"coopcloud.tech/abra/pkg/recipe" | 	"coopcloud.tech/abra/pkg/recipe" | ||||||
| 	"coopcloud.tech/abra/pkg/secret" | 	"coopcloud.tech/abra/pkg/secret" | ||||||
|  |  | ||||||
|  | 	"github.com/distribution/reference" | ||||||
| 	composetypes "github.com/docker/cli/cli/compose/types" | 	composetypes "github.com/docker/cli/cli/compose/types" | ||||||
| 	"github.com/docker/docker/api/types/swarm" | 	"github.com/docker/docker/api/types/swarm" | ||||||
| 	dockerClient "github.com/docker/docker/client" | 	dockerClient "github.com/docker/docker/client" | ||||||
| @ -82,19 +82,6 @@ func GetConfigsForStack(cl *dockerClient.Client, app appPkg.App) (map[string]str | |||||||
| 	return configs, nil | 	return configs, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| 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. | // GetImagesForStack retrieves all Docker images for services in a given stack. | ||||||
| func GetImagesForStack(cl *dockerClient.Client, app appPkg.App) (map[string]string, error) { | func GetImagesForStack(cl *dockerClient.Client, app appPkg.App) (map[string]string, error) { | ||||||
| 	filters, err := app.Filters(false, false) | 	filters, err := app.Filters(false, false) | ||||||
| @ -116,12 +103,15 @@ func GetImagesForStack(cl *dockerClient.Client, app appPkg.App) (map[string]stri | |||||||
| 		if service.Spec.TaskTemplate.ContainerSpec != nil { | 		if service.Spec.TaskTemplate.ContainerSpec != nil { | ||||||
| 			imageName := service.Spec.TaskTemplate.ContainerSpec.Image | 			imageName := service.Spec.TaskTemplate.ContainerSpec.Image | ||||||
|  |  | ||||||
| 			imageBaseName, imageTag, err := GetImageNameAndTag(imageName) | 			imageParsed, err := reference.ParseNormalizedNamed(imageName) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Warn(err) | 				log.Warn(err) | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | 			imageBaseName := reference.Path(imageParsed) | ||||||
|  | 			imageTag := imageParsed.(reference.NamedTagged).Tag() | ||||||
|  |  | ||||||
| 			existingImageVersion, ok := images[imageBaseName] | 			existingImageVersion, ok := images[imageBaseName] | ||||||
| 			if !ok { | 			if !ok { | ||||||
| 				// First time seeing this, add to map | 				// First time seeing this, add to map | ||||||
| @ -210,7 +200,10 @@ func GatherImagesForDeploy(cl *dockerClient.Client, app appPkg.App, compose *com | |||||||
| 	newImages := make(map[string]string) | 	newImages := make(map[string]string) | ||||||
|  |  | ||||||
| 	for _, service := range compose.Services { | 	for _, service := range compose.Services { | ||||||
| 		imageBaseName, imageTag, err := GetImageNameAndTag(service.Image) | 		imageParsed, err := reference.ParseNormalizedNamed(service.Image) | ||||||
|  | 		imageBaseName := reference.Path(imageParsed) | ||||||
|  | 		imageTag := imageParsed.(reference.NamedTagged).Tag() | ||||||
|  |  | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Warn(err) | 			log.Warn(err) | ||||||
| 			continue | 			continue | ||||||
| @ -233,13 +226,13 @@ func GatherImagesForDeploy(cl *dockerClient.Client, app appPkg.App, compose *com | |||||||
| 		if currentVersion, exists := currentImages[newImageName]; exists { | 		if currentVersion, exists := currentImages[newImageName]; exists { | ||||||
| 			if currentVersion == newImageVersion { | 			if currentVersion == newImageVersion { | ||||||
| 				if showUnchanged { | 				if showUnchanged { | ||||||
| 					imageInfo = append(imageInfo, fmt.Sprintf("%s: %s (unchanged)", newImageName, newImageVersion)) | 					imageInfo = append(imageInfo, fmt.Sprintf("%s: %s (unchanged)", formatter.StripTagMeta(newImageName), newImageVersion)) | ||||||
| 				} | 				} | ||||||
| 			} else { | 			} else { | ||||||
| 				imageInfo = append(imageInfo, fmt.Sprintf("%s: %s → %s", newImageName, currentVersion, newImageVersion)) | 				imageInfo = append(imageInfo, fmt.Sprintf("%s: %s → %s", formatter.StripTagMeta(newImageName), currentVersion, newImageVersion)) | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			imageInfo = append(imageInfo, fmt.Sprintf("%s: %s (new)", newImageName, newImageVersion)) | 			imageInfo = append(imageInfo, fmt.Sprintf("%s: %s (new)", formatter.StripTagMeta(newImageName), newImageVersion)) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,87 +0,0 @@ | |||||||
| package deploy |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"testing" |  | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func TestGetImageNameAndTag(t *testing.T) { |  | ||||||
| 	tests := []struct { |  | ||||||
| 		name         string |  | ||||||
| 		imageName    string |  | ||||||
| 		expectedName string |  | ||||||
| 		expectedTag  string |  | ||||||
| 		expectError  bool |  | ||||||
| 		description  string |  | ||||||
| 	}{ |  | ||||||
| 		{ |  | ||||||
| 			name:         "standard image with tag", |  | ||||||
| 			imageName:    "nginx:1.23", |  | ||||||
| 			expectedName: "nginx", |  | ||||||
| 			expectedTag:  "1.23", |  | ||||||
| 			expectError:  false, |  | ||||||
| 			description:  "should parse standard image name with tag", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name:         "image with digest", |  | ||||||
| 			imageName:    "nginx:1.23@sha256:abc123", |  | ||||||
| 			expectedName: "nginx", |  | ||||||
| 			expectedTag:  "1.23", |  | ||||||
| 			expectError:  false, |  | ||||||
| 			description:  "should parse image with digest, ignoring digest part", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name:         "image with latest tag", |  | ||||||
| 			imageName:    "redis:latest", |  | ||||||
| 			expectedName: "redis", |  | ||||||
| 			expectedTag:  "latest", |  | ||||||
| 			expectError:  false, |  | ||||||
| 			description:  "should parse image with latest tag", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name:         "image with numeric tag", |  | ||||||
| 			imageName:    "postgres:14", |  | ||||||
| 			expectedName: "postgres", |  | ||||||
| 			expectedTag:  "14", |  | ||||||
| 			expectError:  false, |  | ||||||
| 			description:  "should parse image with numeric tag", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name:         "image with complex name", |  | ||||||
| 			imageName:    "registry.example.com/myapp/api:v1.2.3", |  | ||||||
| 			expectedName: "registry.example.com/myapp/api", |  | ||||||
| 			expectedTag:  "v1.2.3", |  | ||||||
| 			expectError:  false, |  | ||||||
| 			description:  "should parse image with registry prefix and complex name", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name:        "image without tag", |  | ||||||
| 			imageName:   "nginx", |  | ||||||
| 			expectError: true, |  | ||||||
| 			description: "should error when no tag present", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name:        "empty image name", |  | ||||||
| 			imageName:   "", |  | ||||||
| 			expectError: true, |  | ||||||
| 			description: "should error on empty image name", |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, tt := range tests { |  | ||||||
| 		t.Run(tt.name, func(t *testing.T) { |  | ||||||
| 			name, tag, err := GetImageNameAndTag(tt.imageName) |  | ||||||
|  |  | ||||||
| 			if tt.expectError { |  | ||||||
| 				assert.Error(t, err) |  | ||||||
| 				assert.Empty(t, name) |  | ||||||
| 				assert.Empty(t, tag) |  | ||||||
| 			} else { |  | ||||||
| 				assert.NoError(t, err) |  | ||||||
| 				assert.Equal(t, tt.expectedName, name) |  | ||||||
| 				assert.Equal(t, tt.expectedTag, tag) |  | ||||||
| 			} |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
		Reference in New Issue
	
	Block a user