github.com/demonoid81/containerd@v1.3.4/daemon_config_linux_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 "bufio" 21 "bytes" 22 "context" 23 "fmt" 24 "io/ioutil" 25 "os" 26 "os/exec" 27 "path/filepath" 28 "strings" 29 "syscall" 30 "testing" 31 "time" 32 33 "github.com/containerd/containerd/oci" 34 "github.com/containerd/containerd/pkg/testutil" 35 "github.com/containerd/containerd/plugin" 36 "github.com/containerd/containerd/runtime/v2/runc/options" 37 srvconfig "github.com/containerd/containerd/services/server/config" 38 ) 39 40 // the following nolint is for shutting up gometalinter on non-linux. 41 // nolint: unused 42 func newDaemonWithConfig(t *testing.T, configTOML string) (*Client, *daemon, func()) { 43 if testing.Short() { 44 t.Skip() 45 } 46 testutil.RequiresRoot(t) 47 var ( 48 ctrd = daemon{} 49 configTOMLDecoded srvconfig.Config 50 buf = bytes.NewBuffer(nil) 51 ) 52 53 tempDir, err := ioutil.TempDir("", "containerd-test-new-daemon-with-config") 54 if err != nil { 55 t.Fatal(err) 56 } 57 defer func() { 58 if err != nil { 59 os.RemoveAll(tempDir) 60 } 61 }() 62 63 configTOMLFile := filepath.Join(tempDir, "config.toml") 64 if err = ioutil.WriteFile(configTOMLFile, []byte(configTOML), 0600); err != nil { 65 t.Fatal(err) 66 } 67 68 if err = srvconfig.LoadConfig(configTOMLFile, &configTOMLDecoded); err != nil { 69 t.Fatal(err) 70 } 71 72 address := configTOMLDecoded.GRPC.Address 73 if address == "" { 74 address = filepath.Join(tempDir, "containerd.sock") 75 } 76 args := []string{"-c", configTOMLFile} 77 if configTOMLDecoded.Root == "" { 78 args = append(args, "--root", filepath.Join(tempDir, "root")) 79 } 80 if configTOMLDecoded.State == "" { 81 args = append(args, "--state", filepath.Join(tempDir, "state")) 82 } 83 if err = ctrd.start("containerd", address, args, buf, buf); err != nil { 84 t.Fatalf("%v: %s", err, buf.String()) 85 } 86 87 waitCtx, waitCancel := context.WithTimeout(context.TODO(), 2*time.Second) 88 client, err := ctrd.waitForStart(waitCtx) 89 waitCancel() 90 if err != nil { 91 ctrd.Kill() 92 ctrd.Wait() 93 t.Fatalf("%v: %s", err, buf.String()) 94 } 95 96 cleanup := func() { 97 if err := client.Close(); err != nil { 98 t.Fatalf("failed to close client: %v", err) 99 } 100 if err := ctrd.Stop(); err != nil { 101 if err := ctrd.Kill(); err != nil { 102 t.Fatalf("failed to signal containerd: %v", err) 103 } 104 } 105 if err := ctrd.Wait(); err != nil { 106 if _, ok := err.(*exec.ExitError); !ok { 107 t.Fatalf("failed to wait for: %v", err) 108 } 109 } 110 if err := os.RemoveAll(tempDir); err != nil { 111 t.Fatalf("failed to remove %s: %v", tempDir, err) 112 } 113 // cleaning config-specific resources is up to the caller 114 } 115 return client, &ctrd, cleanup 116 } 117 118 // TestDaemonRuntimeRoot ensures plugin.linux.runtime_root is not ignored 119 func TestDaemonRuntimeRoot(t *testing.T) { 120 runtimeRoot, err := ioutil.TempDir("", "containerd-test-runtime-root") 121 if err != nil { 122 t.Fatal(err) 123 } 124 defer func() { 125 if err != nil { 126 os.RemoveAll(runtimeRoot) 127 } 128 }() 129 configTOML := ` 130 version = 1 131 [plugins] 132 [plugins.cri] 133 stream_server_port = "0" 134 ` 135 136 client, _, cleanup := newDaemonWithConfig(t, configTOML) 137 defer cleanup() 138 139 ctx, cancel := testContext(t) 140 defer cancel() 141 // FIXME(AkihiroSuda): import locally frozen image? 142 image, err := client.Pull(ctx, testImage, WithPullUnpack) 143 if err != nil { 144 t.Fatal(err) 145 } 146 147 id := t.Name() 148 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), withProcessArgs("top")), WithRuntime(plugin.RuntimeRuncV1, &options.Options{ 149 Root: runtimeRoot, 150 })) 151 if err != nil { 152 t.Fatal(err) 153 } 154 defer container.Delete(ctx, WithSnapshotCleanup) 155 156 task, err := container.NewTask(ctx, empty()) 157 if err != nil { 158 t.Fatal(err) 159 } 160 defer task.Delete(ctx) 161 162 status, err := task.Wait(ctx) 163 if err != nil { 164 t.Fatal(err) 165 } 166 167 containerPath := filepath.Join(runtimeRoot, testNamespace, id) 168 if _, err = os.Stat(containerPath); err != nil { 169 t.Errorf("error while getting stat for %s: %v", containerPath, err) 170 } 171 172 if err = task.Kill(ctx, syscall.SIGKILL); err != nil { 173 t.Error(err) 174 } 175 <-status 176 } 177 178 // code most copy from https://github.com/opencontainers/runc 179 func getCgroupPath() (map[string]string, error) { 180 cgroupPath := make(map[string]string) 181 f, err := os.Open("/proc/self/mountinfo") 182 if err != nil { 183 return nil, err 184 } 185 defer f.Close() 186 187 scanner := bufio.NewScanner(f) 188 for scanner.Scan() { 189 text := scanner.Text() 190 fields := strings.Split(text, " ") 191 // Safe as mountinfo encodes mountpoints with spaces as \040. 192 index := strings.Index(text, " - ") 193 postSeparatorFields := strings.Fields(text[index+3:]) 194 numPostFields := len(postSeparatorFields) 195 196 // This is an error as we can't detect if the mount is for "cgroup" 197 if numPostFields == 0 { 198 continue 199 } 200 201 if postSeparatorFields[0] == "cgroup" { 202 // Check that the mount is properly formatted. 203 if numPostFields < 3 { 204 continue 205 } 206 cgroupPath[filepath.Base(fields[4])] = fields[4] 207 } 208 } 209 210 return cgroupPath, nil 211 } 212 213 // TestDaemonCustomCgroup ensures plugin.cgroup.path is not ignored 214 func TestDaemonCustomCgroup(t *testing.T) { 215 cgroupPath, err := getCgroupPath() 216 if err != nil { 217 t.Fatal(err) 218 } 219 if len(cgroupPath) == 0 { 220 t.Skip("skip TestDaemonCustomCgroup since no cgroup path available") 221 } 222 223 customCgroup := fmt.Sprintf("%d", time.Now().Nanosecond()) 224 configTOML := ` 225 version = 1 226 [cgroup] 227 path = "` + customCgroup + `"` 228 229 _, _, cleanup := newDaemonWithConfig(t, configTOML) 230 231 defer func() { 232 // do cgroup path clean 233 for _, v := range cgroupPath { 234 if _, err := os.Stat(filepath.Join(v, customCgroup)); err == nil { 235 if err := os.RemoveAll(filepath.Join(v, customCgroup)); err != nil { 236 t.Logf("failed to remove cgroup path %s", filepath.Join(v, customCgroup)) 237 } 238 } 239 } 240 }() 241 242 defer cleanup() 243 244 paths := []string{ 245 "devices", 246 "memory", 247 "cpu", 248 "blkio", 249 } 250 251 for _, p := range paths { 252 v := cgroupPath[p] 253 if v == "" { 254 continue 255 } 256 path := filepath.Join(v, customCgroup) 257 if _, err := os.Stat(path); err != nil { 258 if os.IsNotExist(err) { 259 t.Fatalf("custom cgroup path %s should exist, actually not", path) 260 } 261 } 262 } 263 }