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 }