diff --git a/.changelog/14089.txt b/.changelog/14089.txt new file mode 100644 index 000000000000..6f6d36426d41 --- /dev/null +++ b/.changelog/14089.txt @@ -0,0 +1,3 @@ +```release-note:improvement +docker: Added config option to disable container healthcheck +``` diff --git a/drivers/docker/config.go b/drivers/docker/config.go index 6194d60f1e46..40d98966ad49 100644 --- a/drivers/docker/config.go +++ b/drivers/docker/config.go @@ -112,21 +112,19 @@ func PluginLoader(opts map[string]string) (map[string]interface{}, error) { } var ( - // PluginID is the rawexec plugin metadata registered in the plugin - // catalog. + // PluginID is the docker plugin metadata registered in the plugin catalog. PluginID = loader.PluginID{ Name: pluginName, PluginType: base.PluginTypeDriver, } - // PluginConfig is the rawexec factory function registered in the - // plugin catalog. + // PluginConfig is the docker config factory function registered in the plugin catalog. PluginConfig = &loader.InternalPluginConfig{ Config: map[string]interface{}{}, Factory: func(ctx context.Context, l hclog.Logger) interface{} { return NewDockerDriver(ctx, l) }, } - // pluginInfo is the response returned for the PluginInfo RPC + // pluginInfo is the response returned for the PluginInfo RPC. pluginInfo = &base.PluginInfoResponse{ Type: base.PluginTypeDriver, PluginApiVersions: []string{drivers.ApiVersion010}, @@ -321,6 +319,11 @@ var ( })), }) + // healthchecksBodySpec is the hcl specification for the `healthchecks` block + healthchecksBodySpec = hclspec.NewObject(map[string]*hclspec.Spec{ + "disable": hclspec.NewAttr("disable", "bool", false), + }) + // taskConfigSpec is the hcl specification for the driver config section of // a task within a job. It is returned in the TaskConfigSchema RPC taskConfigSpec = hclspec.NewObject(map[string]*hclspec.Spec{ @@ -354,6 +357,7 @@ var ( "entrypoint": hclspec.NewAttr("entrypoint", "list(string)", false), "extra_hosts": hclspec.NewAttr("extra_hosts", "list(string)", false), "force_pull": hclspec.NewAttr("force_pull", "bool", false), + "healthchecks": hclspec.NewBlock("healthchecks", false, healthchecksBodySpec), "hostname": hclspec.NewAttr("hostname", "string", false), "init": hclspec.NewAttr("init", "bool", false), "interactive": hclspec.NewAttr("interactive", "bool", false), @@ -435,6 +439,7 @@ type TaskConfig struct { Entrypoint []string `codec:"entrypoint"` ExtraHosts []string `codec:"extra_hosts"` ForcePull bool `codec:"force_pull"` + Healthchecks DockerHealthchecks `codec:"healthchecks"` Hostname string `codec:"hostname"` Init bool `codec:"init"` Interactive bool `codec:"interactive"` @@ -514,6 +519,14 @@ type DockerLogging struct { Config hclutils.MapStrStr `codec:"config"` } +type DockerHealthchecks struct { + Disable bool `codec:"disable"` +} + +func (dh *DockerHealthchecks) Disabled() bool { + return dh == nil || dh.Disable +} + type DockerMount struct { Type string `codec:"type"` Target string `codec:"target"` diff --git a/drivers/docker/config_test.go b/drivers/docker/config_test.go index f0aebef0c61f..254973e20b37 100644 --- a/drivers/docker/config_test.go +++ b/drivers/docker/config_test.go @@ -223,6 +223,9 @@ config { entrypoint = ["/bin/bash", "-c"] extra_hosts = ["127.0.0.1 localhost.example.com"] force_pull = true + healthchecks { + disable = true + } hostname = "self.example.com" interactive = true ipc_mode = "host" @@ -376,6 +379,7 @@ config { Entrypoint: []string{"/bin/bash", "-c"}, ExtraHosts: []string{"127.0.0.1 localhost.example.com"}, ForcePull: true, + Healthchecks: DockerHealthchecks{Disable: true}, Hostname: "self.example.com", Interactive: true, IPCMode: "host", diff --git a/drivers/docker/driver.go b/drivers/docker/driver.go index ec2455515df8..0aa9938457e3 100644 --- a/drivers/docker/driver.go +++ b/drivers/docker/driver.go @@ -1236,6 +1236,13 @@ func (d *Driver) createContainerConfig(task *drivers.TaskConfig, driverConfig *T logger.Debug("setting container mac address", "mac_address", config.MacAddress) } + if driverConfig.Healthchecks.Disabled() { + // Override any image-supplied health-check with disable sentinel. + // https://github.com/docker/engine-api/blob/master/types/container/config.go#L16 + config.Healthcheck = &docker.HealthConfig{Test: []string{"NONE"}} + logger.Debug("setting container healthchecks to be disabled") + } + return docker.CreateContainerOptions{ Name: containerName, Config: config, diff --git a/drivers/docker/driver_test.go b/drivers/docker/driver_test.go index 1e43f7718d24..8a76b01ebc8b 100644 --- a/drivers/docker/driver_test.go +++ b/drivers/docker/driver_test.go @@ -31,6 +31,7 @@ import ( "github.com/hashicorp/nomad/plugins/drivers" dtestutil "github.com/hashicorp/nomad/plugins/drivers/testutils" tu "github.com/hashicorp/nomad/testutil" + "github.com/shoenig/test/must" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -845,6 +846,26 @@ func TestDockerDriver_LoggingConfiguration(t *testing.T) { require.Equal(t, loggerConfig, container.HostConfig.LogConfig.Config) } +func TestDockerDriver_HealthchecksDisable(t *testing.T) { + ci.Parallel(t) + testutil.DockerCompatible(t) + + task, cfg, ports := dockerTask(t) + cfg.Healthchecks.Disable = true + defer freeport.Return(ports) + must.NoError(t, task.EncodeConcreteDriverConfig(cfg)) + + client, d, handle, cleanup := dockerSetup(t, task, nil) + defer cleanup() + must.NoError(t, d.WaitUntilStarted(task.ID, 5*time.Second)) + + container, err := client.InspectContainer(handle.containerID) + must.NoError(t, err) + + must.NotNil(t, container.Config.Healthcheck) + must.Eq(t, []string{"NONE"}, container.Config.Healthcheck.Test) +} + func TestDockerDriver_ForcePull(t *testing.T) { ci.Parallel(t) testutil.DockerCompatible(t) diff --git a/website/content/docs/drivers/docker.mdx b/website/content/docs/drivers/docker.mdx index 17177c61dc8b..46b40fc1b0bc 100644 --- a/website/content/docs/drivers/docker.mdx +++ b/website/content/docs/drivers/docker.mdx @@ -128,6 +128,18 @@ config { are mutable. If image's tag is `latest` or omitted, the image will always be pulled regardless of this setting. +- `healthchecks` - (Optional) A configuration block for controlling how the + docker driver manages HEALTHCHECK directives built into the container. Set + `healthchecks.disable` to disable any built-in healthcheck. + + ```hcl + config { + healthchecks { + disable = true + } + } + ``` + - `hostname` - (Optional) The hostname to assign to the container. When launching more than one of a task (using `count`) with this option set, every container the task starts will have the same hostname.