Skip to content
This repository was archived by the owner on Mar 9, 2022. It is now read-only.

Commit 326b8b7

Browse files
committed
Pull container image and apply image config.
Signed-off-by: Lantao Liu <[email protected]>
1 parent aa1ffe4 commit 326b8b7

File tree

8 files changed

+262
-54
lines changed

8 files changed

+262
-54
lines changed

cmd/cri-containerd/cri_containerd.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ func main() {
4848
if err != nil {
4949
glog.Exitf("Failed to create CRI containerd service %+v: %v", o, err)
5050
}
51+
if err := service.Start(); err != nil {
52+
glog.Exitf("Failed to start CRI containerd service: %v", err)
53+
}
5154

5255
s := server.NewCRIContainerdServer(o.SocketPath, service, service)
5356
if err := s.Run(); err != nil {

pkg/server/container_create.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ import (
2020
"fmt"
2121
"time"
2222

23+
rootfsapi "github.com/containerd/containerd/api/services/rootfs"
2324
"github.com/golang/glog"
25+
imagedigest "github.com/opencontainers/go-digest"
2426
"golang.org/x/net/context"
25-
2627
"k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
2728

2829
"github.com/kubernetes-incubator/cri-containerd/pkg/metadata"
@@ -68,9 +69,23 @@ func (c *criContainerdService) CreateContainer(ctx context.Context, r *runtime.C
6869
Config: config,
6970
}
7071

71-
// TODO(random-liu): [P0] Prepare container rootfs.
72-
73-
// TODO(random-liu): [P0] Set ImageRef in ContainerMetadata with image id.
72+
// Prepare container image snapshot. For container, the image should have
73+
// been pulled before creating the container, so do not ensure the image.
74+
image := config.GetImage().GetImage()
75+
imageMeta, err := c.getImageMetadata(ctx, image, false /*ensure*/)
76+
if err != nil {
77+
return nil, fmt.Errorf("failed to get container image %q: %v", image, err)
78+
}
79+
if _, err := c.rootfsService.Prepare(ctx, &rootfsapi.PrepareRequest{
80+
Name: id,
81+
// We are sure that ChainID must be a digest.
82+
ChainID: imagedigest.Digest(imageMeta.ChainID),
83+
Readonly: config.GetLinux().GetSecurityContext().GetReadonlyRootfs(),
84+
}); err != nil {
85+
return nil, fmt.Errorf("failed to prepare container rootfs %q: %v", imageMeta.ChainID, err)
86+
}
87+
// TODO(random-liu): [P0] Cleanup snapshot on failure after switching to new rootfs api.
88+
meta.ImageRef = imageMeta.ID
7489

7590
// Create container root directory.
7691
containerRootDir := getContainerRootDir(c.rootDir, id)

pkg/server/container_create_test.go

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,17 @@ import (
2121
"os"
2222
"testing"
2323

24+
rootfsapi "github.com/containerd/containerd/api/services/rootfs"
25+
imagedigest "github.com/opencontainers/go-digest"
26+
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
2427
"github.com/stretchr/testify/assert"
2528
"github.com/stretchr/testify/require"
2629
"golang.org/x/net/context"
2730
"k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
2831

2932
"github.com/kubernetes-incubator/cri-containerd/pkg/metadata"
3033
ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing"
34+
servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing"
3135
)
3236

3337
func TestCreateContainer(t *testing.T) {
@@ -42,10 +46,20 @@ func TestCreateContainer(t *testing.T) {
4246
Namespace: "test-sandbox-namespace",
4347
Attempt: 2,
4448
}
49+
// Use an image id to avoid image name resolution.
50+
// TODO(random-liu): Change this to image name after we have complete image
51+
// management unit test framework.
52+
testImage := "sha256:c75bebcdd211f41b3a460c7bf82970ed6c75acaab9cd4c9a4e125b03ca113799"
53+
testChainID := imagedigest.Digest("test-chain-id")
54+
testImageMetadata := metadata.ImageMetadata{
55+
ID: testImage,
56+
ChainID: testChainID.String(),
57+
Config: &imagespec.ImageConfig{},
58+
}
4559
testConfig := &runtime.ContainerConfig{
4660
Metadata: testNameMeta,
4761
Image: &runtime.ImageSpec{
48-
Image: "test-image",
62+
Image: testImage,
4963
},
5064
Labels: map[string]string{"a": "b"},
5165
Annotations: map[string]string{"c": "d"},
@@ -55,12 +69,14 @@ func TestCreateContainer(t *testing.T) {
5569
}
5670

5771
for desc, test := range map[string]struct {
58-
sandboxMetadata *metadata.SandboxMetadata
59-
reserveNameErr bool
60-
createRootDirErr error
61-
createMetadataErr bool
62-
expectErr bool
63-
expectMeta *metadata.ContainerMetadata
72+
sandboxMetadata *metadata.SandboxMetadata
73+
reserveNameErr bool
74+
imageMetadataErr bool
75+
prepareSnapshotErr error
76+
createRootDirErr error
77+
createMetadataErr bool
78+
expectErr bool
79+
expectMeta *metadata.ContainerMetadata
6480
}{
6581
"should return error if sandbox does not exist": {
6682
sandboxMetadata: nil,
@@ -84,6 +100,24 @@ func TestCreateContainer(t *testing.T) {
84100
createRootDirErr: errors.New("random error"),
85101
expectErr: true,
86102
},
103+
"should return error if image is not pulled": {
104+
sandboxMetadata: &metadata.SandboxMetadata{
105+
ID: testSandboxID,
106+
Name: makeSandboxName(testSandboxNameMeta),
107+
Config: testSandboxConfig,
108+
},
109+
imageMetadataErr: true,
110+
expectErr: true,
111+
},
112+
"should return error if prepare snapshot fails": {
113+
sandboxMetadata: &metadata.SandboxMetadata{
114+
ID: testSandboxID,
115+
Name: makeSandboxName(testSandboxNameMeta),
116+
Config: testSandboxConfig,
117+
},
118+
prepareSnapshotErr: errors.New("random error"),
119+
expectErr: true,
120+
},
87121
"should be able to create container successfully": {
88122
sandboxMetadata: &metadata.SandboxMetadata{
89123
ID: testSandboxID,
@@ -94,12 +128,14 @@ func TestCreateContainer(t *testing.T) {
94128
expectMeta: &metadata.ContainerMetadata{
95129
Name: makeContainerName(testNameMeta, testSandboxNameMeta),
96130
SandboxID: testSandboxID,
131+
ImageRef: testImage,
97132
Config: testConfig,
98133
},
99134
},
100135
} {
101136
t.Logf("TestCase %q", desc)
102137
c := newTestCRIContainerdService()
138+
fakeRootfsClient := c.rootfsService.(*servertesting.FakeRootfsClient)
103139
fakeOS := c.os.(*ostesting.FakeOS)
104140
if test.sandboxMetadata != nil {
105141
assert.NoError(t, c.sandboxStore.Create(*test.sandboxMetadata))
@@ -108,6 +144,13 @@ func TestCreateContainer(t *testing.T) {
108144
if test.reserveNameErr {
109145
assert.NoError(t, c.containerNameIndex.Reserve(containerName, "random id"))
110146
}
147+
if !test.imageMetadataErr {
148+
assert.NoError(t, c.imageMetadataStore.Create(testImageMetadata))
149+
}
150+
if test.prepareSnapshotErr != nil {
151+
fakeRootfsClient.InjectError("prepare", test.prepareSnapshotErr)
152+
}
153+
fakeRootfsClient.SetFakeChainIDs([]imagedigest.Digest{testChainID})
111154
rootExists := false
112155
rootPath := ""
113156
fakeOS.MkdirAllFn = func(path string, perm os.FileMode) error {
@@ -153,5 +196,15 @@ func TestCreateContainer(t *testing.T) {
153196
// TODO(random-liu): Use fake clock to test CreatedAt.
154197
test.expectMeta.CreatedAt = meta.CreatedAt
155198
assert.Equal(t, test.expectMeta, meta, "container metadata should be created")
199+
200+
assert.Equal(t, []string{"prepare"}, fakeRootfsClient.GetCalledNames(), "prepare should be called")
201+
calls := fakeRootfsClient.GetCalledDetails()
202+
prepareOpts := calls[0].Argument.(*rootfsapi.PrepareRequest)
203+
assert.Equal(t, &rootfsapi.PrepareRequest{
204+
Name: id,
205+
ChainID: testChainID,
206+
// TODO(random-liu): Test readonly rootfs.
207+
Readonly: false,
208+
}, prepareOpts, "prepare request should be correct")
156209
}
157210
}

pkg/server/container_remove.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func (c *criContainerdService) RemoveContainer(ctx context.Context, r *runtime.R
6969
// kubelet implementation, we'll never start a container once we decide to remove it,
7070
// so we don't need the "Dead" state for now.
7171

72-
// TODO(random-liu): [P0] Cleanup container rootfs.
72+
// TODO(random-liu): [P0] Cleanup snapshot after switching to new snapshot api.
7373

7474
// Cleanup container root directory.
7575
containerRootDir := getContainerRootDir(c.rootDir, id)

pkg/server/container_start.go

Lines changed: 49 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,15 @@ import (
2323
"os"
2424
"time"
2525

26+
"github.com/containerd/containerd/api/services/execution"
27+
rootfsapi "github.com/containerd/containerd/api/services/rootfs"
28+
"github.com/containerd/containerd/api/types/container"
2629
prototypes "github.com/gogo/protobuf/types"
2730
"github.com/golang/glog"
31+
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
2832
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
2933
"github.com/opencontainers/runtime-tools/generate"
3034
"golang.org/x/net/context"
31-
32-
"github.com/containerd/containerd/api/services/execution"
33-
"github.com/containerd/containerd/api/types/container"
34-
"github.com/containerd/containerd/api/types/mount"
35-
3635
"k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
3736

3837
"github.com/kubernetes-incubator/cri-containerd/pkg/metadata"
@@ -114,12 +113,11 @@ func (c *criContainerdService) startContainer(ctx context.Context, id string, me
114113
glog.V(2).Infof("Sandbox container %q is running with pid %d", sandboxID, sandboxPid)
115114

116115
// Generate containerd container create options.
117-
// TODO(random-liu): [P0] Create container rootfs with image ref.
118-
// TODO(random-liu): [P0] Apply default image config.
119-
// Use fixed rootfs path for now.
120-
const rootPath = "/"
121-
122-
spec, err := c.generateContainerSpec(id, sandboxPid, config, sandboxConfig)
116+
imageMeta, err := c.imageMetadataStore.Get(meta.ImageRef)
117+
if err != nil {
118+
return fmt.Errorf("failed to get container image %q: %v", meta.ImageRef, err)
119+
}
120+
spec, err := c.generateContainerSpec(id, sandboxPid, config, sandboxConfig, imageMeta.Config)
123121
if err != nil {
124122
return fmt.Errorf("failed to generate container %q spec: %v", id, err)
125123
}
@@ -169,31 +167,27 @@ func (c *criContainerdService) startContainer(ctx context.Context, id string, me
169167
}(stderrPipe)
170168
}
171169

170+
// Get rootfs mounts.
171+
mountsResp, err := c.rootfsService.Mounts(ctx, &rootfsapi.MountsRequest{Name: id})
172+
if err != nil {
173+
return fmt.Errorf("failed to get rootfs mounts %q: %v", id, err)
174+
}
175+
172176
// Create containerd container.
173177
createOpts := &execution.CreateRequest{
174178
ID: id,
175179
Spec: &prototypes.Any{
176180
TypeUrl: runtimespec.Version,
177181
Value: rawSpec,
178182
},
179-
// TODO(random-liu): [P0] Get rootfs mount from containerd.
180-
Rootfs: []*mount.Mount{
181-
{
182-
Type: "bind",
183-
Source: rootPath,
184-
Options: []string{
185-
"rw",
186-
"rbind",
187-
},
188-
},
189-
},
183+
Rootfs: mountsResp.Mounts,
190184
Runtime: defaultRuntime,
191185
Stdin: stdin,
192186
Stdout: stdout,
193187
Stderr: stderr,
194188
Terminal: config.GetTty(),
195189
}
196-
glog.V(2).Infof("Create containerd container (id=%q, name=%q) with options %+v.",
190+
glog.V(5).Infof("Create containerd container (id=%q, name=%q) with options %+v.",
197191
id, meta.Name, createOpts)
198192
createResp, err := c.containerService.Create(ctx, createOpts)
199193
if err != nil {
@@ -219,7 +213,8 @@ func (c *criContainerdService) startContainer(ctx context.Context, id string, me
219213
return nil
220214
}
221215

222-
func (c *criContainerdService) generateContainerSpec(id string, sandboxPid uint32, config *runtime.ContainerConfig, sandboxConfig *runtime.PodSandboxConfig) (*runtimespec.Spec, error) {
216+
func (c *criContainerdService) generateContainerSpec(id string, sandboxPid uint32, config *runtime.ContainerConfig,
217+
sandboxConfig *runtime.PodSandboxConfig, imageConfig *imagespec.ImageConfig) (*runtimespec.Spec, error) {
223218
// Creates a spec Generator with the default spec.
224219
// TODO(random-liu): [P2] Move container runtime spec generation into a helper function.
225220
g := generate.New()
@@ -228,14 +223,21 @@ func (c *criContainerdService) generateContainerSpec(id string, sandboxPid uint3
228223
// pre-defined directory.
229224
g.SetRootPath(relativeRootfsPath)
230225

231-
if len(config.GetCommand()) != 0 || len(config.GetArgs()) != 0 {
232-
g.SetProcessArgs(append(config.GetCommand(), config.GetArgs()...))
226+
if err := setOCIProcessArgs(&g, config, imageConfig); err != nil {
227+
return nil, err
233228
}
234229

235230
if config.GetWorkingDir() != "" {
236231
g.SetProcessCwd(config.GetWorkingDir())
232+
} else if imageConfig.WorkingDir != "" {
233+
g.SetProcessCwd(imageConfig.WorkingDir)
237234
}
238235

236+
// Apply envs from image config first, so that envs from container config
237+
// can override them.
238+
if err := addImageEnvs(&g, imageConfig.Env); err != nil {
239+
return nil, err
240+
}
239241
for _, e := range config.GetEnvs() {
240242
g.AddProcessEnv(e.GetKey(), e.GetValue())
241243
}
@@ -288,6 +290,27 @@ func (c *criContainerdService) generateContainerSpec(id string, sandboxPid uint3
288290
return g.Spec(), nil
289291
}
290292

293+
// setOCIProcessArgs sets process args. It returns error if the final arg list
294+
// is empty.
295+
func setOCIProcessArgs(g *generate.Generator, config *runtime.ContainerConfig, imageConfig *imagespec.ImageConfig) error {
296+
command, args := config.GetCommand(), config.GetArgs()
297+
// The following logic is migrated from https://github.com/moby/moby/blob/master/daemon/commit.go
298+
// TODO(random-liu): Figure out whether the following logic is
299+
if len(command) == 0 {
300+
if len(args) == 0 {
301+
args = imageConfig.Cmd
302+
}
303+
if command == nil {
304+
command = imageConfig.Entrypoint
305+
}
306+
}
307+
if len(command) == 0 && len(args) == 0 {
308+
return fmt.Errorf("no command specified")
309+
}
310+
g.SetProcessArgs(append(command, args...))
311+
return nil
312+
}
313+
291314
// addOCIBindMounts adds bind mounts.
292315
func addOCIBindMounts(g *generate.Generator, mounts []*runtime.Mount) {
293316
for _, mount := range mounts {

0 commit comments

Comments
 (0)