...

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

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

     1  //go:build windows
     2  
     3  package main
     4  
     5  import (
     6  	"context"
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  	"time"
    11  
    12  	"github.com/Microsoft/hcsshim/internal/hcs"
    13  	"github.com/Microsoft/hcsshim/internal/memory"
    14  	"github.com/Microsoft/hcsshim/internal/oc"
    15  	"github.com/Microsoft/hcsshim/internal/winapi"
    16  	"github.com/containerd/containerd/runtime/v2/task"
    17  	"github.com/gogo/protobuf/proto"
    18  	"github.com/pkg/errors"
    19  	"github.com/sirupsen/logrus"
    20  	"github.com/urfave/cli"
    21  )
    22  
    23  // LimitedRead reads at max `readLimitBytes` bytes from the file at path `filePath`. If the file has
    24  // more than `readLimitBytes` bytes of data then first `readLimitBytes` will be returned.
    25  func limitedRead(filePath string, readLimitBytes int64) ([]byte, error) {
    26  	f, err := os.Open(filePath)
    27  	if err != nil {
    28  		return nil, errors.Wrapf(err, "limited read failed to open file: %s", filePath)
    29  	}
    30  	defer f.Close()
    31  	if fi, err := f.Stat(); err == nil {
    32  		if fi.Size() < readLimitBytes {
    33  			readLimitBytes = fi.Size()
    34  		}
    35  		buf := make([]byte, readLimitBytes)
    36  		_, err := f.Read(buf)
    37  		if err != nil {
    38  			return []byte{}, errors.Wrapf(err, "limited read failed during file read: %s", filePath)
    39  		}
    40  		return buf, nil
    41  	}
    42  	return []byte{}, errors.Wrapf(err, "limited read failed during file stat: %s", filePath)
    43  }
    44  
    45  var deleteCommand = cli.Command{
    46  	Name: "delete",
    47  	Usage: `
    48  This command allows containerd to delete any container resources created, mounted, and/or run by a shim when containerd can no longer communicate over rpc. This happens if a shim is SIGKILL'd with a running container. These resources will need to be cleaned up when containerd loses the connection to a shim. This is also used when containerd boots and reconnects to shims. If a bundle is still on disk but containerd cannot connect to a shim, the delete command is invoked.
    49  
    50  The delete command will be executed in the container's bundle as its cwd.
    51  `,
    52  	SkipArgReorder: true,
    53  	Action: func(cCtx *cli.Context) (err error) {
    54  		// We cant write anything to stdout for this cmd other than the
    55  		// task.DeleteResponse by protocol. We can write to stderr which will be
    56  		// logged as a warning in containerd.
    57  
    58  		ctx, span := oc.StartSpan(context.Background(), "delete")
    59  		defer span.End()
    60  		defer func() { oc.SetSpanStatus(span, err) }()
    61  
    62  		bundleFlag := cCtx.GlobalString("bundle")
    63  		if bundleFlag == "" {
    64  			return errors.New("bundle is required")
    65  		}
    66  
    67  		// hcsshim shim writes panic logs in the bundle directory in a file named "panic.log"
    68  		// log those messages (if any) on stderr so that it shows up in containerd's log.
    69  		// This should be done as the first thing so that we don't miss any panic logs even if
    70  		// something goes wrong during delete op.
    71  		// The file can be very large so read only first 1MB of data.
    72  		readLimit := int64(memory.MiB) // 1MB
    73  		logBytes, err := limitedRead(filepath.Join(bundleFlag, "panic.log"), readLimit)
    74  		if err == nil && len(logBytes) > 0 {
    75  			if int64(len(logBytes)) == readLimit {
    76  				logrus.Warnf("shim panic log file %s is larger than 1MB, logging only first 1MB", filepath.Join(bundleFlag, "panic.log"))
    77  			}
    78  			logrus.WithField("log", string(logBytes)).Warn("found shim panic logs during delete")
    79  		} else if err != nil && !errors.Is(err, os.ErrNotExist) {
    80  			logrus.WithError(err).Warn("failed to open shim panic log")
    81  		}
    82  
    83  		// Attempt to find the hcssystem for this bundle and terminate it.
    84  		if sys, _ := hcs.OpenComputeSystem(ctx, idFlag); sys != nil {
    85  			defer sys.Close()
    86  			if err := sys.Terminate(ctx); err != nil {
    87  				fmt.Fprintf(os.Stderr, "failed to terminate '%s': %v", idFlag, err)
    88  			} else {
    89  				ch := make(chan error, 1)
    90  				go func() { ch <- sys.Wait() }()
    91  				t := time.NewTimer(time.Second * 30)
    92  				select {
    93  				case <-t.C:
    94  					sys.Close()
    95  					return fmt.Errorf("timed out waiting for '%s' to terminate", idFlag)
    96  				case err := <-ch:
    97  					t.Stop()
    98  					if err != nil {
    99  						fmt.Fprintf(os.Stderr, "failed to wait for '%s' to terminate: %v", idFlag, err)
   100  					}
   101  				}
   102  			}
   103  		}
   104  
   105  		// For Host Process containers if a group name is passed as the user for the container the shim will create a
   106  		// temporary user for the container to run as and add it to the specified group. On container exit the account will
   107  		// be deleted, but if the shim crashed unexpectedly (panic, terminated etc.) then the account may still be around.
   108  		// The username will be the container ID so try and delete it here. The username character limit is 20, so we need to
   109  		// slice down the container ID a bit.
   110  		username := idFlag
   111  		if len(username) > winapi.UserNameCharLimit {
   112  			username = username[:winapi.UserNameCharLimit]
   113  		}
   114  
   115  		// Always try and delete the user, if it doesn't exist we'll get a specific error code that we can use to
   116  		// not log any warnings.
   117  		if err := winapi.NetUserDel(
   118  			"",
   119  			username,
   120  		); err != nil && err != winapi.NERR_UserNotFound {
   121  			fmt.Fprintf(os.Stderr, "failed to delete user %q: %v", username, err)
   122  		}
   123  
   124  		if data, err := proto.Marshal(&task.DeleteResponse{
   125  			ExitedAt:   time.Now(),
   126  			ExitStatus: 255,
   127  		}); err != nil {
   128  			return err
   129  		} else {
   130  			if _, err := os.Stdout.Write(data); err != nil {
   131  				return err
   132  			}
   133  		}
   134  		return nil
   135  	},
   136  }
   137  

View as plain text