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  }