github.com/containerd/Containerd@v1.4.13/oci/spec_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 oci 18 19 import ( 20 "context" 21 "runtime" 22 "testing" 23 24 "github.com/containerd/containerd/containers" 25 "github.com/containerd/containerd/namespaces" 26 specs "github.com/opencontainers/runtime-spec/specs-go" 27 ) 28 29 func TestGenerateSpec(t *testing.T) { 30 t.Parallel() 31 32 ctx := namespaces.WithNamespace(context.Background(), "testing") 33 s, err := GenerateSpec(ctx, nil, &containers.Container{ID: t.Name()}) 34 if err != nil { 35 t.Fatal(err) 36 } 37 if s == nil { 38 t.Fatal("GenerateSpec() returns a nil spec") 39 } 40 41 if runtime.GOOS != "windows" { 42 // check for matching caps 43 defaults := defaultUnixCaps() 44 for _, cl := range [][]string{ 45 s.Process.Capabilities.Bounding, 46 s.Process.Capabilities.Permitted, 47 s.Process.Capabilities.Inheritable, 48 s.Process.Capabilities.Effective, 49 } { 50 for i := 0; i < len(defaults); i++ { 51 if cl[i] != defaults[i] { 52 t.Errorf("cap at %d does not match set %q != %q", i, defaults[i], cl[i]) 53 } 54 } 55 } 56 57 // check default namespaces 58 defaultNS := defaultUnixNamespaces() 59 for i, ns := range s.Linux.Namespaces { 60 if defaultNS[i] != ns { 61 t.Errorf("ns at %d does not match set %q != %q", i, defaultNS[i], ns) 62 } 63 } 64 } else { 65 if s.Windows == nil { 66 t.Fatal("Windows section of spec not filled in on Windows platform") 67 } 68 } 69 70 // test that we don't have tty set 71 if s.Process.Terminal { 72 t.Error("terminal set on default process") 73 } 74 } 75 76 func TestGenerateSpecWithPlatform(t *testing.T) { 77 t.Parallel() 78 79 ctx := namespaces.WithNamespace(context.Background(), "testing") 80 platforms := []string{"windows/amd64", "linux/amd64"} 81 for _, p := range platforms { 82 t.Logf("Testing platform: %s", p) 83 s, err := GenerateSpecWithPlatform(ctx, nil, p, &containers.Container{ID: t.Name()}) 84 if err != nil { 85 t.Fatalf("failed to generate spec: %v", err) 86 } 87 88 if s.Root == nil { 89 t.Fatal("expected non nil Root section.") 90 } 91 if s.Process == nil { 92 t.Fatal("expected non nil Process section.") 93 } 94 if p == "windows/amd64" { 95 if s.Linux != nil { 96 t.Fatal("expected nil Linux section") 97 } 98 if s.Windows == nil { 99 t.Fatal("expected non nil Windows section") 100 } 101 } else { 102 if s.Linux == nil { 103 t.Fatal("expected non nil Linux section") 104 } 105 if runtime.GOOS == "windows" && s.Windows == nil { 106 t.Fatal("expected non nil Windows section for LCOW") 107 } else if runtime.GOOS != "windows" && s.Windows != nil { 108 t.Fatal("expected nil Windows section") 109 } 110 } 111 } 112 } 113 114 func TestSpecWithTTY(t *testing.T) { 115 t.Parallel() 116 117 ctx := namespaces.WithNamespace(context.Background(), "testing") 118 s, err := GenerateSpec(ctx, nil, &containers.Container{ID: t.Name()}, WithTTY) 119 if err != nil { 120 t.Fatal(err) 121 } 122 if !s.Process.Terminal { 123 t.Error("terminal net set WithTTY()") 124 } 125 if runtime.GOOS != "windows" { 126 v := s.Process.Env[len(s.Process.Env)-1] 127 if v != "TERM=xterm" { 128 t.Errorf("xterm not set in env for TTY") 129 } 130 } else { 131 if len(s.Process.Env) != 0 { 132 t.Fatal("Windows process args should be empty by default") 133 } 134 } 135 } 136 137 func TestWithLinuxNamespace(t *testing.T) { 138 t.Parallel() 139 140 ctx := namespaces.WithNamespace(context.Background(), "testing") 141 replacedNS := specs.LinuxNamespace{Type: specs.NetworkNamespace, Path: "/var/run/netns/test"} 142 143 var s *specs.Spec 144 var err error 145 if runtime.GOOS != "windows" { 146 s, err = GenerateSpec(ctx, nil, &containers.Container{ID: t.Name()}, WithLinuxNamespace(replacedNS)) 147 } else { 148 s, err = GenerateSpecWithPlatform(ctx, nil, "linux/amd64", &containers.Container{ID: t.Name()}, WithLinuxNamespace(replacedNS)) 149 } 150 if err != nil { 151 t.Fatal(err) 152 } 153 154 defaultNS := defaultUnixNamespaces() 155 found := false 156 for i, ns := range s.Linux.Namespaces { 157 if ns == replacedNS && !found { 158 found = true 159 continue 160 } 161 if defaultNS[i] != ns { 162 t.Errorf("ns at %d does not match set %q != %q", i, defaultNS[i], ns) 163 } 164 } 165 } 166 167 func TestWithCapabilities(t *testing.T) { 168 t.Parallel() 169 170 ctx := namespaces.WithNamespace(context.Background(), "testing") 171 172 opts := []SpecOpts{ 173 WithCapabilities([]string{"CAP_SYS_ADMIN"}), 174 } 175 var s *specs.Spec 176 var err error 177 if runtime.GOOS != "windows" { 178 s, err = GenerateSpec(ctx, nil, &containers.Container{ID: t.Name()}, opts...) 179 } else { 180 s, err = GenerateSpecWithPlatform(ctx, nil, "linux/amd64", &containers.Container{ID: t.Name()}, opts...) 181 } 182 if err != nil { 183 t.Fatal(err) 184 } 185 186 if len(s.Process.Capabilities.Bounding) != 1 || s.Process.Capabilities.Bounding[0] != "CAP_SYS_ADMIN" { 187 t.Error("Unexpected capabilities set") 188 } 189 if len(s.Process.Capabilities.Effective) != 1 || s.Process.Capabilities.Effective[0] != "CAP_SYS_ADMIN" { 190 t.Error("Unexpected capabilities set") 191 } 192 if len(s.Process.Capabilities.Permitted) != 1 || s.Process.Capabilities.Permitted[0] != "CAP_SYS_ADMIN" { 193 t.Error("Unexpected capabilities set") 194 } 195 if len(s.Process.Capabilities.Inheritable) != 1 || s.Process.Capabilities.Inheritable[0] != "CAP_SYS_ADMIN" { 196 t.Error("Unexpected capabilities set") 197 } 198 } 199 200 func TestWithCapabilitiesNil(t *testing.T) { 201 t.Parallel() 202 203 ctx := namespaces.WithNamespace(context.Background(), "testing") 204 205 s, err := GenerateSpec(ctx, nil, &containers.Container{ID: t.Name()}, 206 WithCapabilities(nil), 207 ) 208 if err != nil { 209 t.Fatal(err) 210 } 211 212 if len(s.Process.Capabilities.Bounding) != 0 { 213 t.Errorf("Unexpected capabilities set: length is non zero (%d)", len(s.Process.Capabilities.Bounding)) 214 } 215 if len(s.Process.Capabilities.Effective) != 0 { 216 t.Errorf("Unexpected capabilities set: length is non zero (%d)", len(s.Process.Capabilities.Effective)) 217 } 218 if len(s.Process.Capabilities.Permitted) != 0 { 219 t.Errorf("Unexpected capabilities set: length is non zero (%d)", len(s.Process.Capabilities.Permitted)) 220 } 221 if len(s.Process.Capabilities.Inheritable) != 0 { 222 t.Errorf("Unexpected capabilities set: length is non zero (%d)", len(s.Process.Capabilities.Inheritable)) 223 } 224 } 225 226 func TestPopulateDefaultWindowsSpec(t *testing.T) { 227 var ( 228 c = containers.Container{ID: "TestWithDefaultSpec"} 229 ctx = namespaces.WithNamespace(context.Background(), "test") 230 ) 231 var expected Spec 232 233 populateDefaultWindowsSpec(ctx, &expected, c.ID) 234 if expected.Windows == nil { 235 t.Error("Cannot populate windows Spec") 236 } 237 } 238 239 func TestPopulateDefaultUnixSpec(t *testing.T) { 240 var ( 241 c = containers.Container{ID: "TestWithDefaultSpec"} 242 ctx = namespaces.WithNamespace(context.Background(), "test") 243 ) 244 var expected Spec 245 246 populateDefaultUnixSpec(ctx, &expected, c.ID) 247 if expected.Linux == nil { 248 t.Error("Cannot populate Unix Spec") 249 } 250 } 251 252 func TestWithPrivileged(t *testing.T) { 253 t.Parallel() 254 255 ctx := namespaces.WithNamespace(context.Background(), "testing") 256 257 opts := []SpecOpts{ 258 WithCapabilities(nil), 259 WithMounts([]specs.Mount{ 260 {Type: "cgroup", Destination: "/sys/fs/cgroup", Options: []string{"ro"}}, 261 }), 262 WithPrivileged, 263 } 264 var s *specs.Spec 265 var err error 266 if runtime.GOOS != "windows" { 267 s, err = GenerateSpec(ctx, nil, &containers.Container{ID: t.Name()}, opts...) 268 } else { 269 s, err = GenerateSpecWithPlatform(ctx, nil, "linux/amd64", &containers.Container{ID: t.Name()}, opts...) 270 } 271 if err != nil { 272 t.Fatal(err) 273 } 274 275 if len(s.Process.Capabilities.Bounding) == 0 { 276 t.Error("Expected capabilities to be set with privileged") 277 } 278 279 var foundSys, foundCgroup bool 280 for _, m := range s.Mounts { 281 switch m.Type { 282 case "sysfs": 283 foundSys = true 284 var found bool 285 for _, o := range m.Options { 286 switch o { 287 case "ro": 288 t.Errorf("Found unexpected read only %s mount", m.Type) 289 case "rw": 290 found = true 291 } 292 } 293 if !found { 294 t.Errorf("Did not find rw mount option for %s", m.Type) 295 } 296 case "cgroup": 297 foundCgroup = true 298 var found bool 299 for _, o := range m.Options { 300 switch o { 301 case "ro": 302 t.Errorf("Found unexpected read only %s mount", m.Type) 303 case "rw": 304 found = true 305 } 306 } 307 if !found { 308 t.Errorf("Did not find rw mount option for %s", m.Type) 309 } 310 } 311 } 312 if !foundSys { 313 t.Error("Did not find mount for sysfs") 314 } 315 if !foundCgroup { 316 t.Error("Did not find mount for cgroupfs") 317 } 318 }