So we don't print those <no value> in the client and we don't fail
executing inspect templates with API field names.
Make sure those fields are initialized as empty slices when
a container is loaded from disk and their values are nil.
Signed-off-by: David Calavera <david.calavera@gmail.com>
| ... | ... |
@@ -152,7 +152,28 @@ func (container *Container) readHostConfig() error {
|
| 152 | 152 |
} |
| 153 | 153 |
defer f.Close() |
| 154 | 154 |
|
| 155 |
- return json.NewDecoder(f).Decode(&container.hostConfig) |
|
| 155 |
+ if err := json.NewDecoder(f).Decode(&container.hostConfig); err != nil {
|
|
| 156 |
+ return err |
|
| 157 |
+ } |
|
| 158 |
+ |
|
| 159 |
+ // Make sure the dns fields are never nil. |
|
| 160 |
+ // New containers don't ever have those fields nil, |
|
| 161 |
+ // but pre created containers can still have those nil values. |
|
| 162 |
+ // See https://github.com/docker/docker/pull/17779 |
|
| 163 |
+ // for a more detailed explanation on why we don't want that. |
|
| 164 |
+ if container.hostConfig.DNS == nil {
|
|
| 165 |
+ container.hostConfig.DNS = make([]string, 0) |
|
| 166 |
+ } |
|
| 167 |
+ |
|
| 168 |
+ if container.hostConfig.DNSSearch == nil {
|
|
| 169 |
+ container.hostConfig.DNSSearch = make([]string, 0) |
|
| 170 |
+ } |
|
| 171 |
+ |
|
| 172 |
+ if container.hostConfig.DNSOptions == nil {
|
|
| 173 |
+ container.hostConfig.DNSOptions = make([]string, 0) |
|
| 174 |
+ } |
|
| 175 |
+ |
|
| 176 |
+ return nil |
|
| 156 | 177 |
} |
| 157 | 178 |
|
| 158 | 179 |
func (container *Container) writeHostConfig() error {
|
| ... | ... |
@@ -1,10 +1,15 @@ |
| 1 | 1 |
package daemon |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "io/ioutil" |
|
| 5 |
+ "os" |
|
| 6 |
+ "path/filepath" |
|
| 4 | 7 |
"testing" |
| 5 | 8 |
|
| 6 | 9 |
"github.com/docker/docker/pkg/signal" |
| 7 | 10 |
"github.com/docker/docker/runconfig" |
| 11 |
+ "github.com/docker/docker/volume" |
|
| 12 |
+ "github.com/docker/docker/volume/drivers" |
|
| 8 | 13 |
) |
| 9 | 14 |
|
| 10 | 15 |
func TestGetFullName(t *testing.T) {
|
| ... | ... |
@@ -64,3 +69,68 @@ func TestContainerStopSignal(t *testing.T) {
|
| 64 | 64 |
t.Fatalf("Expected 9, got %v", s)
|
| 65 | 65 |
} |
| 66 | 66 |
} |
| 67 |
+ |
|
| 68 |
+func TestContainerInitDNS(t *testing.T) {
|
|
| 69 |
+ tmp, err := ioutil.TempDir("", "docker-container-test-")
|
|
| 70 |
+ if err != nil {
|
|
| 71 |
+ t.Fatal(err) |
|
| 72 |
+ } |
|
| 73 |
+ defer os.RemoveAll(tmp) |
|
| 74 |
+ |
|
| 75 |
+ containerID := "d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e" |
|
| 76 |
+ containerPath := filepath.Join(tmp, containerID) |
|
| 77 |
+ if err := os.MkdirAll(containerPath, 0755); err != nil {
|
|
| 78 |
+ t.Fatal(err) |
|
| 79 |
+ } |
|
| 80 |
+ |
|
| 81 |
+ config := `{"State":{"Running":true,"Paused":false,"Restarting":false,"OOMKilled":false,"Dead":false,"Pid":2464,"ExitCode":0,
|
|
| 82 |
+"Error":"","StartedAt":"2015-05-26T16:48:53.869308965Z","FinishedAt":"0001-01-01T00:00:00Z"}, |
|
| 83 |
+"ID":"d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e","Created":"2015-05-26T16:48:53.7987917Z","Path":"top", |
|
| 84 |
+"Args":[],"Config":{"Hostname":"d59df5276e7b","Domainname":"","User":"","Memory":0,"MemorySwap":0,"CpuShares":0,"Cpuset":"",
|
|
| 85 |
+"AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"PortSpecs":null,"ExposedPorts":null,"Tty":true,"OpenStdin":true, |
|
| 86 |
+"StdinOnce":false,"Env":null,"Cmd":["top"],"Image":"ubuntu:latest","Volumes":null,"WorkingDir":"","Entrypoint":null, |
|
| 87 |
+"NetworkDisabled":false,"MacAddress":"","OnBuild":null,"Labels":{}},"Image":"07f8e8c5e66084bef8f848877857537ffe1c47edd01a93af27e7161672ad0e95",
|
|
| 88 |
+"NetworkSettings":{"IPAddress":"172.17.0.1","IPPrefixLen":16,"MacAddress":"02:42:ac:11:00:01","LinkLocalIPv6Address":"fe80::42:acff:fe11:1",
|
|
| 89 |
+"LinkLocalIPv6PrefixLen":64,"GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"Gateway":"172.17.42.1","IPv6Gateway":"","Bridge":"docker0","Ports":{}},
|
|
| 90 |
+"ResolvConfPath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/resolv.conf", |
|
| 91 |
+"HostnamePath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/hostname", |
|
| 92 |
+"HostsPath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/hosts", |
|
| 93 |
+"LogPath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e-json.log", |
|
| 94 |
+"Name":"/ubuntu","Driver":"aufs","MountLabel":"","ProcessLabel":"","AppArmorProfile":"","RestartCount":0, |
|
| 95 |
+"UpdateDns":false,"Volumes":{},"VolumesRW":{},"AppliedVolumesFrom":null}`
|
|
| 96 |
+ |
|
| 97 |
+ if err = ioutil.WriteFile(filepath.Join(containerPath, "config.json"), []byte(config), 0644); err != nil {
|
|
| 98 |
+ t.Fatal(err) |
|
| 99 |
+ } |
|
| 100 |
+ |
|
| 101 |
+ hostConfig := `{"Binds":[],"ContainerIDFile":"","Memory":0,"MemorySwap":0,"CpuShares":0,"CpusetCpus":"",
|
|
| 102 |
+"Privileged":false,"PortBindings":{},"Links":null,"PublishAllPorts":false,"Dns":null,"DnsOptions":null,"DnsSearch":null,"ExtraHosts":null,"VolumesFrom":null,
|
|
| 103 |
+"Devices":[],"NetworkMode":"bridge","IpcMode":"","PidMode":"","CapAdd":null,"CapDrop":null,"RestartPolicy":{"Name":"no","MaximumRetryCount":0},
|
|
| 104 |
+"SecurityOpt":null,"ReadonlyRootfs":false,"Ulimits":null,"LogConfig":{"Type":"","Config":null},"CgroupParent":""}`
|
|
| 105 |
+ if err = ioutil.WriteFile(filepath.Join(containerPath, "hostconfig.json"), []byte(hostConfig), 0644); err != nil {
|
|
| 106 |
+ t.Fatal(err) |
|
| 107 |
+ } |
|
| 108 |
+ |
|
| 109 |
+ daemon, err := initDaemonWithVolumeStore(tmp) |
|
| 110 |
+ if err != nil {
|
|
| 111 |
+ t.Fatal(err) |
|
| 112 |
+ } |
|
| 113 |
+ defer volumedrivers.Unregister(volume.DefaultDriverName) |
|
| 114 |
+ |
|
| 115 |
+ c, err := daemon.load(containerID) |
|
| 116 |
+ if err != nil {
|
|
| 117 |
+ t.Fatal(err) |
|
| 118 |
+ } |
|
| 119 |
+ |
|
| 120 |
+ if c.hostConfig.DNS == nil {
|
|
| 121 |
+ t.Fatal("Expected container DNS to not be nil")
|
|
| 122 |
+ } |
|
| 123 |
+ |
|
| 124 |
+ if c.hostConfig.DNSSearch == nil {
|
|
| 125 |
+ t.Fatal("Expected container DNSSearch to not be nil")
|
|
| 126 |
+ } |
|
| 127 |
+ |
|
| 128 |
+ if c.hostConfig.DNSOptions == nil {
|
|
| 129 |
+ t.Fatal("Expected container DNSOptions to not be nil")
|
|
| 130 |
+ } |
|
| 131 |
+} |
| ... | ... |
@@ -182,7 +182,7 @@ func TestLoadWithVolume(t *testing.T) {
|
| 182 | 182 |
t.Fatal(err) |
| 183 | 183 |
} |
| 184 | 184 |
|
| 185 |
- daemon, err := initDaemonForVolumesTest(tmp) |
|
| 185 |
+ daemon, err := initDaemonWithVolumeStore(tmp) |
|
| 186 | 186 |
if err != nil {
|
| 187 | 187 |
t.Fatal(err) |
| 188 | 188 |
} |
| ... | ... |
@@ -270,7 +270,7 @@ func TestLoadWithBindMount(t *testing.T) {
|
| 270 | 270 |
t.Fatal(err) |
| 271 | 271 |
} |
| 272 | 272 |
|
| 273 |
- daemon, err := initDaemonForVolumesTest(tmp) |
|
| 273 |
+ daemon, err := initDaemonWithVolumeStore(tmp) |
|
| 274 | 274 |
if err != nil {
|
| 275 | 275 |
t.Fatal(err) |
| 276 | 276 |
} |
| ... | ... |
@@ -361,7 +361,7 @@ func TestLoadWithVolume17RC(t *testing.T) {
|
| 361 | 361 |
t.Fatal(err) |
| 362 | 362 |
} |
| 363 | 363 |
|
| 364 |
- daemon, err := initDaemonForVolumesTest(tmp) |
|
| 364 |
+ daemon, err := initDaemonWithVolumeStore(tmp) |
|
| 365 | 365 |
if err != nil {
|
| 366 | 366 |
t.Fatal(err) |
| 367 | 367 |
} |
| ... | ... |
@@ -466,7 +466,7 @@ func TestRemoveLocalVolumesFollowingSymlinks(t *testing.T) {
|
| 466 | 466 |
t.Fatal(err) |
| 467 | 467 |
} |
| 468 | 468 |
|
| 469 |
- daemon, err := initDaemonForVolumesTest(tmp) |
|
| 469 |
+ daemon, err := initDaemonWithVolumeStore(tmp) |
|
| 470 | 470 |
if err != nil {
|
| 471 | 471 |
t.Fatal(err) |
| 472 | 472 |
} |
| ... | ... |
@@ -502,7 +502,7 @@ func TestRemoveLocalVolumesFollowingSymlinks(t *testing.T) {
|
| 502 | 502 |
} |
| 503 | 503 |
} |
| 504 | 504 |
|
| 505 |
-func initDaemonForVolumesTest(tmp string) (*Daemon, error) {
|
|
| 505 |
+func initDaemonWithVolumeStore(tmp string) (*Daemon, error) {
|
|
| 506 | 506 |
daemon := &Daemon{
|
| 507 | 507 |
repository: tmp, |
| 508 | 508 |
root: tmp, |
| ... | ... |
@@ -329,3 +329,11 @@ func (s *DockerSuite) TestInspectTempateError(c *check.C) {
|
| 329 | 329 |
c.Assert(err, check.Not(check.IsNil)) |
| 330 | 330 |
c.Assert(out, checker.Contains, "Template parsing error") |
| 331 | 331 |
} |
| 332 |
+ |
|
| 333 |
+func (s *DockerSuite) TestInspectJSONFields(c *check.C) {
|
|
| 334 |
+ dockerCmd(c, "run", "--name=busybox", "-d", "busybox", "top") |
|
| 335 |
+ out, _, err := dockerCmdWithError("inspect", "--type=container", "--format='{{.HostConfig.Dns}}'", "busybox")
|
|
| 336 |
+ |
|
| 337 |
+ c.Assert(err, check.IsNil) |
|
| 338 |
+ c.Assert(out, checker.Equals, "[]\n") |
|
| 339 |
+} |
| ... | ... |
@@ -98,6 +98,16 @@ func (opts *ListOpts) GetAll() []string {
|
| 98 | 98 |
return (*opts.values) |
| 99 | 99 |
} |
| 100 | 100 |
|
| 101 |
+// GetAllOrEmpty returns the values of the slice |
|
| 102 |
+// or an empty slice when there are no values. |
|
| 103 |
+func (opts *ListOpts) GetAllOrEmpty() []string {
|
|
| 104 |
+ v := *opts.values |
|
| 105 |
+ if v == nil {
|
|
| 106 |
+ return make([]string, 0) |
|
| 107 |
+ } |
|
| 108 |
+ return v |
|
| 109 |
+} |
|
| 110 |
+ |
|
| 101 | 111 |
// Get checks the existence of the specified key. |
| 102 | 112 |
func (opts *ListOpts) Get(key string) bool {
|
| 103 | 113 |
for _, k := range *opts.values {
|
| ... | ... |
@@ -348,27 +348,32 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe |
| 348 | 348 |
PortBindings: portBindings, |
| 349 | 349 |
Links: flLinks.GetAll(), |
| 350 | 350 |
PublishAllPorts: *flPublishAll, |
| 351 |
- DNS: flDNS.GetAll(), |
|
| 352 |
- DNSSearch: flDNSSearch.GetAll(), |
|
| 353 |
- DNSOptions: flDNSOptions.GetAll(), |
|
| 354 |
- ExtraHosts: flExtraHosts.GetAll(), |
|
| 355 |
- VolumesFrom: flVolumesFrom.GetAll(), |
|
| 356 |
- NetworkMode: NetworkMode(*flNetMode), |
|
| 357 |
- IpcMode: ipcMode, |
|
| 358 |
- PidMode: pidMode, |
|
| 359 |
- UTSMode: utsMode, |
|
| 360 |
- Devices: deviceMappings, |
|
| 361 |
- CapAdd: stringutils.NewStrSlice(flCapAdd.GetAll()...), |
|
| 362 |
- CapDrop: stringutils.NewStrSlice(flCapDrop.GetAll()...), |
|
| 363 |
- GroupAdd: flGroupAdd.GetAll(), |
|
| 364 |
- RestartPolicy: restartPolicy, |
|
| 365 |
- SecurityOpt: flSecurityOpt.GetAll(), |
|
| 366 |
- ReadonlyRootfs: *flReadonlyRootfs, |
|
| 367 |
- Ulimits: flUlimits.GetList(), |
|
| 368 |
- LogConfig: LogConfig{Type: *flLoggingDriver, Config: loggingOpts},
|
|
| 369 |
- CgroupParent: *flCgroupParent, |
|
| 370 |
- VolumeDriver: *flVolumeDriver, |
|
| 371 |
- Isolation: IsolationLevel(*flIsolation), |
|
| 351 |
+ // Make sure the dns fields are never nil. |
|
| 352 |
+ // New containers don't ever have those fields nil, |
|
| 353 |
+ // but pre created containers can still have those nil values. |
|
| 354 |
+ // See https://github.com/docker/docker/pull/17779 |
|
| 355 |
+ // for a more detailed explanation on why we don't want that. |
|
| 356 |
+ DNS: flDNS.GetAllOrEmpty(), |
|
| 357 |
+ DNSSearch: flDNSSearch.GetAllOrEmpty(), |
|
| 358 |
+ DNSOptions: flDNSOptions.GetAllOrEmpty(), |
|
| 359 |
+ ExtraHosts: flExtraHosts.GetAll(), |
|
| 360 |
+ VolumesFrom: flVolumesFrom.GetAll(), |
|
| 361 |
+ NetworkMode: NetworkMode(*flNetMode), |
|
| 362 |
+ IpcMode: ipcMode, |
|
| 363 |
+ PidMode: pidMode, |
|
| 364 |
+ UTSMode: utsMode, |
|
| 365 |
+ Devices: deviceMappings, |
|
| 366 |
+ CapAdd: stringutils.NewStrSlice(flCapAdd.GetAll()...), |
|
| 367 |
+ CapDrop: stringutils.NewStrSlice(flCapDrop.GetAll()...), |
|
| 368 |
+ GroupAdd: flGroupAdd.GetAll(), |
|
| 369 |
+ RestartPolicy: restartPolicy, |
|
| 370 |
+ SecurityOpt: flSecurityOpt.GetAll(), |
|
| 371 |
+ ReadonlyRootfs: *flReadonlyRootfs, |
|
| 372 |
+ Ulimits: flUlimits.GetList(), |
|
| 373 |
+ LogConfig: LogConfig{Type: *flLoggingDriver, Config: loggingOpts},
|
|
| 374 |
+ CgroupParent: *flCgroupParent, |
|
| 375 |
+ VolumeDriver: *flVolumeDriver, |
|
| 376 |
+ Isolation: IsolationLevel(*flIsolation), |
|
| 372 | 377 |
} |
| 373 | 378 |
|
| 374 | 379 |
// When allocating stdin in attached mode, close stdin at client disconnect |