github.com/docker/containerd@v0.2.9-0.20170509230648-8ef7df579710/api/grpc/server/server.go (about) 1 package server 2 3 import ( 4 "bufio" 5 "errors" 6 "fmt" 7 "os" 8 "strconv" 9 "strings" 10 "syscall" 11 "time" 12 13 "google.golang.org/grpc" 14 "google.golang.org/grpc/codes" 15 16 "github.com/containerd/containerd" 17 "github.com/containerd/containerd/api/grpc/types" 18 "github.com/containerd/containerd/runtime" 19 "github.com/containerd/containerd/supervisor" 20 "github.com/golang/protobuf/ptypes" 21 "golang.org/x/net/context" 22 ) 23 24 type apiServer struct { 25 sv *supervisor.Supervisor 26 } 27 28 // NewServer returns grpc server instance 29 func NewServer(sv *supervisor.Supervisor) types.APIServer { 30 return &apiServer{ 31 sv: sv, 32 } 33 } 34 35 func (s *apiServer) GetServerVersion(ctx context.Context, c *types.GetServerVersionRequest) (*types.GetServerVersionResponse, error) { 36 return &types.GetServerVersionResponse{ 37 Major: containerd.VersionMajor, 38 Minor: containerd.VersionMinor, 39 Patch: containerd.VersionPatch, 40 Revision: containerd.GitCommit, 41 }, nil 42 } 43 44 func (s *apiServer) CreateContainer(ctx context.Context, c *types.CreateContainerRequest) (*types.CreateContainerResponse, error) { 45 if c.BundlePath == "" { 46 return nil, errors.New("empty bundle path") 47 } 48 e := &supervisor.StartTask{} 49 e.ID = c.Id 50 e.BundlePath = c.BundlePath 51 e.Stdin = c.Stdin 52 e.Stdout = c.Stdout 53 e.Stderr = c.Stderr 54 e.Labels = c.Labels 55 e.NoPivotRoot = c.NoPivotRoot 56 e.Runtime = c.Runtime 57 e.RuntimeArgs = c.RuntimeArgs 58 e.StartResponse = make(chan supervisor.StartResponse, 1) 59 e.Ctx = ctx 60 if c.Checkpoint != "" { 61 e.CheckpointDir = c.CheckpointDir 62 e.Checkpoint = &runtime.Checkpoint{ 63 Name: c.Checkpoint, 64 } 65 } 66 s.sv.SendTask(e) 67 if err := <-e.ErrorCh(); err != nil { 68 return nil, err 69 } 70 r := <-e.StartResponse 71 apiC, err := createAPIContainer(r.Container, false) 72 if err != nil { 73 return nil, err 74 } 75 return &types.CreateContainerResponse{ 76 Container: apiC, 77 }, nil 78 } 79 80 func (s *apiServer) CreateCheckpoint(ctx context.Context, r *types.CreateCheckpointRequest) (*types.CreateCheckpointResponse, error) { 81 e := &supervisor.CreateCheckpointTask{} 82 e.ID = r.Id 83 e.CheckpointDir = r.CheckpointDir 84 e.Checkpoint = &runtime.Checkpoint{ 85 Name: r.Checkpoint.Name, 86 Exit: r.Checkpoint.Exit, 87 TCP: r.Checkpoint.Tcp, 88 UnixSockets: r.Checkpoint.UnixSockets, 89 Shell: r.Checkpoint.Shell, 90 EmptyNS: r.Checkpoint.EmptyNS, 91 } 92 93 s.sv.SendTask(e) 94 if err := <-e.ErrorCh(); err != nil { 95 return nil, err 96 } 97 return &types.CreateCheckpointResponse{}, nil 98 } 99 100 func (s *apiServer) DeleteCheckpoint(ctx context.Context, r *types.DeleteCheckpointRequest) (*types.DeleteCheckpointResponse, error) { 101 if r.Name == "" { 102 return nil, errors.New("checkpoint name cannot be empty") 103 } 104 e := &supervisor.DeleteCheckpointTask{} 105 e.ID = r.Id 106 e.CheckpointDir = r.CheckpointDir 107 e.Checkpoint = &runtime.Checkpoint{ 108 Name: r.Name, 109 } 110 s.sv.SendTask(e) 111 if err := <-e.ErrorCh(); err != nil { 112 return nil, err 113 } 114 return &types.DeleteCheckpointResponse{}, nil 115 } 116 117 func (s *apiServer) ListCheckpoint(ctx context.Context, r *types.ListCheckpointRequest) (*types.ListCheckpointResponse, error) { 118 e := &supervisor.GetContainersTask{} 119 s.sv.SendTask(e) 120 if err := <-e.ErrorCh(); err != nil { 121 return nil, err 122 } 123 var container runtime.Container 124 for _, c := range e.Containers { 125 if c.ID() == r.Id { 126 container = c 127 break 128 } 129 } 130 if container == nil { 131 return nil, grpc.Errorf(codes.NotFound, "no such containers") 132 } 133 var out []*types.Checkpoint 134 checkpoints, err := container.Checkpoints(r.CheckpointDir) 135 if err != nil { 136 return nil, err 137 } 138 for _, c := range checkpoints { 139 out = append(out, &types.Checkpoint{ 140 Name: c.Name, 141 Tcp: c.TCP, 142 Shell: c.Shell, 143 UnixSockets: c.UnixSockets, 144 // TODO: figure out timestamp 145 //Timestamp: c.Timestamp, 146 }) 147 } 148 return &types.ListCheckpointResponse{Checkpoints: out}, nil 149 } 150 151 func (s *apiServer) Signal(ctx context.Context, r *types.SignalRequest) (*types.SignalResponse, error) { 152 e := &supervisor.SignalTask{} 153 e.ID = r.Id 154 e.PID = r.Pid 155 e.Signal = syscall.Signal(int(r.Signal)) 156 s.sv.SendTask(e) 157 if err := <-e.ErrorCh(); err != nil { 158 return nil, err 159 } 160 return &types.SignalResponse{}, nil 161 } 162 163 func (s *apiServer) State(ctx context.Context, r *types.StateRequest) (*types.StateResponse, error) { 164 165 getState := func(c runtime.Container) (interface{}, error) { 166 return createAPIContainer(c, true) 167 } 168 169 e := &supervisor.GetContainersTask{} 170 e.ID = r.Id 171 e.GetState = getState 172 s.sv.SendTask(e) 173 if err := <-e.ErrorCh(); err != nil { 174 return nil, err 175 } 176 m := s.sv.Machine() 177 state := &types.StateResponse{ 178 Machine: &types.Machine{ 179 Cpus: uint32(m.Cpus), 180 Memory: uint64(m.Memory), 181 }, 182 } 183 for idx := range e.Containers { 184 state.Containers = append(state.Containers, e.States[idx].(*types.Container)) 185 } 186 return state, nil 187 } 188 189 func createAPIContainer(c runtime.Container, getPids bool) (*types.Container, error) { 190 processes, err := c.Processes() 191 if err != nil { 192 return nil, grpc.Errorf(codes.Internal, "get processes for container: "+err.Error()) 193 } 194 var procs []*types.Process 195 for _, p := range processes { 196 oldProc := p.Spec() 197 stdio := p.Stdio() 198 proc := &types.Process{ 199 Pid: p.ID(), 200 SystemPid: uint32(p.SystemPid()), 201 Terminal: oldProc.Terminal, 202 Args: oldProc.Args, 203 Env: oldProc.Env, 204 Cwd: oldProc.Cwd, 205 Stdin: stdio.Stdin, 206 Stdout: stdio.Stdout, 207 Stderr: stdio.Stderr, 208 } 209 proc.User = &types.User{ 210 Uid: oldProc.User.UID, 211 Gid: oldProc.User.GID, 212 AdditionalGids: oldProc.User.AdditionalGids, 213 } 214 // FIXME: trying to mimic api compat with only using one field 215 proc.Capabilities = oldProc.Capabilities.Effective 216 proc.ApparmorProfile = oldProc.ApparmorProfile 217 proc.SelinuxLabel = oldProc.SelinuxLabel 218 proc.NoNewPrivileges = oldProc.NoNewPrivileges 219 for _, rl := range oldProc.Rlimits { 220 proc.Rlimits = append(proc.Rlimits, &types.Rlimit{ 221 Type: rl.Type, 222 Soft: rl.Soft, 223 Hard: rl.Hard, 224 }) 225 } 226 procs = append(procs, proc) 227 } 228 var pids []int 229 state := c.State() 230 if getPids && (state == runtime.Running || state == runtime.Paused) { 231 if pids, err = c.Pids(); err != nil { 232 return nil, grpc.Errorf(codes.Internal, "get all pids for container: "+err.Error()) 233 } 234 } 235 return &types.Container{ 236 Id: c.ID(), 237 BundlePath: c.Path(), 238 Processes: procs, 239 Labels: c.Labels(), 240 Status: string(state), 241 Pids: toUint32(pids), 242 Runtime: c.Runtime(), 243 }, nil 244 } 245 246 func toUint32(its []int) []uint32 { 247 o := []uint32{} 248 for _, i := range its { 249 o = append(o, uint32(i)) 250 } 251 return o 252 } 253 254 func (s *apiServer) UpdateContainer(ctx context.Context, r *types.UpdateContainerRequest) (*types.UpdateContainerResponse, error) { 255 e := &supervisor.UpdateTask{} 256 e.ID = r.Id 257 e.State = runtime.State(r.Status) 258 if r.Resources != nil { 259 rs := r.Resources 260 e.Resources = &runtime.Resource{} 261 if rs.CpuShares != 0 { 262 e.Resources.CPUShares = int64(rs.CpuShares) 263 } 264 if rs.BlkioWeight != 0 { 265 e.Resources.BlkioWeight = uint16(rs.BlkioWeight) 266 } 267 if rs.CpuPeriod != 0 { 268 e.Resources.CPUPeriod = int64(rs.CpuPeriod) 269 } 270 if rs.CpuQuota != 0 { 271 e.Resources.CPUQuota = int64(rs.CpuQuota) 272 } 273 if rs.CpusetCpus != "" { 274 e.Resources.CpusetCpus = rs.CpusetCpus 275 } 276 if rs.CpusetMems != "" { 277 e.Resources.CpusetMems = rs.CpusetMems 278 } 279 if rs.KernelMemoryLimit != 0 { 280 e.Resources.KernelMemory = int64(rs.KernelMemoryLimit) 281 } 282 if rs.KernelTCPMemoryLimit != 0 { 283 e.Resources.KernelTCPMemory = int64(rs.KernelTCPMemoryLimit) 284 } 285 if rs.MemoryLimit != 0 { 286 e.Resources.Memory = int64(rs.MemoryLimit) 287 } 288 if rs.MemoryReservation != 0 { 289 e.Resources.MemoryReservation = int64(rs.MemoryReservation) 290 } 291 if rs.MemorySwap != 0 { 292 e.Resources.MemorySwap = int64(rs.MemorySwap) 293 } 294 if rs.PidsLimit != 0 { 295 e.Resources.PidsLimit = int64(rs.PidsLimit) 296 } 297 } 298 s.sv.SendTask(e) 299 if err := <-e.ErrorCh(); err != nil { 300 return nil, err 301 } 302 return &types.UpdateContainerResponse{}, nil 303 } 304 305 func (s *apiServer) UpdateProcess(ctx context.Context, r *types.UpdateProcessRequest) (*types.UpdateProcessResponse, error) { 306 e := &supervisor.UpdateProcessTask{} 307 e.ID = r.Id 308 e.PID = r.Pid 309 e.Height = int(r.Height) 310 e.Width = int(r.Width) 311 e.CloseStdin = r.CloseStdin 312 s.sv.SendTask(e) 313 if err := <-e.ErrorCh(); err != nil { 314 return nil, err 315 } 316 return &types.UpdateProcessResponse{}, nil 317 } 318 319 func (s *apiServer) Events(r *types.EventsRequest, stream types.API_EventsServer) error { 320 t := time.Time{} 321 if r.Timestamp != nil { 322 from, err := ptypes.Timestamp(r.Timestamp) 323 if err != nil { 324 return err 325 } 326 t = from 327 } 328 if r.StoredOnly && t.IsZero() { 329 return fmt.Errorf("invalid parameter: StoredOnly cannot be specified without setting a valid Timestamp") 330 } 331 events := s.sv.Events(t, r.StoredOnly, r.Id) 332 defer s.sv.Unsubscribe(events) 333 for e := range events { 334 tsp, err := ptypes.TimestampProto(e.Timestamp) 335 if err != nil { 336 return err 337 } 338 if r.Id == "" || e.ID == r.Id { 339 if err := stream.Send(&types.Event{ 340 Id: e.ID, 341 Type: e.Type, 342 Timestamp: tsp, 343 Pid: e.PID, 344 Status: uint32(e.Status), 345 }); err != nil { 346 return err 347 } 348 } 349 } 350 return nil 351 } 352 353 func convertToPb(st *runtime.Stat) *types.StatsResponse { 354 tsp, _ := ptypes.TimestampProto(st.Timestamp) 355 pbSt := &types.StatsResponse{ 356 Timestamp: tsp, 357 CgroupStats: &types.CgroupStats{}, 358 } 359 systemUsage, _ := getSystemCPUUsage() 360 pbSt.CgroupStats.CpuStats = &types.CpuStats{ 361 CpuUsage: &types.CpuUsage{ 362 TotalUsage: st.CPU.Usage.Total, 363 PercpuUsage: st.CPU.Usage.Percpu, 364 UsageInKernelmode: st.CPU.Usage.Kernel, 365 UsageInUsermode: st.CPU.Usage.User, 366 }, 367 ThrottlingData: &types.ThrottlingData{ 368 Periods: st.CPU.Throttling.Periods, 369 ThrottledPeriods: st.CPU.Throttling.ThrottledPeriods, 370 ThrottledTime: st.CPU.Throttling.ThrottledTime, 371 }, 372 SystemUsage: systemUsage, 373 } 374 pbSt.CgroupStats.MemoryStats = &types.MemoryStats{ 375 Cache: st.Memory.Cache, 376 Usage: &types.MemoryData{ 377 Usage: st.Memory.Usage.Usage, 378 MaxUsage: st.Memory.Usage.Max, 379 Failcnt: st.Memory.Usage.Failcnt, 380 Limit: st.Memory.Usage.Limit, 381 }, 382 SwapUsage: &types.MemoryData{ 383 Usage: st.Memory.Swap.Usage, 384 MaxUsage: st.Memory.Swap.Max, 385 Failcnt: st.Memory.Swap.Failcnt, 386 Limit: st.Memory.Swap.Limit, 387 }, 388 KernelUsage: &types.MemoryData{ 389 Usage: st.Memory.Kernel.Usage, 390 MaxUsage: st.Memory.Kernel.Max, 391 Failcnt: st.Memory.Kernel.Failcnt, 392 Limit: st.Memory.Kernel.Limit, 393 }, 394 Stats: st.Memory.Raw, 395 } 396 pbSt.CgroupStats.BlkioStats = &types.BlkioStats{ 397 IoServiceBytesRecursive: convertBlkioEntryToPb(st.Blkio.IoServiceBytesRecursive), 398 IoServicedRecursive: convertBlkioEntryToPb(st.Blkio.IoServicedRecursive), 399 IoQueuedRecursive: convertBlkioEntryToPb(st.Blkio.IoQueuedRecursive), 400 IoServiceTimeRecursive: convertBlkioEntryToPb(st.Blkio.IoServiceTimeRecursive), 401 IoWaitTimeRecursive: convertBlkioEntryToPb(st.Blkio.IoWaitTimeRecursive), 402 IoMergedRecursive: convertBlkioEntryToPb(st.Blkio.IoMergedRecursive), 403 IoTimeRecursive: convertBlkioEntryToPb(st.Blkio.IoTimeRecursive), 404 SectorsRecursive: convertBlkioEntryToPb(st.Blkio.SectorsRecursive), 405 } 406 pbSt.CgroupStats.HugetlbStats = make(map[string]*types.HugetlbStats) 407 for k, st := range st.Hugetlb { 408 pbSt.CgroupStats.HugetlbStats[k] = &types.HugetlbStats{ 409 Usage: st.Usage, 410 MaxUsage: st.Max, 411 Failcnt: st.Failcnt, 412 } 413 } 414 pbSt.CgroupStats.PidsStats = &types.PidsStats{ 415 Current: st.Pids.Current, 416 Limit: st.Pids.Limit, 417 } 418 return pbSt 419 } 420 421 func convertBlkioEntryToPb(b []runtime.BlkioEntry) []*types.BlkioStatsEntry { 422 var pbEs []*types.BlkioStatsEntry 423 for _, e := range b { 424 pbEs = append(pbEs, &types.BlkioStatsEntry{ 425 Major: e.Major, 426 Minor: e.Minor, 427 Op: e.Op, 428 Value: e.Value, 429 }) 430 } 431 return pbEs 432 } 433 434 const nanoSecondsPerSecond = 1e9 435 436 // getSystemCPUUsage returns the host system's cpu usage in 437 // nanoseconds. An error is returned if the format of the underlying 438 // file does not match. 439 // 440 // Uses /proc/stat defined by POSIX. Looks for the cpu 441 // statistics line and then sums up the first seven fields 442 // provided. See `man 5 proc` for details on specific field 443 // information. 444 func getSystemCPUUsage() (uint64, error) { 445 var line string 446 f, err := os.Open("/proc/stat") 447 if err != nil { 448 return 0, err 449 } 450 bufReader := bufio.NewReaderSize(nil, 128) 451 defer func() { 452 bufReader.Reset(nil) 453 f.Close() 454 }() 455 bufReader.Reset(f) 456 err = nil 457 for err == nil { 458 line, err = bufReader.ReadString('\n') 459 if err != nil { 460 break 461 } 462 parts := strings.Fields(line) 463 switch parts[0] { 464 case "cpu": 465 if len(parts) < 8 { 466 return 0, fmt.Errorf("bad format of cpu stats") 467 } 468 var totalClockTicks uint64 469 for _, i := range parts[1:8] { 470 v, err := strconv.ParseUint(i, 10, 64) 471 if err != nil { 472 return 0, fmt.Errorf("error parsing cpu stats") 473 } 474 totalClockTicks += v 475 } 476 return (totalClockTicks * nanoSecondsPerSecond) / 477 clockTicksPerSecond, nil 478 } 479 } 480 return 0, fmt.Errorf("bad stats format") 481 } 482 483 func (s *apiServer) Stats(ctx context.Context, r *types.StatsRequest) (*types.StatsResponse, error) { 484 e := &supervisor.StatsTask{} 485 e.ID = r.Id 486 e.Stat = make(chan *runtime.Stat, 1) 487 s.sv.SendTask(e) 488 if err := <-e.ErrorCh(); err != nil { 489 return nil, err 490 } 491 stats := <-e.Stat 492 t := convertToPb(stats) 493 return t, nil 494 }