Skip to content

Commit f411271

Browse files
authored
Merge pull request kubernetes#90061 from marosset/runtimehandler-image-spec-annotations
Add annotations to CRI ImageSpec objects
2 parents ba2855e + 9036772 commit f411271

File tree

12 files changed

+888
-329
lines changed

12 files changed

+888
-329
lines changed

pkg/kubelet/container/runtime.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import (
2525
"strings"
2626
"time"
2727

28-
"k8s.io/api/core/v1"
28+
v1 "k8s.io/api/core/v1"
2929
"k8s.io/apimachinery/pkg/types"
3030
"k8s.io/client-go/tools/remotecommand"
3131
"k8s.io/client-go/util/flowcontrol"
@@ -47,7 +47,11 @@ type Version interface {
4747
// value of a Container's Image field, but in the future it will include more detailed
4848
// information about the different image types.
4949
type ImageSpec struct {
50+
// ID of the image.
5051
Image string
52+
// The annotations for the image.
53+
// This should be passed to CRI during image pulls and returned when images are listed.
54+
Annotations []Annotation
5155
}
5256

5357
// ImageStats contains statistics about all the images currently available.
@@ -349,6 +353,8 @@ type Image struct {
349353
RepoDigests []string
350354
// The size of the image in bytes.
351355
Size int64
356+
// ImageSpec for the image which include annotations.
357+
Spec ImageSpec
352358
}
353359

354360
type EnvVar struct {

pkg/kubelet/container/testing/fake_runtime.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import (
2525
"sync"
2626
"time"
2727

28-
"k8s.io/api/core/v1"
28+
v1 "k8s.io/api/core/v1"
2929
"k8s.io/apimachinery/pkg/types"
3030
"k8s.io/client-go/util/flowcontrol"
3131
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
@@ -300,6 +300,13 @@ func (f *FakeRuntime) PullImage(image kubecontainer.ImageSpec, pullSecrets []v1.
300300
defer f.Unlock()
301301

302302
f.CalledFunctions = append(f.CalledFunctions, "PullImage")
303+
if f.Err == nil {
304+
i := kubecontainer.Image{
305+
ID: image.Image,
306+
Spec: image,
307+
}
308+
f.ImageList = append(f.ImageList, i)
309+
}
303310
return image.Image, f.Err
304311
}
305312

pkg/kubelet/images/image_manager.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,18 @@ func (m *imageManager) EnsureImageExists(pod *v1.Pod, container *v1.Container, p
100100
return "", msg, ErrInvalidImageName
101101
}
102102

103-
spec := kubecontainer.ImageSpec{Image: image}
103+
var podAnnotations []kubecontainer.Annotation
104+
for k, v := range pod.GetAnnotations() {
105+
podAnnotations = append(podAnnotations, kubecontainer.Annotation{
106+
Name: k,
107+
Value: v,
108+
})
109+
}
110+
111+
spec := kubecontainer.ImageSpec{
112+
Image: image,
113+
Annotations: podAnnotations,
114+
}
104115
imageRef, err := m.imageService.GetImageRef(spec)
105116
if err != nil {
106117
msg := fmt.Sprintf("Failed to inspect image %q: %v", container.Image, err)

pkg/kubelet/images/image_manager_test.go

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import (
2222
"time"
2323

2424
"github.com/stretchr/testify/assert"
25-
"k8s.io/api/core/v1"
25+
v1 "k8s.io/api/core/v1"
2626
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2727
"k8s.io/apimachinery/pkg/util/clock"
2828
"k8s.io/client-go/tools/record"
@@ -202,3 +202,48 @@ func TestApplyDefaultImageTag(t *testing.T) {
202202
}
203203
}
204204
}
205+
206+
func TestPullAndListImageWithPodAnnotations(t *testing.T) {
207+
pod := &v1.Pod{
208+
ObjectMeta: metav1.ObjectMeta{
209+
Name: "test_pod",
210+
Namespace: "test-ns",
211+
UID: "bar",
212+
ResourceVersion: "42",
213+
SelfLink: "/api/v1/pods/foo",
214+
Annotations: map[string]string{
215+
"kubernetes.io/runtimehandler": "handler_name",
216+
},
217+
}}
218+
c := pullerTestCase{ // pull missing image
219+
containerImage: "missing_image",
220+
policy: v1.PullIfNotPresent,
221+
inspectErr: nil,
222+
pullerErr: nil,
223+
expected: []pullerExpects{
224+
{[]string{"GetImageRef", "PullImage"}, nil},
225+
}}
226+
227+
useSerializedEnv := true
228+
puller, fakeClock, fakeRuntime, container := pullerTestEnv(c, useSerializedEnv)
229+
fakeRuntime.CalledFunctions = nil
230+
fakeRuntime.ImageList = []Image{}
231+
fakeClock.Step(time.Second)
232+
233+
_, _, err := puller.EnsureImageExists(pod, container, nil, nil)
234+
assert.NoError(t, fakeRuntime.AssertCalls(c.expected[0].calls), "tick=%d", 0)
235+
assert.Equal(t, c.expected[0].err, err, "tick=%d", 0)
236+
237+
images, _ := fakeRuntime.ListImages()
238+
assert.Equal(t, 1, len(images), "ListImages() count")
239+
240+
image := images[0]
241+
assert.Equal(t, "missing_image:latest", image.ID, "Image ID")
242+
243+
expectedAnnotations := []Annotation{
244+
{
245+
Name: "kubernetes.io/runtimehandler",
246+
Value: "handler_name",
247+
}}
248+
assert.Equal(t, expectedAnnotations, image.Spec.Annotations, "image spec annotations")
249+
}

pkg/kubelet/kuberuntime/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ load(
99
go_library(
1010
name = "go_default_library",
1111
srcs = [
12+
"convert.go",
1213
"doc.go",
1314
"fake_kuberuntime_manager.go",
1415
"helpers.go",
@@ -91,6 +92,7 @@ go_library(
9192
go_test(
9293
name = "go_default_test",
9394
srcs = [
95+
"convert_test.go",
9496
"helpers_linux_test.go",
9597
"helpers_test.go",
9698
"instrumented_services_test.go",

pkg/kubelet/kuberuntime/convert.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package kuberuntime
18+
19+
import (
20+
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
21+
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
22+
)
23+
24+
// This file contains help function to kuberuntime types to CRI runtime API types, or vice versa.
25+
26+
func toKubeContainerImageSpec(image *runtimeapi.Image) kubecontainer.ImageSpec {
27+
var annotations []kubecontainer.Annotation
28+
29+
if image.Spec != nil && image.Spec.Annotations != nil {
30+
for k, v := range image.Spec.Annotations {
31+
annotations = append(annotations, kubecontainer.Annotation{
32+
Name: k,
33+
Value: v,
34+
})
35+
}
36+
}
37+
38+
return kubecontainer.ImageSpec{
39+
Image: image.Id,
40+
Annotations: annotations,
41+
}
42+
}
43+
44+
func toRuntimeAPIImageSpec(imageSpec kubecontainer.ImageSpec) *runtimeapi.ImageSpec {
45+
var annotations = make(map[string]string)
46+
if imageSpec.Annotations != nil {
47+
for _, a := range imageSpec.Annotations {
48+
annotations[a.Name] = a.Value
49+
}
50+
}
51+
return &runtimeapi.ImageSpec{
52+
Image: imageSpec.Image,
53+
Annotations: annotations,
54+
}
55+
}
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package kuberuntime
18+
19+
import (
20+
"testing"
21+
22+
"github.com/stretchr/testify/assert"
23+
24+
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
25+
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
26+
)
27+
28+
func TestConvertToKubeContainerImageSpec(t *testing.T) {
29+
testCases := []struct {
30+
input *runtimeapi.Image
31+
expected kubecontainer.ImageSpec
32+
}{
33+
{
34+
input: &runtimeapi.Image{
35+
Id: "test",
36+
Spec: nil,
37+
},
38+
expected: kubecontainer.ImageSpec{
39+
Image: "test",
40+
Annotations: []kubecontainer.Annotation(nil),
41+
},
42+
},
43+
{
44+
input: &runtimeapi.Image{
45+
Id: "test",
46+
Spec: &runtimeapi.ImageSpec{
47+
Annotations: nil,
48+
},
49+
},
50+
expected: kubecontainer.ImageSpec{
51+
Image: "test",
52+
Annotations: []kubecontainer.Annotation(nil),
53+
},
54+
},
55+
{
56+
input: &runtimeapi.Image{
57+
Id: "test",
58+
Spec: &runtimeapi.ImageSpec{
59+
Annotations: map[string]string{},
60+
},
61+
},
62+
expected: kubecontainer.ImageSpec{
63+
Image: "test",
64+
Annotations: []kubecontainer.Annotation(nil),
65+
},
66+
},
67+
{
68+
input: &runtimeapi.Image{
69+
Id: "test",
70+
Spec: &runtimeapi.ImageSpec{
71+
Annotations: map[string]string{
72+
"kubernetes.io/os": "linux",
73+
"kubernetes.io/runtimehandler": "handler",
74+
},
75+
},
76+
},
77+
expected: kubecontainer.ImageSpec{
78+
Image: "test",
79+
Annotations: []kubecontainer.Annotation{
80+
{
81+
Name: "kubernetes.io/os",
82+
Value: "linux",
83+
},
84+
{
85+
Name: "kubernetes.io/runtimehandler",
86+
Value: "handler",
87+
},
88+
},
89+
},
90+
},
91+
}
92+
93+
for _, test := range testCases {
94+
actual := toKubeContainerImageSpec(test.input)
95+
assert.Equal(t, test.expected, actual)
96+
}
97+
}
98+
99+
func TestConvertToRuntimeAPIImageSpec(t *testing.T) {
100+
testCases := []struct {
101+
input kubecontainer.ImageSpec
102+
expected *runtimeapi.ImageSpec
103+
}{
104+
{
105+
input: kubecontainer.ImageSpec{
106+
Image: "test",
107+
Annotations: nil,
108+
},
109+
expected: &runtimeapi.ImageSpec{
110+
Image: "test",
111+
Annotations: map[string]string{},
112+
},
113+
},
114+
{
115+
input: kubecontainer.ImageSpec{
116+
Image: "test",
117+
Annotations: []kubecontainer.Annotation{},
118+
},
119+
expected: &runtimeapi.ImageSpec{
120+
Image: "test",
121+
Annotations: map[string]string{},
122+
},
123+
},
124+
{
125+
input: kubecontainer.ImageSpec{
126+
Image: "test",
127+
Annotations: []kubecontainer.Annotation{
128+
{
129+
Name: "kubernetes.io/os",
130+
Value: "linux",
131+
},
132+
{
133+
Name: "kubernetes.io/runtimehandler",
134+
Value: "handler",
135+
},
136+
},
137+
},
138+
expected: &runtimeapi.ImageSpec{
139+
Image: "test",
140+
Annotations: map[string]string{
141+
"kubernetes.io/os": "linux",
142+
"kubernetes.io/runtimehandler": "handler",
143+
},
144+
},
145+
},
146+
}
147+
148+
for _, test := range testCases {
149+
actual := toRuntimeAPIImageSpec(test.input)
150+
assert.Equal(t, test.expected, actual)
151+
}
152+
}

pkg/kubelet/kuberuntime/kuberuntime_image.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ limitations under the License.
1717
package kuberuntime
1818

1919
import (
20-
"k8s.io/api/core/v1"
20+
v1 "k8s.io/api/core/v1"
2121
utilerrors "k8s.io/apimachinery/pkg/util/errors"
2222
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
2323
"k8s.io/klog/v2"
@@ -40,7 +40,8 @@ func (m *kubeGenericRuntimeManager) PullImage(image kubecontainer.ImageSpec, pul
4040
return "", err
4141
}
4242

43-
imgSpec := &runtimeapi.ImageSpec{Image: img}
43+
imgSpec := toRuntimeAPIImageSpec(image)
44+
4445
creds, withCredentials := keyring.Lookup(repoToPull)
4546
if !withCredentials {
4647
klog.V(3).Infof("Pulling image %q without credentials", img)
@@ -80,7 +81,7 @@ func (m *kubeGenericRuntimeManager) PullImage(image kubecontainer.ImageSpec, pul
8081
// GetImageRef gets the ID of the image which has already been in
8182
// the local storage. It returns ("", nil) if the image isn't in the local storage.
8283
func (m *kubeGenericRuntimeManager) GetImageRef(image kubecontainer.ImageSpec) (string, error) {
83-
status, err := m.imageService.ImageStatus(&runtimeapi.ImageSpec{Image: image.Image})
84+
status, err := m.imageService.ImageStatus(toRuntimeAPIImageSpec(image))
8485
if err != nil {
8586
klog.Errorf("ImageStatus for image %q failed: %v", image, err)
8687
return "", err
@@ -107,6 +108,7 @@ func (m *kubeGenericRuntimeManager) ListImages() ([]kubecontainer.Image, error)
107108
Size: int64(img.Size_),
108109
RepoTags: img.RepoTags,
109110
RepoDigests: img.RepoDigests,
111+
Spec: toKubeContainerImageSpec(img),
110112
})
111113
}
112114

0 commit comments

Comments
 (0)