123 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			123 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package supervisor
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"fmt"
 | 
						|
	"io/ioutil"
 | 
						|
	"log"
 | 
						|
	"net"
 | 
						|
	"os"
 | 
						|
	"os/exec"
 | 
						|
	"path/filepath"
 | 
						|
	"syscall"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/docker/containerd/api/shim"
 | 
						|
	"github.com/pkg/errors"
 | 
						|
	"google.golang.org/grpc"
 | 
						|
	"google.golang.org/grpc/grpclog"
 | 
						|
)
 | 
						|
 | 
						|
func newShimClient(root, id string) (*shimClient, error) {
 | 
						|
	if err := os.Mkdir(root, 0700); err != nil {
 | 
						|
		return nil, errors.Wrap(err, "failed to create shim working dir")
 | 
						|
	}
 | 
						|
 | 
						|
	cmd := exec.Command("containerd-shim")
 | 
						|
	cmd.Dir = root
 | 
						|
	cmd.SysProcAttr = &syscall.SysProcAttr{
 | 
						|
		Setpgid: true,
 | 
						|
	}
 | 
						|
	if err := cmd.Start(); err != nil {
 | 
						|
		return nil, errors.Wrapf(err, "failed to start shim")
 | 
						|
	}
 | 
						|
 | 
						|
	socket := filepath.Join(root, "shim.sock")
 | 
						|
	sc, err := connectToShim(socket)
 | 
						|
	if err != nil {
 | 
						|
		syscall.Kill(cmd.Process.Pid, syscall.SIGKILL)
 | 
						|
		cmd.Wait()
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	s := &shimClient{
 | 
						|
		ShimClient: sc,
 | 
						|
		shimCmd:    cmd,
 | 
						|
		syncCh:     make(chan struct{}),
 | 
						|
		root:       root,
 | 
						|
		id:         id,
 | 
						|
	}
 | 
						|
	go func() {
 | 
						|
		cmd.Wait()
 | 
						|
		close(s.syncCh)
 | 
						|
	}()
 | 
						|
 | 
						|
	return s, nil
 | 
						|
}
 | 
						|
 | 
						|
func loadShimClient(root, id string) (*shimClient, error) {
 | 
						|
	socket := filepath.Join(root, "shim.sock")
 | 
						|
	client, err := connectToShim(socket)
 | 
						|
	if err != nil {
 | 
						|
		// TODO: failed to connect to the shim, check if it's alive
 | 
						|
		//   - if it is kill it
 | 
						|
		//   - in both case call runc killall and runc delete on the id
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	resp, err := client.State(context.Background(), &shim.StateRequest{})
 | 
						|
	if err != nil {
 | 
						|
		return nil, errors.Wrapf(err, "failed to fetch state for container %s", id)
 | 
						|
	}
 | 
						|
 | 
						|
	return &shimClient{
 | 
						|
		ShimClient: client,
 | 
						|
		root:       root,
 | 
						|
		id:         id,
 | 
						|
		initPid:    resp.InitPid,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
type shimClient struct {
 | 
						|
	shim.ShimClient
 | 
						|
	shimCmd *exec.Cmd
 | 
						|
	syncCh  chan struct{}
 | 
						|
	root    string
 | 
						|
	id      string
 | 
						|
	initPid uint32
 | 
						|
}
 | 
						|
 | 
						|
func (s *shimClient) stop() {
 | 
						|
	if s.shimCmd != nil {
 | 
						|
		select {
 | 
						|
		case <-s.syncCh:
 | 
						|
		default:
 | 
						|
			syscall.Kill(s.shimCmd.Process.Pid, syscall.SIGTERM)
 | 
						|
			select {
 | 
						|
			case <-s.syncCh:
 | 
						|
			case <-time.After(10 * time.Second):
 | 
						|
				syscall.Kill(s.shimCmd.Process.Pid, syscall.SIGKILL)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	os.RemoveAll(s.root)
 | 
						|
}
 | 
						|
 | 
						|
func connectToShim(socket string) (shim.ShimClient, error) {
 | 
						|
	// reset the logger for grpc to log to dev/null so that it does not mess with our stdio
 | 
						|
	grpclog.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags))
 | 
						|
	dialOpts := []grpc.DialOption{grpc.WithInsecure(), grpc.WithTimeout(100 * time.Second)}
 | 
						|
	dialOpts = append(dialOpts,
 | 
						|
		grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {
 | 
						|
			return net.DialTimeout("unix", socket, timeout)
 | 
						|
		}),
 | 
						|
		grpc.WithBlock(),
 | 
						|
		grpc.WithTimeout(2*time.Second),
 | 
						|
	)
 | 
						|
	conn, err := grpc.Dial(fmt.Sprintf("unix://%s", socket), dialOpts...)
 | 
						|
	if err != nil {
 | 
						|
		return nil, errors.Wrapf(err, "failed to connect to shim via \"%s\"", fmt.Sprintf("unix://%s", socket))
 | 
						|
	}
 | 
						|
	return shim.NewShimClient(conn), nil
 | 
						|
}
 |