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