github.com/demonoid81/containerd@v1.3.4/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 TestWithPrivileged(t *testing.T) { 227 t.Parallel() 228 229 ctx := namespaces.WithNamespace(context.Background(), "testing") 230 231 opts := []SpecOpts{ 232 WithCapabilities(nil), 233 WithMounts([]specs.Mount{ 234 {Type: "cgroup", Destination: "/sys/fs/cgroup", Options: []string{"ro"}}, 235 }), 236 WithPrivileged, 237 } 238 var s *specs.Spec 239 var err error 240 if runtime.GOOS != "windows" { 241 s, err = GenerateSpec(ctx, nil, &containers.Container{ID: t.Name()}, opts...) 242 } else { 243 s, err = GenerateSpecWithPlatform(ctx, nil, "linux/amd64", &containers.Container{ID: t.Name()}, opts...) 244 } 245 if err != nil { 246 t.Fatal(err) 247 } 248 249 if len(s.Process.Capabilities.Bounding) == 0 { 250 t.Error("Expected capabilities to be set with privileged") 251 } 252 253 var foundSys, foundCgroup bool 254 for _, m := range s.Mounts { 255 switch m.Type { 256 case "sysfs": 257 foundSys = true 258 var found bool 259 for _, o := range m.Options { 260 switch o { 261 case "ro": 262 t.Errorf("Found unexpected read only %s mount", m.Type) 263 case "rw": 264 found = true 265 } 266 } 267 if !found { 268 t.Errorf("Did not find rw mount option for %s", m.Type) 269 } 270 case "cgroup": 271 foundCgroup = true 272 var found bool 273 for _, o := range m.Options { 274 switch o { 275 case "ro": 276 t.Errorf("Found unexpected read only %s mount", m.Type) 277 case "rw": 278 found = true 279 } 280 } 281 if !found { 282 t.Errorf("Did not find rw mount option for %s", m.Type) 283 } 284 } 285 } 286 if !foundSys { 287 t.Error("Did not find mount for sysfs") 288 } 289 if !foundCgroup { 290 t.Error("Did not find mount for cgroupfs") 291 } 292 }