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