github.com/demonoid81/containerd@v1.3.4/client_test.go (about) 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 containerd 18 19 import ( 20 "bytes" 21 "context" 22 "flag" 23 "fmt" 24 "io" 25 "io/ioutil" 26 "os" 27 "os/exec" 28 "testing" 29 "time" 30 31 "github.com/containerd/containerd/defaults" 32 "github.com/containerd/containerd/images" 33 "github.com/containerd/containerd/log" 34 "github.com/containerd/containerd/log/logtest" 35 "github.com/containerd/containerd/namespaces" 36 "github.com/containerd/containerd/pkg/testutil" 37 "github.com/containerd/containerd/platforms" 38 "github.com/containerd/containerd/sys" 39 "github.com/sirupsen/logrus" 40 ) 41 42 var ( 43 address string 44 noDaemon bool 45 noCriu bool 46 supportsCriu bool 47 testNamespace = "testing" 48 ctrdStdioFilePath string 49 50 ctrd = &daemon{} 51 ) 52 53 func init() { 54 flag.StringVar(&address, "address", defaultAddress, "The address to the containerd socket for use in the tests") 55 flag.BoolVar(&noDaemon, "no-daemon", false, "Do not start a dedicated daemon for the tests") 56 flag.BoolVar(&noCriu, "no-criu", false, "Do not run the checkpoint tests") 57 } 58 59 func testContext(t testing.TB) (context.Context, context.CancelFunc) { 60 ctx, cancel := context.WithCancel(context.Background()) 61 ctx = namespaces.WithNamespace(ctx, testNamespace) 62 if t != nil { 63 ctx = logtest.WithT(ctx, t) 64 } 65 return ctx, cancel 66 } 67 68 func TestMain(m *testing.M) { 69 flag.Parse() 70 if testing.Short() { 71 os.Exit(m.Run()) 72 } 73 testutil.RequiresRootM() 74 // check if criu is installed on the system 75 _, err := exec.LookPath("criu") 76 supportsCriu = err == nil && !noCriu 77 78 var ( 79 buf = bytes.NewBuffer(nil) 80 ctx, cancel = testContext(nil) 81 ) 82 defer cancel() 83 84 if !noDaemon { 85 sys.ForceRemoveAll(defaultRoot) 86 87 stdioFile, err := ioutil.TempFile("", "") 88 if err != nil { 89 fmt.Fprintf(os.Stderr, "could not create a new stdio temp file: %s\n", err) 90 os.Exit(1) 91 } 92 defer func() { 93 stdioFile.Close() 94 os.Remove(stdioFile.Name()) 95 }() 96 ctrdStdioFilePath = stdioFile.Name() 97 stdioWriter := io.MultiWriter(stdioFile, buf) 98 99 err = ctrd.start("containerd", address, []string{ 100 "--root", defaultRoot, 101 "--state", defaultState, 102 "--log-level", "debug", 103 "--config", createShimDebugConfig(), 104 }, stdioWriter, stdioWriter) 105 if err != nil { 106 fmt.Fprintf(os.Stderr, "%s: %s\n", err, buf.String()) 107 os.Exit(1) 108 } 109 } 110 111 waitCtx, waitCancel := context.WithTimeout(ctx, 2*time.Second) 112 client, err := ctrd.waitForStart(waitCtx) 113 waitCancel() 114 if err != nil { 115 ctrd.Kill() 116 ctrd.Wait() 117 fmt.Fprintf(os.Stderr, "%s: %s\n", err, buf.String()) 118 os.Exit(1) 119 } 120 121 // print out the version in information 122 version, err := client.Version(ctx) 123 if err != nil { 124 fmt.Fprintf(os.Stderr, "error getting version: %s\n", err) 125 os.Exit(1) 126 } 127 128 // allow comparison with containerd under test 129 log.G(ctx).WithFields(logrus.Fields{ 130 "version": version.Version, 131 "revision": version.Revision, 132 "runtime": os.Getenv("TEST_RUNTIME"), 133 }).Info("running tests against containerd") 134 135 // pull a seed image 136 log.G(ctx).Info("start to pull seed image") 137 if _, err = client.Pull(ctx, testImage, WithPullUnpack); err != nil { 138 fmt.Fprintf(os.Stderr, "%s: %s\n", err, buf.String()) 139 ctrd.Kill() 140 ctrd.Wait() 141 os.Exit(1) 142 } 143 144 if err := client.Close(); err != nil { 145 fmt.Fprintln(os.Stderr, "failed to close client", err) 146 } 147 148 // run the test 149 status := m.Run() 150 151 if !noDaemon { 152 // tear down the daemon and resources created 153 if err := ctrd.Stop(); err != nil { 154 if err := ctrd.Kill(); err != nil { 155 fmt.Fprintln(os.Stderr, "failed to signal containerd", err) 156 } 157 } 158 if err := ctrd.Wait(); err != nil { 159 if _, ok := err.(*exec.ExitError); !ok { 160 fmt.Fprintln(os.Stderr, "failed to wait for containerd", err) 161 } 162 } 163 164 if err := sys.ForceRemoveAll(defaultRoot); err != nil { 165 fmt.Fprintln(os.Stderr, "failed to remove test root dir", err) 166 os.Exit(1) 167 } 168 // only print containerd logs if the test failed 169 if status != 0 { 170 fmt.Fprintln(os.Stderr, buf.String()) 171 } 172 } 173 os.Exit(status) 174 } 175 176 func newClient(t testing.TB, address string, opts ...ClientOpt) (*Client, error) { 177 if testing.Short() { 178 t.Skip() 179 } 180 if rt := os.Getenv("TEST_RUNTIME"); rt != "" { 181 opts = append(opts, WithDefaultRuntime(rt)) 182 } 183 // testutil.RequiresRoot(t) is not needed here (already called in TestMain) 184 return New(address, opts...) 185 } 186 187 func TestNewClient(t *testing.T) { 188 t.Parallel() 189 190 client, err := newClient(t, address) 191 if err != nil { 192 t.Fatal(err) 193 } 194 if client == nil { 195 t.Fatal("New() returned nil client") 196 } 197 if err := client.Close(); err != nil { 198 t.Errorf("client closed returned error %v", err) 199 } 200 } 201 202 // All the container's tests depends on this, we need it to run first. 203 func TestImagePull(t *testing.T) { 204 client, err := newClient(t, address) 205 if err != nil { 206 t.Fatal(err) 207 } 208 defer client.Close() 209 210 ctx, cancel := testContext(t) 211 defer cancel() 212 _, err = client.Pull(ctx, testImage, WithPlatformMatcher(platforms.Default())) 213 if err != nil { 214 t.Fatal(err) 215 } 216 } 217 218 func TestImagePullAllPlatforms(t *testing.T) { 219 client, err := newClient(t, address) 220 if err != nil { 221 t.Fatal(err) 222 } 223 defer client.Close() 224 ctx, cancel := testContext(t) 225 defer cancel() 226 227 cs := client.ContentStore() 228 img, err := client.Fetch(ctx, "docker.io/library/busybox:latest") 229 if err != nil { 230 t.Fatal(err) 231 } 232 index := img.Target 233 manifests, err := images.Children(ctx, cs, index) 234 if err != nil { 235 t.Fatal(err) 236 } 237 for _, manifest := range manifests { 238 children, err := images.Children(ctx, cs, manifest) 239 if err != nil { 240 t.Fatal("Th") 241 } 242 // check if childless data type has blob in content store 243 for _, desc := range children { 244 ra, err := cs.ReaderAt(ctx, desc) 245 if err != nil { 246 t.Fatal(err) 247 } 248 ra.Close() 249 } 250 } 251 } 252 253 func TestImagePullSomePlatforms(t *testing.T) { 254 client, err := newClient(t, address) 255 if err != nil { 256 t.Fatal(err) 257 } 258 defer client.Close() 259 ctx, cancel := testContext(t) 260 defer cancel() 261 262 cs := client.ContentStore() 263 platformList := []string{"linux/amd64", "linux/arm64/v8", "linux/s390x"} 264 m := make(map[string]platforms.Matcher) 265 var opts []RemoteOpt 266 267 for _, platform := range platformList { 268 p, err := platforms.Parse(platform) 269 if err != nil { 270 t.Fatal(err) 271 } 272 m[platform] = platforms.NewMatcher(p) 273 opts = append(opts, WithPlatform(platform)) 274 } 275 276 img, err := client.Fetch(ctx, "k8s.gcr.io/pause:3.1", opts...) 277 if err != nil { 278 t.Fatal(err) 279 } 280 281 index := img.Target 282 manifests, err := images.Children(ctx, cs, index) 283 if err != nil { 284 t.Fatal(err) 285 } 286 287 count := 0 288 for _, manifest := range manifests { 289 children, err := images.Children(ctx, cs, manifest) 290 291 found := false 292 for _, matcher := range m { 293 if manifest.Platform == nil { 294 t.Fatal("manifest should have proper platform") 295 } 296 if matcher.Match(*manifest.Platform) { 297 count++ 298 found = true 299 } 300 } 301 302 if found { 303 if len(children) == 0 { 304 t.Fatal("manifest should have pulled children content") 305 } 306 307 // check if childless data type has blob in content store 308 for _, desc := range children { 309 ra, err := cs.ReaderAt(ctx, desc) 310 if err != nil { 311 t.Fatal(err) 312 } 313 ra.Close() 314 } 315 } else if err == nil { 316 t.Fatal("manifest should not have pulled children content") 317 } 318 } 319 320 if count != len(platformList) { 321 t.Fatal("expected a different number of pulled manifests") 322 } 323 } 324 325 func TestImagePullSchema1(t *testing.T) { 326 client, err := newClient(t, address) 327 if err != nil { 328 t.Fatal(err) 329 } 330 defer client.Close() 331 332 ctx, cancel := testContext(t) 333 defer cancel() 334 schema1TestImage := "gcr.io/google_containers/pause:3.0@sha256:0d093c962a6c2dd8bb8727b661e2b5f13e9df884af9945b4cc7088d9350cd3ee" 335 _, err = client.Pull(ctx, schema1TestImage, WithPlatform(platforms.DefaultString()), WithSchema1Conversion) 336 if err != nil { 337 t.Fatal(err) 338 } 339 } 340 341 func TestImagePullWithConcurrencyLimit(t *testing.T) { 342 client, err := newClient(t, address) 343 if err != nil { 344 t.Fatal(err) 345 } 346 defer client.Close() 347 348 ctx, cancel := testContext(t) 349 defer cancel() 350 _, err = client.Pull(ctx, testImage, 351 WithPlatformMatcher(platforms.Default()), 352 WithMaxConcurrentDownloads(2)) 353 if err != nil { 354 t.Fatal(err) 355 } 356 } 357 358 func TestClientReconnect(t *testing.T) { 359 t.Parallel() 360 361 ctx, cancel := testContext(t) 362 defer cancel() 363 364 client, err := newClient(t, address) 365 if err != nil { 366 t.Fatal(err) 367 } 368 if client == nil { 369 t.Fatal("New() returned nil client") 370 } 371 ok, err := client.IsServing(ctx) 372 if err != nil { 373 t.Fatal(err) 374 } 375 if !ok { 376 t.Fatal("containerd is not serving") 377 } 378 if err := client.Reconnect(); err != nil { 379 t.Fatal(err) 380 } 381 if ok, err = client.IsServing(ctx); err != nil { 382 t.Fatal(err) 383 } 384 if !ok { 385 t.Fatal("containerd is not serving") 386 } 387 if err := client.Close(); err != nil { 388 t.Errorf("client closed returned error %v", err) 389 } 390 } 391 392 func createShimDebugConfig() string { 393 f, err := ioutil.TempFile("", "containerd-config-") 394 if err != nil { 395 fmt.Fprintf(os.Stderr, "Failed to create config file: %s\n", err) 396 os.Exit(1) 397 } 398 defer f.Close() 399 if _, err := f.WriteString("version = 1\n"); err != nil { 400 fmt.Fprintf(os.Stderr, "Failed to write to config file %s: %s\n", f.Name(), err) 401 os.Exit(1) 402 } 403 404 if _, err := f.WriteString("[plugins.linux]\n\tshim_debug = true\n"); err != nil { 405 fmt.Fprintf(os.Stderr, "Failed to write to config file %s: %s\n", f.Name(), err) 406 os.Exit(1) 407 } 408 409 return f.Name() 410 } 411 412 func TestDefaultRuntimeWithNamespaceLabels(t *testing.T) { 413 client, err := newClient(t, address) 414 if err != nil { 415 t.Fatal(err) 416 } 417 defer client.Close() 418 419 ctx, cancel := testContext(t) 420 defer cancel() 421 namespaces := client.NamespaceService() 422 testRuntime := "testRuntime" 423 runtimeLabel := defaults.DefaultRuntimeNSLabel 424 if err := namespaces.SetLabel(ctx, testNamespace, runtimeLabel, testRuntime); err != nil { 425 t.Fatal(err) 426 } 427 428 testClient, err := New(address, WithDefaultNamespace(testNamespace)) 429 if err != nil { 430 t.Fatal(err) 431 } 432 defer testClient.Close() 433 if testClient.runtime != testRuntime { 434 t.Error("failed to set default runtime from namespace labels") 435 } 436 }