From 2ce4cceb6f8ee3ed3814a6e2c5f99241d430e556 Mon Sep 17 00:00:00 2001 From: Tomasz Zaremba Date: Wed, 27 Sep 2023 09:05:07 +0200 Subject: [PATCH] fix: pass manifest digest to ensure new docker image download --- assets/knative/service.yaml.tmpl | 4 ++-- src/cli/deploy.go | 7 ++++++- src/services/docker/docker.go | 21 +++++++++++++++++++ src/services/k8s/knative.go | 18 ++++++++++++---- src/services/k8s/knative_test.go | 36 +++++++++++++++++++++++++------- 5 files changed, 71 insertions(+), 15 deletions(-) diff --git a/assets/knative/service.yaml.tmpl b/assets/knative/service.yaml.tmpl index 8c3abab..84b74c7 100644 --- a/assets/knative/service.yaml.tmpl +++ b/assets/knative/service.yaml.tmpl @@ -1,11 +1,11 @@ apiVersion: serving.knative.dev/v1 kind: Service metadata: - name: {{ .Name }} + name: {{ .DockerImage.Name }} namespace: default spec: template: spec: containers: - - image: {{ .RemoteTag }} + - image: {{ .DockerImage.RemoteTag }}@{{ .ImageDigest }} imagePullPolicy: Always diff --git a/src/cli/deploy.go b/src/cli/deploy.go index 134ad05..fe24f42 100644 --- a/src/cli/deploy.go +++ b/src/cli/deploy.go @@ -20,7 +20,12 @@ func (c *icli) Deploy(cCtx *cli.Context) error { return err } - return knative.Apply(cCtx.String(namespaceFlag), config, project, c.dockerImage) + imageDigest, err := c.DockerService.GetImageDigest() + if err != nil { + return err + } + + return knative.Apply(cCtx.String(namespaceFlag), config, project, c.dockerImage, imageDigest) } func (c icli) DeployCMD() *cli.Command { diff --git a/src/services/docker/docker.go b/src/services/docker/docker.go index 7af5280..8dba39a 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) GetImageDigest() (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/k8s/knative.go b/src/services/k8s/knative.go index 744dd6f..3951ac5 100644 --- a/src/services/k8s/knative.go +++ b/src/services/k8s/knative.go @@ -23,6 +23,11 @@ import ( servingv1client "knative.dev/serving/pkg/client/clientset/versioned/typed/serving/v1" ) +type Image struct { + DockerImage docker.DockerImage + ImageDigest string +} + func Config(endpoint string, token string, caCrt []byte) (*rest.Config, error) { if _, err := certutil.NewPoolFromBytes(caCrt); err != nil { return nil, fmt.Errorf("Expected to load root CA from bytes, but got err: %v", err) @@ -37,7 +42,7 @@ func Config(endpoint string, token string, caCrt []byte) (*rest.Config, error) { }, nil } -func loadManifest(project *project.Project, dockerImage docker.DockerImage) (*servingv1.Service, error) { +func loadManifest(project *project.Project, image Image) (*servingv1.Service, error) { knativeTemplate := path.Join("assets", "knative", "service.yaml.tmpl") template, err := template.ParseFS(project.Resources, knativeTemplate) if err != nil { @@ -46,7 +51,7 @@ func loadManifest(project *project.Project, dockerImage docker.DockerImage) (*se output := &bytes.Buffer{} // TODO replace map[string]string{} with proper values - if err = template.Execute(output, dockerImage); err != nil { + if err = template.Execute(output, image); err != nil { return nil, err } @@ -71,12 +76,17 @@ func loadManifest(project *project.Project, dockerImage docker.DockerImage) (*se return service, nil } -func Apply(namespace string, config *rest.Config, project *project.Project, dockerImage docker.DockerImage) error { +func Apply(namespace string, config *rest.Config, project *project.Project, dockerImage docker.DockerImage, imageDigest string) error { log.Info("Deploying Knative service", "host", config.Host, "name", project.Name, "namespace", namespace) ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5) defer cancel() - serviceManifest, err := loadManifest(project, dockerImage) + image := Image{ + DockerImage: dockerImage, + ImageDigest: imageDigest, + } + + serviceManifest, err := loadManifest(project, image) if err != nil { return err } diff --git a/src/services/k8s/knative_test.go b/src/services/k8s/knative_test.go index 8ba5320..97a31a2 100644 --- a/src/services/k8s/knative_test.go +++ b/src/services/k8s/knative_test.go @@ -3,6 +3,7 @@ package k8s import ( "encoding/base64" "fmt" + v1 "k8s.io/api/core/v1" "os" "path" "testing" @@ -54,22 +55,41 @@ func TestConfig(t *testing.T) { } func TestLoadManifest(t *testing.T) { - proj_knative := &project.Project{Name: "knative_test", + projKnative := &project.Project{Name: "knative_test", Directory: path.Join(root, "example"), Resources: os.DirFS(root), } - docker_image := docker.DockerImage{ - Registry: "example.com", - Directory: ".", - Name: "test", - Tag: "v1.1.0", + image := Image{ + DockerImage: docker.DockerImage{ + Registry: "example.com", + Directory: ".", + Name: "test", + Tag: "v1.1.0", + }, + ImageDigest: "sha256:3556949350924148437d1c40a2bfd74335fcd045da8b298e4ec4b6d5c53b4dc8", } - _, err := loadManifest(proj_knative, docker_image) - + knativeService, err := loadManifest(projKnative, image) if err != nil { t.Fatalf(fmt.Sprintf("Error: %v", err)) } + knativeServiceManifestName := knativeService.ObjectMeta.Name + expectedKnativeServiceManifestName := image.DockerImage.Name + if knativeServiceManifestName != expectedKnativeServiceManifestName { + t.Fatalf("Expected name '%s' got %s", expectedKnativeServiceManifestName, knativeServiceManifestName) + } + + knativeServiceManifestContainerImage := knativeService.Spec.Template.Spec.Containers[0].Image + expectedKnativeServiceManifestContainerImage := fmt.Sprintf("%s@%s", image.DockerImage.RemoteTag(), image.ImageDigest) + if knativeServiceManifestContainerImage != expectedKnativeServiceManifestContainerImage { + t.Fatalf("Expected image '%s' got %s", expectedKnativeServiceManifestContainerImage, knativeServiceManifestContainerImage) + } + + knativeServiceManifestContainerImagePullPolicy := knativeService.Spec.Template.Spec.Containers[0].ImagePullPolicy + expectedKnativeServiceManifestContainerImagePullPolicy := v1.PullAlways + if knativeServiceManifestContainerImagePullPolicy != expectedKnativeServiceManifestContainerImagePullPolicy { + t.Fatalf("Expected image pull policy '%s' got %s", expectedKnativeServiceManifestContainerImagePullPolicy, knativeServiceManifestContainerImagePullPolicy) + } }