github.com/kaisenlinux/docker@v0.0.0-20230510090727-ea55db55fac7/swarmkit/agent/exec/dockerapi/container_test.go (about) 1 package dockerapi 2 3 import ( 4 "reflect" 5 "testing" 6 "time" 7 8 enginecontainer "github.com/docker/docker/api/types/container" 9 enginemount "github.com/docker/docker/api/types/mount" 10 "github.com/docker/docker/api/types/strslice" 11 "github.com/docker/go-units" 12 "github.com/docker/swarmkit/api" 13 gogotypes "github.com/gogo/protobuf/types" 14 ) 15 16 func TestVolumesAndBinds(t *testing.T) { 17 type testCase struct { 18 explain string 19 config api.Mount 20 x enginemount.Mount 21 } 22 23 cases := []testCase{ 24 {"Simple bind mount", api.Mount{Type: api.MountTypeBind, Source: "/banana", Target: "/kerfluffle"}, 25 enginemount.Mount{Type: enginemount.TypeBind, Source: "/banana", Target: "/kerfluffle"}}, 26 {"Bind mound with propagation", api.Mount{Type: api.MountTypeBind, Source: "/banana", Target: "/kerfluffle", BindOptions: &api.Mount_BindOptions{Propagation: api.MountPropagationRPrivate}}, 27 enginemount.Mount{Type: enginemount.TypeBind, Source: "/banana", Target: "/kerfluffle", BindOptions: &enginemount.BindOptions{Propagation: enginemount.PropagationRPrivate}}}, 28 {"Simple volume with source", api.Mount{Type: api.MountTypeVolume, Source: "banana", Target: "/kerfluffle"}, 29 enginemount.Mount{Type: enginemount.TypeVolume, Source: "banana", Target: "/kerfluffle"}}, 30 {"Volume with options", api.Mount{Type: api.MountTypeVolume, Source: "banana", Target: "/kerfluffle", VolumeOptions: &api.Mount_VolumeOptions{NoCopy: true}}, 31 enginemount.Mount{Type: enginemount.TypeVolume, Source: "banana", Target: "/kerfluffle", VolumeOptions: &enginemount.VolumeOptions{NoCopy: true}}}, 32 {"Volume with no source", api.Mount{Type: api.MountTypeVolume, Target: "/kerfluffle"}, 33 enginemount.Mount{Type: enginemount.TypeVolume, Target: "/kerfluffle"}}, 34 {"Named pipe using Windows format", api.Mount{Type: api.MountTypeNamedPipe, Source: `\\.\pipe\foo`, Target: `\\.\pipe\foo`}, 35 enginemount.Mount{Type: enginemount.TypeNamedPipe, Source: `\\.\pipe\foo`, Target: `\\.\pipe\foo`}}, 36 {"Named pipe using Unix format", api.Mount{Type: api.MountTypeNamedPipe, Source: "//./pipe/foo", Target: "//./pipe/foo"}, 37 enginemount.Mount{Type: enginemount.TypeNamedPipe, Source: "//./pipe/foo", Target: "//./pipe/foo"}}, 38 } 39 40 for _, c := range cases { 41 cfg := containerConfig{ 42 task: &api.Task{ 43 Spec: api.TaskSpec{Runtime: &api.TaskSpec_Container{ 44 Container: &api.ContainerSpec{ 45 Mounts: []api.Mount{c.config}, 46 }, 47 }}, 48 }, 49 } 50 51 if vols := cfg.config().Volumes; len(vols) != 0 { 52 t.Fatalf("expected no anonymous volumes: %v", vols) 53 } 54 mounts := cfg.hostConfig().Mounts 55 if len(mounts) != 1 { 56 t.Fatalf("expected 1 mount: %v", mounts) 57 } 58 59 if !reflect.DeepEqual(mounts[0], c.x) { 60 t.Log(c.explain) 61 t.Logf("expected: %+v, got: %+v", c.x, mounts[0]) 62 switch c.x.Type { 63 case enginemount.TypeVolume: 64 t.Logf("expected volume opts: %+v, got: %+v", c.x.VolumeOptions, mounts[0].VolumeOptions) 65 if c.x.VolumeOptions.DriverConfig != nil { 66 t.Logf("expected volume driver config: %+v, got: %+v", c.x.VolumeOptions.DriverConfig, mounts[0].VolumeOptions.DriverConfig) 67 } 68 case enginemount.TypeBind: 69 t.Logf("expected bind opts: %+v, got: %+v", c.x.BindOptions, mounts[0].BindOptions) 70 } 71 t.Fail() 72 } 73 } 74 } 75 76 func TestTmpfsOptions(t *testing.T) { 77 type testCase struct { 78 explain string 79 config api.Mount 80 x string 81 } 82 83 cases := []testCase{ 84 {"Tmpfs mount with exec option", api.Mount{Type: api.MountTypeTmpfs, Target: "/kerfluffle", TmpfsOptions: &api.Mount_TmpfsOptions{Options: "exec"}}, "exec"}, 85 {"Tmpfs mount with noexec option", api.Mount{Type: api.MountTypeTmpfs, Target: "/kerfluffle", TmpfsOptions: &api.Mount_TmpfsOptions{Options: "noexec"}}, "noexec"}, 86 } 87 88 for _, c := range cases { 89 cfg := containerConfig{ 90 task: &api.Task{ 91 Spec: api.TaskSpec{Runtime: &api.TaskSpec_Container{ 92 Container: &api.ContainerSpec{ 93 Mounts: []api.Mount{c.config}, 94 }, 95 }}, 96 }, 97 } 98 99 mountOpts, ok := cfg.hostConfig().Tmpfs["/kerfluffle"] 100 if !ok { 101 t.Fatalf("expected 1 mount, found none") 102 } 103 104 if mountOpts != c.x { 105 t.Log(c.explain) 106 t.Logf("expected Tmpfs opts: %+v, got: %+v", c.x, mountOpts) 107 t.Fail() 108 } 109 } 110 } 111 112 func TestHealthcheck(t *testing.T) { 113 c := containerConfig{ 114 task: &api.Task{ 115 Spec: api.TaskSpec{Runtime: &api.TaskSpec_Container{ 116 Container: &api.ContainerSpec{ 117 Healthcheck: &api.HealthConfig{ 118 Test: []string{"a", "b", "c"}, 119 Interval: gogotypes.DurationProto(time.Second), 120 Timeout: gogotypes.DurationProto(time.Minute), 121 Retries: 10, 122 StartPeriod: gogotypes.DurationProto(time.Minute), 123 }, 124 }, 125 }}, 126 }, 127 } 128 config := c.config() 129 expected := &enginecontainer.HealthConfig{ 130 Test: []string{"a", "b", "c"}, 131 Interval: time.Second, 132 Timeout: time.Minute, 133 Retries: 10, 134 StartPeriod: time.Minute, 135 } 136 if !reflect.DeepEqual(config.Healthcheck, expected) { 137 t.Fatalf("expected %#v, got %#v", expected, config.Healthcheck) 138 } 139 } 140 141 func TestExtraHosts(t *testing.T) { 142 c := containerConfig{ 143 task: &api.Task{ 144 Spec: api.TaskSpec{Runtime: &api.TaskSpec_Container{ 145 Container: &api.ContainerSpec{ 146 Hosts: []string{ 147 "1.2.3.4 example.com", 148 "5.6.7.8 example.org", 149 "127.0.0.1 mylocal", 150 }, 151 }, 152 }}, 153 }, 154 } 155 156 hostConfig := c.hostConfig() 157 if len(hostConfig.ExtraHosts) != 3 { 158 t.Fatalf("expected 3 extra hosts: %v", hostConfig.ExtraHosts) 159 } 160 161 expected := "example.com:1.2.3.4" 162 actual := hostConfig.ExtraHosts[0] 163 if actual != expected { 164 t.Fatalf("expected %s, got %s", expected, actual) 165 } 166 167 expected = "example.org:5.6.7.8" 168 actual = hostConfig.ExtraHosts[1] 169 if actual != expected { 170 t.Fatalf("expected %s, got %s", expected, actual) 171 } 172 173 expected = "mylocal:127.0.0.1" 174 actual = hostConfig.ExtraHosts[2] 175 if actual != expected { 176 t.Fatalf("expected %s, got %s", expected, actual) 177 } 178 } 179 180 func TestPidLimit(t *testing.T) { 181 c := containerConfig{ 182 task: &api.Task{ 183 Spec: api.TaskSpec{Runtime: &api.TaskSpec_Container{ 184 Container: &api.ContainerSpec{ 185 PidsLimit: 10, 186 }, 187 }}, 188 }, 189 } 190 191 hostConfig := c.hostConfig() 192 expected := int64(10) 193 actual := hostConfig.PidsLimit 194 195 if expected != *actual { 196 t.Fatalf("expected %d, got %d", expected, actual) 197 } 198 } 199 200 func TestStopSignal(t *testing.T) { 201 c := containerConfig{ 202 task: &api.Task{ 203 Spec: api.TaskSpec{Runtime: &api.TaskSpec_Container{ 204 Container: &api.ContainerSpec{ 205 StopSignal: "SIGWINCH", 206 }, 207 }}, 208 }, 209 } 210 211 expected := "SIGWINCH" 212 actual := c.config().StopSignal 213 if actual != expected { 214 t.Fatalf("expected %s, got %s", expected, actual) 215 } 216 } 217 218 func TestInit(t *testing.T) { 219 c := containerConfig{ 220 task: &api.Task{ 221 Spec: api.TaskSpec{Runtime: &api.TaskSpec_Container{ 222 Container: &api.ContainerSpec{ 223 StopSignal: "SIGWINCH", 224 }, 225 }}, 226 }, 227 } 228 var expected *bool 229 actual := c.hostConfig().Init 230 if actual != expected { 231 t.Fatalf("expected %v, got %v", expected, actual) 232 } 233 c.task.Spec.GetContainer().Init = &gogotypes.BoolValue{ 234 Value: true, 235 } 236 actual = c.hostConfig().Init 237 if actual == nil || !*actual { 238 t.Fatalf("expected &true, got %v", actual) 239 } 240 } 241 242 func TestIsolation(t *testing.T) { 243 c := containerConfig{ 244 task: &api.Task{ 245 Spec: api.TaskSpec{ 246 Runtime: &api.TaskSpec_Container{ 247 Container: &api.ContainerSpec{ 248 Isolation: api.ContainerIsolationHyperV, 249 }, 250 }, 251 }, 252 }, 253 } 254 255 expected := "hyperv" 256 actual := string(c.hostConfig().Isolation) 257 if actual != expected { 258 t.Fatalf("expected %s, got %s", expected, actual) 259 } 260 } 261 262 func TestCapabilityAdd(t *testing.T) { 263 c := containerConfig{ 264 task: &api.Task{ 265 Spec: api.TaskSpec{ 266 Runtime: &api.TaskSpec_Container{ 267 Container: &api.ContainerSpec{ 268 CapabilityAdd: []string{"CAP_NET_RAW", "CAP_SYS_CHROOT"}, 269 }, 270 }, 271 }, 272 }, 273 } 274 275 expected := strslice.StrSlice{"CAP_NET_RAW", "CAP_SYS_CHROOT"} 276 actual := c.hostConfig().CapAdd 277 if !reflect.DeepEqual(actual, expected) { 278 t.Fatalf("expected %s, got %s", expected, actual) 279 } 280 } 281 282 func TestCapabilityDrop(t *testing.T) { 283 c := containerConfig{ 284 task: &api.Task{ 285 Spec: api.TaskSpec{ 286 Runtime: &api.TaskSpec_Container{ 287 Container: &api.ContainerSpec{ 288 CapabilityDrop: []string{"CAP_KILL"}, 289 }, 290 }, 291 }, 292 }, 293 } 294 295 expected := strslice.StrSlice{"CAP_KILL"} 296 actual := c.hostConfig().CapDrop 297 if !reflect.DeepEqual(actual, expected) { 298 t.Fatalf("expected %s, got %s", expected, actual) 299 } 300 } 301 302 func TestUlimits(t *testing.T) { 303 c := containerConfig{ 304 task: &api.Task{ 305 Spec: api.TaskSpec{ 306 Runtime: &api.TaskSpec_Container{ 307 Container: &api.ContainerSpec{ 308 Ulimits: []*api.ContainerSpec_Ulimit{ 309 { 310 Name: "nofile", 311 Soft: 1024, 312 Hard: 2048, 313 }, 314 }, 315 }, 316 }, 317 }, 318 }, 319 } 320 321 expected := []*units.Ulimit{ 322 { 323 Name: "nofile", 324 Soft: 1024, 325 Hard: 2048, 326 }, 327 } 328 actual := c.resources().Ulimits 329 if !reflect.DeepEqual(actual, expected) { 330 t.Fatalf("expected %v, got %v", expected, actual) 331 } 332 }