Skip to content

Commit be9336f

Browse files
authored
Merge pull request containerd#9634 from dmcgowan/add-migration-tests
Add migration tests
2 parents e73942d + d7689ae commit be9336f

File tree

8 files changed

+740
-16
lines changed

8 files changed

+740
-16
lines changed

cmd/containerd/command/config.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,28 @@ var configCommand = cli.Command{
123123
}
124124
}
125125

126+
plugins, err := server.LoadPlugins(ctx, config)
127+
if err != nil {
128+
return err
129+
}
130+
if len(plugins) != 0 {
131+
if config.Plugins == nil {
132+
config.Plugins = make(map[string]interface{})
133+
}
134+
for _, p := range plugins {
135+
if p.Config == nil {
136+
continue
137+
}
138+
139+
pc, err := config.Decode(ctx, p.URI(), p.Config)
140+
if err != nil {
141+
return err
142+
}
143+
144+
config.Plugins[p.URI()] = pc
145+
}
146+
}
147+
126148
config.Version = srvconfig.CurrentConfigVersion
127149

128150
return toml.NewEncoder(os.Stdout).SetIndentTables(true).Encode(config)

cmd/containerd/server/config/config.go

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"io"
3131
"os"
3232
"path/filepath"
33+
"reflect"
3334
"strings"
3435

3536
"dario.cat/mergo"
@@ -310,12 +311,6 @@ func LoadConfig(ctx context.Context, path string, out *Config) error {
310311
pending = append(pending, imports...)
311312
}
312313

313-
// Fix up the list of config files loaded
314-
out.Imports = []string{}
315-
for path := range loaded {
316-
out.Imports = append(out.Imports, path)
317-
}
318-
319314
err := out.ValidateVersion()
320315
if err != nil {
321316
return fmt.Errorf("failed to load TOML from %s: %w", path, err)
@@ -408,9 +403,11 @@ func resolveImports(parent string, imports []string) ([]string, error) {
408403
// 0 1 1
409404
// []{"1"} []{"2"} []{"1","2"}
410405
// []{"1"} []{} []{"1"}
406+
// []{"1", "2"} []{"1"} []{"1","2"}
407+
// []{} []{"2"} []{"2"}
411408
// Maps merged by keys, but values are replaced entirely.
412409
func mergeConfig(to, from *Config) error {
413-
err := mergo.Merge(to, from, mergo.WithOverride, mergo.WithAppendSlice)
410+
err := mergo.Merge(to, from, mergo.WithOverride, mergo.WithTransformers(sliceTransformer{}))
414411
if err != nil {
415412
return err
416413
}
@@ -435,6 +432,43 @@ func mergeConfig(to, from *Config) error {
435432
return nil
436433
}
437434

435+
type sliceTransformer struct{}
436+
437+
func (sliceTransformer) Transformer(t reflect.Type) func(dst, src reflect.Value) error {
438+
if t.Kind() != reflect.Slice {
439+
return nil
440+
}
441+
return func(dst, src reflect.Value) error {
442+
if !dst.CanSet() {
443+
return nil
444+
}
445+
if src.Type() != dst.Type() {
446+
return fmt.Errorf("cannot append two slice with different type (%s, %s)", src.Type(), dst.Type())
447+
}
448+
for i := 0; i < src.Len(); i++ {
449+
found := false
450+
for j := 0; j < dst.Len(); j++ {
451+
srcv := src.Index(i)
452+
dstv := dst.Index(j)
453+
if !srcv.CanInterface() || !dstv.CanInterface() {
454+
if srcv.Equal(dstv) {
455+
found = true
456+
break
457+
}
458+
} else if reflect.DeepEqual(srcv.Interface(), dstv.Interface()) {
459+
found = true
460+
break
461+
}
462+
}
463+
if !found {
464+
dst.Set(reflect.Append(dst, src.Index(i)))
465+
}
466+
}
467+
468+
return nil
469+
}
470+
}
471+
438472
// V2DisabledFilter matches based on URI
439473
func V2DisabledFilter(list []string) plugin.DisableFilter {
440474
set := make(map[string]struct{}, len(list))

cmd/containerd/server/config/config_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ func TestMergeConfigs(t *testing.T) {
5050
Version: 2,
5151
Root: "new_root",
5252
RequiredPlugins: []string{"io.containerd.new_plugin1.v1", "io.containerd.new_plugin2.v1"},
53+
DisabledPlugins: []string{"io.containerd.old_plugin.v1"},
5354
OOMScore: 2,
5455
Timeouts: map[string]string{"b": "2"},
5556
StreamProcessors: map[string]StreamProcessor{"1": {Path: "3"}},
@@ -191,8 +192,8 @@ imports = ["data1.toml", "data2.toml"]
191192

192193
sort.Strings(out.Imports)
193194
assert.Equal(t, []string{
194-
filepath.Join(tempDir, "data1.toml"),
195-
filepath.Join(tempDir, "data2.toml"),
195+
"data1.toml",
196+
"data2.toml",
196197
}, out.Imports)
197198
}
198199

integration/client/migration_test.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
Copyright The containerd 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 client
18+
19+
import (
20+
"bytes"
21+
"os"
22+
"os/exec"
23+
"path/filepath"
24+
"runtime"
25+
"strings"
26+
"testing"
27+
28+
"github.com/stretchr/testify/assert"
29+
"github.com/stretchr/testify/require"
30+
)
31+
32+
func TestMigration(t *testing.T) {
33+
currentDefault := filepath.Join(t.TempDir(), "default.toml")
34+
defaultContent, err := currentDefaultConfig()
35+
require.NoError(t, err)
36+
require.NoError(t, os.WriteFile(currentDefault, []byte(defaultContent), 0644))
37+
38+
type migrationTest struct {
39+
Name string
40+
File string
41+
Migrated string
42+
}
43+
migrationTests := []migrationTest{
44+
{
45+
Name: "CurrentDefault",
46+
File: currentDefault,
47+
Migrated: defaultContent,
48+
},
49+
}
50+
51+
// Only run the old version migration tests for the same platform
52+
// and build settings the default config was generated for.
53+
if runtime.GOOS == "linux" && runtime.GOARCH == "amd64" && strings.Contains(defaultContent, "btrfs") && strings.Contains(defaultContent, "devmapper") {
54+
migrationTests = append(migrationTests, []migrationTest{
55+
{
56+
Name: "1.6-Default",
57+
File: "testdata/default-1.6.toml",
58+
Migrated: defaultContent,
59+
},
60+
{
61+
Name: "1.7-Default",
62+
File: "testdata/default-1.7.toml",
63+
Migrated: defaultContent,
64+
},
65+
}...)
66+
}
67+
68+
for _, tc := range migrationTests {
69+
tc := tc
70+
t.Run(tc.Name, func(t *testing.T) {
71+
buf := bytes.NewBuffer(nil)
72+
cmd := exec.Command("containerd", "-c", tc.File, "config", "migrate")
73+
cmd.Stdout = buf
74+
cmd.Stderr = os.Stderr
75+
require.NoError(t, cmd.Run())
76+
actual := buf.String()
77+
assert.Equal(t, tc.Migrated, actual)
78+
})
79+
}
80+
81+
}
82+
83+
func currentDefaultConfig() (string, error) {
84+
cmd := exec.Command("containerd", "config", "default")
85+
buf := bytes.NewBuffer(nil)
86+
cmd.Stdout = buf
87+
cmd.Stderr = os.Stderr
88+
if err := cmd.Run(); err != nil {
89+
return "", err
90+
}
91+
return buf.String(), nil
92+
}

0 commit comments

Comments
 (0)