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  }