github.com/reds/docker@v1.11.2-rc1/libcontainerd/client_linux.go (about) 1 package libcontainerd 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "os" 7 "path/filepath" 8 "strings" 9 "sync" 10 "syscall" 11 12 "github.com/Sirupsen/logrus" 13 containerd "github.com/docker/containerd/api/grpc/types" 14 "github.com/docker/docker/pkg/idtools" 15 "github.com/docker/docker/pkg/mount" 16 "github.com/opencontainers/specs/specs-go" 17 "golang.org/x/net/context" 18 ) 19 20 type client struct { 21 clientCommon 22 23 // Platform specific properties below here. 24 remote *remote 25 q queue 26 exitNotifiers map[string]*exitNotifier 27 } 28 29 func (clnt *client) AddProcess(containerID, processFriendlyName string, specp Process) error { 30 clnt.lock(containerID) 31 defer clnt.unlock(containerID) 32 container, err := clnt.getContainer(containerID) 33 if err != nil { 34 return err 35 } 36 37 spec, err := container.spec() 38 if err != nil { 39 return err 40 } 41 sp := spec.Process 42 sp.Args = specp.Args 43 sp.Terminal = specp.Terminal 44 if specp.Env != nil { 45 sp.Env = specp.Env 46 } 47 if specp.Cwd != nil { 48 sp.Cwd = *specp.Cwd 49 } 50 if specp.User != nil { 51 sp.User = specs.User{ 52 UID: specp.User.UID, 53 GID: specp.User.GID, 54 AdditionalGids: specp.User.AdditionalGids, 55 } 56 } 57 if specp.Capabilities != nil { 58 sp.Capabilities = specp.Capabilities 59 } 60 61 p := container.newProcess(processFriendlyName) 62 63 r := &containerd.AddProcessRequest{ 64 Args: sp.Args, 65 Cwd: sp.Cwd, 66 Terminal: sp.Terminal, 67 Id: containerID, 68 Env: sp.Env, 69 User: &containerd.User{ 70 Uid: sp.User.UID, 71 Gid: sp.User.GID, 72 AdditionalGids: sp.User.AdditionalGids, 73 }, 74 Pid: processFriendlyName, 75 Stdin: p.fifo(syscall.Stdin), 76 Stdout: p.fifo(syscall.Stdout), 77 Stderr: p.fifo(syscall.Stderr), 78 Capabilities: sp.Capabilities, 79 ApparmorProfile: sp.ApparmorProfile, 80 SelinuxLabel: sp.SelinuxLabel, 81 NoNewPrivileges: sp.NoNewPrivileges, 82 Rlimits: convertRlimits(sp.Rlimits), 83 } 84 85 iopipe, err := p.openFifos(sp.Terminal) 86 if err != nil { 87 return err 88 } 89 90 if _, err := clnt.remote.apiClient.AddProcess(context.Background(), r); err != nil { 91 p.closeFifos(iopipe) 92 return err 93 } 94 95 container.processes[processFriendlyName] = p 96 97 clnt.unlock(containerID) 98 99 if err := clnt.backend.AttachStreams(processFriendlyName, *iopipe); err != nil { 100 return err 101 } 102 clnt.lock(containerID) 103 104 return nil 105 } 106 107 func (clnt *client) prepareBundleDir(uid, gid int) (string, error) { 108 root, err := filepath.Abs(clnt.remote.stateDir) 109 if err != nil { 110 return "", err 111 } 112 if uid == 0 && gid == 0 { 113 return root, nil 114 } 115 p := string(filepath.Separator) 116 for _, d := range strings.Split(root, string(filepath.Separator))[1:] { 117 p = filepath.Join(p, d) 118 fi, err := os.Stat(p) 119 if err != nil && !os.IsNotExist(err) { 120 return "", err 121 } 122 if os.IsNotExist(err) || fi.Mode()&1 == 0 { 123 p = fmt.Sprintf("%s.%d.%d", p, uid, gid) 124 if err := idtools.MkdirAs(p, 0700, uid, gid); err != nil && !os.IsExist(err) { 125 return "", err 126 } 127 } 128 } 129 return p, nil 130 } 131 132 func (clnt *client) Create(containerID string, spec Spec, options ...CreateOption) (err error) { 133 clnt.lock(containerID) 134 defer clnt.unlock(containerID) 135 136 if ctr, err := clnt.getContainer(containerID); err == nil { 137 if ctr.restarting { 138 ctr.restartManager.Cancel() 139 ctr.clean() 140 } else { 141 return fmt.Errorf("Container %s is aleady active", containerID) 142 } 143 } 144 145 uid, gid, err := getRootIDs(specs.Spec(spec)) 146 if err != nil { 147 return err 148 } 149 dir, err := clnt.prepareBundleDir(uid, gid) 150 if err != nil { 151 return err 152 } 153 154 container := clnt.newContainer(filepath.Join(dir, containerID), options...) 155 if err := container.clean(); err != nil { 156 return err 157 } 158 159 defer func() { 160 if err != nil { 161 container.clean() 162 clnt.deleteContainer(containerID) 163 } 164 }() 165 166 if err := idtools.MkdirAllAs(container.dir, 0700, uid, gid); err != nil && !os.IsExist(err) { 167 return err 168 } 169 170 f, err := os.Create(filepath.Join(container.dir, configFilename)) 171 if err != nil { 172 return err 173 } 174 defer f.Close() 175 if err := json.NewEncoder(f).Encode(spec); err != nil { 176 return err 177 } 178 179 return container.start() 180 } 181 182 func (clnt *client) Signal(containerID string, sig int) error { 183 clnt.lock(containerID) 184 defer clnt.unlock(containerID) 185 _, err := clnt.remote.apiClient.Signal(context.Background(), &containerd.SignalRequest{ 186 Id: containerID, 187 Pid: InitFriendlyName, 188 Signal: uint32(sig), 189 }) 190 return err 191 } 192 193 func (clnt *client) Resize(containerID, processFriendlyName string, width, height int) error { 194 clnt.lock(containerID) 195 defer clnt.unlock(containerID) 196 if _, err := clnt.getContainer(containerID); err != nil { 197 return err 198 } 199 _, err := clnt.remote.apiClient.UpdateProcess(context.Background(), &containerd.UpdateProcessRequest{ 200 Id: containerID, 201 Pid: processFriendlyName, 202 Width: uint32(width), 203 Height: uint32(height), 204 }) 205 return err 206 } 207 208 func (clnt *client) Pause(containerID string) error { 209 return clnt.setState(containerID, StatePause) 210 } 211 212 func (clnt *client) setState(containerID, state string) error { 213 clnt.lock(containerID) 214 container, err := clnt.getContainer(containerID) 215 if err != nil { 216 clnt.unlock(containerID) 217 return err 218 } 219 if container.systemPid == 0 { 220 clnt.unlock(containerID) 221 return fmt.Errorf("No active process for container %s", containerID) 222 } 223 st := "running" 224 if state == StatePause { 225 st = "paused" 226 } 227 chstate := make(chan struct{}) 228 _, err = clnt.remote.apiClient.UpdateContainer(context.Background(), &containerd.UpdateContainerRequest{ 229 Id: containerID, 230 Pid: InitFriendlyName, 231 Status: st, 232 }) 233 if err != nil { 234 clnt.unlock(containerID) 235 return err 236 } 237 container.pauseMonitor.append(state, chstate) 238 clnt.unlock(containerID) 239 <-chstate 240 return nil 241 } 242 243 func (clnt *client) Resume(containerID string) error { 244 return clnt.setState(containerID, StateResume) 245 } 246 247 func (clnt *client) Stats(containerID string) (*Stats, error) { 248 resp, err := clnt.remote.apiClient.Stats(context.Background(), &containerd.StatsRequest{containerID}) 249 if err != nil { 250 return nil, err 251 } 252 return (*Stats)(resp), nil 253 } 254 255 // Take care of the old 1.11.0 behavior in case the version upgrade 256 // happenned without a clean daemon shutdown 257 func (clnt *client) cleanupOldRootfs(containerID string) { 258 // Unmount and delete the bundle folder 259 if mts, err := mount.GetMounts(); err == nil { 260 for _, mts := range mts { 261 if strings.HasSuffix(mts.Mountpoint, containerID+"/rootfs") { 262 if err := syscall.Unmount(mts.Mountpoint, syscall.MNT_DETACH); err == nil { 263 os.RemoveAll(strings.TrimSuffix(mts.Mountpoint, "/rootfs")) 264 } 265 break 266 } 267 } 268 } 269 } 270 271 func (clnt *client) setExited(containerID string) error { 272 clnt.lock(containerID) 273 defer clnt.unlock(containerID) 274 275 var exitCode uint32 276 if event, ok := clnt.remote.pastEvents[containerID]; ok { 277 exitCode = event.Status 278 delete(clnt.remote.pastEvents, containerID) 279 } 280 281 err := clnt.backend.StateChanged(containerID, StateInfo{ 282 State: StateExit, 283 ExitCode: exitCode, 284 }) 285 286 clnt.cleanupOldRootfs(containerID) 287 288 return err 289 } 290 291 func (clnt *client) GetPidsForContainer(containerID string) ([]int, error) { 292 cont, err := clnt.getContainerdContainer(containerID) 293 if err != nil { 294 return nil, err 295 } 296 pids := make([]int, len(cont.Pids)) 297 for i, p := range cont.Pids { 298 pids[i] = int(p) 299 } 300 return pids, nil 301 } 302 303 // Summary returns a summary of the processes running in a container. 304 // This is a no-op on Linux. 305 func (clnt *client) Summary(containerID string) ([]Summary, error) { 306 return nil, nil 307 } 308 309 func (clnt *client) getContainerdContainer(containerID string) (*containerd.Container, error) { 310 resp, err := clnt.remote.apiClient.State(context.Background(), &containerd.StateRequest{Id: containerID}) 311 if err != nil { 312 return nil, err 313 } 314 for _, cont := range resp.Containers { 315 if cont.Id == containerID { 316 return cont, nil 317 } 318 } 319 return nil, fmt.Errorf("invalid state response") 320 } 321 322 func (clnt *client) newContainer(dir string, options ...CreateOption) *container { 323 container := &container{ 324 containerCommon: containerCommon{ 325 process: process{ 326 dir: dir, 327 processCommon: processCommon{ 328 containerID: filepath.Base(dir), 329 client: clnt, 330 friendlyName: InitFriendlyName, 331 }, 332 }, 333 processes: make(map[string]*process), 334 }, 335 } 336 for _, option := range options { 337 if err := option.Apply(container); err != nil { 338 logrus.Error(err) 339 } 340 } 341 return container 342 } 343 344 func (clnt *client) UpdateResources(containerID string, resources Resources) error { 345 clnt.lock(containerID) 346 defer clnt.unlock(containerID) 347 container, err := clnt.getContainer(containerID) 348 if err != nil { 349 return err 350 } 351 if container.systemPid == 0 { 352 return fmt.Errorf("No active process for container %s", containerID) 353 } 354 _, err = clnt.remote.apiClient.UpdateContainer(context.Background(), &containerd.UpdateContainerRequest{ 355 Id: containerID, 356 Pid: InitFriendlyName, 357 Resources: (*containerd.UpdateResource)(&resources), 358 }) 359 if err != nil { 360 return err 361 } 362 return nil 363 } 364 365 func (clnt *client) getExitNotifier(containerID string) *exitNotifier { 366 clnt.mapMutex.RLock() 367 defer clnt.mapMutex.RUnlock() 368 return clnt.exitNotifiers[containerID] 369 } 370 371 func (clnt *client) getOrCreateExitNotifier(containerID string) *exitNotifier { 372 clnt.mapMutex.Lock() 373 w, ok := clnt.exitNotifiers[containerID] 374 defer clnt.mapMutex.Unlock() 375 if !ok { 376 w = &exitNotifier{c: make(chan struct{}), client: clnt} 377 clnt.exitNotifiers[containerID] = w 378 } 379 return w 380 } 381 382 type exitNotifier struct { 383 id string 384 client *client 385 c chan struct{} 386 once sync.Once 387 } 388 389 func (en *exitNotifier) close() { 390 en.once.Do(func() { 391 close(en.c) 392 en.client.mapMutex.Lock() 393 if en == en.client.exitNotifiers[en.id] { 394 delete(en.client.exitNotifiers, en.id) 395 } 396 en.client.mapMutex.Unlock() 397 }) 398 } 399 func (en *exitNotifier) wait() <-chan struct{} { 400 return en.c 401 }