github.com/kobeld/docker@v1.12.0-rc1/daemon/cluster/executor/container/adapter.go (about) 1 package container 2 3 import ( 4 "encoding/base64" 5 "encoding/json" 6 "fmt" 7 "io" 8 "strings" 9 "syscall" 10 11 "github.com/Sirupsen/logrus" 12 executorpkg "github.com/docker/docker/daemon/cluster/executor" 13 "github.com/docker/engine-api/types" 14 "github.com/docker/libnetwork" 15 "github.com/docker/swarmkit/api" 16 "github.com/docker/swarmkit/log" 17 "golang.org/x/net/context" 18 ) 19 20 // containerAdapter conducts remote operations for a container. All calls 21 // are mostly naked calls to the client API, seeded with information from 22 // containerConfig. 23 type containerAdapter struct { 24 backend executorpkg.Backend 25 container *containerConfig 26 } 27 28 func newContainerAdapter(b executorpkg.Backend, task *api.Task) (*containerAdapter, error) { 29 ctnr, err := newContainerConfig(task) 30 if err != nil { 31 return nil, err 32 } 33 34 return &containerAdapter{ 35 container: ctnr, 36 backend: b, 37 }, nil 38 } 39 40 func (c *containerAdapter) pullImage(ctx context.Context) error { 41 // if the image needs to be pulled, the auth config will be retrieved and updated 42 encodedAuthConfig := c.container.task.ServiceAnnotations.Labels[fmt.Sprintf("%v.registryauth", systemLabelPrefix)] 43 44 authConfig := &types.AuthConfig{} 45 if encodedAuthConfig != "" { 46 if err := json.NewDecoder(base64.NewDecoder(base64.URLEncoding, strings.NewReader(encodedAuthConfig))).Decode(authConfig); err != nil { 47 logrus.Warnf("invalid authconfig: %v", err) 48 } 49 } 50 51 pr, pw := io.Pipe() 52 metaHeaders := map[string][]string{} 53 go func() { 54 err := c.backend.PullImage(ctx, c.container.image(), "", metaHeaders, authConfig, pw) 55 pw.CloseWithError(err) 56 }() 57 58 dec := json.NewDecoder(pr) 59 m := map[string]interface{}{} 60 for { 61 if err := dec.Decode(&m); err != nil { 62 if err == io.EOF { 63 break 64 } 65 return err 66 } 67 // TOOD(stevvooe): Report this status somewhere. 68 logrus.Debugln("pull progress", m) 69 } 70 // if the final stream object contained an error, return it 71 if errMsg, ok := m["error"]; ok { 72 return fmt.Errorf("%v", errMsg) 73 } 74 return nil 75 } 76 77 func (c *containerAdapter) createNetworks(ctx context.Context) error { 78 for _, network := range c.container.networks() { 79 ncr, err := c.container.networkCreateRequest(network) 80 if err != nil { 81 return err 82 } 83 84 if err := c.backend.CreateManagedNetwork(ncr); err != nil { // todo name missing 85 if _, ok := err.(libnetwork.NetworkNameError); ok { 86 continue 87 } 88 89 return err 90 } 91 } 92 93 return nil 94 } 95 96 func (c *containerAdapter) removeNetworks(ctx context.Context) error { 97 for _, nid := range c.container.networks() { 98 if err := c.backend.DeleteManagedNetwork(nid); err != nil { 99 if _, ok := err.(*libnetwork.ActiveEndpointsError); ok { 100 continue 101 } 102 log.G(ctx).Errorf("network %s remove failed: %v", nid, err) 103 return err 104 } 105 } 106 107 return nil 108 } 109 110 func (c *containerAdapter) create(ctx context.Context, backend executorpkg.Backend) error { 111 var cr types.ContainerCreateResponse 112 var err error 113 if cr, err = backend.CreateManagedContainer(types.ContainerCreateConfig{ 114 Name: c.container.name(), 115 Config: c.container.config(), 116 HostConfig: c.container.hostConfig(), 117 // Use the first network in container create 118 NetworkingConfig: c.container.createNetworkingConfig(), 119 }); err != nil { 120 return err 121 } 122 123 // Docker daemon currently doesnt support multiple networks in container create 124 // Connect to all other networks 125 nc := c.container.connectNetworkingConfig() 126 127 if nc != nil { 128 for n, ep := range nc.EndpointsConfig { 129 logrus.Errorf("CONNECT %s : %v", n, ep.IPAMConfig.IPv4Address) 130 if err := backend.ConnectContainerToNetwork(cr.ID, n, ep); err != nil { 131 return err 132 } 133 } 134 } 135 136 if err := backend.UpdateContainerServiceConfig(cr.ID, c.container.serviceConfig()); err != nil { 137 return err 138 } 139 140 return nil 141 } 142 143 func (c *containerAdapter) start(ctx context.Context) error { 144 return c.backend.ContainerStart(c.container.name(), nil) 145 } 146 147 func (c *containerAdapter) inspect(ctx context.Context) (types.ContainerJSON, error) { 148 cs, err := c.backend.ContainerInspectCurrent(c.container.name(), false) 149 if ctx.Err() != nil { 150 return types.ContainerJSON{}, ctx.Err() 151 } 152 if err != nil { 153 return types.ContainerJSON{}, err 154 } 155 return *cs, nil 156 } 157 158 // events issues a call to the events API and returns a channel with all 159 // events. The stream of events can be shutdown by cancelling the context. 160 // 161 // A chan struct{} is returned that will be closed if the event procressing 162 // fails and needs to be restarted. 163 func (c *containerAdapter) wait(ctx context.Context) error { 164 return c.backend.ContainerWaitWithContext(ctx, c.container.name()) 165 } 166 167 func (c *containerAdapter) shutdown(ctx context.Context) error { 168 // Default stop grace period to 10s. 169 stopgrace := 10 170 spec := c.container.spec() 171 if spec.StopGracePeriod != nil { 172 stopgrace = int(spec.StopGracePeriod.Seconds) 173 } 174 return c.backend.ContainerStop(c.container.name(), stopgrace) 175 } 176 177 func (c *containerAdapter) terminate(ctx context.Context) error { 178 return c.backend.ContainerKill(c.container.name(), uint64(syscall.SIGKILL)) 179 } 180 181 func (c *containerAdapter) remove(ctx context.Context) error { 182 return c.backend.ContainerRm(c.container.name(), &types.ContainerRmConfig{ 183 RemoveVolume: true, 184 ForceRemove: true, 185 }) 186 } 187 188 func (c *containerAdapter) createVolumes(ctx context.Context, backend executorpkg.Backend) error { 189 // Create plugin volumes that are embedded inside a Mount 190 for _, mount := range c.container.task.Spec.GetContainer().Mounts { 191 if mount.Type != api.MountTypeVolume { 192 continue 193 } 194 195 if mount.VolumeOptions == nil { 196 continue 197 } 198 199 if mount.VolumeOptions.DriverConfig == nil { 200 continue 201 } 202 203 req := c.container.volumeCreateRequest(&mount) 204 205 // Check if this volume exists on the engine 206 if _, err := backend.VolumeCreate(req.Name, req.Driver, req.DriverOpts, req.Labels); err != nil { 207 // TODO(amitshukla): Today, volume create through the engine api does not return an error 208 // when the named volume with the same parameters already exists. 209 // It returns an error if the driver name is different - that is a valid error 210 return err 211 } 212 213 } 214 215 return nil 216 } 217 218 // todo: typed/wrapped errors 219 func isContainerCreateNameConflict(err error) bool { 220 return strings.Contains(err.Error(), "Conflict. The name") 221 } 222 223 func isUnknownContainer(err error) bool { 224 return strings.Contains(err.Error(), "No such container:") 225 } 226 227 func isStoppedContainer(err error) bool { 228 return strings.Contains(err.Error(), "is already stopped") 229 }