...

Source file src/github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/start.go

Documentation: github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1

     1  //go:build windows
     2  
     3  package main
     4  
     5  import (
     6  	gocontext "context"
     7  	"encoding/json"
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  
    14  	"github.com/Microsoft/go-winio"
    15  	"github.com/Microsoft/hcsshim/internal/oci"
    16  	"github.com/Microsoft/hcsshim/pkg/annotations"
    17  	"github.com/containerd/containerd/runtime/v2/shim"
    18  	"github.com/containerd/containerd/runtime/v2/task"
    19  	"github.com/containerd/ttrpc"
    20  	"github.com/pkg/errors"
    21  	"github.com/sirupsen/logrus"
    22  	"github.com/urfave/cli"
    23  )
    24  
    25  var startCommand = cli.Command{
    26  	Name: "start",
    27  	Usage: `
    28  This command will launch new shims.
    29  
    30  The start command, as well as all binary calls to the shim, has the bundle for the container set as the cwd.
    31  
    32  The start command MUST return an address to a shim for containerd to issue API requests for container operations.
    33  
    34  The start command can either start a new shim or return an address to an existing shim based on the shim's logic.
    35  `,
    36  	SkipArgReorder: true,
    37  	Action: func(context *cli.Context) (err error) {
    38  		// We cant write anything to stdout/stderr for this cmd.
    39  		logrus.SetOutput(io.Discard)
    40  
    41  		// On Windows there are two scenarios that will launch a shim.
    42  		//
    43  		// 1. The config.json in the bundle path contains the kubernetes
    44  		// annotation `io.kubernetes.cri.container-type = sandbox`. This shim
    45  		// will be served for the POD itself and all
    46  		// `io.kubernetes.cri.container-type = container` with a matching
    47  		// `io.kubernetes.cri.sandbox-id`. For any calls to start where the
    48  		// config.json contains the `io.kubernetes.cri.container-type =
    49  		// container` annotation a shim path to the
    50  		// `io.kubernetes.cri.sandbox-id` will be returned.
    51  		//
    52  		// 2. The container does not have any kubernetes annotations and
    53  		// therefore is a process isolated Windows Container, a hypervisor
    54  		// isolated Windows Container, or a hypervisor isolated Linux Container
    55  		// on Windows.
    56  
    57  		const addrFmt = "\\\\.\\pipe\\ProtectedPrefix\\Administrators\\containerd-shim-%s-%s-pipe"
    58  
    59  		var (
    60  			address string
    61  			pid     int
    62  		)
    63  
    64  		cwd, err := os.Getwd()
    65  		if err != nil {
    66  			return err
    67  		}
    68  
    69  		a, err := getSpecAnnotations(cwd)
    70  		if err != nil {
    71  			return err
    72  		}
    73  
    74  		ct, sbid, err := oci.GetSandboxTypeAndID(a)
    75  		if err != nil {
    76  			return err
    77  		}
    78  
    79  		if ct == oci.KubernetesContainerTypeContainer {
    80  			address = fmt.Sprintf(addrFmt, namespaceFlag, sbid)
    81  
    82  			// Connect to the hosting shim and get the pid
    83  			c, err := winio.DialPipe(address, nil)
    84  			if err != nil {
    85  				return errors.Wrap(err, "failed to connect to hosting shim")
    86  			}
    87  			cl := ttrpc.NewClient(c, ttrpc.WithOnClose(func() { c.Close() }))
    88  			t := task.NewTaskClient(cl)
    89  			ctx := gocontext.Background()
    90  			req := &task.ConnectRequest{ID: sbid}
    91  			cr, err := t.Connect(ctx, req)
    92  
    93  			cl.Close()
    94  			c.Close()
    95  			if err != nil {
    96  				return errors.Wrap(err, "failed to get shim pid from hosting shim")
    97  			}
    98  			pid = int(cr.ShimPid)
    99  		}
   100  
   101  		// We need to serve a new one.
   102  		if address == "" {
   103  			isSandbox := ct == oci.KubernetesContainerTypeSandbox
   104  			if isSandbox && idFlag != sbid {
   105  				return errors.Errorf(
   106  					"'id' and '%s' must match for '%s=%s'",
   107  					annotations.KubernetesSandboxID,
   108  					annotations.KubernetesContainerType,
   109  					oci.KubernetesContainerTypeSandbox)
   110  			}
   111  
   112  			self, err := os.Executable()
   113  			if err != nil {
   114  				return err
   115  			}
   116  
   117  			r, w, err := os.Pipe()
   118  			if err != nil {
   119  				return err
   120  			}
   121  			defer r.Close()
   122  			defer w.Close()
   123  
   124  			f, err := os.Create(filepath.Join(cwd, "panic.log"))
   125  			if err != nil {
   126  				return err
   127  			}
   128  			defer f.Close()
   129  
   130  			address = fmt.Sprintf(addrFmt, namespaceFlag, idFlag)
   131  			args := []string{
   132  				self,
   133  				"--namespace", namespaceFlag,
   134  				"--address", addressFlag,
   135  				"--publish-binary", containerdBinaryFlag,
   136  				"--id", idFlag,
   137  				"serve",
   138  				"--socket", address,
   139  			}
   140  			if isSandbox {
   141  				args = append(args, "--is-sandbox")
   142  			}
   143  			cmd := &exec.Cmd{
   144  				Path:   self,
   145  				Args:   args,
   146  				Env:    os.Environ(),
   147  				Dir:    cwd,
   148  				Stdin:  os.Stdin,
   149  				Stdout: w,
   150  				Stderr: f,
   151  			}
   152  
   153  			if err := cmd.Start(); err != nil {
   154  				return err
   155  			}
   156  			w.Close()
   157  			defer func() {
   158  				if err != nil {
   159  					_ = cmd.Process.Kill()
   160  				}
   161  			}()
   162  
   163  			// Forward the invocation stderr until the serve command closes it.
   164  			_, err = io.Copy(os.Stderr, r)
   165  			if err != nil {
   166  				return err
   167  			}
   168  			pid = cmd.Process.Pid
   169  		}
   170  
   171  		if err := shim.WritePidFile(filepath.Join(cwd, "shim.pid"), pid); err != nil {
   172  			return err
   173  		}
   174  		if err := shim.WriteAddress(filepath.Join(cwd, "address"), address); err != nil {
   175  			return err
   176  		}
   177  
   178  		// Write the address new or existing to stdout
   179  		if _, err := fmt.Fprint(os.Stdout, address); err != nil {
   180  			return err
   181  		}
   182  		return nil
   183  	},
   184  }
   185  
   186  func getSpecAnnotations(bundlePath string) (map[string]string, error) {
   187  	// specAnnotations is a minimal representation for oci.Spec that we need
   188  	// to serve a shim.
   189  	type specAnnotations struct {
   190  		// Annotations contains arbitrary metadata for the container.
   191  		Annotations map[string]string `json:"annotations,omitempty"`
   192  	}
   193  	f, err := os.OpenFile(filepath.Join(bundlePath, "config.json"), os.O_RDONLY, 0)
   194  	if err != nil {
   195  		return nil, err
   196  	}
   197  	defer f.Close()
   198  	var spec specAnnotations
   199  	if err := json.NewDecoder(f).Decode(&spec); err != nil {
   200  		return nil, errors.Wrap(err, "failed to deserialize valid OCI spec")
   201  	}
   202  	return spec.Annotations, nil
   203  }
   204  

View as plain text