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  }