github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/drivers/docker/network.go (about) 1 package docker 2 3 import ( 4 "fmt" 5 6 docker "github.com/fsouza/go-dockerclient" 7 "github.com/hashicorp/nomad/plugins/drivers" 8 ) 9 10 const ( 11 // dockerNetSpecLabelKey is the label added when we create a pause 12 // container to own the network namespace, and the NetworkIsolationSpec we 13 // get back from CreateNetwork has this label set as the container ID. 14 // We'll use this to generate a hostname for the task in the event the user 15 // did not specify a custom one. Please see dockerNetSpecHostnameKey. 16 dockerNetSpecLabelKey = "docker_sandbox_container_id" 17 18 // dockerNetSpecHostnameKey is the label added when we create a pause 19 // container and the task group network include a user supplied hostname 20 // parameter. 21 dockerNetSpecHostnameKey = "docker_sandbox_hostname" 22 ) 23 24 func (d *Driver) CreateNetwork(allocID string, createSpec *drivers.NetworkCreateRequest) (*drivers.NetworkIsolationSpec, bool, error) { 25 // Initialize docker API clients 26 client, _, err := d.dockerClients() 27 if err != nil { 28 return nil, false, fmt.Errorf("failed to connect to docker daemon: %s", err) 29 } 30 31 if err := d.pullInfraImage(allocID); err != nil { 32 return nil, false, err 33 } 34 35 config, err := d.createSandboxContainerConfig(allocID, createSpec) 36 if err != nil { 37 return nil, false, err 38 } 39 40 specFromContainer := func(c *docker.Container, hostname string) *drivers.NetworkIsolationSpec { 41 spec := &drivers.NetworkIsolationSpec{ 42 Mode: drivers.NetIsolationModeGroup, 43 Path: c.NetworkSettings.SandboxKey, 44 Labels: map[string]string{ 45 dockerNetSpecLabelKey: c.ID, 46 }, 47 } 48 49 // If the user supplied a hostname, set the label. 50 if hostname != "" { 51 spec.Labels[dockerNetSpecHostnameKey] = hostname 52 } 53 54 return spec 55 } 56 57 // We want to return a flag that tells us if the container already 58 // existed so that callers can decide whether or not to recreate 59 // the task's network namespace associations. 60 container, err := d.containerByName(config.Name) 61 if err != nil { 62 return nil, false, err 63 } 64 if container != nil && container.State.Running { 65 return specFromContainer(container, createSpec.Hostname), false, nil 66 } 67 68 container, err = d.createContainer(client, *config, d.config.InfraImage) 69 if err != nil { 70 return nil, false, err 71 } 72 73 if err = d.startContainer(container); err != nil { 74 return nil, false, err 75 } 76 77 // until the container is started, InspectContainerWithOptions 78 // returns a mostly-empty struct 79 container, err = client.InspectContainerWithOptions(docker.InspectContainerOptions{ 80 ID: container.ID, 81 }) 82 if err != nil { 83 return nil, false, err 84 } 85 86 return specFromContainer(container, createSpec.Hostname), true, nil 87 } 88 89 func (d *Driver) DestroyNetwork(allocID string, spec *drivers.NetworkIsolationSpec) error { 90 client, _, err := d.dockerClients() 91 if err != nil { 92 return fmt.Errorf("failed to connect to docker daemon: %s", err) 93 } 94 95 if err := client.RemoveContainer(docker.RemoveContainerOptions{ 96 Force: true, 97 ID: spec.Labels[dockerNetSpecLabelKey], 98 }); err != nil { 99 return err 100 } 101 102 if d.config.GC.Image { 103 104 // The Docker image ID is needed in order to correctly update the image 105 // reference count. Any error finding this, however, should not result 106 // in an error shutting down the allocrunner. 107 dockerImage, err := client.InspectImage(d.config.InfraImage) 108 if err != nil { 109 d.logger.Warn("InspectImage failed for infra_image container destroy", 110 "image", d.config.InfraImage, "error", err) 111 return nil 112 } 113 d.coordinator.RemoveImage(dockerImage.ID, allocID) 114 } 115 116 return nil 117 } 118 119 // createSandboxContainerConfig creates a docker container configuration which 120 // starts a container with an empty network namespace. 121 func (d *Driver) createSandboxContainerConfig(allocID string, createSpec *drivers.NetworkCreateRequest) (*docker.CreateContainerOptions, error) { 122 123 return &docker.CreateContainerOptions{ 124 Name: fmt.Sprintf("nomad_init_%s", allocID), 125 Config: &docker.Config{ 126 Image: d.config.InfraImage, 127 Hostname: createSpec.Hostname, 128 }, 129 HostConfig: &docker.HostConfig{ 130 // Set the network mode to none which creates a network namespace 131 // with only a loopback interface. 132 NetworkMode: "none", 133 }, 134 }, nil 135 } 136 137 // pullInfraImage conditionally pulls the `infra_image` from the Docker registry 138 // only if its name uses the "latest" tag or the image doesn't already exist locally. 139 func (d *Driver) pullInfraImage(allocID string) error { 140 repo, tag := parseDockerImage(d.config.InfraImage) 141 142 // There's a (narrow) time-of-check-time-of-use race here. If we call 143 // InspectImage and then a concurrent task shutdown happens before we call 144 // IncrementImageReference, we could end up removing the image, and it 145 // would no longer exist by the time we get to PullImage below. 146 d.coordinator.imageLock.Lock() 147 148 if tag != "latest" { 149 dockerImage, err := client.InspectImage(d.config.InfraImage) 150 if err != nil { 151 d.logger.Debug("InspectImage failed for infra_image container pull", 152 "image", d.config.InfraImage, "error", err) 153 } else if dockerImage != nil { 154 // Image exists, so no pull is attempted; just increment its reference 155 // count and unlock the image lock. 156 d.coordinator.incrementImageReferenceImpl(dockerImage.ID, d.config.InfraImage, allocID) 157 d.coordinator.imageLock.Unlock() 158 return nil 159 } 160 } 161 162 // At this point we have performed all the image work needed, so unlock. It 163 // is possible in environments with slow networks that the image pull may 164 // take a while, so while defer unlock would be best, this allows us to 165 // remove the lock sooner. 166 d.coordinator.imageLock.Unlock() 167 168 authOptions, err := firstValidAuth(repo, []authBackend{ 169 authFromDockerConfig(d.config.Auth.Config), 170 authFromHelper(d.config.Auth.Helper), 171 }) 172 if err != nil { 173 d.logger.Debug("auth failed for infra_image container pull", "image", d.config.InfraImage, "error", err) 174 } 175 176 _, err = d.coordinator.PullImage(d.config.InfraImage, authOptions, allocID, noopLogEventFn, d.config.infraImagePullTimeoutDuration, d.config.pullActivityTimeoutDuration) 177 return err 178 }