github.com/containerd/Containerd@v1.4.13/runtime/v2/binary.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package v2
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"io"
    23  	"os"
    24  	gruntime "runtime"
    25  	"strings"
    26  
    27  	"github.com/containerd/containerd/events/exchange"
    28  	"github.com/containerd/containerd/log"
    29  	"github.com/containerd/containerd/namespaces"
    30  	"github.com/containerd/containerd/runtime"
    31  	client "github.com/containerd/containerd/runtime/v2/shim"
    32  	"github.com/containerd/containerd/runtime/v2/task"
    33  	"github.com/containerd/ttrpc"
    34  	"github.com/gogo/protobuf/types"
    35  	"github.com/pkg/errors"
    36  	"github.com/sirupsen/logrus"
    37  )
    38  
    39  func shimBinary(ctx context.Context, bundle *Bundle, runtime, containerdAddress string, containerdTTRPCAddress string, events *exchange.Exchange, rt *runtime.TaskList) *binary {
    40  	return &binary{
    41  		bundle:                 bundle,
    42  		runtime:                runtime,
    43  		containerdAddress:      containerdAddress,
    44  		containerdTTRPCAddress: containerdTTRPCAddress,
    45  		events:                 events,
    46  		rtTasks:                rt,
    47  	}
    48  }
    49  
    50  type binary struct {
    51  	runtime                string
    52  	containerdAddress      string
    53  	containerdTTRPCAddress string
    54  	bundle                 *Bundle
    55  	events                 *exchange.Exchange
    56  	rtTasks                *runtime.TaskList
    57  }
    58  
    59  func (b *binary) Start(ctx context.Context, opts *types.Any, onClose func()) (_ *shim, err error) {
    60  	args := []string{"-id", b.bundle.ID}
    61  	if logrus.GetLevel() == logrus.DebugLevel {
    62  		args = append(args, "-debug")
    63  	}
    64  	args = append(args, "start")
    65  
    66  	cmd, err := client.Command(
    67  		ctx,
    68  		b.runtime,
    69  		b.containerdAddress,
    70  		b.containerdTTRPCAddress,
    71  		b.bundle.Path,
    72  		opts,
    73  		args...,
    74  	)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	// Windows needs a namespace when openShimLog
    79  	ns, _ := namespaces.Namespace(ctx)
    80  	shimCtx, cancelShimLog := context.WithCancel(namespaces.WithNamespace(context.Background(), ns))
    81  	defer func() {
    82  		if err != nil {
    83  			cancelShimLog()
    84  		}
    85  	}()
    86  	f, err := openShimLog(shimCtx, b.bundle, client.AnonDialer)
    87  	if err != nil {
    88  		return nil, errors.Wrap(err, "open shim log pipe")
    89  	}
    90  	defer func() {
    91  		if err != nil {
    92  			f.Close()
    93  		}
    94  	}()
    95  	// open the log pipe and block until the writer is ready
    96  	// this helps with synchronization of the shim
    97  	// copy the shim's logs to containerd's output
    98  	go func() {
    99  		defer f.Close()
   100  		_, err := io.Copy(os.Stderr, f)
   101  		err = checkCopyShimLogError(ctx, err)
   102  		if err != nil {
   103  			log.G(ctx).WithError(err).Error("copy shim log")
   104  		}
   105  	}()
   106  	out, err := cmd.CombinedOutput()
   107  	if err != nil {
   108  		return nil, errors.Wrapf(err, "%s", out)
   109  	}
   110  	address := strings.TrimSpace(string(out))
   111  	conn, err := client.Connect(address, client.AnonDialer)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	onCloseWithShimLog := func() {
   116  		onClose()
   117  		cancelShimLog()
   118  		f.Close()
   119  	}
   120  	client := ttrpc.NewClient(conn, ttrpc.WithOnClose(onCloseWithShimLog))
   121  	return &shim{
   122  		bundle:  b.bundle,
   123  		client:  client,
   124  		task:    task.NewTaskClient(client),
   125  		events:  b.events,
   126  		rtTasks: b.rtTasks,
   127  	}, nil
   128  }
   129  
   130  func (b *binary) Delete(ctx context.Context) (*runtime.Exit, error) {
   131  	log.G(ctx).Info("cleaning up dead shim")
   132  
   133  	// Windows cannot delete the current working directory while an
   134  	// executable is in use with it. For the cleanup case we invoke with the
   135  	// default work dir and forward the bundle path on the cmdline.
   136  	var bundlePath string
   137  	if gruntime.GOOS != "windows" {
   138  		bundlePath = b.bundle.Path
   139  	}
   140  
   141  	cmd, err := client.Command(ctx,
   142  		b.runtime,
   143  		b.containerdAddress,
   144  		b.containerdTTRPCAddress,
   145  		bundlePath,
   146  		nil,
   147  		"-id", b.bundle.ID,
   148  		"-bundle", b.bundle.Path,
   149  		"delete")
   150  	if err != nil {
   151  		return nil, err
   152  	}
   153  	var (
   154  		out  = bytes.NewBuffer(nil)
   155  		errb = bytes.NewBuffer(nil)
   156  	)
   157  	cmd.Stdout = out
   158  	cmd.Stderr = errb
   159  	if err := cmd.Run(); err != nil {
   160  		return nil, errors.Wrapf(err, "%s", errb.String())
   161  	}
   162  	s := errb.String()
   163  	if s != "" {
   164  		log.G(ctx).Warnf("cleanup warnings %s", s)
   165  	}
   166  	var response task.DeleteResponse
   167  	if err := response.Unmarshal(out.Bytes()); err != nil {
   168  		return nil, err
   169  	}
   170  	if err := b.bundle.Delete(); err != nil {
   171  		return nil, err
   172  	}
   173  	return &runtime.Exit{
   174  		Status:    response.ExitStatus,
   175  		Timestamp: response.ExitedAt,
   176  		Pid:       response.Pid,
   177  	}, nil
   178  }