diff --git a/pkg/kubelet/container/runtime.go b/pkg/kubelet/container/runtime.go index 71f6dc20f7e..ce88a6b47d7 100644 --- a/pkg/kubelet/container/runtime.go +++ b/pkg/kubelet/container/runtime.go @@ -25,7 +25,7 @@ import ( "strings" "time" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/remotecommand" "k8s.io/client-go/util/flowcontrol" @@ -47,7 +47,11 @@ type Version interface { // value of a Container's Image field, but in the future it will include more detailed // information about the different image types. type ImageSpec struct { + // ID of the image. Image string + // The annotations for the image. + // This should be passed to CRI during image pulls and returned when images are listed. + Annotations []Annotation } // ImageStats contains statistics about all the images currently available. @@ -349,6 +353,8 @@ type Image struct { RepoDigests []string // The size of the image in bytes. Size int64 + // ImageSpec for the image which include annotations. + Spec ImageSpec } type EnvVar struct { diff --git a/pkg/kubelet/images/image_manager.go b/pkg/kubelet/images/image_manager.go index 791fed00725..85d6c1e4831 100644 --- a/pkg/kubelet/images/image_manager.go +++ b/pkg/kubelet/images/image_manager.go @@ -101,7 +101,18 @@ func (m *imageManager) EnsureImageExists(pod *v1.Pod, container *v1.Container, p return "", msg, ErrInvalidImageName } - spec := kubecontainer.ImageSpec{Image: image} + var podAnnotations []kubecontainer.Annotation + for k, v := range pod.GetAnnotations() { + podAnnotations = append(podAnnotations, kubecontainer.Annotation{ + Name: k, + Value: v, + }) + } + + spec := kubecontainer.ImageSpec{ + Image: image, + Annotations: podAnnotations, + } imageRef, err := m.imageService.GetImageRef(spec) if err != nil { msg := fmt.Sprintf("Failed to inspect image %q: %v", container.Image, err) diff --git a/pkg/kubelet/kuberuntime/BUILD b/pkg/kubelet/kuberuntime/BUILD index 702e7d52d92..4b2a7e2b066 100644 --- a/pkg/kubelet/kuberuntime/BUILD +++ b/pkg/kubelet/kuberuntime/BUILD @@ -9,6 +9,7 @@ load( go_library( name = "go_default_library", srcs = [ + "convert.go", "doc.go", "fake_kuberuntime_manager.go", "helpers.go", @@ -91,6 +92,7 @@ go_library( go_test( name = "go_default_test", srcs = [ + "convert_test.go", "helpers_linux_test.go", "helpers_test.go", "instrumented_services_test.go", diff --git a/pkg/kubelet/kuberuntime/convert.go b/pkg/kubelet/kuberuntime/convert.go new file mode 100644 index 00000000000..35feaaa8f31 --- /dev/null +++ b/pkg/kubelet/kuberuntime/convert.go @@ -0,0 +1,55 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kuberuntime + +import ( + runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2" + kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" +) + +// This file contains help function to kuberuntime types to CRI runtime API types, or vice versa. + +func toKubeContainerImageSpec(image *runtimeapi.Image) kubecontainer.ImageSpec { + var annotations []kubecontainer.Annotation + + if image.Spec != nil && image.Spec.Annotations != nil { + for k, v := range image.Spec.Annotations { + annotations = append(annotations, kubecontainer.Annotation{ + Name: k, + Value: v, + }) + } + } + + return kubecontainer.ImageSpec{ + Image: image.Id, + Annotations: annotations, + } +} + +func toRuntimeAPIImageSpec(imageSpec kubecontainer.ImageSpec) *runtimeapi.ImageSpec { + var annotations = make(map[string]string) + if imageSpec.Annotations != nil { + for _, a := range imageSpec.Annotations { + annotations[a.Name] = a.Value + } + } + return &runtimeapi.ImageSpec{ + Image: imageSpec.Image, + Annotations: annotations, + } +} diff --git a/pkg/kubelet/kuberuntime/convert_test.go b/pkg/kubelet/kuberuntime/convert_test.go new file mode 100644 index 00000000000..7e2e95d2d85 --- /dev/null +++ b/pkg/kubelet/kuberuntime/convert_test.go @@ -0,0 +1,152 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kuberuntime + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2" + kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" +) + +func TestConvertToKubeContainerImageSpec(t *testing.T) { + testCases := []struct { + input *runtimeapi.Image + expected kubecontainer.ImageSpec + }{ + { + input: &runtimeapi.Image{ + Id: "test", + Spec: nil, + }, + expected: kubecontainer.ImageSpec{ + Image: "test", + Annotations: []kubecontainer.Annotation(nil), + }, + }, + { + input: &runtimeapi.Image{ + Id: "test", + Spec: &runtimeapi.ImageSpec{ + Annotations: nil, + }, + }, + expected: kubecontainer.ImageSpec{ + Image: "test", + Annotations: []kubecontainer.Annotation(nil), + }, + }, + { + input: &runtimeapi.Image{ + Id: "test", + Spec: &runtimeapi.ImageSpec{ + Annotations: map[string]string{}, + }, + }, + expected: kubecontainer.ImageSpec{ + Image: "test", + Annotations: []kubecontainer.Annotation(nil), + }, + }, + { + input: &runtimeapi.Image{ + Id: "test", + Spec: &runtimeapi.ImageSpec{ + Annotations: map[string]string{ + "kubernetes.io/os": "linux", + "kubernetes.io/runtimehandler": "handler", + }, + }, + }, + expected: kubecontainer.ImageSpec{ + Image: "test", + Annotations: []kubecontainer.Annotation{ + { + Name: "kubernetes.io/os", + Value: "linux", + }, + { + Name: "kubernetes.io/runtimehandler", + Value: "handler", + }, + }, + }, + }, + } + + for _, test := range testCases { + actual := toKubeContainerImageSpec(test.input) + assert.Equal(t, test.expected, actual) + } +} + +func TestConvertToRuntimeAPIImageSpec(t *testing.T) { + testCases := []struct { + input kubecontainer.ImageSpec + expected *runtimeapi.ImageSpec + }{ + { + input: kubecontainer.ImageSpec{ + Image: "test", + Annotations: nil, + }, + expected: &runtimeapi.ImageSpec{ + Image: "test", + Annotations: map[string]string{}, + }, + }, + { + input: kubecontainer.ImageSpec{ + Image: "test", + Annotations: []kubecontainer.Annotation{}, + }, + expected: &runtimeapi.ImageSpec{ + Image: "test", + Annotations: map[string]string{}, + }, + }, + { + input: kubecontainer.ImageSpec{ + Image: "test", + Annotations: []kubecontainer.Annotation{ + { + Name: "kubernetes.io/os", + Value: "linux", + }, + { + Name: "kubernetes.io/runtimehandler", + Value: "handler", + }, + }, + }, + expected: &runtimeapi.ImageSpec{ + Image: "test", + Annotations: map[string]string{ + "kubernetes.io/os": "linux", + "kubernetes.io/runtimehandler": "handler", + }, + }, + }, + } + + for _, test := range testCases { + actual := toRuntimeAPIImageSpec(test.input) + assert.Equal(t, test.expected, actual) + } +} diff --git a/pkg/kubelet/kuberuntime/kuberuntime_image.go b/pkg/kubelet/kuberuntime/kuberuntime_image.go index fcab3f5314b..74422031b32 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_image.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_image.go @@ -17,7 +17,7 @@ limitations under the License. package kuberuntime import ( - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" utilerrors "k8s.io/apimachinery/pkg/util/errors" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2" "k8s.io/klog" @@ -40,7 +40,8 @@ func (m *kubeGenericRuntimeManager) PullImage(image kubecontainer.ImageSpec, pul return "", err } - imgSpec := &runtimeapi.ImageSpec{Image: img} + imgSpec := toRuntimeAPIImageSpec(image) + creds, withCredentials := keyring.Lookup(repoToPull) if !withCredentials { klog.V(3).Infof("Pulling image %q without credentials", img) @@ -80,7 +81,7 @@ func (m *kubeGenericRuntimeManager) PullImage(image kubecontainer.ImageSpec, pul // GetImageRef gets the ID of the image which has already been in // the local storage. It returns ("", nil) if the image isn't in the local storage. func (m *kubeGenericRuntimeManager) GetImageRef(image kubecontainer.ImageSpec) (string, error) { - status, err := m.imageService.ImageStatus(&runtimeapi.ImageSpec{Image: image.Image}) + status, err := m.imageService.ImageStatus(toRuntimeAPIImageSpec(image)) if err != nil { klog.Errorf("ImageStatus for image %q failed: %v", image, err) return "", err @@ -107,6 +108,7 @@ func (m *kubeGenericRuntimeManager) ListImages() ([]kubecontainer.Image, error) Size: int64(img.Size_), RepoTags: img.RepoTags, RepoDigests: img.RepoDigests, + Spec: toKubeContainerImageSpec(img), }) }