github.com/hinshun/containerd@v0.2.7/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/docker/containerd" 17 "github.com/docker/containerd/api/grpc/types" 18 "github.com/docker/containerd/runtime" 19 "github.com/docker/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 proc.Capabilities = oldProc.Capabilities 215 proc.ApparmorProfile = oldProc.ApparmorProfile 216 proc.SelinuxLabel = oldProc.SelinuxLabel 217 proc.NoNewPrivileges = oldProc.NoNewPrivileges 218 for _, rl := range oldProc.Rlimits { 219 proc.Rlimits = append(proc.Rlimits, &types.Rlimit{ 220 Type: rl.Type, 221 Soft: rl.Soft, 222 Hard: rl.Hard, 223 }) 224 } 225 procs = append(procs, proc) 226 } 227 var pids []int 228 state := c.State() 229 if getPids && (state == runtime.Running || state == runtime.Paused) { 230 if pids, err = c.Pids(); err != nil { 231 return nil, grpc.Errorf(codes.Internal, "get all pids for container: "+err.Error()) 232 } 233 } 234 return &types.Container{ 235 Id: c.ID(), 236 BundlePath: c.Path(), 237 Processes: procs, 238 Labels: c.Labels(), 239 Status: string(state), 240 Pids: toUint32(pids), 241 Runtime: c.Runtime(), 242 }, nil 243 } 244 245 func toUint32(its []int) []uint32 { 246 o := []uint32{} 247 for _, i := range its { 248 o = append(o, uint32(i)) 249 } 250 return o 251 } 252 253 func (s *apiServer) UpdateContainer(ctx context.Context, r *types.UpdateContainerRequest) (*types.UpdateContainerResponse, error) { 254 e := &supervisor.UpdateTask{} 255 e.ID = r.Id 256 e.State = runtime.State(r.Status) 257 if r.Resources != nil { 258 rs := r.Resources 259 e.Resources = &runtime.Resource{} 260 if rs.CpuShares != 0 { 261 e.Resources.CPUShares = int64(rs.CpuShares) 262 } 263 if rs.BlkioWeight != 0 { 264 e.Resources.BlkioWeight = uint16(rs.BlkioWeight) 265 } 266 if rs.CpuPeriod != 0 { 267 e.Resources.CPUPeriod = int64(rs.CpuPeriod) 268 } 269 if rs.CpuQuota != 0 { 270 e.Resources.CPUQuota = int64(rs.CpuQuota) 271 } 272 if rs.CpusetCpus != "" { 273 e.Resources.CpusetCpus = rs.CpusetCpus 274 } 275 if rs.CpusetMems != "" { 276 e.Resources.CpusetMems = rs.CpusetMems 277 } 278 if rs.KernelMemoryLimit != 0 { 279 e.Resources.KernelMemory = int64(rs.KernelMemoryLimit) 280 } 281 if rs.KernelTCPMemoryLimit != 0 { 282 e.Resources.KernelTCPMemory = int64(rs.KernelTCPMemoryLimit) 283 } 284 if rs.MemoryLimit != 0 { 285 e.Resources.Memory = int64(rs.MemoryLimit) 286 } 287 if rs.MemoryReservation != 0 { 288 e.Resources.MemoryReservation = int64(rs.MemoryReservation) 289 } 290 if rs.MemorySwap != 0 { 291 e.Resources.MemorySwap = int64(rs.MemorySwap) 292 } 293 } 294 s.sv.SendTask(e) 295 if err := <-e.ErrorCh(); err != nil { 296 return nil, err 297 } 298 return &types.UpdateContainerResponse{}, nil 299 } 300 301 func (s *apiServer) UpdateProcess(ctx context.Context, r *types.UpdateProcessRequest) (*types.UpdateProcessResponse, error) { 302 e := &supervisor.UpdateProcessTask{} 303 e.ID = r.Id 304 e.PID = r.Pid 305 e.Height = int(r.Height) 306 e.Width = int(r.Width) 307 e.CloseStdin = r.CloseStdin 308 s.sv.SendTask(e) 309 if err := <-e.ErrorCh(); err != nil { 310 return nil, err 311 } 312 return &types.UpdateProcessResponse{}, nil 313 } 314 315 func (s *apiServer) Events(r *types.EventsRequest, stream types.API_EventsServer) error { 316 t := time.Time{} 317 if r.Timestamp != nil { 318 from, err := ptypes.Timestamp(r.Timestamp) 319 if err != nil { 320 return err 321 } 322 t = from 323 } 324 if r.StoredOnly && t.IsZero() { 325 return fmt.Errorf("invalid parameter: StoredOnly cannot be specified without setting a valid Timestamp") 326 } 327 events := s.sv.Events(t, r.StoredOnly, r.Id) 328 defer s.sv.Unsubscribe(events) 329 for e := range events { 330 tsp, err := ptypes.TimestampProto(e.Timestamp) 331 if err != nil { 332 return err 333 } 334 if r.Id == "" || e.ID == r.Id { 335 if err := stream.Send(&types.Event{ 336 Id: e.ID, 337 Type: e.Type, 338 Timestamp: tsp, 339 Pid: e.PID, 340 Status: uint32(e.Status), 341 }); err != nil { 342 return err 343 } 344 } 345 } 346 return nil 347 } 348 349 func convertToPb(st *runtime.Stat) *types.StatsResponse { 350 tsp, _ := ptypes.TimestampProto(st.Timestamp) 351 pbSt := &types.StatsResponse{ 352 Timestamp: tsp, 353 CgroupStats: &types.CgroupStats{}, 354 } 355 systemUsage, _ := getSystemCPUUsage() 356 pbSt.CgroupStats.CpuStats = &types.CpuStats{ 357 CpuUsage: &types.CpuUsage{ 358 TotalUsage: st.CPU.Usage.Total, 359 PercpuUsage: st.CPU.Usage.Percpu, 360 UsageInKernelmode: st.CPU.Usage.Kernel, 361 UsageInUsermode: st.CPU.Usage.User, 362 }, 363 ThrottlingData: &types.ThrottlingData{ 364 Periods: st.CPU.Throttling.Periods, 365 ThrottledPeriods: st.CPU.Throttling.ThrottledPeriods, 366 ThrottledTime: st.CPU.Throttling.ThrottledTime, 367 }, 368 SystemUsage: systemUsage, 369 } 370 pbSt.CgroupStats.MemoryStats = &types.MemoryStats{ 371 Cache: st.Memory.Cache, 372 Usage: &types.MemoryData{ 373 Usage: st.Memory.Usage.Usage, 374 MaxUsage: st.Memory.Usage.Max, 375 Failcnt: st.Memory.Usage.Failcnt, 376 Limit: st.Memory.Usage.Limit, 377 }, 378 SwapUsage: &types.MemoryData{ 379 Usage: st.Memory.Swap.Usage, 380 MaxUsage: st.Memory.Swap.Max, 381 Failcnt: st.Memory.Swap.Failcnt, 382 Limit: st.Memory.Swap.Limit, 383 }, 384 KernelUsage: &types.MemoryData{ 385 Usage: st.Memory.Kernel.Usage, 386 MaxUsage: st.Memory.Kernel.Max, 387 Failcnt: st.Memory.Kernel.Failcnt, 388 Limit: st.Memory.Kernel.Limit, 389 }, 390 Stats: st.Memory.Raw, 391 } 392 pbSt.CgroupStats.BlkioStats = &types.BlkioStats{ 393 IoServiceBytesRecursive: convertBlkioEntryToPb(st.Blkio.IoServiceBytesRecursive), 394 IoServicedRecursive: convertBlkioEntryToPb(st.Blkio.IoServicedRecursive), 395 IoQueuedRecursive: convertBlkioEntryToPb(st.Blkio.IoQueuedRecursive), 396 IoServiceTimeRecursive: convertBlkioEntryToPb(st.Blkio.IoServiceTimeRecursive), 397 IoWaitTimeRecursive: convertBlkioEntryToPb(st.Blkio.IoWaitTimeRecursive), 398 IoMergedRecursive: convertBlkioEntryToPb(st.Blkio.IoMergedRecursive), 399 IoTimeRecursive: convertBlkioEntryToPb(st.Blkio.IoTimeRecursive), 400 SectorsRecursive: convertBlkioEntryToPb(st.Blkio.SectorsRecursive), 401 } 402 pbSt.CgroupStats.HugetlbStats = make(map[string]*types.HugetlbStats) 403 for k, st := range st.Hugetlb { 404 pbSt.CgroupStats.HugetlbStats[k] = &types.HugetlbStats{ 405 Usage: st.Usage, 406 MaxUsage: st.Max, 407 Failcnt: st.Failcnt, 408 } 409 } 410 pbSt.CgroupStats.PidsStats = &types.PidsStats{ 411 Current: st.Pids.Current, 412 Limit: st.Pids.Limit, 413 } 414 return pbSt 415 } 416 417 func convertBlkioEntryToPb(b []runtime.BlkioEntry) []*types.BlkioStatsEntry { 418 var pbEs []*types.BlkioStatsEntry 419 for _, e := range b { 420 pbEs = append(pbEs, &types.BlkioStatsEntry{ 421 Major: e.Major, 422 Minor: e.Minor, 423 Op: e.Op, 424 Value: e.Value, 425 }) 426 } 427 return pbEs 428 } 429 430 const nanoSecondsPerSecond = 1e9 431 432 // getSystemCPUUsage returns the host system's cpu usage in 433 // nanoseconds. An error is returned if the format of the underlying 434 // file does not match. 435 // 436 // Uses /proc/stat defined by POSIX. Looks for the cpu 437 // statistics line and then sums up the first seven fields 438 // provided. See `man 5 proc` for details on specific field 439 // information. 440 func getSystemCPUUsage() (uint64, error) { 441 var line string 442 f, err := os.Open("/proc/stat") 443 if err != nil { 444 return 0, err 445 } 446 bufReader := bufio.NewReaderSize(nil, 128) 447 defer func() { 448 bufReader.Reset(nil) 449 f.Close() 450 }() 451 bufReader.Reset(f) 452 err = nil 453 for err == nil { 454 line, err = bufReader.ReadString('\n') 455 if err != nil { 456 break 457 } 458 parts := strings.Fields(line) 459 switch parts[0] { 460 case "cpu": 461 if len(parts) < 8 { 462 return 0, fmt.Errorf("bad format of cpu stats") 463 } 464 var totalClockTicks uint64 465 for _, i := range parts[1:8] { 466 v, err := strconv.ParseUint(i, 10, 64) 467 if err != nil { 468 return 0, fmt.Errorf("error parsing cpu stats") 469 } 470 totalClockTicks += v 471 } 472 return (totalClockTicks * nanoSecondsPerSecond) / 473 clockTicksPerSecond, nil 474 } 475 } 476 return 0, fmt.Errorf("bad stats format") 477 } 478 479 func (s *apiServer) Stats(ctx context.Context, r *types.StatsRequest) (*types.StatsResponse, error) { 480 e := &supervisor.StatsTask{} 481 e.ID = r.Id 482 e.Stat = make(chan *runtime.Stat, 1) 483 s.sv.SendTask(e) 484 if err := <-e.ErrorCh(); err != nil { 485 return nil, err 486 } 487 stats := <-e.Stat 488 t := convertToPb(stats) 489 return t, nil 490 }