mirror of
https://github.com/rancher/os.git
synced 2025-09-01 14:48:55 +00:00
Bump libcompose and sync dependencies
This commit is contained in:
462
vendor/github.com/docker/containerd/api/grpc/server/server.go
generated
vendored
462
vendor/github.com/docker/containerd/api/grpc/server/server.go
generated
vendored
@@ -1,462 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
|
||||
"github.com/docker/containerd"
|
||||
"github.com/docker/containerd/api/grpc/types"
|
||||
"github.com/docker/containerd/runtime"
|
||||
"github.com/docker/containerd/supervisor"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type apiServer struct {
|
||||
sv *supervisor.Supervisor
|
||||
}
|
||||
|
||||
// NewServer returns grpc server instance
|
||||
func NewServer(sv *supervisor.Supervisor) types.APIServer {
|
||||
return &apiServer{
|
||||
sv: sv,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *apiServer) GetServerVersion(ctx context.Context, c *types.GetServerVersionRequest) (*types.GetServerVersionResponse, error) {
|
||||
return &types.GetServerVersionResponse{
|
||||
Major: containerd.VersionMajor,
|
||||
Minor: containerd.VersionMinor,
|
||||
Patch: containerd.VersionPatch,
|
||||
Revision: containerd.GitCommit,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *apiServer) CreateContainer(ctx context.Context, c *types.CreateContainerRequest) (*types.CreateContainerResponse, error) {
|
||||
if c.BundlePath == "" {
|
||||
return nil, errors.New("empty bundle path")
|
||||
}
|
||||
e := &supervisor.StartTask{}
|
||||
e.ID = c.Id
|
||||
e.BundlePath = c.BundlePath
|
||||
e.Stdin = c.Stdin
|
||||
e.Stdout = c.Stdout
|
||||
e.Stderr = c.Stderr
|
||||
e.Labels = c.Labels
|
||||
e.NoPivotRoot = c.NoPivotRoot
|
||||
e.StartResponse = make(chan supervisor.StartResponse, 1)
|
||||
if c.Checkpoint != "" {
|
||||
e.Checkpoint = &runtime.Checkpoint{
|
||||
Name: c.Checkpoint,
|
||||
}
|
||||
}
|
||||
s.sv.SendTask(e)
|
||||
if err := <-e.ErrorCh(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := <-e.StartResponse
|
||||
apiC, err := createAPIContainer(r.Container, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &types.CreateContainerResponse{
|
||||
Container: apiC,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *apiServer) CreateCheckpoint(ctx context.Context, r *types.CreateCheckpointRequest) (*types.CreateCheckpointResponse, error) {
|
||||
e := &supervisor.CreateCheckpointTask{}
|
||||
e.ID = r.Id
|
||||
e.Checkpoint = &runtime.Checkpoint{
|
||||
Name: r.Checkpoint.Name,
|
||||
Exit: r.Checkpoint.Exit,
|
||||
Tcp: r.Checkpoint.Tcp,
|
||||
UnixSockets: r.Checkpoint.UnixSockets,
|
||||
Shell: r.Checkpoint.Shell,
|
||||
}
|
||||
s.sv.SendTask(e)
|
||||
if err := <-e.ErrorCh(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &types.CreateCheckpointResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *apiServer) DeleteCheckpoint(ctx context.Context, r *types.DeleteCheckpointRequest) (*types.DeleteCheckpointResponse, error) {
|
||||
if r.Name == "" {
|
||||
return nil, errors.New("checkpoint name cannot be empty")
|
||||
}
|
||||
e := &supervisor.DeleteCheckpointTask{}
|
||||
e.ID = r.Id
|
||||
e.Checkpoint = &runtime.Checkpoint{
|
||||
Name: r.Name,
|
||||
}
|
||||
s.sv.SendTask(e)
|
||||
if err := <-e.ErrorCh(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &types.DeleteCheckpointResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *apiServer) ListCheckpoint(ctx context.Context, r *types.ListCheckpointRequest) (*types.ListCheckpointResponse, error) {
|
||||
e := &supervisor.GetContainersTask{}
|
||||
s.sv.SendTask(e)
|
||||
if err := <-e.ErrorCh(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var container runtime.Container
|
||||
for _, c := range e.Containers {
|
||||
if c.ID() == r.Id {
|
||||
container = c
|
||||
break
|
||||
}
|
||||
}
|
||||
if container == nil {
|
||||
return nil, grpc.Errorf(codes.NotFound, "no such containers")
|
||||
}
|
||||
var out []*types.Checkpoint
|
||||
checkpoints, err := container.Checkpoints()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, c := range checkpoints {
|
||||
out = append(out, &types.Checkpoint{
|
||||
Name: c.Name,
|
||||
Tcp: c.Tcp,
|
||||
Shell: c.Shell,
|
||||
UnixSockets: c.UnixSockets,
|
||||
// TODO: figure out timestamp
|
||||
//Timestamp: c.Timestamp,
|
||||
})
|
||||
}
|
||||
return &types.ListCheckpointResponse{Checkpoints: out}, nil
|
||||
}
|
||||
|
||||
func (s *apiServer) Signal(ctx context.Context, r *types.SignalRequest) (*types.SignalResponse, error) {
|
||||
e := &supervisor.SignalTask{}
|
||||
e.ID = r.Id
|
||||
e.PID = r.Pid
|
||||
e.Signal = syscall.Signal(int(r.Signal))
|
||||
s.sv.SendTask(e)
|
||||
if err := <-e.ErrorCh(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &types.SignalResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *apiServer) State(ctx context.Context, r *types.StateRequest) (*types.StateResponse, error) {
|
||||
e := &supervisor.GetContainersTask{}
|
||||
e.ID = r.Id
|
||||
s.sv.SendTask(e)
|
||||
if err := <-e.ErrorCh(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := s.sv.Machine()
|
||||
state := &types.StateResponse{
|
||||
Machine: &types.Machine{
|
||||
Cpus: uint32(m.Cpus),
|
||||
Memory: uint64(m.Memory),
|
||||
},
|
||||
}
|
||||
for _, c := range e.Containers {
|
||||
apiC, err := createAPIContainer(c, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
state.Containers = append(state.Containers, apiC)
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
|
||||
func createAPIContainer(c runtime.Container, getPids bool) (*types.Container, error) {
|
||||
processes, err := c.Processes()
|
||||
if err != nil {
|
||||
return nil, grpc.Errorf(codes.Internal, "get processes for container: "+err.Error())
|
||||
}
|
||||
var procs []*types.Process
|
||||
for _, p := range processes {
|
||||
oldProc := p.Spec()
|
||||
stdio := p.Stdio()
|
||||
proc := &types.Process{
|
||||
Pid: p.ID(),
|
||||
SystemPid: uint32(p.SystemPid()),
|
||||
Terminal: oldProc.Terminal,
|
||||
Args: oldProc.Args,
|
||||
Env: oldProc.Env,
|
||||
Cwd: oldProc.Cwd,
|
||||
Stdin: stdio.Stdin,
|
||||
Stdout: stdio.Stdout,
|
||||
Stderr: stdio.Stderr,
|
||||
}
|
||||
proc.User = &types.User{
|
||||
Uid: oldProc.User.UID,
|
||||
Gid: oldProc.User.GID,
|
||||
AdditionalGids: oldProc.User.AdditionalGids,
|
||||
}
|
||||
proc.Capabilities = oldProc.Capabilities
|
||||
proc.ApparmorProfile = oldProc.ApparmorProfile
|
||||
proc.SelinuxLabel = oldProc.SelinuxLabel
|
||||
proc.NoNewPrivileges = oldProc.NoNewPrivileges
|
||||
for _, rl := range oldProc.Rlimits {
|
||||
proc.Rlimits = append(proc.Rlimits, &types.Rlimit{
|
||||
Type: rl.Type,
|
||||
Soft: rl.Soft,
|
||||
Hard: rl.Hard,
|
||||
})
|
||||
}
|
||||
procs = append(procs, proc)
|
||||
}
|
||||
var pids []int
|
||||
state := c.State()
|
||||
if getPids && (state == runtime.Running || state == runtime.Paused) {
|
||||
if pids, err = c.Pids(); err != nil {
|
||||
return nil, grpc.Errorf(codes.Internal, "get all pids for container: "+err.Error())
|
||||
}
|
||||
}
|
||||
return &types.Container{
|
||||
Id: c.ID(),
|
||||
BundlePath: c.Path(),
|
||||
Processes: procs,
|
||||
Labels: c.Labels(),
|
||||
Status: string(state),
|
||||
Pids: toUint32(pids),
|
||||
Runtime: c.Runtime(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func toUint32(its []int) []uint32 {
|
||||
o := []uint32{}
|
||||
for _, i := range its {
|
||||
o = append(o, uint32(i))
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
func (s *apiServer) UpdateContainer(ctx context.Context, r *types.UpdateContainerRequest) (*types.UpdateContainerResponse, error) {
|
||||
e := &supervisor.UpdateTask{}
|
||||
e.ID = r.Id
|
||||
e.State = runtime.State(r.Status)
|
||||
if r.Resources != nil {
|
||||
rs := r.Resources
|
||||
e.Resources = &runtime.Resource{}
|
||||
if rs.CpuShares != 0 {
|
||||
e.Resources.CPUShares = int64(rs.CpuShares)
|
||||
}
|
||||
if rs.BlkioWeight != 0 {
|
||||
e.Resources.BlkioWeight = uint16(rs.BlkioWeight)
|
||||
}
|
||||
if rs.CpuPeriod != 0 {
|
||||
e.Resources.CPUPeriod = int64(rs.CpuPeriod)
|
||||
}
|
||||
if rs.CpuQuota != 0 {
|
||||
e.Resources.CPUQuota = int64(rs.CpuQuota)
|
||||
}
|
||||
if rs.CpusetCpus != "" {
|
||||
e.Resources.CpusetCpus = rs.CpusetCpus
|
||||
}
|
||||
if rs.CpusetMems != "" {
|
||||
e.Resources.CpusetMems = rs.CpusetMems
|
||||
}
|
||||
if rs.KernelMemoryLimit != 0 {
|
||||
e.Resources.KernelMemory = int64(rs.KernelMemoryLimit)
|
||||
}
|
||||
if rs.MemoryLimit != 0 {
|
||||
e.Resources.Memory = int64(rs.MemoryLimit)
|
||||
}
|
||||
if rs.MemoryReservation != 0 {
|
||||
e.Resources.MemoryReservation = int64(rs.MemoryReservation)
|
||||
}
|
||||
if rs.MemorySwap != 0 {
|
||||
e.Resources.MemorySwap = int64(rs.MemorySwap)
|
||||
}
|
||||
}
|
||||
s.sv.SendTask(e)
|
||||
if err := <-e.ErrorCh(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &types.UpdateContainerResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *apiServer) UpdateProcess(ctx context.Context, r *types.UpdateProcessRequest) (*types.UpdateProcessResponse, error) {
|
||||
e := &supervisor.UpdateProcessTask{}
|
||||
e.ID = r.Id
|
||||
e.PID = r.Pid
|
||||
e.Height = int(r.Height)
|
||||
e.Width = int(r.Width)
|
||||
e.CloseStdin = r.CloseStdin
|
||||
s.sv.SendTask(e)
|
||||
if err := <-e.ErrorCh(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &types.UpdateProcessResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *apiServer) Events(r *types.EventsRequest, stream types.API_EventsServer) error {
|
||||
t := time.Time{}
|
||||
if r.Timestamp != 0 {
|
||||
t = time.Unix(int64(r.Timestamp), 0)
|
||||
}
|
||||
events := s.sv.Events(t)
|
||||
defer s.sv.Unsubscribe(events)
|
||||
for e := range events {
|
||||
if err := stream.Send(&types.Event{
|
||||
Id: e.ID,
|
||||
Type: e.Type,
|
||||
Timestamp: uint64(e.Timestamp.Unix()),
|
||||
Pid: e.PID,
|
||||
Status: uint32(e.Status),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertToPb(st *runtime.Stat) *types.StatsResponse {
|
||||
pbSt := &types.StatsResponse{
|
||||
Timestamp: uint64(st.Timestamp.Unix()),
|
||||
CgroupStats: &types.CgroupStats{},
|
||||
}
|
||||
systemUsage, _ := getSystemCPUUsage()
|
||||
pbSt.CgroupStats.CpuStats = &types.CpuStats{
|
||||
CpuUsage: &types.CpuUsage{
|
||||
TotalUsage: st.Cpu.Usage.Total,
|
||||
PercpuUsage: st.Cpu.Usage.Percpu,
|
||||
UsageInKernelmode: st.Cpu.Usage.Kernel,
|
||||
UsageInUsermode: st.Cpu.Usage.User,
|
||||
},
|
||||
ThrottlingData: &types.ThrottlingData{
|
||||
Periods: st.Cpu.Throttling.Periods,
|
||||
ThrottledPeriods: st.Cpu.Throttling.ThrottledPeriods,
|
||||
ThrottledTime: st.Cpu.Throttling.ThrottledTime,
|
||||
},
|
||||
SystemUsage: systemUsage,
|
||||
}
|
||||
pbSt.CgroupStats.MemoryStats = &types.MemoryStats{
|
||||
Cache: st.Memory.Cache,
|
||||
Usage: &types.MemoryData{
|
||||
Usage: st.Memory.Usage.Usage,
|
||||
MaxUsage: st.Memory.Usage.Max,
|
||||
Failcnt: st.Memory.Usage.Failcnt,
|
||||
Limit: st.Memory.Usage.Limit,
|
||||
},
|
||||
SwapUsage: &types.MemoryData{
|
||||
Usage: st.Memory.Swap.Usage,
|
||||
MaxUsage: st.Memory.Swap.Max,
|
||||
Failcnt: st.Memory.Swap.Failcnt,
|
||||
Limit: st.Memory.Swap.Limit,
|
||||
},
|
||||
KernelUsage: &types.MemoryData{
|
||||
Usage: st.Memory.Kernel.Usage,
|
||||
MaxUsage: st.Memory.Kernel.Max,
|
||||
Failcnt: st.Memory.Kernel.Failcnt,
|
||||
Limit: st.Memory.Kernel.Limit,
|
||||
},
|
||||
Stats: st.Memory.Raw,
|
||||
}
|
||||
pbSt.CgroupStats.BlkioStats = &types.BlkioStats{
|
||||
IoServiceBytesRecursive: convertBlkioEntryToPb(st.Blkio.IoServiceBytesRecursive),
|
||||
IoServicedRecursive: convertBlkioEntryToPb(st.Blkio.IoServicedRecursive),
|
||||
IoQueuedRecursive: convertBlkioEntryToPb(st.Blkio.IoQueuedRecursive),
|
||||
IoServiceTimeRecursive: convertBlkioEntryToPb(st.Blkio.IoServiceTimeRecursive),
|
||||
IoWaitTimeRecursive: convertBlkioEntryToPb(st.Blkio.IoWaitTimeRecursive),
|
||||
IoMergedRecursive: convertBlkioEntryToPb(st.Blkio.IoMergedRecursive),
|
||||
IoTimeRecursive: convertBlkioEntryToPb(st.Blkio.IoTimeRecursive),
|
||||
SectorsRecursive: convertBlkioEntryToPb(st.Blkio.SectorsRecursive),
|
||||
}
|
||||
pbSt.CgroupStats.HugetlbStats = make(map[string]*types.HugetlbStats)
|
||||
for k, st := range st.Hugetlb {
|
||||
pbSt.CgroupStats.HugetlbStats[k] = &types.HugetlbStats{
|
||||
Usage: st.Usage,
|
||||
MaxUsage: st.Max,
|
||||
Failcnt: st.Failcnt,
|
||||
}
|
||||
}
|
||||
pbSt.CgroupStats.PidsStats = &types.PidsStats{
|
||||
Current: st.Pids.Current,
|
||||
Limit: st.Pids.Limit,
|
||||
}
|
||||
return pbSt
|
||||
}
|
||||
|
||||
func convertBlkioEntryToPb(b []runtime.BlkioEntry) []*types.BlkioStatsEntry {
|
||||
var pbEs []*types.BlkioStatsEntry
|
||||
for _, e := range b {
|
||||
pbEs = append(pbEs, &types.BlkioStatsEntry{
|
||||
Major: e.Major,
|
||||
Minor: e.Minor,
|
||||
Op: e.Op,
|
||||
Value: e.Value,
|
||||
})
|
||||
}
|
||||
return pbEs
|
||||
}
|
||||
|
||||
const nanoSecondsPerSecond = 1e9
|
||||
|
||||
// getSystemCPUUsage returns the host system's cpu usage in
|
||||
// nanoseconds. An error is returned if the format of the underlying
|
||||
// file does not match.
|
||||
//
|
||||
// Uses /proc/stat defined by POSIX. Looks for the cpu
|
||||
// statistics line and then sums up the first seven fields
|
||||
// provided. See `man 5 proc` for details on specific field
|
||||
// information.
|
||||
func getSystemCPUUsage() (uint64, error) {
|
||||
var line string
|
||||
f, err := os.Open("/proc/stat")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
bufReader := bufio.NewReaderSize(nil, 128)
|
||||
defer func() {
|
||||
bufReader.Reset(nil)
|
||||
f.Close()
|
||||
}()
|
||||
bufReader.Reset(f)
|
||||
err = nil
|
||||
for err == nil {
|
||||
line, err = bufReader.ReadString('\n')
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
parts := strings.Fields(line)
|
||||
switch parts[0] {
|
||||
case "cpu":
|
||||
if len(parts) < 8 {
|
||||
return 0, fmt.Errorf("bad format of cpu stats")
|
||||
}
|
||||
var totalClockTicks uint64
|
||||
for _, i := range parts[1:8] {
|
||||
v, err := strconv.ParseUint(i, 10, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error parsing cpu stats")
|
||||
}
|
||||
totalClockTicks += v
|
||||
}
|
||||
return (totalClockTicks * nanoSecondsPerSecond) /
|
||||
clockTicksPerSecond, nil
|
||||
}
|
||||
}
|
||||
return 0, fmt.Errorf("bad stats format")
|
||||
}
|
||||
|
||||
func (s *apiServer) Stats(ctx context.Context, r *types.StatsRequest) (*types.StatsResponse, error) {
|
||||
e := &supervisor.StatsTask{}
|
||||
e.ID = r.Id
|
||||
e.Stat = make(chan *runtime.Stat, 1)
|
||||
s.sv.SendTask(e)
|
||||
if err := <-e.ErrorCh(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats := <-e.Stat
|
||||
t := convertToPb(stats)
|
||||
return t, nil
|
||||
}
|
59
vendor/github.com/docker/containerd/api/grpc/server/server_linux.go
generated
vendored
59
vendor/github.com/docker/containerd/api/grpc/server/server_linux.go
generated
vendored
@@ -1,59 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/containerd/api/grpc/types"
|
||||
"github.com/docker/containerd/specs"
|
||||
"github.com/docker/containerd/supervisor"
|
||||
"github.com/opencontainers/runc/libcontainer/system"
|
||||
ocs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var clockTicksPerSecond = uint64(system.GetClockTicks())
|
||||
|
||||
func (s *apiServer) AddProcess(ctx context.Context, r *types.AddProcessRequest) (*types.AddProcessResponse, error) {
|
||||
process := &specs.ProcessSpec{
|
||||
Terminal: r.Terminal,
|
||||
Args: r.Args,
|
||||
Env: r.Env,
|
||||
Cwd: r.Cwd,
|
||||
}
|
||||
process.User = ocs.User{
|
||||
UID: r.User.Uid,
|
||||
GID: r.User.Gid,
|
||||
AdditionalGids: r.User.AdditionalGids,
|
||||
}
|
||||
process.Capabilities = r.Capabilities
|
||||
process.ApparmorProfile = r.ApparmorProfile
|
||||
process.SelinuxLabel = r.SelinuxLabel
|
||||
process.NoNewPrivileges = r.NoNewPrivileges
|
||||
for _, rl := range r.Rlimits {
|
||||
process.Rlimits = append(process.Rlimits, ocs.Rlimit{
|
||||
Type: rl.Type,
|
||||
Soft: rl.Soft,
|
||||
Hard: rl.Hard,
|
||||
})
|
||||
}
|
||||
if r.Id == "" {
|
||||
return nil, fmt.Errorf("container id cannot be empty")
|
||||
}
|
||||
if r.Pid == "" {
|
||||
return nil, fmt.Errorf("process id cannot be empty")
|
||||
}
|
||||
e := &supervisor.AddProcessTask{}
|
||||
e.ID = r.Id
|
||||
e.PID = r.Pid
|
||||
e.ProcessSpec = process
|
||||
e.Stdin = r.Stdin
|
||||
e.Stdout = r.Stdout
|
||||
e.Stderr = r.Stderr
|
||||
e.StartResponse = make(chan supervisor.StartResponse, 1)
|
||||
s.sv.SendTask(e)
|
||||
if err := <-e.ErrorCh(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
<-e.StartResponse
|
||||
return &types.AddProcessResponse{}, nil
|
||||
}
|
14
vendor/github.com/docker/containerd/api/grpc/server/server_solaris.go
generated
vendored
14
vendor/github.com/docker/containerd/api/grpc/server/server_solaris.go
generated
vendored
@@ -1,14 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/docker/containerd/api/grpc/types"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var clockTicksPerSecond uint64
|
||||
|
||||
func (s *apiServer) AddProcess(ctx context.Context, r *types.AddProcessRequest) (*types.AddProcessResponse, error) {
|
||||
return &types.AddProcessResponse{}, errors.New("apiServer AddProcess() not implemented on Solaris")
|
||||
}
|
1430
vendor/github.com/docker/containerd/api/grpc/types/api.pb.go
generated
vendored
1430
vendor/github.com/docker/containerd/api/grpc/types/api.pb.go
generated
vendored
File diff suppressed because it is too large
Load Diff
305
vendor/github.com/docker/containerd/api/grpc/types/api.proto
generated
vendored
305
vendor/github.com/docker/containerd/api/grpc/types/api.proto
generated
vendored
@@ -1,305 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package types;
|
||||
|
||||
service API {
|
||||
rpc GetServerVersion(GetServerVersionRequest) returns (GetServerVersionResponse) {}
|
||||
rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse) {}
|
||||
rpc UpdateContainer(UpdateContainerRequest) returns (UpdateContainerResponse) {}
|
||||
rpc Signal(SignalRequest) returns (SignalResponse) {}
|
||||
rpc UpdateProcess(UpdateProcessRequest) returns (UpdateProcessResponse) {}
|
||||
rpc AddProcess(AddProcessRequest) returns (AddProcessResponse) {}
|
||||
rpc CreateCheckpoint(CreateCheckpointRequest) returns (CreateCheckpointResponse) {}
|
||||
rpc DeleteCheckpoint(DeleteCheckpointRequest) returns (DeleteCheckpointResponse) {}
|
||||
rpc ListCheckpoint(ListCheckpointRequest) returns (ListCheckpointResponse) {}
|
||||
rpc State(StateRequest) returns (StateResponse) {}
|
||||
rpc Events(EventsRequest) returns (stream Event) {}
|
||||
rpc Stats(StatsRequest) returns (StatsResponse) {}
|
||||
}
|
||||
|
||||
message GetServerVersionRequest {
|
||||
}
|
||||
|
||||
message GetServerVersionResponse {
|
||||
uint32 major = 1;
|
||||
uint32 minor = 2;
|
||||
uint32 patch = 3;
|
||||
string revision = 4;
|
||||
}
|
||||
|
||||
message UpdateProcessRequest {
|
||||
string id = 1;
|
||||
string pid = 2;
|
||||
bool closeStdin = 3; // Close stdin of the container
|
||||
uint32 width = 4;
|
||||
uint32 height = 5;
|
||||
}
|
||||
|
||||
message UpdateProcessResponse {
|
||||
}
|
||||
|
||||
message CreateContainerRequest {
|
||||
string id = 1; // ID of container
|
||||
string bundlePath = 2; // path to OCI bundle
|
||||
string checkpoint = 3; // checkpoint name if you want to create immediate checkpoint (optional)
|
||||
string stdin = 4; // path to the file where stdin will be read (optional)
|
||||
string stdout = 5; // path to file where stdout will be written (optional)
|
||||
string stderr = 6; // path to file where stderr will be written (optional)
|
||||
repeated string labels = 7;
|
||||
bool noPivotRoot = 8;
|
||||
}
|
||||
|
||||
message CreateContainerResponse {
|
||||
Container container = 1;
|
||||
}
|
||||
|
||||
message SignalRequest {
|
||||
string id = 1; // ID of container
|
||||
string pid = 2; // PID of process inside container
|
||||
uint32 signal = 3; // Signal which will be sent, you can find value in "man 7 signal"
|
||||
}
|
||||
|
||||
message SignalResponse {
|
||||
}
|
||||
|
||||
message AddProcessRequest {
|
||||
string id = 1; // ID of container
|
||||
bool terminal = 2; // Use tty for container stdio
|
||||
User user = 3; // User under which process will be run
|
||||
repeated string args = 4; // Arguments for process, first is binary path itself
|
||||
repeated string env = 5; // List of environment variables for process
|
||||
string cwd = 6; // Workind directory of process
|
||||
string pid = 7; // Process ID
|
||||
string stdin = 8; // path to the file where stdin will be read (optional)
|
||||
string stdout = 9; // path to file where stdout will be written (optional)
|
||||
string stderr = 10; // path to file where stderr will be written (optional)
|
||||
repeated string capabilities = 11;
|
||||
string apparmorProfile = 12;
|
||||
string selinuxLabel = 13;
|
||||
bool noNewPrivileges = 14;
|
||||
repeated Rlimit rlimits = 15;
|
||||
}
|
||||
|
||||
message Rlimit {
|
||||
string type = 1;
|
||||
uint64 soft = 2;
|
||||
uint64 hard = 3;
|
||||
}
|
||||
|
||||
message User {
|
||||
uint32 uid = 1; // UID of user
|
||||
uint32 gid = 2; // GID of user
|
||||
repeated uint32 additionalGids = 3; // Additional groups to which user will be added
|
||||
}
|
||||
|
||||
message AddProcessResponse {
|
||||
}
|
||||
|
||||
message CreateCheckpointRequest {
|
||||
string id = 1; // ID of container
|
||||
Checkpoint checkpoint = 2; // Checkpoint configuration
|
||||
}
|
||||
|
||||
message CreateCheckpointResponse {
|
||||
}
|
||||
|
||||
message DeleteCheckpointRequest {
|
||||
string id = 1; // ID of container
|
||||
string name = 2; // Name of checkpoint
|
||||
}
|
||||
|
||||
message DeleteCheckpointResponse {
|
||||
}
|
||||
|
||||
message ListCheckpointRequest {
|
||||
string id = 1; // ID of container
|
||||
}
|
||||
|
||||
message Checkpoint {
|
||||
string name = 1; // Name of checkpoint
|
||||
bool exit = 2; // checkpoint configuration: should container exit on checkpoint or not
|
||||
bool tcp = 3; // allow open tcp connections
|
||||
bool unixSockets = 4; // allow external unix sockets
|
||||
bool shell = 5; // allow shell-jobs
|
||||
}
|
||||
|
||||
message ListCheckpointResponse {
|
||||
repeated Checkpoint checkpoints = 1; // List of checkpoints
|
||||
}
|
||||
|
||||
message StateRequest {
|
||||
string id = 1; // container id for a single container
|
||||
}
|
||||
|
||||
message ContainerState {
|
||||
string status = 1;
|
||||
}
|
||||
|
||||
message Process {
|
||||
string pid = 1;
|
||||
bool terminal = 2; // Use tty for container stdio
|
||||
User user = 3; // User under which process will be run
|
||||
repeated string args = 4; // Arguments for process, first is binary path itself
|
||||
repeated string env = 5; // List of environment variables for process
|
||||
string cwd = 6; // Workind directory of process
|
||||
uint32 systemPid = 7;
|
||||
string stdin = 8; // path to the file where stdin will be read (optional)
|
||||
string stdout = 9; // path to file where stdout will be written (optional)
|
||||
string stderr = 10; // path to file where stderr will be written (optional)
|
||||
repeated string capabilities = 11;
|
||||
string apparmorProfile = 12;
|
||||
string selinuxLabel = 13;
|
||||
bool noNewPrivileges = 14;
|
||||
repeated Rlimit rlimits = 15;
|
||||
}
|
||||
|
||||
message Container {
|
||||
string id = 1; // ID of container
|
||||
string bundlePath = 2; // Path to OCI bundle
|
||||
repeated Process processes = 3; // List of processes which run in container
|
||||
string status = 4; // Container status ("running", "paused", etc.)
|
||||
repeated string labels = 5;
|
||||
repeated uint32 pids = 6;
|
||||
string runtime = 7; // runtime used to execute the container
|
||||
}
|
||||
|
||||
// Machine is information about machine on which containerd is run
|
||||
message Machine {
|
||||
uint32 cpus = 1; // number of cpus
|
||||
uint64 memory = 2; // amount of memory
|
||||
}
|
||||
|
||||
// StateResponse is information about containerd daemon
|
||||
message StateResponse {
|
||||
repeated Container containers = 1;
|
||||
Machine machine = 2;
|
||||
}
|
||||
|
||||
message UpdateContainerRequest {
|
||||
string id = 1; // ID of container
|
||||
string pid = 2;
|
||||
string status = 3; // Status to whcih containerd will try to change
|
||||
UpdateResource resources =4;
|
||||
}
|
||||
|
||||
message UpdateResource {
|
||||
uint32 blkioWeight =1;
|
||||
uint32 cpuShares = 2;
|
||||
uint32 cpuPeriod = 3;
|
||||
uint32 cpuQuota = 4;
|
||||
string cpusetCpus = 5;
|
||||
string cpusetMems = 6;
|
||||
uint32 memoryLimit = 7;
|
||||
uint32 memorySwap = 8;
|
||||
uint32 memoryReservation = 9;
|
||||
uint32 kernelMemoryLimit = 10;
|
||||
}
|
||||
|
||||
message UpdateContainerResponse {
|
||||
}
|
||||
|
||||
message EventsRequest {
|
||||
uint64 timestamp = 1;
|
||||
}
|
||||
|
||||
message Event {
|
||||
string type = 1;
|
||||
string id = 2;
|
||||
uint32 status = 3;
|
||||
string pid = 4;
|
||||
uint64 timestamp = 5;
|
||||
}
|
||||
|
||||
message NetworkStats {
|
||||
string name = 1; // name of network interface
|
||||
uint64 rx_bytes = 2;
|
||||
uint64 rx_Packets = 3;
|
||||
uint64 Rx_errors = 4;
|
||||
uint64 Rx_dropped = 5;
|
||||
uint64 Tx_bytes = 6;
|
||||
uint64 Tx_packets = 7;
|
||||
uint64 Tx_errors = 8;
|
||||
uint64 Tx_dropped = 9;
|
||||
}
|
||||
|
||||
message CpuUsage {
|
||||
uint64 total_usage = 1;
|
||||
repeated uint64 percpu_usage = 2;
|
||||
uint64 usage_in_kernelmode = 3;
|
||||
uint64 usage_in_usermode = 4;
|
||||
}
|
||||
|
||||
message ThrottlingData {
|
||||
uint64 periods = 1;
|
||||
uint64 throttled_periods = 2;
|
||||
uint64 throttled_time = 3;
|
||||
}
|
||||
|
||||
message CpuStats {
|
||||
CpuUsage cpu_usage = 1;
|
||||
ThrottlingData throttling_data = 2;
|
||||
uint64 system_usage = 3;
|
||||
}
|
||||
|
||||
message PidsStats {
|
||||
uint64 current = 1;
|
||||
uint64 limit = 2;
|
||||
}
|
||||
|
||||
message MemoryData {
|
||||
uint64 usage = 1;
|
||||
uint64 max_usage = 2;
|
||||
uint64 failcnt = 3;
|
||||
uint64 limit = 4;
|
||||
}
|
||||
|
||||
message MemoryStats {
|
||||
uint64 cache = 1;
|
||||
MemoryData usage = 2;
|
||||
MemoryData swap_usage = 3;
|
||||
MemoryData kernel_usage = 4;
|
||||
map<string, uint64> stats = 5;
|
||||
}
|
||||
|
||||
message BlkioStatsEntry {
|
||||
uint64 major = 1;
|
||||
uint64 minor = 2;
|
||||
string op = 3;
|
||||
uint64 value = 4;
|
||||
}
|
||||
|
||||
message BlkioStats {
|
||||
repeated BlkioStatsEntry io_service_bytes_recursive = 1; // number of bytes tranferred to and from the block device
|
||||
repeated BlkioStatsEntry io_serviced_recursive = 2;
|
||||
repeated BlkioStatsEntry io_queued_recursive = 3;
|
||||
repeated BlkioStatsEntry io_service_time_recursive = 4;
|
||||
repeated BlkioStatsEntry io_wait_time_recursive = 5;
|
||||
repeated BlkioStatsEntry io_merged_recursive = 6;
|
||||
repeated BlkioStatsEntry io_time_recursive = 7;
|
||||
repeated BlkioStatsEntry sectors_recursive = 8;
|
||||
}
|
||||
|
||||
message HugetlbStats {
|
||||
uint64 usage = 1;
|
||||
uint64 max_usage = 2;
|
||||
uint64 failcnt = 3;
|
||||
uint64 limit = 4;
|
||||
}
|
||||
|
||||
message CgroupStats {
|
||||
CpuStats cpu_stats = 1;
|
||||
MemoryStats memory_stats = 2;
|
||||
BlkioStats blkio_stats = 3;
|
||||
map<string, HugetlbStats> hugetlb_stats = 4; // the map is in the format "size of hugepage: stats of the hugepage"
|
||||
PidsStats pids_stats = 5;
|
||||
}
|
||||
|
||||
message StatsResponse {
|
||||
repeated NetworkStats network_stats = 1;
|
||||
CgroupStats cgroup_stats = 2;
|
||||
uint64 timestamp = 3;
|
||||
};
|
||||
|
||||
message StatsRequest {
|
||||
string id = 1;
|
||||
}
|
19
vendor/github.com/docker/containerd/archutils/epoll.go
generated
vendored
19
vendor/github.com/docker/containerd/archutils/epoll.go
generated
vendored
@@ -1,19 +0,0 @@
|
||||
// +build linux,!arm64
|
||||
|
||||
package archutils
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func EpollCreate1(flag int) (int, error) {
|
||||
return syscall.EpollCreate1(flag)
|
||||
}
|
||||
|
||||
func EpollCtl(epfd int, op int, fd int, event *syscall.EpollEvent) error {
|
||||
return syscall.EpollCtl(epfd, op, fd, event)
|
||||
}
|
||||
|
||||
func EpollWait(epfd int, events []syscall.EpollEvent, msec int) (int, error) {
|
||||
return syscall.EpollWait(epfd, events, msec)
|
||||
}
|
70
vendor/github.com/docker/containerd/archutils/epoll_arm64.go
generated
vendored
70
vendor/github.com/docker/containerd/archutils/epoll_arm64.go
generated
vendored
@@ -1,70 +0,0 @@
|
||||
// +build linux,arm64
|
||||
|
||||
package archutils
|
||||
|
||||
// #include <sys/epoll.h>
|
||||
/*
|
||||
int EpollCreate1(int flag) {
|
||||
return epoll_create1(flag);
|
||||
}
|
||||
|
||||
int EpollCtl(int efd, int op,int sfd, int events, int fd) {
|
||||
struct epoll_event event;
|
||||
event.events = events;
|
||||
event.data.fd = fd;
|
||||
|
||||
return epoll_ctl(efd, op, sfd, &event);
|
||||
}
|
||||
|
||||
struct event_t {
|
||||
uint32_t events;
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct epoll_event events[128];
|
||||
int run_epoll_wait(int fd, struct event_t *event) {
|
||||
int n, i;
|
||||
n = epoll_wait(fd, events, 128, -1);
|
||||
for (i = 0; i < n; i++) {
|
||||
event[i].events = events[i].events;
|
||||
event[i].fd = events[i].data.fd;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func EpollCreate1(flag int) (int, error) {
|
||||
fd := int(C.EpollCreate1(C.int(flag)))
|
||||
if fd < 0 {
|
||||
return fd, fmt.Errorf("failed to create epoll, errno is %d", fd)
|
||||
}
|
||||
return fd, nil
|
||||
}
|
||||
|
||||
func EpollCtl(epfd int, op int, fd int, event *syscall.EpollEvent) error {
|
||||
errno := C.EpollCtl(C.int(epfd), C.int(syscall.EPOLL_CTL_ADD), C.int(fd), C.int(event.Events), C.int(event.Fd))
|
||||
if errno < 0 {
|
||||
return fmt.Errorf("Failed to ctl epoll")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func EpollWait(epfd int, events []syscall.EpollEvent, msec int) (int, error) {
|
||||
var c_events [128]C.struct_event_t
|
||||
n := int(C.run_epoll_wait(C.int(epfd), (*C.struct_event_t)(unsafe.Pointer(&c_events))))
|
||||
if n < 0 {
|
||||
return int(n), fmt.Errorf("Failed to wait epoll")
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
events[i].Fd = int32(c_events[i].fd)
|
||||
events[i].Events = uint32(c_events[i].events)
|
||||
}
|
||||
return int(n), nil
|
||||
}
|
581
vendor/github.com/docker/containerd/runtime/container.go
generated
vendored
581
vendor/github.com/docker/containerd/runtime/container.go
generated
vendored
@@ -1,581 +0,0 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/containerd/specs"
|
||||
"github.com/docker/containerd/subreaper/exec"
|
||||
ocs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
type Container interface {
|
||||
// ID returns the container ID
|
||||
ID() string
|
||||
// Path returns the path to the bundle
|
||||
Path() string
|
||||
// Start starts the init process of the container
|
||||
Start(checkpoint string, s Stdio) (Process, error)
|
||||
// Exec starts another process in an existing container
|
||||
Exec(string, specs.ProcessSpec, Stdio) (Process, error)
|
||||
// Delete removes the container's state and any resources
|
||||
Delete() error
|
||||
// Processes returns all the containers processes that have been added
|
||||
Processes() ([]Process, error)
|
||||
// State returns the containers runtime state
|
||||
State() State
|
||||
// Resume resumes a paused container
|
||||
Resume() error
|
||||
// Pause pauses a running container
|
||||
Pause() error
|
||||
// RemoveProcess removes the specified process from the container
|
||||
RemoveProcess(string) error
|
||||
// Checkpoints returns all the checkpoints for a container
|
||||
Checkpoints() ([]Checkpoint, error)
|
||||
// Checkpoint creates a new checkpoint
|
||||
Checkpoint(Checkpoint) error
|
||||
// DeleteCheckpoint deletes the checkpoint for the provided name
|
||||
DeleteCheckpoint(name string) error
|
||||
// Labels are user provided labels for the container
|
||||
Labels() []string
|
||||
// Pids returns all pids inside the container
|
||||
Pids() ([]int, error)
|
||||
// Stats returns realtime container stats and resource information
|
||||
Stats() (*Stat, error)
|
||||
// Name or path of the OCI compliant runtime used to execute the container
|
||||
Runtime() string
|
||||
// OOM signals the channel if the container received an OOM notification
|
||||
OOM() (OOM, error)
|
||||
// UpdateResource updates the containers resources to new values
|
||||
UpdateResources(*Resource) error
|
||||
|
||||
// Status return the current status of the container.
|
||||
Status() (State, error)
|
||||
}
|
||||
|
||||
type OOM interface {
|
||||
io.Closer
|
||||
FD() int
|
||||
ContainerID() string
|
||||
Flush()
|
||||
Removed() bool
|
||||
}
|
||||
|
||||
type Stdio struct {
|
||||
Stdin string
|
||||
Stdout string
|
||||
Stderr string
|
||||
}
|
||||
|
||||
func NewStdio(stdin, stdout, stderr string) Stdio {
|
||||
for _, s := range []*string{
|
||||
&stdin, &stdout, &stderr,
|
||||
} {
|
||||
if *s == "" {
|
||||
*s = "/dev/null"
|
||||
}
|
||||
}
|
||||
return Stdio{
|
||||
Stdin: stdin,
|
||||
Stdout: stdout,
|
||||
Stderr: stderr,
|
||||
}
|
||||
}
|
||||
|
||||
type ContainerOpts struct {
|
||||
Root string
|
||||
ID string
|
||||
Bundle string
|
||||
Runtime string
|
||||
RuntimeArgs []string
|
||||
Shim string
|
||||
Labels []string
|
||||
NoPivotRoot bool
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// New returns a new container
|
||||
func New(opts ContainerOpts) (Container, error) {
|
||||
c := &container{
|
||||
root: opts.Root,
|
||||
id: opts.ID,
|
||||
bundle: opts.Bundle,
|
||||
labels: opts.Labels,
|
||||
processes: make(map[string]Process),
|
||||
runtime: opts.Runtime,
|
||||
runtimeArgs: opts.RuntimeArgs,
|
||||
shim: opts.Shim,
|
||||
noPivotRoot: opts.NoPivotRoot,
|
||||
timeout: opts.Timeout,
|
||||
}
|
||||
if err := os.Mkdir(filepath.Join(c.root, c.id), 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := os.Create(filepath.Join(c.root, c.id, StateFile))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
if err := json.NewEncoder(f).Encode(state{
|
||||
Bundle: c.bundle,
|
||||
Labels: c.labels,
|
||||
Runtime: c.runtime,
|
||||
RuntimeArgs: c.runtimeArgs,
|
||||
Shim: c.shim,
|
||||
NoPivotRoot: opts.NoPivotRoot,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func Load(root, id string, timeout time.Duration) (Container, error) {
|
||||
var s state
|
||||
f, err := os.Open(filepath.Join(root, id, StateFile))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
if err := json.NewDecoder(f).Decode(&s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c := &container{
|
||||
root: root,
|
||||
id: id,
|
||||
bundle: s.Bundle,
|
||||
labels: s.Labels,
|
||||
runtime: s.Runtime,
|
||||
runtimeArgs: s.RuntimeArgs,
|
||||
shim: s.Shim,
|
||||
noPivotRoot: s.NoPivotRoot,
|
||||
processes: make(map[string]Process),
|
||||
timeout: timeout,
|
||||
}
|
||||
dirs, err := ioutil.ReadDir(filepath.Join(root, id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, d := range dirs {
|
||||
if !d.IsDir() {
|
||||
continue
|
||||
}
|
||||
pid := d.Name()
|
||||
s, err := readProcessState(filepath.Join(root, id, pid))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p, err := loadProcess(filepath.Join(root, id, pid), pid, c, s)
|
||||
if err != nil {
|
||||
logrus.WithField("id", id).WithField("pid", pid).Debug("containerd: error loading process %s", err)
|
||||
continue
|
||||
}
|
||||
c.processes[pid] = p
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func readProcessState(dir string) (*ProcessState, error) {
|
||||
f, err := os.Open(filepath.Join(dir, "process.json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
var s ProcessState
|
||||
if err := json.NewDecoder(f).Decode(&s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
type container struct {
|
||||
// path to store runtime state information
|
||||
root string
|
||||
id string
|
||||
bundle string
|
||||
runtime string
|
||||
runtimeArgs []string
|
||||
shim string
|
||||
processes map[string]Process
|
||||
labels []string
|
||||
oomFds []int
|
||||
noPivotRoot bool
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
func (c *container) ID() string {
|
||||
return c.id
|
||||
}
|
||||
|
||||
func (c *container) Path() string {
|
||||
return c.bundle
|
||||
}
|
||||
|
||||
func (c *container) Labels() []string {
|
||||
return c.labels
|
||||
}
|
||||
|
||||
func (c *container) readSpec() (*specs.Spec, error) {
|
||||
var spec specs.Spec
|
||||
f, err := os.Open(filepath.Join(c.bundle, "config.json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
if err := json.NewDecoder(f).Decode(&spec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &spec, nil
|
||||
}
|
||||
|
||||
func (c *container) Delete() error {
|
||||
err := os.RemoveAll(filepath.Join(c.root, c.id))
|
||||
|
||||
args := c.runtimeArgs
|
||||
args = append(args, "delete", c.id)
|
||||
if derr := exec.Command(c.runtime, args...).Run(); err == nil {
|
||||
err = derr
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *container) Processes() ([]Process, error) {
|
||||
out := []Process{}
|
||||
for _, p := range c.processes {
|
||||
out = append(out, p)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *container) RemoveProcess(pid string) error {
|
||||
delete(c.processes, pid)
|
||||
return os.RemoveAll(filepath.Join(c.root, c.id, pid))
|
||||
}
|
||||
|
||||
func (c *container) State() State {
|
||||
proc := c.processes["init"]
|
||||
if proc == nil {
|
||||
return Stopped
|
||||
}
|
||||
return proc.State()
|
||||
}
|
||||
|
||||
func (c *container) Runtime() string {
|
||||
return c.runtime
|
||||
}
|
||||
|
||||
func (c *container) Pause() error {
|
||||
args := c.runtimeArgs
|
||||
args = append(args, "pause", c.id)
|
||||
b, err := exec.Command(c.runtime, args...).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %q", err.Error(), string(b))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *container) Resume() error {
|
||||
args := c.runtimeArgs
|
||||
args = append(args, "resume", c.id)
|
||||
b, err := exec.Command(c.runtime, args...).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %q", err.Error(), string(b))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *container) Checkpoints() ([]Checkpoint, error) {
|
||||
dirs, err := ioutil.ReadDir(filepath.Join(c.bundle, "checkpoints"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out []Checkpoint
|
||||
for _, d := range dirs {
|
||||
if !d.IsDir() {
|
||||
continue
|
||||
}
|
||||
path := filepath.Join(c.bundle, "checkpoints", d.Name(), "config.json")
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var cpt Checkpoint
|
||||
if err := json.Unmarshal(data, &cpt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, cpt)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *container) Checkpoint(cpt Checkpoint) error {
|
||||
if err := os.MkdirAll(filepath.Join(c.bundle, "checkpoints"), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
path := filepath.Join(c.bundle, "checkpoints", cpt.Name)
|
||||
if err := os.Mkdir(path, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.Create(filepath.Join(path, "config.json"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cpt.Created = time.Now()
|
||||
err = json.NewEncoder(f).Encode(cpt)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args := []string{
|
||||
"checkpoint",
|
||||
"--image-path", path,
|
||||
}
|
||||
add := func(flags ...string) {
|
||||
args = append(args, flags...)
|
||||
}
|
||||
add(c.runtimeArgs...)
|
||||
if !cpt.Exit {
|
||||
add("--leave-running")
|
||||
}
|
||||
if cpt.Shell {
|
||||
add("--shell-job")
|
||||
}
|
||||
if cpt.Tcp {
|
||||
add("--tcp-established")
|
||||
}
|
||||
if cpt.UnixSockets {
|
||||
add("--ext-unix-sk")
|
||||
}
|
||||
add(c.id)
|
||||
out, err := exec.Command(c.runtime, args...).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %q", err.Error(), string(out))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *container) DeleteCheckpoint(name string) error {
|
||||
return os.RemoveAll(filepath.Join(c.bundle, "checkpoints", name))
|
||||
}
|
||||
|
||||
func (c *container) Start(checkpoint string, s Stdio) (Process, error) {
|
||||
processRoot := filepath.Join(c.root, c.id, InitProcessID)
|
||||
if err := os.Mkdir(processRoot, 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
spec, err := c.readSpec()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config := &processConfig{
|
||||
checkpoint: checkpoint,
|
||||
root: processRoot,
|
||||
id: InitProcessID,
|
||||
c: c,
|
||||
stdio: s,
|
||||
spec: spec,
|
||||
processSpec: specs.ProcessSpec(spec.Process),
|
||||
}
|
||||
p, err := c.newProcess(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := p.Start(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.processes[InitProcessID] = p
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (c *container) Exec(pid string, pspec specs.ProcessSpec, s Stdio) (pp Process, err error) {
|
||||
processRoot := filepath.Join(c.root, c.id, pid)
|
||||
if err := os.Mkdir(processRoot, 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
c.RemoveProcess(pid)
|
||||
}
|
||||
}()
|
||||
spec, err := c.readSpec()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config := &processConfig{
|
||||
exec: true,
|
||||
id: pid,
|
||||
root: processRoot,
|
||||
c: c,
|
||||
processSpec: pspec,
|
||||
spec: spec,
|
||||
stdio: s,
|
||||
}
|
||||
p, err := c.newProcess(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := p.Start(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.processes[pid] = p
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func hostIDFromMap(id uint32, mp []ocs.IDMapping) int {
|
||||
for _, m := range mp {
|
||||
if (id >= m.ContainerID) && (id <= (m.ContainerID + m.Size - 1)) {
|
||||
return int(m.HostID + (id - m.ContainerID))
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *container) Pids() ([]int, error) {
|
||||
args := c.runtimeArgs
|
||||
args = append(args, "ps", "--format=json", c.id)
|
||||
out, err := exec.Command(c.runtime, args...).CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %q", err.Error(), out)
|
||||
}
|
||||
var pids []int
|
||||
if err := json.Unmarshal(out, &pids); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pids, nil
|
||||
}
|
||||
|
||||
func (c *container) Stats() (*Stat, error) {
|
||||
now := time.Now()
|
||||
args := c.runtimeArgs
|
||||
args = append(args, "events", "--stats", c.id)
|
||||
out, err := exec.Command(c.runtime, args...).CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %q", err.Error(), out)
|
||||
}
|
||||
s := struct {
|
||||
Data *Stat `json:"data"`
|
||||
}{}
|
||||
if err := json.Unmarshal(out, &s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Data.Timestamp = now
|
||||
return s.Data, nil
|
||||
}
|
||||
|
||||
// Status implements the runtime Container interface.
|
||||
func (c *container) Status() (State, error) {
|
||||
args := c.runtimeArgs
|
||||
args = append(args, "state", c.id)
|
||||
|
||||
out, err := exec.Command(c.runtime, args...).CombinedOutput()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%s: %q", err.Error(), out)
|
||||
}
|
||||
|
||||
// We only require the runtime json output to have a top level Status field.
|
||||
var s struct {
|
||||
Status State `json:"status"`
|
||||
}
|
||||
if err := json.Unmarshal(out, &s); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return s.Status, nil
|
||||
}
|
||||
|
||||
func (c *container) writeEventFD(root string, cfd, efd int) error {
|
||||
f, err := os.OpenFile(filepath.Join(root, "cgroup.event_control"), os.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = f.WriteString(fmt.Sprintf("%d %d", efd, cfd))
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *container) newProcess(config *processConfig) (Process, error) {
|
||||
if c.shim == "" {
|
||||
return newDirectProcess(config)
|
||||
}
|
||||
return newProcess(config)
|
||||
}
|
||||
|
||||
type waitArgs struct {
|
||||
pid int
|
||||
err error
|
||||
}
|
||||
|
||||
// isAlive checks if the shim that launched the container is still alive
|
||||
func isAlive(cmd *exec.Cmd) (bool, error) {
|
||||
if err := syscall.Kill(cmd.Process.Pid, 0); err != nil {
|
||||
if err == syscall.ESRCH {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
type oom struct {
|
||||
id string
|
||||
root string
|
||||
control *os.File
|
||||
eventfd int
|
||||
}
|
||||
|
||||
func (o *oom) ContainerID() string {
|
||||
return o.id
|
||||
}
|
||||
|
||||
func (o *oom) FD() int {
|
||||
return o.eventfd
|
||||
}
|
||||
|
||||
func (o *oom) Flush() {
|
||||
buf := make([]byte, 8)
|
||||
syscall.Read(o.eventfd, buf)
|
||||
}
|
||||
|
||||
func (o *oom) Removed() bool {
|
||||
_, err := os.Lstat(filepath.Join(o.root, "cgroup.event_control"))
|
||||
return os.IsNotExist(err)
|
||||
}
|
||||
|
||||
func (o *oom) Close() error {
|
||||
err := syscall.Close(o.eventfd)
|
||||
if cerr := o.control.Close(); err == nil {
|
||||
err = cerr
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type message struct {
|
||||
Level string `json:"level"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
func readLogMessages(path string) ([]message, error) {
|
||||
var out []message
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
dec := json.NewDecoder(f)
|
||||
for {
|
||||
var m message
|
||||
if err := dec.Decode(&m); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, m)
|
||||
}
|
||||
return out, nil
|
||||
}
|
134
vendor/github.com/docker/containerd/runtime/container_linux.go
generated
vendored
134
vendor/github.com/docker/containerd/runtime/container_linux.go
generated
vendored
@@ -1,134 +0,0 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/docker/containerd/specs"
|
||||
"github.com/docker/containerd/subreaper/exec"
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
ocs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
func (c *container) getLibctContainer() (libcontainer.Container, error) {
|
||||
runtimeRoot := "/run/runc"
|
||||
|
||||
// Check that the root wasn't changed
|
||||
for _, opt := range c.runtimeArgs {
|
||||
if strings.HasPrefix(opt, "--root=") {
|
||||
runtimeRoot = strings.TrimPrefix(opt, "--root=")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
f, err := libcontainer.New(runtimeRoot, libcontainer.Cgroupfs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f.Load(c.id)
|
||||
}
|
||||
|
||||
func (c *container) OOM() (OOM, error) {
|
||||
container, err := c.getLibctContainer()
|
||||
if err != nil {
|
||||
if lerr, ok := err.(libcontainer.Error); ok {
|
||||
// with oom registration sometimes the container can run, exit, and be destroyed
|
||||
// faster than we can get the state back so we can just ignore this
|
||||
if lerr.Code() == libcontainer.ContainerNotExists {
|
||||
return nil, ErrContainerExited
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
state, err := container.State()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
memoryPath := state.CgroupPaths["memory"]
|
||||
return c.getMemeoryEventFD(memoryPath)
|
||||
}
|
||||
|
||||
func u64Ptr(i uint64) *uint64 { return &i }
|
||||
|
||||
func (c *container) UpdateResources(r *Resource) error {
|
||||
sr := ocs.Resources{
|
||||
Memory: &ocs.Memory{
|
||||
Limit: u64Ptr(uint64(r.Memory)),
|
||||
Reservation: u64Ptr(uint64(r.MemoryReservation)),
|
||||
Swap: u64Ptr(uint64(r.MemorySwap)),
|
||||
Kernel: u64Ptr(uint64(r.KernelMemory)),
|
||||
},
|
||||
CPU: &ocs.CPU{
|
||||
Shares: u64Ptr(uint64(r.CPUShares)),
|
||||
Quota: u64Ptr(uint64(r.CPUQuota)),
|
||||
Period: u64Ptr(uint64(r.CPUPeriod)),
|
||||
Cpus: &r.CpusetCpus,
|
||||
Mems: &r.CpusetMems,
|
||||
},
|
||||
BlockIO: &ocs.BlockIO{
|
||||
Weight: &r.BlkioWeight,
|
||||
},
|
||||
}
|
||||
|
||||
srStr := bytes.NewBuffer(nil)
|
||||
if err := json.NewEncoder(srStr).Encode(&sr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := c.runtimeArgs
|
||||
args = append(args, "update", "-r", "-", c.id)
|
||||
cmd := exec.Command(c.runtime, args...)
|
||||
cmd.Stdin = srStr
|
||||
b, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf(string(b))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getRootIDs(s *specs.Spec) (int, int, error) {
|
||||
if s == nil {
|
||||
return 0, 0, nil
|
||||
}
|
||||
var hasUserns bool
|
||||
for _, ns := range s.Linux.Namespaces {
|
||||
if ns.Type == ocs.UserNamespace {
|
||||
hasUserns = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasUserns {
|
||||
return 0, 0, nil
|
||||
}
|
||||
uid := hostIDFromMap(0, s.Linux.UIDMappings)
|
||||
gid := hostIDFromMap(0, s.Linux.GIDMappings)
|
||||
return uid, gid, nil
|
||||
}
|
||||
|
||||
func (c *container) getMemeoryEventFD(root string) (*oom, error) {
|
||||
f, err := os.Open(filepath.Join(root, "memory.oom_control"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fd, _, serr := syscall.RawSyscall(syscall.SYS_EVENTFD2, 0, syscall.FD_CLOEXEC, 0)
|
||||
if serr != 0 {
|
||||
f.Close()
|
||||
return nil, serr
|
||||
}
|
||||
if err := c.writeEventFD(root, int(f.Fd()), int(fd)); err != nil {
|
||||
syscall.Close(int(fd))
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
return &oom{
|
||||
root: root,
|
||||
id: c.id,
|
||||
eventfd: int(fd),
|
||||
control: f,
|
||||
}, nil
|
||||
}
|
19
vendor/github.com/docker/containerd/runtime/container_solaris.go
generated
vendored
19
vendor/github.com/docker/containerd/runtime/container_solaris.go
generated
vendored
@@ -1,19 +0,0 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/docker/containerd/specs"
|
||||
)
|
||||
|
||||
func (c *container) OOM() (OOM, error) {
|
||||
return nil, errors.New("runtime OOM() not implemented on Solaris")
|
||||
}
|
||||
|
||||
func (c *container) UpdateResources(r *Resource) error {
|
||||
return errors.New("runtime UpdateResources() not implemented on Solaris")
|
||||
}
|
||||
|
||||
func getRootIDs(s *specs.Spec) (int, int, error) {
|
||||
return 0, 0, errors.New("runtime getRootIDs() not implemented on Solaris")
|
||||
}
|
283
vendor/github.com/docker/containerd/runtime/direct_process.go
generated
vendored
283
vendor/github.com/docker/containerd/runtime/direct_process.go
generated
vendored
@@ -1,283 +0,0 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/docker/containerd/specs"
|
||||
"github.com/docker/containerd/subreaper"
|
||||
"github.com/docker/containerd/subreaper/exec"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
)
|
||||
|
||||
type directProcess struct {
|
||||
*process
|
||||
sync.WaitGroup
|
||||
|
||||
io stdio
|
||||
console libcontainer.Console
|
||||
consolePath string
|
||||
exec bool
|
||||
checkpoint string
|
||||
specs *specs.Spec
|
||||
}
|
||||
|
||||
func newDirectProcess(config *processConfig) (*directProcess, error) {
|
||||
lp, err := newProcess(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &directProcess{
|
||||
specs: config.spec,
|
||||
process: lp,
|
||||
exec: config.exec,
|
||||
checkpoint: config.checkpoint,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *directProcess) CloseStdin() error {
|
||||
if d.io.stdin != nil {
|
||||
return d.io.stdin.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *directProcess) Resize(w, h int) error {
|
||||
if d.console == nil {
|
||||
return nil
|
||||
}
|
||||
ws := term.Winsize{
|
||||
Width: uint16(w),
|
||||
Height: uint16(h),
|
||||
}
|
||||
return term.SetWinsize(d.console.Fd(), &ws)
|
||||
}
|
||||
|
||||
func (d *directProcess) openIO() (*os.File, *os.File, *os.File, error) {
|
||||
uid, gid, err := getRootIDs(d.specs)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
if d.spec.Terminal {
|
||||
console, err := libcontainer.NewConsole(uid, gid)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
d.console = console
|
||||
d.consolePath = console.Path()
|
||||
stdin, err := os.OpenFile(d.stdio.Stdin, syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
go io.Copy(console, stdin)
|
||||
stdout, err := os.OpenFile(d.stdio.Stdout, syscall.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
d.Add(1)
|
||||
go func() {
|
||||
io.Copy(stdout, console)
|
||||
console.Close()
|
||||
d.Done()
|
||||
}()
|
||||
d.io.stdin = stdin
|
||||
d.io.stdout = stdout
|
||||
d.io.stderr = stdout
|
||||
return nil, nil, nil, nil
|
||||
}
|
||||
|
||||
stdin, err := os.OpenFile(d.stdio.Stdin, syscall.O_RDONLY|syscall.O_NONBLOCK, 0)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
stdout, err := os.OpenFile(d.stdio.Stdout, syscall.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
stderr, err := os.OpenFile(d.stdio.Stderr, syscall.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
d.io.stdin = stdin
|
||||
d.io.stdout = stdout
|
||||
d.io.stderr = stderr
|
||||
return stdin, stdout, stderr, nil
|
||||
}
|
||||
|
||||
func (d *directProcess) loadCheckpoint(bundle string) (*Checkpoint, error) {
|
||||
if d.checkpoint == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
f, err := os.Open(filepath.Join(bundle, "checkpoints", d.checkpoint, "config.json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
var cpt Checkpoint
|
||||
if err := json.NewDecoder(f).Decode(&cpt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cpt, nil
|
||||
}
|
||||
|
||||
func (d *directProcess) Start() error {
|
||||
cwd, err := filepath.Abs(d.root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stdin, stdout, stderr, err := d.openIO()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
checkpoint, err := d.loadCheckpoint(d.container.bundle)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logPath := filepath.Join(cwd, "log.json")
|
||||
args := append([]string{
|
||||
"--log", logPath,
|
||||
"--log-format", "json",
|
||||
}, d.container.runtimeArgs...)
|
||||
if d.exec {
|
||||
args = append(args, "exec",
|
||||
"--process", filepath.Join(cwd, "process.json"),
|
||||
"--console", d.consolePath,
|
||||
)
|
||||
} else if checkpoint != nil {
|
||||
args = append(args, "restore",
|
||||
"--image-path", filepath.Join(d.container.bundle, "checkpoints", checkpoint.Name),
|
||||
)
|
||||
add := func(flags ...string) {
|
||||
args = append(args, flags...)
|
||||
}
|
||||
if checkpoint.Shell {
|
||||
add("--shell-job")
|
||||
}
|
||||
if checkpoint.Tcp {
|
||||
add("--tcp-established")
|
||||
}
|
||||
if checkpoint.UnixSockets {
|
||||
add("--ext-unix-sk")
|
||||
}
|
||||
if d.container.noPivotRoot {
|
||||
add("--no-pivot")
|
||||
}
|
||||
} else {
|
||||
args = append(args, "start",
|
||||
"--bundle", d.container.bundle,
|
||||
"--console", d.consolePath,
|
||||
)
|
||||
if d.container.noPivotRoot {
|
||||
args = append(args, "--no-pivot")
|
||||
}
|
||||
}
|
||||
args = append(args,
|
||||
"-d",
|
||||
"--pid-file", filepath.Join(cwd, "pid"),
|
||||
d.container.id,
|
||||
)
|
||||
cmd := exec.Command(d.container.runtime, args...)
|
||||
cmd.Dir = d.container.bundle
|
||||
cmd.Stdin = stdin
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
// set the parent death signal to SIGKILL so that if containerd dies the container
|
||||
// process also dies
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Pdeathsig: syscall.SIGKILL,
|
||||
}
|
||||
|
||||
exitSubscription := subreaper.Subscribe()
|
||||
err = d.startCmd(cmd)
|
||||
if err != nil {
|
||||
subreaper.Unsubscribe(exitSubscription)
|
||||
d.delete()
|
||||
return err
|
||||
}
|
||||
|
||||
go d.watch(cmd, exitSubscription)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *directProcess) watch(cmd *exec.Cmd, exitSubscription *subreaper.Subscription) {
|
||||
defer subreaper.Unsubscribe(exitSubscription)
|
||||
defer d.delete()
|
||||
|
||||
f, err := os.OpenFile(path.Join(d.root, ExitFile), syscall.O_WRONLY, 0)
|
||||
if err == nil {
|
||||
defer f.Close()
|
||||
}
|
||||
|
||||
exitCode := 0
|
||||
if err = cmd.Wait(); err != nil {
|
||||
if exitError, ok := err.(exec.ExitCodeError); ok {
|
||||
exitCode = exitError.Code
|
||||
}
|
||||
}
|
||||
|
||||
if exitCode == 0 {
|
||||
pid, err := d.getPidFromFile()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
exitSubscription.SetPid(pid)
|
||||
exitCode = exitSubscription.Wait()
|
||||
}
|
||||
|
||||
writeInt(path.Join(d.root, ExitStatusFile), exitCode)
|
||||
}
|
||||
|
||||
func (d *directProcess) delete() {
|
||||
if d.console != nil {
|
||||
d.console.Close()
|
||||
}
|
||||
d.io.Close()
|
||||
d.Wait()
|
||||
if !d.exec {
|
||||
exec.Command(d.container.runtime, append(d.container.runtimeArgs, "delete", d.container.id)...).Run()
|
||||
}
|
||||
}
|
||||
|
||||
func writeInt(path string, i int) error {
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = fmt.Fprintf(f, "%d", i)
|
||||
return err
|
||||
}
|
||||
|
||||
type stdio struct {
|
||||
stdin *os.File
|
||||
stdout *os.File
|
||||
stderr *os.File
|
||||
}
|
||||
|
||||
func (s stdio) Close() error {
|
||||
err := s.stdin.Close()
|
||||
if oerr := s.stdout.Close(); err == nil {
|
||||
err = oerr
|
||||
}
|
||||
if oerr := s.stderr.Close(); err == nil {
|
||||
err = oerr
|
||||
}
|
||||
return err
|
||||
}
|
340
vendor/github.com/docker/containerd/runtime/process.go
generated
vendored
340
vendor/github.com/docker/containerd/runtime/process.go
generated
vendored
@@ -1,340 +0,0 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/docker/containerd/specs"
|
||||
"github.com/docker/containerd/subreaper/exec"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type Process interface {
|
||||
io.Closer
|
||||
|
||||
// ID of the process.
|
||||
// This is either "init" when it is the container's init process or
|
||||
// it is a user provided id for the process similar to the container id
|
||||
ID() string
|
||||
CloseStdin() error
|
||||
Resize(int, int) error
|
||||
// ExitFD returns the fd the provides an event when the process exits
|
||||
ExitFD() int
|
||||
// ExitStatus returns the exit status of the process or an error if it
|
||||
// has not exited
|
||||
ExitStatus() (int, error)
|
||||
// Spec returns the process spec that created the process
|
||||
Spec() specs.ProcessSpec
|
||||
// Signal sends the provided signal to the process
|
||||
Signal(os.Signal) error
|
||||
// Container returns the container that the process belongs to
|
||||
Container() Container
|
||||
// Stdio of the container
|
||||
Stdio() Stdio
|
||||
// SystemPid is the pid on the system
|
||||
SystemPid() int
|
||||
// State returns if the process is running or not
|
||||
State() State
|
||||
// Start executes the process
|
||||
Start() error
|
||||
}
|
||||
|
||||
type processConfig struct {
|
||||
id string
|
||||
root string
|
||||
processSpec specs.ProcessSpec
|
||||
spec *specs.Spec
|
||||
c *container
|
||||
stdio Stdio
|
||||
exec bool
|
||||
checkpoint string
|
||||
}
|
||||
|
||||
func newProcess(config *processConfig) (*process, error) {
|
||||
p := &process{
|
||||
root: config.root,
|
||||
id: config.id,
|
||||
container: config.c,
|
||||
spec: config.processSpec,
|
||||
stdio: config.stdio,
|
||||
}
|
||||
uid, gid, err := getRootIDs(config.spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := os.Create(filepath.Join(config.root, "process.json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
ps := ProcessState{
|
||||
ProcessSpec: config.processSpec,
|
||||
Exec: config.exec,
|
||||
PlatformProcessState: PlatformProcessState{
|
||||
Checkpoint: config.checkpoint,
|
||||
RootUID: uid,
|
||||
RootGID: gid,
|
||||
},
|
||||
Stdin: config.stdio.Stdin,
|
||||
Stdout: config.stdio.Stdout,
|
||||
Stderr: config.stdio.Stderr,
|
||||
RuntimeArgs: config.c.runtimeArgs,
|
||||
NoPivotRoot: config.c.noPivotRoot,
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(f).Encode(ps); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exit, err := getExitPipe(filepath.Join(config.root, ExitFile))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
control, err := getControlPipe(filepath.Join(config.root, ControlFile))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.exitPipe = exit
|
||||
p.controlPipe = control
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (p *process) Start() error {
|
||||
cmd := exec.Command(p.container.shim,
|
||||
p.container.id, p.container.bundle, p.container.runtime,
|
||||
)
|
||||
cmd.Dir = p.root
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
}
|
||||
|
||||
return p.startCmd(cmd)
|
||||
}
|
||||
|
||||
func loadProcess(root, id string, c *container, s *ProcessState) (*process, error) {
|
||||
p := &process{
|
||||
root: root,
|
||||
id: id,
|
||||
container: c,
|
||||
spec: s.ProcessSpec,
|
||||
stdio: Stdio{
|
||||
Stdin: s.Stdin,
|
||||
Stdout: s.Stdout,
|
||||
Stderr: s.Stderr,
|
||||
},
|
||||
}
|
||||
if _, err := p.getPidFromFile(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := p.ExitStatus(); err != nil {
|
||||
if err == ErrProcessNotExited {
|
||||
exit, err := getExitPipe(filepath.Join(root, ExitFile))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.exitPipe = exit
|
||||
return p, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
type process struct {
|
||||
root string
|
||||
id string
|
||||
pid int
|
||||
exitPipe *os.File
|
||||
controlPipe *os.File
|
||||
container *container
|
||||
spec specs.ProcessSpec
|
||||
stdio Stdio
|
||||
}
|
||||
|
||||
func (p *process) ID() string {
|
||||
return p.id
|
||||
}
|
||||
|
||||
func (p *process) Container() Container {
|
||||
return p.container
|
||||
}
|
||||
|
||||
func (p *process) SystemPid() int {
|
||||
return p.pid
|
||||
}
|
||||
|
||||
// ExitFD returns the fd of the exit pipe
|
||||
func (p *process) ExitFD() int {
|
||||
return int(p.exitPipe.Fd())
|
||||
}
|
||||
|
||||
func (p *process) CloseStdin() error {
|
||||
_, err := fmt.Fprintf(p.controlPipe, "%d %d %d\n", 0, 0, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *process) Resize(w, h int) error {
|
||||
_, err := fmt.Fprintf(p.controlPipe, "%d %d %d\n", 1, w, h)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *process) ExitStatus() (int, error) {
|
||||
data, err := ioutil.ReadFile(filepath.Join(p.root, ExitStatusFile))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return -1, ErrProcessNotExited
|
||||
}
|
||||
return -1, err
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return -1, ErrProcessNotExited
|
||||
}
|
||||
return strconv.Atoi(string(data))
|
||||
}
|
||||
|
||||
func (p *process) Spec() specs.ProcessSpec {
|
||||
return p.spec
|
||||
}
|
||||
|
||||
func (p *process) Stdio() Stdio {
|
||||
return p.stdio
|
||||
}
|
||||
|
||||
// Close closes any open files and/or resouces on the process
|
||||
func (p *process) Close() error {
|
||||
return p.exitPipe.Close()
|
||||
}
|
||||
|
||||
func (p *process) State() State {
|
||||
if p.pid == 0 {
|
||||
return Stopped
|
||||
}
|
||||
err := syscall.Kill(p.pid, 0)
|
||||
if err != nil && err == syscall.ESRCH {
|
||||
return Stopped
|
||||
}
|
||||
return Running
|
||||
}
|
||||
|
||||
func (p *process) getPidFromFile() (int, error) {
|
||||
data, err := ioutil.ReadFile(filepath.Join(p.root, "pid"))
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
i, err := strconv.Atoi(string(data))
|
||||
if err != nil {
|
||||
return -1, errInvalidPidInt
|
||||
}
|
||||
p.pid = i
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func getExitPipe(path string) (*os.File, error) {
|
||||
if err := unix.Mkfifo(path, 0755); err != nil && !os.IsExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
// add NONBLOCK in case the other side has already closed or else
|
||||
// this function would never return
|
||||
return os.OpenFile(path, syscall.O_RDONLY|syscall.O_NONBLOCK, 0)
|
||||
}
|
||||
|
||||
func getControlPipe(path string) (*os.File, error) {
|
||||
if err := unix.Mkfifo(path, 0755); err != nil && !os.IsExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
return os.OpenFile(path, syscall.O_RDWR|syscall.O_NONBLOCK, 0)
|
||||
}
|
||||
|
||||
// Signal sends the provided signal to the process
|
||||
func (p *process) Signal(s os.Signal) error {
|
||||
return syscall.Kill(p.pid, s.(syscall.Signal))
|
||||
}
|
||||
|
||||
func (p *process) startCmd(cmd *exec.Cmd) error {
|
||||
if err := cmd.Start(); err != nil {
|
||||
if exErr, ok := err.(*exec.Error); ok {
|
||||
if exErr.Err == exec.ErrNotFound || exErr.Err == os.ErrNotExist {
|
||||
return fmt.Errorf("%s not installed on system", p.container.shim)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
if err := p.waitForStart(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *process) waitForStart(cmd *exec.Cmd) error {
|
||||
wc := make(chan error, 1)
|
||||
go func() {
|
||||
for {
|
||||
if _, err := p.getPidFromFile(); err != nil {
|
||||
if os.IsNotExist(err) || err == errInvalidPidInt {
|
||||
alive, err := isAlive(cmd)
|
||||
if err != nil {
|
||||
wc <- err
|
||||
return
|
||||
}
|
||||
if !alive {
|
||||
// runc could have failed to run the container so lets get the error
|
||||
// out of the logs or the shim could have encountered an error
|
||||
messages, err := readLogMessages(filepath.Join(p.root, "shim-log.json"))
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
wc <- err
|
||||
return
|
||||
}
|
||||
for _, m := range messages {
|
||||
if m.Level == "error" {
|
||||
wc <- fmt.Errorf("shim error: %v", m.Msg)
|
||||
return
|
||||
}
|
||||
}
|
||||
// no errors reported back from shim, check for runc/runtime errors
|
||||
messages, err = readLogMessages(filepath.Join(p.root, "log.json"))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = ErrContainerNotStarted
|
||||
}
|
||||
wc <- err
|
||||
return
|
||||
}
|
||||
for _, m := range messages {
|
||||
if m.Level == "error" {
|
||||
wc <- fmt.Errorf("oci runtime error: %v", m.Msg)
|
||||
return
|
||||
}
|
||||
}
|
||||
wc <- ErrContainerNotStarted
|
||||
return
|
||||
}
|
||||
time.Sleep(15 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
wc <- err
|
||||
return
|
||||
}
|
||||
// the pid file was read successfully
|
||||
wc <- nil
|
||||
return
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case err := <-wc:
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case <-time.After(p.container.timeout):
|
||||
cmd.Process.Kill()
|
||||
cmd.Wait()
|
||||
return ErrContainerStartTimeout
|
||||
}
|
||||
}
|
99
vendor/github.com/docker/containerd/runtime/runtime.go
generated
vendored
99
vendor/github.com/docker/containerd/runtime/runtime.go
generated
vendored
@@ -1,99 +0,0 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/docker/containerd/specs"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotChildProcess = errors.New("containerd: not a child process for container")
|
||||
ErrInvalidContainerType = errors.New("containerd: invalid container type for runtime")
|
||||
ErrCheckpointNotExists = errors.New("containerd: checkpoint does not exist for container")
|
||||
ErrCheckpointExists = errors.New("containerd: checkpoint already exists")
|
||||
ErrContainerExited = errors.New("containerd: container has exited")
|
||||
ErrTerminalsNotSupported = errors.New("containerd: terminals are not supported for runtime")
|
||||
ErrProcessNotExited = errors.New("containerd: process has not exited")
|
||||
ErrProcessExited = errors.New("containerd: process has exited")
|
||||
ErrContainerNotStarted = errors.New("containerd: container not started")
|
||||
ErrContainerStartTimeout = errors.New("containerd: container did not start before the specified timeout")
|
||||
|
||||
errNoPidFile = errors.New("containerd: no process pid file found")
|
||||
errInvalidPidInt = errors.New("containerd: process pid is invalid")
|
||||
errNotImplemented = errors.New("containerd: not implemented")
|
||||
)
|
||||
|
||||
const (
|
||||
ExitFile = "exit"
|
||||
ExitStatusFile = "exitStatus"
|
||||
StateFile = "state.json"
|
||||
ControlFile = "control"
|
||||
InitProcessID = "init"
|
||||
)
|
||||
|
||||
type Checkpoint struct {
|
||||
// Timestamp is the time that checkpoint happened
|
||||
Created time.Time `json:"created"`
|
||||
// Name is the name of the checkpoint
|
||||
Name string `json:"name"`
|
||||
// Tcp checkpoints open tcp connections
|
||||
Tcp bool `json:"tcp"`
|
||||
// UnixSockets persists unix sockets in the checkpoint
|
||||
UnixSockets bool `json:"unixSockets"`
|
||||
// Shell persists tty sessions in the checkpoint
|
||||
Shell bool `json:"shell"`
|
||||
// Exit exits the container after the checkpoint is finished
|
||||
Exit bool `json:"exit"`
|
||||
}
|
||||
|
||||
// PlatformProcessState container platform-specific fields in the ProcessState structure
|
||||
type PlatformProcessState struct {
|
||||
Checkpoint string `json:"checkpoint"`
|
||||
RootUID int `json:"rootUID"`
|
||||
RootGID int `json:"rootGID"`
|
||||
}
|
||||
type State string
|
||||
|
||||
type Resource struct {
|
||||
CPUShares int64
|
||||
BlkioWeight uint16
|
||||
CPUPeriod int64
|
||||
CPUQuota int64
|
||||
CpusetCpus string
|
||||
CpusetMems string
|
||||
KernelMemory int64
|
||||
Memory int64
|
||||
MemoryReservation int64
|
||||
MemorySwap int64
|
||||
}
|
||||
|
||||
const (
|
||||
Paused = State("paused")
|
||||
Stopped = State("stopped")
|
||||
Running = State("running")
|
||||
)
|
||||
|
||||
type state struct {
|
||||
Bundle string `json:"bundle"`
|
||||
Labels []string `json:"labels"`
|
||||
Stdin string `json:"stdin"`
|
||||
Stdout string `json:"stdout"`
|
||||
Stderr string `json:"stderr"`
|
||||
Runtime string `json:"runtime"`
|
||||
RuntimeArgs []string `json:"runtimeArgs"`
|
||||
Shim string `json:"shim"`
|
||||
NoPivotRoot bool `json:"noPivotRoot"`
|
||||
}
|
||||
|
||||
type ProcessState struct {
|
||||
specs.ProcessSpec
|
||||
Exec bool `json:"exec"`
|
||||
Stdin string `json:"containerdStdin"`
|
||||
Stdout string `json:"containerdStdout"`
|
||||
Stderr string `json:"containerdStderr"`
|
||||
RuntimeArgs []string `json:"runtimeArgs"`
|
||||
NoPivotRoot bool `json:"noPivotRoot"`
|
||||
|
||||
PlatformProcessState
|
||||
}
|
77
vendor/github.com/docker/containerd/runtime/stats.go
generated
vendored
77
vendor/github.com/docker/containerd/runtime/stats.go
generated
vendored
@@ -1,77 +0,0 @@
|
||||
package runtime
|
||||
|
||||
import "time"
|
||||
|
||||
type Stat struct {
|
||||
// Timestamp is the time that the statistics where collected
|
||||
Timestamp time.Time
|
||||
Cpu Cpu `json:"cpu"`
|
||||
Memory Memory `json:"memory"`
|
||||
Pids Pids `json:"pids"`
|
||||
Blkio Blkio `json:"blkio"`
|
||||
Hugetlb map[string]Hugetlb `json:"hugetlb"`
|
||||
}
|
||||
|
||||
type Hugetlb struct {
|
||||
Usage uint64 `json:"usage,omitempty"`
|
||||
Max uint64 `json:"max,omitempty"`
|
||||
Failcnt uint64 `json:"failcnt"`
|
||||
}
|
||||
|
||||
type BlkioEntry struct {
|
||||
Major uint64 `json:"major,omitempty"`
|
||||
Minor uint64 `json:"minor,omitempty"`
|
||||
Op string `json:"op,omitempty"`
|
||||
Value uint64 `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
type Blkio struct {
|
||||
IoServiceBytesRecursive []BlkioEntry `json:"ioServiceBytesRecursive,omitempty"`
|
||||
IoServicedRecursive []BlkioEntry `json:"ioServicedRecursive,omitempty"`
|
||||
IoQueuedRecursive []BlkioEntry `json:"ioQueueRecursive,omitempty"`
|
||||
IoServiceTimeRecursive []BlkioEntry `json:"ioServiceTimeRecursive,omitempty"`
|
||||
IoWaitTimeRecursive []BlkioEntry `json:"ioWaitTimeRecursive,omitempty"`
|
||||
IoMergedRecursive []BlkioEntry `json:"ioMergedRecursive,omitempty"`
|
||||
IoTimeRecursive []BlkioEntry `json:"ioTimeRecursive,omitempty"`
|
||||
SectorsRecursive []BlkioEntry `json:"sectorsRecursive,omitempty"`
|
||||
}
|
||||
|
||||
type Pids struct {
|
||||
Current uint64 `json:"current,omitempty"`
|
||||
Limit uint64 `json:"limit,omitempty"`
|
||||
}
|
||||
|
||||
type Throttling struct {
|
||||
Periods uint64 `json:"periods,omitempty"`
|
||||
ThrottledPeriods uint64 `json:"throttledPeriods,omitempty"`
|
||||
ThrottledTime uint64 `json:"throttledTime,omitempty"`
|
||||
}
|
||||
|
||||
type CpuUsage struct {
|
||||
// Units: nanoseconds.
|
||||
Total uint64 `json:"total,omitempty"`
|
||||
Percpu []uint64 `json:"percpu,omitempty"`
|
||||
Kernel uint64 `json:"kernel"`
|
||||
User uint64 `json:"user"`
|
||||
}
|
||||
|
||||
type Cpu struct {
|
||||
Usage CpuUsage `json:"usage,omitempty"`
|
||||
Throttling Throttling `json:"throttling,omitempty"`
|
||||
}
|
||||
|
||||
type MemoryEntry struct {
|
||||
Limit uint64 `json:"limit"`
|
||||
Usage uint64 `json:"usage,omitempty"`
|
||||
Max uint64 `json:"max,omitempty"`
|
||||
Failcnt uint64 `json:"failcnt"`
|
||||
}
|
||||
|
||||
type Memory struct {
|
||||
Cache uint64 `json:"cache,omitempty"`
|
||||
Usage MemoryEntry `json:"usage,omitempty"`
|
||||
Swap MemoryEntry `json:"swap,omitempty"`
|
||||
Kernel MemoryEntry `json:"kernel,omitempty"`
|
||||
KernelTCP MemoryEntry `json:"kernelTCP,omitempty"`
|
||||
Raw map[string]uint64 `json:"raw,omitempty"`
|
||||
}
|
9
vendor/github.com/docker/containerd/specs/spec_linux.go
generated
vendored
9
vendor/github.com/docker/containerd/specs/spec_linux.go
generated
vendored
@@ -1,9 +0,0 @@
|
||||
package specs
|
||||
|
||||
import ocs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
|
||||
type (
|
||||
ProcessSpec ocs.Process
|
||||
Spec ocs.Spec
|
||||
Rlimit ocs.Rlimit
|
||||
)
|
8
vendor/github.com/docker/containerd/specs/spec_solaris.go
generated
vendored
8
vendor/github.com/docker/containerd/specs/spec_solaris.go
generated
vendored
@@ -1,8 +0,0 @@
|
||||
package specs
|
||||
|
||||
import ocs "github.com/opencontainers/specs/specs-go"
|
||||
|
||||
type (
|
||||
ProcessSpec ocs.Process
|
||||
Spec ocs.Spec
|
||||
)
|
43
vendor/github.com/docker/containerd/supervisor/add_process.go
generated
vendored
43
vendor/github.com/docker/containerd/supervisor/add_process.go
generated
vendored
@@ -1,43 +0,0 @@
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/docker/containerd/runtime"
|
||||
"github.com/docker/containerd/specs"
|
||||
)
|
||||
|
||||
type AddProcessTask struct {
|
||||
baseTask
|
||||
ID string
|
||||
PID string
|
||||
Stdout string
|
||||
Stderr string
|
||||
Stdin string
|
||||
ProcessSpec *specs.ProcessSpec
|
||||
StartResponse chan StartResponse
|
||||
}
|
||||
|
||||
func (s *Supervisor) addProcess(t *AddProcessTask) error {
|
||||
start := time.Now()
|
||||
ci, ok := s.containers[t.ID]
|
||||
if !ok {
|
||||
return ErrContainerNotFound
|
||||
}
|
||||
process, err := ci.container.Exec(t.PID, *t.ProcessSpec, runtime.NewStdio(t.Stdin, t.Stdout, t.Stderr))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.monitorProcess(process); err != nil {
|
||||
return err
|
||||
}
|
||||
ExecProcessTimer.UpdateSince(start)
|
||||
t.StartResponse <- StartResponse{}
|
||||
s.notifySubscribers(Event{
|
||||
Timestamp: time.Now(),
|
||||
Type: StateStartProcess,
|
||||
PID: t.PID,
|
||||
ID: t.ID,
|
||||
})
|
||||
return nil
|
||||
}
|
33
vendor/github.com/docker/containerd/supervisor/checkpoint.go
generated
vendored
33
vendor/github.com/docker/containerd/supervisor/checkpoint.go
generated
vendored
@@ -1,33 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package supervisor
|
||||
|
||||
import "github.com/docker/containerd/runtime"
|
||||
|
||||
type CreateCheckpointTask struct {
|
||||
baseTask
|
||||
ID string
|
||||
Checkpoint *runtime.Checkpoint
|
||||
}
|
||||
|
||||
func (s *Supervisor) createCheckpoint(t *CreateCheckpointTask) error {
|
||||
i, ok := s.containers[t.ID]
|
||||
if !ok {
|
||||
return ErrContainerNotFound
|
||||
}
|
||||
return i.container.Checkpoint(*t.Checkpoint)
|
||||
}
|
||||
|
||||
type DeleteCheckpointTask struct {
|
||||
baseTask
|
||||
ID string
|
||||
Checkpoint *runtime.Checkpoint
|
||||
}
|
||||
|
||||
func (s *Supervisor) deleteCheckpoint(t *DeleteCheckpointTask) error {
|
||||
i, ok := s.containers[t.ID]
|
||||
if !ok {
|
||||
return ErrContainerNotFound
|
||||
}
|
||||
return i.container.DeleteCheckpoint(t.Checkpoint.Name)
|
||||
}
|
57
vendor/github.com/docker/containerd/supervisor/create.go
generated
vendored
57
vendor/github.com/docker/containerd/supervisor/create.go
generated
vendored
@@ -1,57 +0,0 @@
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/docker/containerd/runtime"
|
||||
)
|
||||
|
||||
type StartTask struct {
|
||||
baseTask
|
||||
ID string
|
||||
BundlePath string
|
||||
Stdout string
|
||||
Stderr string
|
||||
Stdin string
|
||||
StartResponse chan StartResponse
|
||||
Labels []string
|
||||
NoPivotRoot bool
|
||||
Checkpoint *runtime.Checkpoint
|
||||
}
|
||||
|
||||
func (s *Supervisor) start(t *StartTask) error {
|
||||
start := time.Now()
|
||||
container, err := runtime.New(runtime.ContainerOpts{
|
||||
Root: s.stateDir,
|
||||
ID: t.ID,
|
||||
Bundle: t.BundlePath,
|
||||
Runtime: s.runtime,
|
||||
RuntimeArgs: s.runtimeArgs,
|
||||
Shim: s.shim,
|
||||
Labels: t.Labels,
|
||||
NoPivotRoot: t.NoPivotRoot,
|
||||
Timeout: s.timeout,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.containers[t.ID] = &containerInfo{
|
||||
container: container,
|
||||
}
|
||||
ContainersCounter.Inc(1)
|
||||
task := &startTask{
|
||||
Err: t.ErrorCh(),
|
||||
Container: container,
|
||||
StartResponse: t.StartResponse,
|
||||
Stdin: t.Stdin,
|
||||
Stdout: t.Stdout,
|
||||
Stderr: t.Stderr,
|
||||
}
|
||||
if t.Checkpoint != nil {
|
||||
task.Checkpoint = t.Checkpoint.Name
|
||||
}
|
||||
|
||||
s.startTasks <- task
|
||||
ContainerCreateTimer.UpdateSince(start)
|
||||
return errDeferredResponse
|
||||
}
|
8
vendor/github.com/docker/containerd/supervisor/create_solaris.go
generated
vendored
8
vendor/github.com/docker/containerd/supervisor/create_solaris.go
generated
vendored
@@ -1,8 +0,0 @@
|
||||
package supervisor
|
||||
|
||||
type platformStartTask struct {
|
||||
}
|
||||
|
||||
// Checkpoint not supported on Solaris
|
||||
func (task *startTask) setTaskCheckpoint(t *StartTask) {
|
||||
}
|
42
vendor/github.com/docker/containerd/supervisor/delete.go
generated
vendored
42
vendor/github.com/docker/containerd/supervisor/delete.go
generated
vendored
@@ -1,42 +0,0 @@
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/containerd/runtime"
|
||||
)
|
||||
|
||||
type DeleteTask struct {
|
||||
baseTask
|
||||
ID string
|
||||
Status int
|
||||
PID string
|
||||
NoEvent bool
|
||||
}
|
||||
|
||||
func (s *Supervisor) delete(t *DeleteTask) error {
|
||||
if i, ok := s.containers[t.ID]; ok {
|
||||
start := time.Now()
|
||||
if err := s.deleteContainer(i.container); err != nil {
|
||||
logrus.WithField("error", err).Error("containerd: deleting container")
|
||||
}
|
||||
if !t.NoEvent {
|
||||
s.notifySubscribers(Event{
|
||||
Type: StateExit,
|
||||
Timestamp: time.Now(),
|
||||
ID: t.ID,
|
||||
Status: t.Status,
|
||||
PID: t.PID,
|
||||
})
|
||||
}
|
||||
ContainersCounter.Dec(1)
|
||||
ContainerDeleteTimer.UpdateSince(start)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Supervisor) deleteContainer(container runtime.Container) error {
|
||||
delete(s.containers, container.ID())
|
||||
return container.Delete()
|
||||
}
|
24
vendor/github.com/docker/containerd/supervisor/errors.go
generated
vendored
24
vendor/github.com/docker/containerd/supervisor/errors.go
generated
vendored
@@ -1,24 +0,0 @@
|
||||
package supervisor
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
// External errors
|
||||
ErrTaskChanNil = errors.New("containerd: task channel is nil")
|
||||
ErrBundleNotFound = errors.New("containerd: bundle not found")
|
||||
ErrContainerNotFound = errors.New("containerd: container not found")
|
||||
ErrContainerExists = errors.New("containerd: container already exists")
|
||||
ErrProcessNotFound = errors.New("containerd: processs not found for container")
|
||||
ErrUnknownContainerStatus = errors.New("containerd: unknown container status ")
|
||||
ErrUnknownTask = errors.New("containerd: unknown task type")
|
||||
|
||||
// Internal errors
|
||||
errShutdown = errors.New("containerd: supervisor is shutdown")
|
||||
errRootNotAbs = errors.New("containerd: rootfs path is not an absolute path")
|
||||
errNoContainerForPid = errors.New("containerd: pid not registered for any container")
|
||||
// internal error where the handler will defer to another for the final response
|
||||
//
|
||||
// TODO: we could probably do a typed error with another error channel for this to make it
|
||||
// less like magic
|
||||
errDeferredResponse = errors.New("containerd: deferred response")
|
||||
)
|
81
vendor/github.com/docker/containerd/supervisor/exit.go
generated
vendored
81
vendor/github.com/docker/containerd/supervisor/exit.go
generated
vendored
@@ -1,81 +0,0 @@
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/containerd/runtime"
|
||||
)
|
||||
|
||||
type ExitTask struct {
|
||||
baseTask
|
||||
Process runtime.Process
|
||||
}
|
||||
|
||||
func (s *Supervisor) exit(t *ExitTask) error {
|
||||
start := time.Now()
|
||||
proc := t.Process
|
||||
status, err := proc.ExitStatus()
|
||||
if err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"error": err,
|
||||
"pid": proc.ID(),
|
||||
"id": proc.Container().ID(),
|
||||
"systemPid": proc.SystemPid(),
|
||||
}).Error("containerd: get exit status")
|
||||
}
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"pid": proc.ID(),
|
||||
"status": status,
|
||||
"id": proc.Container().ID(),
|
||||
"systemPid": proc.SystemPid(),
|
||||
}).Debug("containerd: process exited")
|
||||
|
||||
// if the process is the the init process of the container then
|
||||
// fire a separate event for this process
|
||||
if proc.ID() != runtime.InitProcessID {
|
||||
ne := &ExecExitTask{
|
||||
ID: proc.Container().ID(),
|
||||
PID: proc.ID(),
|
||||
Status: status,
|
||||
Process: proc,
|
||||
}
|
||||
s.SendTask(ne)
|
||||
return nil
|
||||
}
|
||||
container := proc.Container()
|
||||
ne := &DeleteTask{
|
||||
ID: container.ID(),
|
||||
Status: status,
|
||||
PID: proc.ID(),
|
||||
}
|
||||
s.SendTask(ne)
|
||||
|
||||
ExitProcessTimer.UpdateSince(start)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type ExecExitTask struct {
|
||||
baseTask
|
||||
ID string
|
||||
PID string
|
||||
Status int
|
||||
Process runtime.Process
|
||||
}
|
||||
|
||||
func (s *Supervisor) execExit(t *ExecExitTask) error {
|
||||
container := t.Process.Container()
|
||||
// exec process: we remove this process without notifying the main event loop
|
||||
if err := container.RemoveProcess(t.PID); err != nil {
|
||||
logrus.WithField("error", err).Error("containerd: find container for pid")
|
||||
}
|
||||
s.notifySubscribers(Event{
|
||||
Timestamp: time.Now(),
|
||||
ID: t.ID,
|
||||
Type: StateExit,
|
||||
PID: t.PID,
|
||||
Status: t.Status,
|
||||
})
|
||||
return nil
|
||||
}
|
28
vendor/github.com/docker/containerd/supervisor/get_containers.go
generated
vendored
28
vendor/github.com/docker/containerd/supervisor/get_containers.go
generated
vendored
@@ -1,28 +0,0 @@
|
||||
package supervisor
|
||||
|
||||
import "github.com/docker/containerd/runtime"
|
||||
|
||||
type GetContainersTask struct {
|
||||
baseTask
|
||||
ID string
|
||||
Containers []runtime.Container
|
||||
}
|
||||
|
||||
func (s *Supervisor) getContainers(t *GetContainersTask) error {
|
||||
|
||||
if t.ID != "" {
|
||||
ci, ok := s.containers[t.ID]
|
||||
if !ok {
|
||||
return ErrContainerNotFound
|
||||
}
|
||||
t.Containers = append(t.Containers, ci.container)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, ci := range s.containers {
|
||||
t.Containers = append(t.Containers, ci.container)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
25
vendor/github.com/docker/containerd/supervisor/machine.go
generated
vendored
25
vendor/github.com/docker/containerd/supervisor/machine.go
generated
vendored
@@ -1,25 +0,0 @@
|
||||
// +build !solaris
|
||||
|
||||
package supervisor
|
||||
|
||||
import "github.com/cloudfoundry/gosigar"
|
||||
|
||||
type Machine struct {
|
||||
Cpus int
|
||||
Memory int64
|
||||
}
|
||||
|
||||
func CollectMachineInformation() (Machine, error) {
|
||||
m := Machine{}
|
||||
cpu := sigar.CpuList{}
|
||||
if err := cpu.Get(); err != nil {
|
||||
return m, err
|
||||
}
|
||||
m.Cpus = len(cpu.List)
|
||||
mem := sigar.Mem{}
|
||||
if err := mem.Get(); err != nil {
|
||||
return m, err
|
||||
}
|
||||
m.Memory = int64(mem.Total / 1024 / 1024)
|
||||
return m, nil
|
||||
}
|
15
vendor/github.com/docker/containerd/supervisor/machine_solaris.go
generated
vendored
15
vendor/github.com/docker/containerd/supervisor/machine_solaris.go
generated
vendored
@@ -1,15 +0,0 @@
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
type Machine struct {
|
||||
Cpus int
|
||||
Memory int64
|
||||
}
|
||||
|
||||
func CollectMachineInformation() (Machine, error) {
|
||||
m := Machine{}
|
||||
return m, errors.New("supervisor CollectMachineInformation not implemented on Solaris")
|
||||
}
|
31
vendor/github.com/docker/containerd/supervisor/metrics.go
generated
vendored
31
vendor/github.com/docker/containerd/supervisor/metrics.go
generated
vendored
@@ -1,31 +0,0 @@
|
||||
package supervisor
|
||||
|
||||
import "github.com/rcrowley/go-metrics"
|
||||
|
||||
var (
|
||||
ContainerCreateTimer = metrics.NewTimer()
|
||||
ContainerDeleteTimer = metrics.NewTimer()
|
||||
ContainerStartTimer = metrics.NewTimer()
|
||||
ContainerStatsTimer = metrics.NewTimer()
|
||||
ContainersCounter = metrics.NewCounter()
|
||||
EventSubscriberCounter = metrics.NewCounter()
|
||||
TasksCounter = metrics.NewCounter()
|
||||
ExecProcessTimer = metrics.NewTimer()
|
||||
ExitProcessTimer = metrics.NewTimer()
|
||||
EpollFdCounter = metrics.NewCounter()
|
||||
)
|
||||
|
||||
func Metrics() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"container-create-time": ContainerCreateTimer,
|
||||
"container-delete-time": ContainerDeleteTimer,
|
||||
"container-start-time": ContainerStartTimer,
|
||||
"container-stats-time": ContainerStatsTimer,
|
||||
"containers": ContainersCounter,
|
||||
"event-subscribers": EventSubscriberCounter,
|
||||
"tasks": TasksCounter,
|
||||
"exec-process-time": ExecProcessTimer,
|
||||
"exit-process-time": ExitProcessTimer,
|
||||
"epoll-fds": EpollFdCounter,
|
||||
}
|
||||
}
|
129
vendor/github.com/docker/containerd/supervisor/monitor_linux.go
generated
vendored
129
vendor/github.com/docker/containerd/supervisor/monitor_linux.go
generated
vendored
@@ -1,129 +0,0 @@
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/containerd/archutils"
|
||||
"github.com/docker/containerd/runtime"
|
||||
)
|
||||
|
||||
func NewMonitor() (*Monitor, error) {
|
||||
m := &Monitor{
|
||||
receivers: make(map[int]interface{}),
|
||||
exits: make(chan runtime.Process, 1024),
|
||||
ooms: make(chan string, 1024),
|
||||
}
|
||||
fd, err := archutils.EpollCreate1(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.epollFd = fd
|
||||
go m.start()
|
||||
return m, nil
|
||||
}
|
||||
|
||||
type Monitor struct {
|
||||
m sync.Mutex
|
||||
receivers map[int]interface{}
|
||||
exits chan runtime.Process
|
||||
ooms chan string
|
||||
epollFd int
|
||||
}
|
||||
|
||||
func (m *Monitor) Exits() chan runtime.Process {
|
||||
return m.exits
|
||||
}
|
||||
|
||||
func (m *Monitor) OOMs() chan string {
|
||||
return m.ooms
|
||||
}
|
||||
|
||||
func (m *Monitor) Monitor(p runtime.Process) error {
|
||||
m.m.Lock()
|
||||
defer m.m.Unlock()
|
||||
fd := p.ExitFD()
|
||||
event := syscall.EpollEvent{
|
||||
Fd: int32(fd),
|
||||
Events: syscall.EPOLLHUP,
|
||||
}
|
||||
if err := archutils.EpollCtl(m.epollFd, syscall.EPOLL_CTL_ADD, fd, &event); err != nil {
|
||||
return err
|
||||
}
|
||||
EpollFdCounter.Inc(1)
|
||||
m.receivers[fd] = p
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Monitor) MonitorOOM(c runtime.Container) error {
|
||||
m.m.Lock()
|
||||
defer m.m.Unlock()
|
||||
o, err := c.OOM()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fd := o.FD()
|
||||
event := syscall.EpollEvent{
|
||||
Fd: int32(fd),
|
||||
Events: syscall.EPOLLHUP | syscall.EPOLLIN,
|
||||
}
|
||||
if err := archutils.EpollCtl(m.epollFd, syscall.EPOLL_CTL_ADD, fd, &event); err != nil {
|
||||
return err
|
||||
}
|
||||
EpollFdCounter.Inc(1)
|
||||
m.receivers[fd] = o
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Monitor) Close() error {
|
||||
return syscall.Close(m.epollFd)
|
||||
}
|
||||
|
||||
func (m *Monitor) start() {
|
||||
var events [128]syscall.EpollEvent
|
||||
for {
|
||||
n, err := archutils.EpollWait(m.epollFd, events[:], -1)
|
||||
if err != nil {
|
||||
if err == syscall.EINTR {
|
||||
continue
|
||||
}
|
||||
logrus.WithField("error", err).Fatal("containerd: epoll wait")
|
||||
}
|
||||
// process events
|
||||
for i := 0; i < n; i++ {
|
||||
fd := int(events[i].Fd)
|
||||
m.m.Lock()
|
||||
r := m.receivers[fd]
|
||||
switch t := r.(type) {
|
||||
case runtime.Process:
|
||||
if events[i].Events == syscall.EPOLLHUP {
|
||||
delete(m.receivers, fd)
|
||||
if err = syscall.EpollCtl(m.epollFd, syscall.EPOLL_CTL_DEL, fd, &syscall.EpollEvent{
|
||||
Events: syscall.EPOLLHUP,
|
||||
Fd: int32(fd),
|
||||
}); err != nil {
|
||||
logrus.WithField("error", err).Error("containerd: epoll remove fd")
|
||||
}
|
||||
if err := t.Close(); err != nil {
|
||||
logrus.WithField("error", err).Error("containerd: close process IO")
|
||||
}
|
||||
EpollFdCounter.Dec(1)
|
||||
m.exits <- t
|
||||
}
|
||||
case runtime.OOM:
|
||||
// always flush the event fd
|
||||
t.Flush()
|
||||
if t.Removed() {
|
||||
delete(m.receivers, fd)
|
||||
// epoll will remove the fd from its set after it has been closed
|
||||
t.Close()
|
||||
EpollFdCounter.Dec(1)
|
||||
} else {
|
||||
m.ooms <- t.ContainerID()
|
||||
}
|
||||
}
|
||||
m.m.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
38
vendor/github.com/docker/containerd/supervisor/monitor_solaris.go
generated
vendored
38
vendor/github.com/docker/containerd/supervisor/monitor_solaris.go
generated
vendored
@@ -1,38 +0,0 @@
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/docker/containerd/runtime"
|
||||
)
|
||||
|
||||
func NewMonitor() (*Monitor, error) {
|
||||
return &Monitor{}, errors.New("Monitor NewMonitor() not implemented on Solaris")
|
||||
}
|
||||
|
||||
type Monitor struct {
|
||||
ooms chan string
|
||||
}
|
||||
|
||||
func (m *Monitor) Exits() chan runtime.Process {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Monitor) OOMs() chan string {
|
||||
return m.ooms
|
||||
}
|
||||
|
||||
func (m *Monitor) Monitor(p runtime.Process) error {
|
||||
return errors.New("Monitor Monitor() not implemented on Solaris")
|
||||
}
|
||||
|
||||
func (m *Monitor) MonitorOOM(c runtime.Container) error {
|
||||
return errors.New("Monitor MonitorOOM() not implemented on Solaris")
|
||||
}
|
||||
|
||||
func (m *Monitor) Close() error {
|
||||
return errors.New("Monitor Close() not implemented on Solaris")
|
||||
}
|
||||
|
||||
func (m *Monitor) start() {
|
||||
}
|
22
vendor/github.com/docker/containerd/supervisor/oom.go
generated
vendored
22
vendor/github.com/docker/containerd/supervisor/oom.go
generated
vendored
@@ -1,22 +0,0 @@
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
type OOMTask struct {
|
||||
baseTask
|
||||
ID string
|
||||
}
|
||||
|
||||
func (s *Supervisor) oom(t *OOMTask) error {
|
||||
logrus.WithField("id", t.ID).Debug("containerd: container oom")
|
||||
s.notifySubscribers(Event{
|
||||
Timestamp: time.Now(),
|
||||
ID: t.ID,
|
||||
Type: StateOOM,
|
||||
})
|
||||
return nil
|
||||
}
|
27
vendor/github.com/docker/containerd/supervisor/signal.go
generated
vendored
27
vendor/github.com/docker/containerd/supervisor/signal.go
generated
vendored
@@ -1,27 +0,0 @@
|
||||
package supervisor
|
||||
|
||||
import "os"
|
||||
|
||||
type SignalTask struct {
|
||||
baseTask
|
||||
ID string
|
||||
PID string
|
||||
Signal os.Signal
|
||||
}
|
||||
|
||||
func (s *Supervisor) signal(t *SignalTask) error {
|
||||
i, ok := s.containers[t.ID]
|
||||
if !ok {
|
||||
return ErrContainerNotFound
|
||||
}
|
||||
processes, err := i.container.Processes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range processes {
|
||||
if p.ID() == t.PID {
|
||||
return p.Signal(t.Signal)
|
||||
}
|
||||
}
|
||||
return ErrProcessNotFound
|
||||
}
|
27
vendor/github.com/docker/containerd/supervisor/sort.go
generated
vendored
27
vendor/github.com/docker/containerd/supervisor/sort.go
generated
vendored
@@ -1,27 +0,0 @@
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/docker/containerd/runtime"
|
||||
)
|
||||
|
||||
func sortProcesses(p []runtime.Process) {
|
||||
sort.Sort(&processSorter{p})
|
||||
}
|
||||
|
||||
type processSorter struct {
|
||||
processes []runtime.Process
|
||||
}
|
||||
|
||||
func (s *processSorter) Len() int {
|
||||
return len(s.processes)
|
||||
}
|
||||
|
||||
func (s *processSorter) Swap(i, j int) {
|
||||
s.processes[i], s.processes[j] = s.processes[j], s.processes[i]
|
||||
}
|
||||
|
||||
func (s *processSorter) Less(i, j int) bool {
|
||||
return s.processes[j].ID() == "init"
|
||||
}
|
33
vendor/github.com/docker/containerd/supervisor/stats.go
generated
vendored
33
vendor/github.com/docker/containerd/supervisor/stats.go
generated
vendored
@@ -1,33 +0,0 @@
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/docker/containerd/runtime"
|
||||
)
|
||||
|
||||
type StatsTask struct {
|
||||
baseTask
|
||||
ID string
|
||||
Stat chan *runtime.Stat
|
||||
}
|
||||
|
||||
func (s *Supervisor) stats(t *StatsTask) error {
|
||||
start := time.Now()
|
||||
i, ok := s.containers[t.ID]
|
||||
if !ok {
|
||||
return ErrContainerNotFound
|
||||
}
|
||||
// TODO: use workers for this
|
||||
go func() {
|
||||
s, err := i.container.Stats()
|
||||
if err != nil {
|
||||
t.ErrorCh() <- err
|
||||
return
|
||||
}
|
||||
t.ErrorCh() <- nil
|
||||
t.Stat <- s
|
||||
ContainerStatsTimer.UpdateSince(start)
|
||||
}()
|
||||
return errDeferredResponse
|
||||
}
|
385
vendor/github.com/docker/containerd/supervisor/supervisor.go
generated
vendored
385
vendor/github.com/docker/containerd/supervisor/supervisor.go
generated
vendored
@@ -1,385 +0,0 @@
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/containerd/runtime"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultBufferSize = 2048 // size of queue in eventloop
|
||||
)
|
||||
|
||||
// New returns an initialized Process supervisor.
|
||||
func New(stateDir string, runtimeName, shimName string, runtimeArgs []string, timeout time.Duration, retainCount int) (*Supervisor, error) {
|
||||
startTasks := make(chan *startTask, 10)
|
||||
if err := os.MkdirAll(stateDir, 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
machine, err := CollectMachineInformation()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
monitor, err := NewMonitor()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := &Supervisor{
|
||||
stateDir: stateDir,
|
||||
containers: make(map[string]*containerInfo),
|
||||
startTasks: startTasks,
|
||||
machine: machine,
|
||||
subscribers: make(map[chan Event]struct{}),
|
||||
tasks: make(chan Task, defaultBufferSize),
|
||||
monitor: monitor,
|
||||
runtime: runtimeName,
|
||||
runtimeArgs: runtimeArgs,
|
||||
shim: shimName,
|
||||
timeout: timeout,
|
||||
}
|
||||
if err := setupEventLog(s, retainCount); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go s.exitHandler()
|
||||
go s.oomHandler()
|
||||
if err := s.restore(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
type containerInfo struct {
|
||||
container runtime.Container
|
||||
}
|
||||
|
||||
func setupEventLog(s *Supervisor, retainCount int) error {
|
||||
if err := readEventLog(s); err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.WithField("count", len(s.eventLog)).Debug("containerd: read past events")
|
||||
events := s.Events(time.Time{})
|
||||
return eventLogger(s, filepath.Join(s.stateDir, "events.log"), events, retainCount)
|
||||
}
|
||||
|
||||
func eventLogger(s *Supervisor, path string, events chan Event, retainCount int) error {
|
||||
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND|os.O_TRUNC, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
var (
|
||||
count = len(s.eventLog)
|
||||
enc = json.NewEncoder(f)
|
||||
)
|
||||
for e := range events {
|
||||
// if we have a specified retain count make sure the truncate the event
|
||||
// log if it grows past the specified number of events to keep.
|
||||
if retainCount > 0 {
|
||||
if count > retainCount {
|
||||
logrus.Debug("truncating event log")
|
||||
// close the log file
|
||||
if f != nil {
|
||||
f.Close()
|
||||
}
|
||||
slice := retainCount - 1
|
||||
l := len(s.eventLog)
|
||||
if slice >= l {
|
||||
slice = l
|
||||
}
|
||||
s.eventLock.Lock()
|
||||
s.eventLog = s.eventLog[len(s.eventLog)-slice:]
|
||||
s.eventLock.Unlock()
|
||||
if f, err = os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND|os.O_TRUNC, 0755); err != nil {
|
||||
logrus.WithField("error", err).Error("containerd: open event to journal")
|
||||
continue
|
||||
}
|
||||
enc = json.NewEncoder(f)
|
||||
count = 0
|
||||
for _, le := range s.eventLog {
|
||||
if err := enc.Encode(le); err != nil {
|
||||
logrus.WithField("error", err).Error("containerd: write event to journal")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
s.eventLock.Lock()
|
||||
s.eventLog = append(s.eventLog, e)
|
||||
s.eventLock.Unlock()
|
||||
count++
|
||||
if err := enc.Encode(e); err != nil {
|
||||
logrus.WithField("error", err).Error("containerd: write event to journal")
|
||||
}
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func readEventLog(s *Supervisor) error {
|
||||
f, err := os.Open(filepath.Join(s.stateDir, "events.log"))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
dec := json.NewDecoder(f)
|
||||
for {
|
||||
var e Event
|
||||
if err := dec.Decode(&e); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
s.eventLog = append(s.eventLog, e)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Supervisor struct {
|
||||
// stateDir is the directory on the system to store container runtime state information.
|
||||
stateDir string
|
||||
// name of the OCI compatible runtime used to execute containers
|
||||
runtime string
|
||||
runtimeArgs []string
|
||||
shim string
|
||||
containers map[string]*containerInfo
|
||||
startTasks chan *startTask
|
||||
// we need a lock around the subscribers map only because additions and deletions from
|
||||
// the map are via the API so we cannot really control the concurrency
|
||||
subscriberLock sync.RWMutex
|
||||
subscribers map[chan Event]struct{}
|
||||
machine Machine
|
||||
tasks chan Task
|
||||
monitor *Monitor
|
||||
eventLog []Event
|
||||
eventLock sync.Mutex
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// Stop closes all startTasks and sends a SIGTERM to each container's pid1 then waits for they to
|
||||
// terminate. After it has handled all the SIGCHILD events it will close the signals chan
|
||||
// and exit. Stop is a non-blocking call and will return after the containers have been signaled
|
||||
func (s *Supervisor) Stop() {
|
||||
// Close the startTasks channel so that no new containers get started
|
||||
close(s.startTasks)
|
||||
}
|
||||
|
||||
// Close closes any open files in the supervisor but expects that Stop has been
|
||||
// callsed so that no more containers are started.
|
||||
func (s *Supervisor) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
PID string `json:"pid,omitempty"`
|
||||
Status int `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// Events returns an event channel that external consumers can use to receive updates
|
||||
// on container events
|
||||
func (s *Supervisor) Events(from time.Time) chan Event {
|
||||
s.subscriberLock.Lock()
|
||||
defer s.subscriberLock.Unlock()
|
||||
c := make(chan Event, defaultBufferSize)
|
||||
EventSubscriberCounter.Inc(1)
|
||||
s.subscribers[c] = struct{}{}
|
||||
if !from.IsZero() {
|
||||
// replay old event
|
||||
s.eventLock.Lock()
|
||||
past := s.eventLog[:]
|
||||
s.eventLock.Unlock()
|
||||
for _, e := range past {
|
||||
if e.Timestamp.After(from) {
|
||||
c <- e
|
||||
}
|
||||
}
|
||||
// Notify the client that from now on it's live events
|
||||
c <- Event{
|
||||
Type: StateLive,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Unsubscribe removes the provided channel from receiving any more events
|
||||
func (s *Supervisor) Unsubscribe(sub chan Event) {
|
||||
s.subscriberLock.Lock()
|
||||
defer s.subscriberLock.Unlock()
|
||||
delete(s.subscribers, sub)
|
||||
close(sub)
|
||||
EventSubscriberCounter.Dec(1)
|
||||
}
|
||||
|
||||
// notifySubscribers will send the provided event to the external subscribers
|
||||
// of the events channel
|
||||
func (s *Supervisor) notifySubscribers(e Event) {
|
||||
s.subscriberLock.RLock()
|
||||
defer s.subscriberLock.RUnlock()
|
||||
for sub := range s.subscribers {
|
||||
// do a non-blocking send for the channel
|
||||
select {
|
||||
case sub <- e:
|
||||
default:
|
||||
logrus.WithField("event", e.Type).Warn("containerd: event not sent to subscriber")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start is a non-blocking call that runs the supervisor for monitoring contianer processes and
|
||||
// executing new containers.
|
||||
//
|
||||
// This event loop is the only thing that is allowed to modify state of containers and processes
|
||||
// therefore it is save to do operations in the handlers that modify state of the system or
|
||||
// state of the Supervisor
|
||||
func (s *Supervisor) Start() error {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"stateDir": s.stateDir,
|
||||
"runtime": s.runtime,
|
||||
"runtimeArgs": s.runtimeArgs,
|
||||
"memory": s.machine.Memory,
|
||||
"cpus": s.machine.Cpus,
|
||||
}).Debug("containerd: supervisor running")
|
||||
go func() {
|
||||
for i := range s.tasks {
|
||||
s.handleTask(i)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Machine returns the machine information for which the
|
||||
// supervisor is executing on.
|
||||
func (s *Supervisor) Machine() Machine {
|
||||
return s.machine
|
||||
}
|
||||
|
||||
// SendTask sends the provided event the the supervisors main event loop
|
||||
func (s *Supervisor) SendTask(evt Task) {
|
||||
TasksCounter.Inc(1)
|
||||
s.tasks <- evt
|
||||
}
|
||||
|
||||
func (s *Supervisor) exitHandler() {
|
||||
for p := range s.monitor.Exits() {
|
||||
e := &ExitTask{
|
||||
Process: p,
|
||||
}
|
||||
s.SendTask(e)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Supervisor) oomHandler() {
|
||||
for id := range s.monitor.OOMs() {
|
||||
e := &OOMTask{
|
||||
ID: id,
|
||||
}
|
||||
s.SendTask(e)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Supervisor) monitorProcess(p runtime.Process) error {
|
||||
return s.monitor.Monitor(p)
|
||||
}
|
||||
|
||||
func (s *Supervisor) restore() error {
|
||||
dirs, err := ioutil.ReadDir(s.stateDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, d := range dirs {
|
||||
if !d.IsDir() {
|
||||
continue
|
||||
}
|
||||
id := d.Name()
|
||||
container, err := runtime.Load(s.stateDir, id, s.timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
processes, err := container.Processes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ContainersCounter.Inc(1)
|
||||
s.containers[id] = &containerInfo{
|
||||
container: container,
|
||||
}
|
||||
if err := s.monitor.MonitorOOM(container); err != nil && err != runtime.ErrContainerExited {
|
||||
logrus.WithField("error", err).Error("containerd: notify OOM events")
|
||||
}
|
||||
logrus.WithField("id", id).Debug("containerd: container restored")
|
||||
var exitedProcesses []runtime.Process
|
||||
for _, p := range processes {
|
||||
if p.State() == runtime.Running {
|
||||
if err := s.monitorProcess(p); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
exitedProcesses = append(exitedProcesses, p)
|
||||
}
|
||||
}
|
||||
if len(exitedProcesses) > 0 {
|
||||
// sort processes so that init is fired last because that is how the kernel sends the
|
||||
// exit events
|
||||
sortProcesses(exitedProcesses)
|
||||
for _, p := range exitedProcesses {
|
||||
e := &ExitTask{
|
||||
Process: p,
|
||||
}
|
||||
s.SendTask(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Supervisor) handleTask(i Task) {
|
||||
var err error
|
||||
switch t := i.(type) {
|
||||
case *AddProcessTask:
|
||||
err = s.addProcess(t)
|
||||
case *CreateCheckpointTask:
|
||||
err = s.createCheckpoint(t)
|
||||
case *DeleteCheckpointTask:
|
||||
err = s.deleteCheckpoint(t)
|
||||
case *StartTask:
|
||||
err = s.start(t)
|
||||
case *DeleteTask:
|
||||
err = s.delete(t)
|
||||
case *ExitTask:
|
||||
err = s.exit(t)
|
||||
case *ExecExitTask:
|
||||
err = s.execExit(t)
|
||||
case *GetContainersTask:
|
||||
err = s.getContainers(t)
|
||||
case *SignalTask:
|
||||
err = s.signal(t)
|
||||
case *StatsTask:
|
||||
err = s.stats(t)
|
||||
case *UpdateTask:
|
||||
err = s.updateContainer(t)
|
||||
case *UpdateProcessTask:
|
||||
err = s.updateProcess(t)
|
||||
case *OOMTask:
|
||||
err = s.oom(t)
|
||||
default:
|
||||
err = ErrUnknownTask
|
||||
}
|
||||
if err != errDeferredResponse {
|
||||
i.ErrorCh() <- err
|
||||
close(i.ErrorCh())
|
||||
}
|
||||
}
|
33
vendor/github.com/docker/containerd/supervisor/task.go
generated
vendored
33
vendor/github.com/docker/containerd/supervisor/task.go
generated
vendored
@@ -1,33 +0,0 @@
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/docker/containerd/runtime"
|
||||
)
|
||||
|
||||
// StartResponse is the response containing a started container
|
||||
type StartResponse struct {
|
||||
Container runtime.Container
|
||||
}
|
||||
|
||||
// Task executes an action returning an error chan with either nil or
|
||||
// the error from executing the task
|
||||
type Task interface {
|
||||
// ErrorCh returns a channel used to report and error from an async task
|
||||
ErrorCh() chan error
|
||||
}
|
||||
|
||||
type baseTask struct {
|
||||
errCh chan error
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (t *baseTask) ErrorCh() chan error {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
if t.errCh == nil {
|
||||
t.errCh = make(chan error, 1)
|
||||
}
|
||||
return t.errCh
|
||||
}
|
12
vendor/github.com/docker/containerd/supervisor/types.go
generated
vendored
12
vendor/github.com/docker/containerd/supervisor/types.go
generated
vendored
@@ -1,12 +0,0 @@
|
||||
package supervisor
|
||||
|
||||
// State constants used in Event types
|
||||
const (
|
||||
StateStart = "start-container"
|
||||
StatePause = "pause"
|
||||
StateResume = "resume"
|
||||
StateExit = "exit"
|
||||
StateStartProcess = "start-process"
|
||||
StateOOM = "oom"
|
||||
StateLive = "live"
|
||||
)
|
92
vendor/github.com/docker/containerd/supervisor/update.go
generated
vendored
92
vendor/github.com/docker/containerd/supervisor/update.go
generated
vendored
@@ -1,92 +0,0 @@
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/docker/containerd/runtime"
|
||||
)
|
||||
|
||||
type UpdateTask struct {
|
||||
baseTask
|
||||
ID string
|
||||
State runtime.State
|
||||
Resources *runtime.Resource
|
||||
}
|
||||
|
||||
func (s *Supervisor) updateContainer(t *UpdateTask) error {
|
||||
i, ok := s.containers[t.ID]
|
||||
if !ok {
|
||||
return ErrContainerNotFound
|
||||
}
|
||||
container := i.container
|
||||
if t.State != "" {
|
||||
switch t.State {
|
||||
case runtime.Running:
|
||||
if err := container.Resume(); err != nil {
|
||||
return err
|
||||
}
|
||||
s.notifySubscribers(Event{
|
||||
ID: t.ID,
|
||||
Type: StateResume,
|
||||
Timestamp: time.Now(),
|
||||
})
|
||||
case runtime.Paused:
|
||||
if err := container.Pause(); err != nil {
|
||||
return err
|
||||
}
|
||||
s.notifySubscribers(Event{
|
||||
ID: t.ID,
|
||||
Type: StatePause,
|
||||
Timestamp: time.Now(),
|
||||
})
|
||||
default:
|
||||
return ErrUnknownContainerStatus
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if t.Resources != nil {
|
||||
return container.UpdateResources(t.Resources)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type UpdateProcessTask struct {
|
||||
baseTask
|
||||
ID string
|
||||
PID string
|
||||
CloseStdin bool
|
||||
Width int
|
||||
Height int
|
||||
}
|
||||
|
||||
func (s *Supervisor) updateProcess(t *UpdateProcessTask) error {
|
||||
i, ok := s.containers[t.ID]
|
||||
if !ok {
|
||||
return ErrContainerNotFound
|
||||
}
|
||||
processes, err := i.container.Processes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var process runtime.Process
|
||||
for _, p := range processes {
|
||||
if p.ID() == t.PID {
|
||||
process = p
|
||||
break
|
||||
}
|
||||
}
|
||||
if process == nil {
|
||||
return ErrProcessNotFound
|
||||
}
|
||||
if t.CloseStdin {
|
||||
if err := process.CloseStdin(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if t.Width > 0 || t.Height > 0 {
|
||||
if err := process.Resize(t.Width, t.Height); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
74
vendor/github.com/docker/containerd/supervisor/worker.go
generated
vendored
74
vendor/github.com/docker/containerd/supervisor/worker.go
generated
vendored
@@ -1,74 +0,0 @@
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/containerd/runtime"
|
||||
)
|
||||
|
||||
type Worker interface {
|
||||
Start()
|
||||
}
|
||||
|
||||
type startTask struct {
|
||||
Container runtime.Container
|
||||
Checkpoint string
|
||||
Stdin string
|
||||
Stdout string
|
||||
Stderr string
|
||||
Err chan error
|
||||
StartResponse chan StartResponse
|
||||
}
|
||||
|
||||
func NewWorker(s *Supervisor, wg *sync.WaitGroup) Worker {
|
||||
return &worker{
|
||||
s: s,
|
||||
wg: wg,
|
||||
}
|
||||
}
|
||||
|
||||
type worker struct {
|
||||
wg *sync.WaitGroup
|
||||
s *Supervisor
|
||||
}
|
||||
|
||||
func (w *worker) Start() {
|
||||
defer w.wg.Done()
|
||||
for t := range w.s.startTasks {
|
||||
started := time.Now()
|
||||
process, err := t.Container.Start(t.Checkpoint, runtime.NewStdio(t.Stdin, t.Stdout, t.Stderr))
|
||||
if err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"error": err,
|
||||
"id": t.Container.ID(),
|
||||
}).Error("containerd: start container")
|
||||
t.Err <- err
|
||||
evt := &DeleteTask{
|
||||
ID: t.Container.ID(),
|
||||
NoEvent: true,
|
||||
}
|
||||
w.s.SendTask(evt)
|
||||
continue
|
||||
}
|
||||
if err := w.s.monitor.MonitorOOM(t.Container); err != nil && err != runtime.ErrContainerExited {
|
||||
if process.State() != runtime.Stopped {
|
||||
logrus.WithField("error", err).Error("containerd: notify OOM events")
|
||||
}
|
||||
}
|
||||
if err := w.s.monitorProcess(process); err != nil {
|
||||
logrus.WithField("error", err).Error("containerd: add process to monitor")
|
||||
}
|
||||
ContainerStartTimer.UpdateSince(started)
|
||||
t.Err <- nil
|
||||
t.StartResponse <- StartResponse{
|
||||
Container: t.Container,
|
||||
}
|
||||
w.s.notifySubscribers(Event{
|
||||
Timestamp: time.Now(),
|
||||
ID: t.Container.ID(),
|
||||
Type: StateStart,
|
||||
})
|
||||
}
|
||||
}
|
11
vendor/github.com/docker/containerd/version.go
generated
vendored
11
vendor/github.com/docker/containerd/version.go
generated
vendored
@@ -1,11 +0,0 @@
|
||||
package containerd
|
||||
|
||||
import "fmt"
|
||||
|
||||
const VersionMajor = 0
|
||||
const VersionMinor = 2
|
||||
const VersionPatch = 0
|
||||
|
||||
var Version = fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionPatch)
|
||||
|
||||
var GitCommit = ""
|
Reference in New Issue
Block a user