github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/install.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 containerd
    18  
    19  import (
    20  	"archive/tar"
    21  	"context"
    22  	"os"
    23  	"path/filepath"
    24  	"runtime"
    25  	"strings"
    26  
    27  	"github.com/containerd/containerd/archive"
    28  	"github.com/containerd/containerd/archive/compression"
    29  	"github.com/containerd/containerd/content"
    30  	"github.com/containerd/containerd/images"
    31  	"github.com/pkg/errors"
    32  )
    33  
    34  // Install a binary image into the opt service
    35  func (c *Client) Install(ctx context.Context, image Image, opts ...InstallOpts) error {
    36  	var config InstallConfig
    37  	for _, o := range opts {
    38  		o(&config)
    39  	}
    40  	path, err := c.getInstallPath(ctx, config)
    41  	if err != nil {
    42  		return err
    43  	}
    44  	var (
    45  		cs       = image.ContentStore()
    46  		platform = c.platform
    47  	)
    48  	manifest, err := images.Manifest(ctx, cs, image.Target(), platform)
    49  	if err != nil {
    50  		return err
    51  	}
    52  
    53  	var binDir, libDir string
    54  	if runtime.GOOS == "windows" {
    55  		binDir = "Files\\bin"
    56  		libDir = "Files\\lib"
    57  	} else {
    58  		binDir = "bin"
    59  		libDir = "lib"
    60  	}
    61  	for _, layer := range manifest.Layers {
    62  		ra, err := cs.ReaderAt(ctx, layer)
    63  		if err != nil {
    64  			return err
    65  		}
    66  		cr := content.NewReader(ra)
    67  		r, err := compression.DecompressStream(cr)
    68  		if err != nil {
    69  			return err
    70  		}
    71  		if _, err := archive.Apply(ctx, path, r, archive.WithFilter(func(hdr *tar.Header) (bool, error) {
    72  			d := filepath.Dir(hdr.Name)
    73  			result := d == binDir
    74  
    75  			if config.Libs {
    76  				result = result || d == libDir
    77  			}
    78  
    79  			if runtime.GOOS == "windows" {
    80  				hdr.Name = strings.Replace(hdr.Name, "Files", "", 1)
    81  			}
    82  			if result && !config.Replace {
    83  				if _, err := os.Lstat(filepath.Join(path, hdr.Name)); err == nil {
    84  					return false, errors.Errorf("cannot replace %s in %s", hdr.Name, path)
    85  				}
    86  			}
    87  			return result, nil
    88  		})); err != nil {
    89  			r.Close()
    90  			return err
    91  		}
    92  		r.Close()
    93  	}
    94  	return nil
    95  }
    96  
    97  func (c *Client) getInstallPath(ctx context.Context, config InstallConfig) (string, error) {
    98  	if config.Path != "" {
    99  		return config.Path, nil
   100  	}
   101  	filters := []string{"id==opt"}
   102  	resp, err := c.IntrospectionService().Plugins(ctx, filters)
   103  	if err != nil {
   104  		return "", err
   105  	}
   106  	if len(resp.Plugins) != 1 {
   107  		return "", errors.New("opt service not enabled")
   108  	}
   109  	path := resp.Plugins[0].Exports["path"]
   110  	if path == "" {
   111  		return "", errors.New("opt path not exported")
   112  	}
   113  	return path, nil
   114  }