github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/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  	}
   119  	client := ttrpc.NewClient(conn, ttrpc.WithOnClose(onCloseWithShimLog))
   120  	return &shim{
   121  		bundle:  b.bundle,
   122  		client:  client,
   123  		task:    task.NewTaskClient(client),
   124  		events:  b.events,
   125  		rtTasks: b.rtTasks,
   126  	}, nil
   127  }
   128  
   129  func (b *binary) Delete(ctx context.Context) (*runtime.Exit, error) {
   130  	log.G(ctx).Info("cleaning up dead shim")
   131  
   132  	// Windows cannot delete the current working directory while an
   133  	// executable is in use with it. For the cleanup case we invoke with the
   134  	// default work dir and forward the bundle path on the cmdline.
   135  	var bundlePath string
   136  	if gruntime.GOOS != "windows" {
   137  		bundlePath = b.bundle.Path
   138  	}
   139  
   140  	cmd, err := client.Command(ctx,
   141  		b.runtime,
   142  		b.containerdAddress,
   143  		b.containerdTTRPCAddress,
   144  		bundlePath,
   145  		nil,
   146  		"-id", b.bundle.ID,
   147  		"-bundle", b.bundle.Path,
   148  		"delete")
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  	var (
   153  		out  = bytes.NewBuffer(nil)
   154  		errb = bytes.NewBuffer(nil)
   155  	)
   156  	cmd.Stdout = out
   157  	cmd.Stderr = errb
   158  	if err := cmd.Run(); err != nil {
   159  		return nil, errors.Wrapf(err, "%s", errb.String())
   160  	}
   161  	s := errb.String()
   162  	if s != "" {
   163  		log.G(ctx).Warnf("cleanup warnings %s", s)
   164  	}
   165  	var response task.DeleteResponse
   166  	if err := response.Unmarshal(out.Bytes()); err != nil {
   167  		return nil, err
   168  	}
   169  	if err := b.bundle.Delete(); err != nil {
   170  		return nil, err
   171  	}
   172  	return &runtime.Exit{
   173  		Status:    response.ExitStatus,
   174  		Timestamp: response.ExitedAt,
   175  		Pid:       response.Pid,
   176  	}, nil
   177  }