github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/drivers/docker/docklog/docker_logger_test.go (about) 1 package docklog 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "runtime" 8 "testing" 9 "time" 10 11 docker "github.com/fsouza/go-dockerclient" 12 ctu "github.com/hashicorp/nomad/client/testutil" 13 "github.com/hashicorp/nomad/helper/testlog" 14 "github.com/hashicorp/nomad/testutil" 15 "github.com/stretchr/testify/require" 16 "golang.org/x/net/context" 17 ) 18 19 func testContainerDetails() (image string, imageName string, imageTag string) { 20 if runtime.GOOS == "windows" { 21 return "dantoml/busybox-windows:08012019", 22 "dantoml/busybox-windows", 23 "08012019" 24 } 25 26 return "busybox:1", "busybox", "1" 27 } 28 29 func TestDockerLogger_Success(t *testing.T) { 30 ctu.DockerCompatible(t) 31 32 t.Parallel() 33 require := require.New(t) 34 35 containerImage, containerImageName, containerImageTag := testContainerDetails() 36 37 client, err := docker.NewClientFromEnv() 38 if err != nil { 39 t.Skip("docker unavailable:", err) 40 } 41 42 if img, err := client.InspectImage(containerImage); err != nil || img == nil { 43 t.Log("image not found locally, downloading...") 44 err = client.PullImage(docker.PullImageOptions{ 45 Repository: containerImageName, 46 Tag: containerImageTag, 47 }, docker.AuthConfiguration{}) 48 require.NoError(err, "failed to pull image") 49 } 50 51 containerConf := docker.CreateContainerOptions{ 52 Config: &docker.Config{ 53 Cmd: []string{ 54 "sh", "-c", "touch ~/docklog; tail -f ~/docklog", 55 }, 56 Image: containerImage, 57 }, 58 Context: context.Background(), 59 } 60 61 container, err := client.CreateContainer(containerConf) 62 require.NoError(err) 63 64 defer client.RemoveContainer(docker.RemoveContainerOptions{ 65 ID: container.ID, 66 Force: true, 67 }) 68 69 err = client.StartContainer(container.ID, nil) 70 require.NoError(err) 71 72 testutil.WaitForResult(func() (bool, error) { 73 container, err = client.InspectContainer(container.ID) 74 if err != nil { 75 return false, err 76 } 77 if !container.State.Running { 78 return false, fmt.Errorf("container not running") 79 } 80 return true, nil 81 }, func(err error) { 82 require.NoError(err) 83 }) 84 85 stdout := &noopCloser{bytes.NewBuffer(nil)} 86 stderr := &noopCloser{bytes.NewBuffer(nil)} 87 88 dl := NewDockerLogger(testlog.HCLogger(t)).(*dockerLogger) 89 dl.stdout = stdout 90 dl.stderr = stderr 91 require.NoError(dl.Start(&StartOpts{ 92 ContainerID: container.ID, 93 })) 94 95 echoToContainer(t, client, container.ID, "abc") 96 echoToContainer(t, client, container.ID, "123") 97 98 testutil.WaitForResult(func() (bool, error) { 99 act := stdout.String() 100 if "abc\n123\n" != act { 101 return false, fmt.Errorf("expected abc\\n123\\n for stdout but got %s", act) 102 } 103 104 return true, nil 105 }, func(err error) { 106 require.NoError(err) 107 }) 108 } 109 110 func TestDockerLogger_Success_TTY(t *testing.T) { 111 ctu.DockerCompatible(t) 112 113 t.Parallel() 114 require := require.New(t) 115 116 containerImage, containerImageName, containerImageTag := testContainerDetails() 117 118 client, err := docker.NewClientFromEnv() 119 if err != nil { 120 t.Skip("docker unavailable:", err) 121 } 122 123 if img, err := client.InspectImage(containerImage); err != nil || img == nil { 124 t.Log("image not found locally, downloading...") 125 err = client.PullImage(docker.PullImageOptions{ 126 Repository: containerImageName, 127 Tag: containerImageTag, 128 }, docker.AuthConfiguration{}) 129 require.NoError(err, "failed to pull image") 130 } 131 132 containerConf := docker.CreateContainerOptions{ 133 Config: &docker.Config{ 134 Cmd: []string{ 135 "sh", "-c", "touch ~/docklog; tail -f ~/docklog", 136 }, 137 Image: containerImage, 138 Tty: true, 139 }, 140 Context: context.Background(), 141 } 142 143 container, err := client.CreateContainer(containerConf) 144 require.NoError(err) 145 146 defer client.RemoveContainer(docker.RemoveContainerOptions{ 147 ID: container.ID, 148 Force: true, 149 }) 150 151 err = client.StartContainer(container.ID, nil) 152 require.NoError(err) 153 154 testutil.WaitForResult(func() (bool, error) { 155 container, err = client.InspectContainer(container.ID) 156 if err != nil { 157 return false, err 158 } 159 if !container.State.Running { 160 return false, fmt.Errorf("container not running") 161 } 162 return true, nil 163 }, func(err error) { 164 require.NoError(err) 165 }) 166 167 stdout := &noopCloser{bytes.NewBuffer(nil)} 168 stderr := &noopCloser{bytes.NewBuffer(nil)} 169 170 dl := NewDockerLogger(testlog.HCLogger(t)).(*dockerLogger) 171 dl.stdout = stdout 172 dl.stderr = stderr 173 require.NoError(dl.Start(&StartOpts{ 174 ContainerID: container.ID, 175 TTY: true, 176 })) 177 178 echoToContainer(t, client, container.ID, "abc") 179 echoToContainer(t, client, container.ID, "123") 180 181 testutil.WaitForResult(func() (bool, error) { 182 act := stdout.String() 183 if "abc\r\n123\r\n" != act { 184 return false, fmt.Errorf("expected abc\\n123\\n for stdout but got %s", act) 185 } 186 187 return true, nil 188 }, func(err error) { 189 require.NoError(err) 190 }) 191 } 192 193 func echoToContainer(t *testing.T, client *docker.Client, id string, line string) { 194 op := docker.CreateExecOptions{ 195 Container: id, 196 Cmd: []string{ 197 "/bin/sh", "-c", 198 fmt.Sprintf("echo %s >>~/docklog", line), 199 }, 200 } 201 202 exec, err := client.CreateExec(op) 203 require.NoError(t, err) 204 require.NoError(t, client.StartExec(exec.ID, docker.StartExecOptions{Detach: true})) 205 } 206 207 func TestDockerLogger_LoggingNotSupported(t *testing.T) { 208 ctu.DockerCompatible(t) 209 210 t.Parallel() 211 require := require.New(t) 212 213 containerImage, containerImageName, containerImageTag := testContainerDetails() 214 215 client, err := docker.NewClientFromEnv() 216 if err != nil { 217 t.Skip("docker unavailable:", err) 218 } 219 220 if img, err := client.InspectImage(containerImage); err != nil || img == nil { 221 t.Log("image not found locally, downloading...") 222 err = client.PullImage(docker.PullImageOptions{ 223 Repository: containerImageName, 224 Tag: containerImageTag, 225 }, docker.AuthConfiguration{}) 226 require.NoError(err, "failed to pull image") 227 } 228 229 containerConf := docker.CreateContainerOptions{ 230 Config: &docker.Config{ 231 Cmd: []string{ 232 "sh", "-c", "touch ~/docklog; tail -f ~/docklog", 233 }, 234 Image: containerImage, 235 }, 236 HostConfig: &docker.HostConfig{ 237 LogConfig: docker.LogConfig{ 238 Type: "gelf", 239 Config: map[string]string{ 240 "gelf-address": "udp://localhost:12201", 241 "mode": "non-blocking", 242 "max-buffer-size": "4m", 243 }, 244 }, 245 }, 246 Context: context.Background(), 247 } 248 249 container, err := client.CreateContainer(containerConf) 250 require.NoError(err) 251 252 defer client.RemoveContainer(docker.RemoveContainerOptions{ 253 ID: container.ID, 254 Force: true, 255 }) 256 257 err = client.StartContainer(container.ID, nil) 258 require.NoError(err) 259 260 testutil.WaitForResult(func() (bool, error) { 261 container, err = client.InspectContainer(container.ID) 262 if err != nil { 263 return false, err 264 } 265 if !container.State.Running { 266 return false, fmt.Errorf("container not running") 267 } 268 return true, nil 269 }, func(err error) { 270 require.NoError(err) 271 }) 272 273 stdout := &noopCloser{bytes.NewBuffer(nil)} 274 stderr := &noopCloser{bytes.NewBuffer(nil)} 275 276 dl := NewDockerLogger(testlog.HCLogger(t)).(*dockerLogger) 277 dl.stdout = stdout 278 dl.stderr = stderr 279 require.NoError(dl.Start(&StartOpts{ 280 ContainerID: container.ID, 281 })) 282 283 select { 284 case <-dl.doneCh: 285 case <-time.After(10 * time.Second): 286 require.Fail("timedout while waiting for docker_logging to terminate") 287 } 288 } 289 290 type noopCloser struct { 291 *bytes.Buffer 292 } 293 294 func (*noopCloser) Close() error { 295 return nil 296 } 297 298 func TestNextBackoff(t *testing.T) { 299 cases := []struct { 300 currentBackoff float64 301 min float64 302 max float64 303 }{ 304 {0.0, 0.5, 1.15}, 305 {5.0, 5.0, 16}, 306 {120, 120, 120}, 307 } 308 309 for _, c := range cases { 310 t.Run(fmt.Sprintf("case %v", c.currentBackoff), func(t *testing.T) { 311 next := nextBackoff(c.currentBackoff) 312 t.Logf("computed backoff(%v) = %v", c.currentBackoff, next) 313 314 require.True(t, next >= c.min, "next backoff is smaller than expected") 315 require.True(t, next <= c.max, "next backoff is larger than expected") 316 }) 317 } 318 } 319 320 func TestIsLoggingTerminalError(t *testing.T) { 321 terminalErrs := []error{ 322 errors.New("docker returned: configured logging driver does not support reading"), 323 &docker.Error{ 324 Status: 501, 325 Message: "configured logging driver does not support reading", 326 }, 327 &docker.Error{ 328 Status: 501, 329 Message: "not implemented", 330 }, 331 } 332 333 for _, err := range terminalErrs { 334 require.Truef(t, isLoggingTerminalError(err), "error should be terminal: %v", err) 335 } 336 337 nonTerminalErrs := []error{ 338 errors.New("not expected"), 339 &docker.Error{ 340 Status: 503, 341 Message: "Service Unavailable", 342 }, 343 } 344 345 for _, err := range nonTerminalErrs { 346 require.Falsef(t, isLoggingTerminalError(err), "error should be terminal: %v", err) 347 } 348 }