github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/cio/io_test.go (about)

     1  // +build !windows
     2  
     3  /*
     4     Copyright The containerd Authors.
     5  
     6     Licensed under the Apache License, Version 2.0 (the "License");
     7     you may not use this file except in compliance with the License.
     8     You may obtain a copy of the License at
     9  
    10         http://www.apache.org/licenses/LICENSE-2.0
    11  
    12     Unless required by applicable law or agreed to in writing, software
    13     distributed under the License is distributed on an "AS IS" BASIS,
    14     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15     See the License for the specific language governing permissions and
    16     limitations under the License.
    17  */
    18  
    19  package cio
    20  
    21  import (
    22  	"bytes"
    23  	"context"
    24  	"io"
    25  	"io/ioutil"
    26  	"net/url"
    27  	"os"
    28  	"path/filepath"
    29  	"runtime"
    30  	"strings"
    31  	"syscall"
    32  	"testing"
    33  
    34  	"github.com/containerd/fifo"
    35  	"github.com/google/go-cmp/cmp/cmpopts"
    36  	"gotest.tools/v3/assert"
    37  	is "gotest.tools/v3/assert/cmp"
    38  )
    39  
    40  func assertHasPrefix(t *testing.T, s, prefix string) {
    41  	t.Helper()
    42  	if !strings.HasPrefix(s, prefix) {
    43  		t.Fatalf("expected %s to start with %s", s, prefix)
    44  	}
    45  }
    46  
    47  func TestNewFIFOSetInDir(t *testing.T) {
    48  	if runtime.GOOS == "windows" {
    49  		t.Skip("NewFIFOSetInDir has different behaviour on windows")
    50  	}
    51  
    52  	root, err := ioutil.TempDir("", "test-new-fifo-set")
    53  	assert.NilError(t, err)
    54  	defer os.RemoveAll(root)
    55  
    56  	fifos, err := NewFIFOSetInDir(root, "theid", true)
    57  	assert.NilError(t, err)
    58  
    59  	dir := filepath.Dir(fifos.Stdin)
    60  	assertHasPrefix(t, dir, root)
    61  	expected := &FIFOSet{
    62  		Config: Config{
    63  			Stdin:    filepath.Join(dir, "theid-stdin"),
    64  			Stdout:   filepath.Join(dir, "theid-stdout"),
    65  			Stderr:   filepath.Join(dir, "theid-stderr"),
    66  			Terminal: true,
    67  		},
    68  	}
    69  	assert.Assert(t, is.DeepEqual(fifos, expected, cmpFIFOSet))
    70  
    71  	files, err := ioutil.ReadDir(root)
    72  	assert.NilError(t, err)
    73  	assert.Check(t, is.Len(files, 1))
    74  
    75  	assert.NilError(t, fifos.Close())
    76  	files, err = ioutil.ReadDir(root)
    77  	assert.NilError(t, err)
    78  	assert.Check(t, is.Len(files, 0))
    79  }
    80  
    81  var cmpFIFOSet = cmpopts.IgnoreUnexported(FIFOSet{})
    82  
    83  func TestNewAttach(t *testing.T) {
    84  	if runtime.GOOS == "windows" {
    85  		t.Skip("setupFIFOProducers not yet implemented on windows")
    86  	}
    87  	var (
    88  		expectedStdin  = "this is the stdin"
    89  		expectedStdout = "this is the stdout"
    90  		expectedStderr = "this is the stderr"
    91  		stdin          = bytes.NewBufferString(expectedStdin)
    92  		stdout         = new(bytes.Buffer)
    93  		stderr         = new(bytes.Buffer)
    94  	)
    95  
    96  	withBytesBuffers := func(streams *Streams) {
    97  		*streams = Streams{Stdin: stdin, Stdout: stdout, Stderr: stderr}
    98  	}
    99  	attacher := NewAttach(withBytesBuffers)
   100  
   101  	fifos, err := NewFIFOSetInDir("", "theid", false)
   102  	assert.NilError(t, err)
   103  
   104  	io, err := attacher(fifos)
   105  	assert.NilError(t, err)
   106  	defer io.Close()
   107  
   108  	producers := setupFIFOProducers(t, io.Config())
   109  	initProducers(t, producers, expectedStdout, expectedStderr)
   110  
   111  	actualStdin, err := ioutil.ReadAll(producers.Stdin)
   112  	assert.NilError(t, err)
   113  
   114  	io.Wait()
   115  	io.Cancel()
   116  	assert.NilError(t, io.Close())
   117  
   118  	assert.Check(t, is.Equal(expectedStdout, stdout.String()))
   119  	assert.Check(t, is.Equal(expectedStderr, stderr.String()))
   120  	assert.Check(t, is.Equal(expectedStdin, string(actualStdin)))
   121  }
   122  
   123  type producers struct {
   124  	Stdin  io.ReadCloser
   125  	Stdout io.WriteCloser
   126  	Stderr io.WriteCloser
   127  }
   128  
   129  func setupFIFOProducers(t *testing.T, fifos Config) producers {
   130  	var (
   131  		err   error
   132  		pipes producers
   133  		ctx   = context.Background()
   134  	)
   135  
   136  	pipes.Stdin, err = fifo.OpenFifo(ctx, fifos.Stdin, syscall.O_RDONLY, 0)
   137  	assert.NilError(t, err)
   138  
   139  	pipes.Stdout, err = fifo.OpenFifo(ctx, fifos.Stdout, syscall.O_WRONLY, 0)
   140  	assert.NilError(t, err)
   141  
   142  	pipes.Stderr, err = fifo.OpenFifo(ctx, fifos.Stderr, syscall.O_WRONLY, 0)
   143  	assert.NilError(t, err)
   144  
   145  	return pipes
   146  }
   147  
   148  func initProducers(t *testing.T, producers producers, stdout, stderr string) {
   149  	_, err := producers.Stdout.Write([]byte(stdout))
   150  	assert.NilError(t, err)
   151  	assert.NilError(t, producers.Stdout.Close())
   152  
   153  	_, err = producers.Stderr.Write([]byte(stderr))
   154  	assert.NilError(t, err)
   155  	assert.NilError(t, producers.Stderr.Close())
   156  }
   157  
   158  func TestBinaryIOArgs(t *testing.T) {
   159  	res, err := BinaryIO("/file.bin", map[string]string{"id": "1"})("")
   160  	assert.NilError(t, err)
   161  	assert.Equal(t, "binary:///file.bin?id=1", res.Config().Stdout)
   162  	assert.Equal(t, "binary:///file.bin?id=1", res.Config().Stderr)
   163  }
   164  
   165  func TestBinaryIOAbsolutePath(t *testing.T) {
   166  	res, err := BinaryIO("/full/path/bin", nil)("!")
   167  	assert.NilError(t, err)
   168  
   169  	// Test parse back
   170  	parsed, err := url.Parse(res.Config().Stdout)
   171  	assert.NilError(t, err)
   172  	assert.Equal(t, "binary", parsed.Scheme)
   173  	assert.Equal(t, "/full/path/bin", parsed.Path)
   174  }
   175  
   176  func TestBinaryIOFailOnRelativePath(t *testing.T) {
   177  	_, err := BinaryIO("./bin", nil)("!")
   178  	assert.Error(t, err, "absolute path needed")
   179  }
   180  
   181  func TestLogFileAbsolutePath(t *testing.T) {
   182  	res, err := LogFile("/full/path/file.txt")("!")
   183  	assert.NilError(t, err)
   184  	assert.Equal(t, "file:///full/path/file.txt", res.Config().Stdout)
   185  	assert.Equal(t, "file:///full/path/file.txt", res.Config().Stderr)
   186  
   187  	// Test parse back
   188  	parsed, err := url.Parse(res.Config().Stdout)
   189  	assert.NilError(t, err)
   190  	assert.Equal(t, "file", parsed.Scheme)
   191  	assert.Equal(t, "/full/path/file.txt", parsed.Path)
   192  }
   193  
   194  func TestLogFileFailOnRelativePath(t *testing.T) {
   195  	_, err := LogFile("./file.txt")("!")
   196  	assert.Error(t, err, "absolute path needed")
   197  }
   198  
   199  func TestLogURIGenerator(t *testing.T) {
   200  	for _, tc := range []struct {
   201  		scheme   string
   202  		path     string
   203  		args     map[string]string
   204  		expected string
   205  		err      string
   206  	}{
   207  		{
   208  			scheme:   "fifo",
   209  			path:     "/full/path/pipe.fifo",
   210  			expected: "fifo:///full/path/pipe.fifo",
   211  		},
   212  		{
   213  			scheme: "file",
   214  			path:   "/full/path/file.txt",
   215  			args: map[string]string{
   216  				"maxSize": "100MB",
   217  			},
   218  			expected: "file:///full/path/file.txt?maxSize=100MB",
   219  		},
   220  		{
   221  			scheme: "binary",
   222  			path:   "/full/path/bin",
   223  			args: map[string]string{
   224  				"id": "testing",
   225  			},
   226  			expected: "binary:///full/path/bin?id=testing",
   227  		},
   228  		{
   229  			scheme: "unknown",
   230  			path:   "nowhere",
   231  			err:    "absolute path needed",
   232  		},
   233  	} {
   234  		uri, err := LogURIGenerator(tc.scheme, tc.path, tc.args)
   235  		if err != nil {
   236  			assert.Error(t, err, tc.err)
   237  			continue
   238  		}
   239  		assert.Equal(t, tc.expected, uri.String())
   240  	}
   241  }