0e926ef3fd
Add a new hostname string parameter to the network block which allows operators to specify the hostname of the network namespace. Changing this causes a destructive update to the allocation and it is omitted if empty from API responses. This parameter also supports interpolation. In order to have a hostname passed as a configuration param when creating an allocation network, the CreateNetwork func of the DriverNetworkManager interface needs to be updated. In order to minimize the disruption of future changes, rather than add another string func arg, the function now accepts a request struct along with the allocID param. The struct has the hostname as a field. The in-tree implementations of DriverNetworkManager.CreateNetwork have been modified to account for the function signature change. In updating for the change, the enhancement of adding hostnames to network namespaces has also been added to the Docker driver, whilst the default Linux manager does not current implement it.
127 lines
4 KiB
Go
127 lines
4 KiB
Go
package docker
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
docker "github.com/fsouza/go-dockerclient"
|
|
"github.com/hashicorp/nomad/plugins/drivers"
|
|
)
|
|
|
|
const (
|
|
// dockerNetSpecLabelKey is the label added when we create a pause
|
|
// container to own the network namespace, and the NetworkIsolationSpec we
|
|
// get back from CreateNetwork has this label set as the container ID.
|
|
// We'll use this to generate a hostname for the task in the event the user
|
|
// did not specify a custom one. Please see dockerNetSpecHostnameKey.
|
|
dockerNetSpecLabelKey = "docker_sandbox_container_id"
|
|
|
|
// dockerNetSpecHostnameKey is the label added when we create a pause
|
|
// container and the task group network include a user supplied hostname
|
|
// parameter.
|
|
dockerNetSpecHostnameKey = "docker_sandbox_hostname"
|
|
)
|
|
|
|
func (d *Driver) CreateNetwork(allocID string, createSpec *drivers.NetworkCreateRequest) (*drivers.NetworkIsolationSpec, bool, error) {
|
|
// Initialize docker API clients
|
|
client, _, err := d.dockerClients()
|
|
if err != nil {
|
|
return nil, false, fmt.Errorf("failed to connect to docker daemon: %s", err)
|
|
}
|
|
|
|
repo, _ := parseDockerImage(d.config.InfraImage)
|
|
authOptions, err := firstValidAuth(repo, []authBackend{
|
|
authFromDockerConfig(d.config.Auth.Config),
|
|
authFromHelper(d.config.Auth.Helper),
|
|
})
|
|
if err != nil {
|
|
d.logger.Debug("auth failed for infra container image pull", "image", d.config.InfraImage, "error", err)
|
|
}
|
|
_, err = d.coordinator.PullImage(d.config.InfraImage, authOptions, allocID, noopLogEventFn, d.config.infraImagePullTimeoutDuration, d.config.pullActivityTimeoutDuration)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
config, err := d.createSandboxContainerConfig(allocID, createSpec)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
specFromContainer := func(c *docker.Container, hostname string) *drivers.NetworkIsolationSpec {
|
|
spec := &drivers.NetworkIsolationSpec{
|
|
Mode: drivers.NetIsolationModeGroup,
|
|
Path: c.NetworkSettings.SandboxKey,
|
|
Labels: map[string]string{
|
|
dockerNetSpecLabelKey: c.ID,
|
|
},
|
|
}
|
|
|
|
// If the user supplied a hostname, set the label.
|
|
if hostname != "" {
|
|
spec.Labels[dockerNetSpecHostnameKey] = hostname
|
|
}
|
|
|
|
return spec
|
|
}
|
|
|
|
// We want to return a flag that tells us if the container already
|
|
// existed so that callers can decide whether or not to recreate
|
|
// the task's network namespace associations.
|
|
container, err := d.containerByName(config.Name)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
if container != nil && container.State.Running {
|
|
return specFromContainer(container, createSpec.Hostname), false, nil
|
|
}
|
|
|
|
container, err = d.createContainer(client, *config, d.config.InfraImage)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
if err = d.startContainer(container); err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
// until the container is started, InspectContainerWithOptions
|
|
// returns a mostly-empty struct
|
|
container, err = client.InspectContainerWithOptions(docker.InspectContainerOptions{
|
|
ID: container.ID,
|
|
})
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
return specFromContainer(container, createSpec.Hostname), true, nil
|
|
}
|
|
|
|
func (d *Driver) DestroyNetwork(allocID string, spec *drivers.NetworkIsolationSpec) error {
|
|
client, _, err := d.dockerClients()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to connect to docker daemon: %s", err)
|
|
}
|
|
|
|
return client.RemoveContainer(docker.RemoveContainerOptions{
|
|
Force: true,
|
|
ID: spec.Labels[dockerNetSpecLabelKey],
|
|
})
|
|
}
|
|
|
|
// createSandboxContainerConfig creates a docker container configuration which
|
|
// starts a container with an empty network namespace.
|
|
func (d *Driver) createSandboxContainerConfig(allocID string, createSpec *drivers.NetworkCreateRequest) (*docker.CreateContainerOptions, error) {
|
|
|
|
return &docker.CreateContainerOptions{
|
|
Name: fmt.Sprintf("nomad_init_%s", allocID),
|
|
Config: &docker.Config{
|
|
Image: d.config.InfraImage,
|
|
Hostname: createSpec.Hostname,
|
|
},
|
|
HostConfig: &docker.HostConfig{
|
|
// Set the network mode to none which creates a network namespace
|
|
// with only a loopback interface.
|
|
NetworkMode: "none",
|
|
},
|
|
}, nil
|
|
}
|