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