github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/runtime/v1/linux/bundle.go (about)

     1  // +build linux
     2  
     3  /*
     4     Copyright The containerd Authors.
     5  
     6     Licensed under the Apache License, Version 2.0 (the "License");
     7     you may not use this file except in compliance with the License.
     8     You may obtain a copy of the License at
     9  
    10         http://www.apache.org/licenses/LICENSE-2.0
    11  
    12     Unless required by applicable law or agreed to in writing, software
    13     distributed under the License is distributed on an "AS IS" BASIS,
    14     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15     See the License for the specific language governing permissions and
    16     limitations under the License.
    17  */
    18  
    19  package linux
    20  
    21  import (
    22  	"context"
    23  	"crypto/sha256"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"os"
    27  	"path/filepath"
    28  
    29  	"github.com/containerd/containerd/events/exchange"
    30  	"github.com/containerd/containerd/runtime/linux/runctypes"
    31  	"github.com/containerd/containerd/runtime/v1/shim"
    32  	"github.com/containerd/containerd/runtime/v1/shim/client"
    33  	"github.com/pkg/errors"
    34  )
    35  
    36  // loadBundle loads an existing bundle from disk
    37  func loadBundle(id, path, workdir string) *bundle {
    38  	return &bundle{
    39  		id:      id,
    40  		path:    path,
    41  		workDir: workdir,
    42  	}
    43  }
    44  
    45  // newBundle creates a new bundle on disk at the provided path for the given id
    46  func newBundle(id, path, workDir string, spec []byte) (b *bundle, err error) {
    47  	if err := os.MkdirAll(path, 0711); err != nil {
    48  		return nil, err
    49  	}
    50  	path = filepath.Join(path, id)
    51  	if err := os.Mkdir(path, 0711); err != nil {
    52  		return nil, err
    53  	}
    54  	defer func() {
    55  		if err != nil {
    56  			os.RemoveAll(path)
    57  		}
    58  	}()
    59  	workDir = filepath.Join(workDir, id)
    60  	if err := os.MkdirAll(workDir, 0711); err != nil {
    61  		return nil, err
    62  	}
    63  	defer func() {
    64  		if err != nil {
    65  			os.RemoveAll(workDir)
    66  		}
    67  	}()
    68  	rootfs := filepath.Join(path, "rootfs")
    69  	if err := os.MkdirAll(rootfs, 0711); err != nil {
    70  		return nil, err
    71  	}
    72  	err = ioutil.WriteFile(filepath.Join(path, configFilename), spec, 0666)
    73  	return &bundle{
    74  		id:      id,
    75  		path:    path,
    76  		workDir: workDir,
    77  	}, err
    78  }
    79  
    80  type bundle struct {
    81  	id      string
    82  	path    string
    83  	workDir string
    84  }
    85  
    86  // ShimOpt specifies shim options for initialization and connection
    87  type ShimOpt func(*bundle, string, *runctypes.RuncOptions) (shim.Config, client.Opt)
    88  
    89  // ShimRemote is a ShimOpt for connecting and starting a remote shim
    90  func ShimRemote(c *Config, daemonAddress, cgroup string, exitHandler func()) ShimOpt {
    91  	return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) {
    92  		config := b.shimConfig(ns, c, ropts)
    93  		return config,
    94  			client.WithStart(c.Shim, b.shimAddress(ns), daemonAddress, cgroup, c.ShimDebug, exitHandler)
    95  	}
    96  }
    97  
    98  // ShimLocal is a ShimOpt for using an in process shim implementation
    99  func ShimLocal(c *Config, exchange *exchange.Exchange) ShimOpt {
   100  	return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) {
   101  		return b.shimConfig(ns, c, ropts), client.WithLocal(exchange)
   102  	}
   103  }
   104  
   105  // ShimConnect is a ShimOpt for connecting to an existing remote shim
   106  func ShimConnect(c *Config, onClose func()) ShimOpt {
   107  	return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) {
   108  		return b.shimConfig(ns, c, ropts), client.WithConnect(b.decideShimAddress(ns), onClose)
   109  	}
   110  }
   111  
   112  // NewShimClient connects to the shim managing the bundle and tasks creating it if needed
   113  func (b *bundle) NewShimClient(ctx context.Context, namespace string, getClientOpts ShimOpt, runcOpts *runctypes.RuncOptions) (*client.Client, error) {
   114  	cfg, opt := getClientOpts(b, namespace, runcOpts)
   115  	return client.New(ctx, cfg, opt)
   116  }
   117  
   118  // Delete deletes the bundle from disk
   119  func (b *bundle) Delete() error {
   120  	err := atomicDelete(b.path)
   121  	if err == nil {
   122  		return atomicDelete(b.workDir)
   123  	}
   124  	// error removing the bundle path; still attempt removing work dir
   125  	err2 := atomicDelete(b.workDir)
   126  	if err2 == nil {
   127  		return err
   128  	}
   129  	return errors.Wrapf(err, "Failed to remove both bundle and workdir locations: %v", err2)
   130  }
   131  
   132  func (b *bundle) legacyShimAddress(namespace string) string {
   133  	return filepath.Join(string(filepath.Separator), "containerd-shim", namespace, b.id, "shim.sock")
   134  }
   135  
   136  func (b *bundle) shimAddress(namespace string) string {
   137  	d := sha256.Sum256([]byte(filepath.Join(namespace, b.id)))
   138  	return filepath.Join(string(filepath.Separator), "containerd-shim", fmt.Sprintf("%x.sock", d))
   139  }
   140  
   141  func (b *bundle) loadAddress() (string, error) {
   142  	addressPath := filepath.Join(b.path, "address")
   143  	data, err := ioutil.ReadFile(addressPath)
   144  	if err != nil {
   145  		return "", err
   146  	}
   147  	return string(data), nil
   148  }
   149  
   150  func (b *bundle) decideShimAddress(namespace string) string {
   151  	address, err := b.loadAddress()
   152  	if err != nil {
   153  		return b.legacyShimAddress(namespace)
   154  	}
   155  	return address
   156  }
   157  
   158  func (b *bundle) shimConfig(namespace string, c *Config, runcOptions *runctypes.RuncOptions) shim.Config {
   159  	var (
   160  		criuPath      string
   161  		runtimeRoot   = c.RuntimeRoot
   162  		systemdCgroup bool
   163  	)
   164  	if runcOptions != nil {
   165  		criuPath = runcOptions.CriuPath
   166  		systemdCgroup = runcOptions.SystemdCgroup
   167  		if runcOptions.RuntimeRoot != "" {
   168  			runtimeRoot = runcOptions.RuntimeRoot
   169  		}
   170  	}
   171  	return shim.Config{
   172  		Path:          b.path,
   173  		WorkDir:       b.workDir,
   174  		Namespace:     namespace,
   175  		Criu:          criuPath,
   176  		RuntimeRoot:   runtimeRoot,
   177  		SystemdCgroup: systemdCgroup,
   178  	}
   179  }
   180  
   181  // atomicDelete renames the path to a hidden file before removal
   182  func atomicDelete(path string) error {
   183  	// create a hidden dir for an atomic removal
   184  	atomicPath := filepath.Join(filepath.Dir(path), fmt.Sprintf(".%s", filepath.Base(path)))
   185  	if err := os.Rename(path, atomicPath); err != nil {
   186  		if os.IsNotExist(err) {
   187  			return nil
   188  		}
   189  		return err
   190  	}
   191  	return os.RemoveAll(atomicPath)
   192  }