diff --git a/src/cli/deploy.go b/src/cli/deploy.go index 134ad05..7e3e6d5 100644 --- a/src/cli/deploy.go +++ b/src/cli/deploy.go @@ -20,6 +20,12 @@ func (c *icli) Deploy(cCtx *cli.Context) error { return err } + manifestDigest, err := c.DockerService.GetManifestDigest() + if err != nil { + return err + } + c.dockerImage.ManifestDigest = manifestDigest + return knative.Apply(cCtx.String(namespaceFlag), config, project, c.dockerImage) } diff --git a/src/services/docker/docker-image.go b/src/services/docker/docker-image.go index 407e1a6..c913470 100644 --- a/src/services/docker/docker-image.go +++ b/src/services/docker/docker-image.go @@ -6,14 +6,19 @@ import ( ) type DockerImage struct { - Registry string - Name string - Directory string - Tag string + Registry string + Name string + Directory string + Tag string + ManifestDigest string } func (dockerImage DockerImage) RemoteTag() string { - return fmt.Sprintf("%s/%s:%s", strings.ToLower(dockerImage.Registry), dockerImage.Name, dockerImage.Tag) + if dockerImage.ManifestDigest != "" { + return fmt.Sprintf("%s/%s:%s@%s", strings.ToLower(dockerImage.Registry), dockerImage.Name, dockerImage.Tag, dockerImage.ManifestDigest) + } else { + return fmt.Sprintf("%s/%s:%s", strings.ToLower(dockerImage.Registry), dockerImage.Name, dockerImage.Tag) + } } func (dockerImage DockerImage) LocalTag() string { diff --git a/src/services/docker/docker.go b/src/services/docker/docker.go index 7af5280..4da5bec 100644 --- a/src/services/docker/docker.go +++ b/src/services/docker/docker.go @@ -183,3 +183,24 @@ func (ds DockerService) Push() error { return logger.PrintStream(pushResponse) } + +func (ds DockerService) GetManifestDigest() (string, error) { + logger.PrintInfo("Getting manifest digest for " + ds.dockerImage.RemoteTag()) + logger.PrintInfo("User: " + ds.AuthConfig.Username) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*120) + defer cancel() + + encodedJSON, err := json.Marshal(ds.AuthConfig) + if err != nil { + return "", err + } + + encodedRegistryAuth := base64.URLEncoding.EncodeToString(encodedJSON) + distributionInspect, err := ds.Client.DistributionInspect(ctx, ds.dockerImage.RemoteTag(), encodedRegistryAuth) + if err != nil { + return "", err + } + + digest := distributionInspect.Descriptor.Digest + return fmt.Sprintf("%s", digest), nil +} diff --git a/src/services/docker/docker_test.go b/src/services/docker/docker_test.go index 2439d18..8b64489 100644 --- a/src/services/docker/docker_test.go +++ b/src/services/docker/docker_test.go @@ -30,6 +30,31 @@ func TestLocalTag(t *testing.T) { } func TestRemoteTag(t *testing.T) { + dockerImage := DockerImage{ + Registry: "example.org", + Directory: defaults.ProjectDirectory, + Name: "test", + Tag: "v1.1.0", + ManifestDigest: "sha256:3556949350924148437d1c40a2bfd74335fcd045da8b298e4ec4b6d5c53b4dc8", + } + + ds := DockerService{ + project: project.Project{ + Directory: defaults.ProjectDirectory, + Name: "test", + Version: "v1.1.0", + }, + dockerImage: dockerImage, + } + + remoteTag := ds.dockerImage.RemoteTag() + expected := "example.org/test:v1.1.0@sha256:3556949350924148437d1c40a2bfd74335fcd045da8b298e4ec4b6d5c53b4dc8" + if remoteTag != expected { + t.Fatalf("Expected '%s' got %s", expected, remoteTag) + } +} + +func TestRemoteTagMissingDigest(t *testing.T) { dockerImage := DockerImage{ Registry: "example.org", Directory: defaults.ProjectDirectory, diff --git a/src/services/k8s/knative_test.go b/src/services/k8s/knative_test.go index 8ba5320..21b6ab8 100644 --- a/src/services/k8s/knative_test.go +++ b/src/services/k8s/knative_test.go @@ -60,10 +60,11 @@ func TestLoadManifest(t *testing.T) { } docker_image := docker.DockerImage{ - Registry: "example.com", - Directory: ".", - Name: "test", - Tag: "v1.1.0", + Registry: "example.com", + Directory: ".", + Name: "test", + Tag: "v1.1.0", + ManifestDigest: "sha256:3556949350924148437d1c40a2bfd74335fcd045da8b298e4ec4b6d5c53b4dc8", } _, err := loadManifest(proj_knative, docker_image)