From 5a47c5ec1d25a1192ed9e14f6807d030fb01b6b8 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Wed, 1 Aug 2018 15:49:39 -0400 Subject: [PATCH] Add lib support as an option Some images like `criu` will have extra libs that it requires. This adds lib support via LD_LIBRARY_PATH and InstallOpts Signed-off-by: Michael Crosby --- archive/tar.go | 6 ++- archive/tar_opts.go | 6 +-- cmd/ctr/commands/install/install.go | 20 +++++++++- install.go | 23 +++++++++-- install_opts.go | 38 +++++++++++++++++++ services/opt/service.go | 10 +++++ vendor.conf | 2 +- .../containerd/go-runc/command_linux.go | 2 + .../containerd/go-runc/command_other.go | 5 ++- 9 files changed, 101 insertions(+), 11 deletions(-) create mode 100644 install_opts.go diff --git a/archive/tar.go b/archive/tar.go index 9eeea7816..ac04e3e02 100644 --- a/archive/tar.go +++ b/archive/tar.go @@ -158,7 +158,11 @@ func applyNaive(ctx context.Context, root string, tr *tar.Reader, options ApplyO // Normalize name, for safety and for a simple is-root check hdr.Name = filepath.Clean(hdr.Name) - if !options.Filter(hdr) { + accept, err := options.Filter(hdr) + if err != nil { + return 0, err + } + if !accept { continue } diff --git a/archive/tar_opts.go b/archive/tar_opts.go index a0ee39a03..a08bc102a 100644 --- a/archive/tar_opts.go +++ b/archive/tar_opts.go @@ -22,11 +22,11 @@ import "archive/tar" type ApplyOpt func(options *ApplyOptions) error // Filter specific files from the archive -type Filter func(*tar.Header) bool +type Filter func(*tar.Header) (bool, error) // all allows all files -func all(_ *tar.Header) bool { - return true +func all(_ *tar.Header) (bool, error) { + return true, nil } // WithFilter uses the filter to select which files are to be extracted. diff --git a/cmd/ctr/commands/install/install.go b/cmd/ctr/commands/install/install.go index ad0cb5862..4113418fb 100644 --- a/cmd/ctr/commands/install/install.go +++ b/cmd/ctr/commands/install/install.go @@ -17,6 +17,7 @@ package install import ( + "github.com/containerd/containerd" "github.com/containerd/containerd/cmd/ctr/commands" "github.com/urfave/cli" ) @@ -27,6 +28,16 @@ var Command = cli.Command{ Usage: "install a new package", ArgsUsage: "", Description: "install a new package", + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "libs,l", + Usage: "install libs from the image", + }, + cli.BoolFlag{ + Name: "replace,r", + Usage: "replace any binaries or libs in the opt directory", + }, + }, Action: func(context *cli.Context) error { client, ctx, cancel, err := commands.NewClient(context) if err != nil { @@ -38,6 +49,13 @@ var Command = cli.Command{ if err != nil { return err } - return client.Install(ctx, image) + var opts []containerd.InstallOpts + if context.Bool("libs") { + opts = append(opts, containerd.WithInstallLibs) + } + if context.Bool("replace") { + opts = append(opts, containerd.WithInstallReplace) + } + return client.Install(ctx, image, opts...) }, } diff --git a/install.go b/install.go index 7fa967aca..2aa8b0394 100644 --- a/install.go +++ b/install.go @@ -19,7 +19,7 @@ package containerd import ( "archive/tar" "context" - "errors" + "os" "path/filepath" introspectionapi "github.com/containerd/containerd/api/services/introspection/v1" @@ -28,10 +28,11 @@ import ( "github.com/containerd/containerd/content" "github.com/containerd/containerd/images" "github.com/containerd/containerd/platforms" + "github.com/pkg/errors" ) // Install a binary image into the opt service -func (c *Client) Install(ctx context.Context, image Image) error { +func (c *Client) Install(ctx context.Context, image Image, opts ...InstallOpts) error { resp, err := c.IntrospectionService().Plugins(ctx, &introspectionapi.PluginsRequest{ Filters: []string{ "id==opt", @@ -47,6 +48,10 @@ func (c *Client) Install(ctx context.Context, image Image) error { if path == "" { return errors.New("opt path not exported") } + var config InstallConfig + for _, o := range opts { + o(&config) + } var ( cs = image.ContentStore() platform = platforms.Default() @@ -66,8 +71,18 @@ func (c *Client) Install(ctx context.Context, image Image) error { return err } defer r.Close() - if _, err := archive.Apply(ctx, path, r, archive.WithFilter(func(hdr *tar.Header) bool { - return filepath.Dir(hdr.Name) == "bin" + if _, err := archive.Apply(ctx, path, r, archive.WithFilter(func(hdr *tar.Header) (bool, error) { + d := filepath.Dir(hdr.Name) + result := d == "bin" + if config.Libs { + result = result || d == "lib" + } + if result && !config.Replace { + if _, err := os.Lstat(filepath.Join(path, hdr.Name)); err == nil { + return false, errors.Errorf("cannot replace %s in %s", hdr.Name, path) + } + } + return result, nil })); err != nil { return err } diff --git a/install_opts.go b/install_opts.go new file mode 100644 index 000000000..b11e7f3d6 --- /dev/null +++ b/install_opts.go @@ -0,0 +1,38 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package containerd + +// InstallOpts configures binary installs +type InstallOpts func(*InstallConfig) + +// InstallConfig sets the binary install configuration +type InstallConfig struct { + // Libs installs libs from the image + Libs bool + // Replace will overwrite existing binaries or libs in the opt directory + Replace bool +} + +// WithInstallLibs installs libs from the image +func WithInstallLibs(c *InstallConfig) { + c.Libs = true +} + +// WithInstallReplace will replace existing files +func WithInstallReplace(c *InstallConfig) { + c.Replace = true +} diff --git a/services/opt/service.go b/services/opt/service.go index ce07cc94e..756a05f0a 100644 --- a/services/opt/service.go +++ b/services/opt/service.go @@ -20,6 +20,7 @@ import ( "fmt" "os" "path/filepath" + "runtime" "github.com/containerd/containerd/plugin" "github.com/pkg/errors" @@ -49,6 +50,15 @@ func init() { if err := os.Setenv("PATH", fmt.Sprintf("%s:%s", bin, os.Getenv("PATH"))); err != nil { return nil, errors.Wrapf(err, "set binary image directory in path %s", bin) } + if runtime.GOOS != "windows" { + lib := filepath.Join(path, "lib") + if err := os.MkdirAll(lib, 0711); err != nil { + return nil, err + } + if err := os.Setenv("LD_LIBRARY_PATH", fmt.Sprintf("%s:%s", os.Getenv("LD_LIBRARY_PATH"), lib)); err != nil { + return nil, errors.Wrapf(err, "set binary lib directory in path %s", lib) + } + } return &manager{}, nil }, }) diff --git a/vendor.conf b/vendor.conf index 65b5f6973..99f3b7b16 100644 --- a/vendor.conf +++ b/vendor.conf @@ -1,4 +1,4 @@ -github.com/containerd/go-runc 14606eb66abd9e834e3bd22a4f5f46a3aad54c54 +github.com/containerd/go-runc edcf3de1f4971445c42d61f20d506b30612aa031 github.com/containerd/console 4d8a41f4ce5b9bae77c41786ea2458330f43f081 github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2 github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40 diff --git a/vendor/github.com/containerd/go-runc/command_linux.go b/vendor/github.com/containerd/go-runc/command_linux.go index d97400d06..71b52f9de 100644 --- a/vendor/github.com/containerd/go-runc/command_linux.go +++ b/vendor/github.com/containerd/go-runc/command_linux.go @@ -18,6 +18,7 @@ package runc import ( "context" + "os" "os/exec" "syscall" ) @@ -31,6 +32,7 @@ func (r *Runc) command(context context.Context, args ...string) *exec.Cmd { cmd.SysProcAttr = &syscall.SysProcAttr{ Setpgid: r.Setpgid, } + cmd.Env = os.Environ() if r.PdeathSignal != 0 { cmd.SysProcAttr.Pdeathsig = r.PdeathSignal } diff --git a/vendor/github.com/containerd/go-runc/command_other.go b/vendor/github.com/containerd/go-runc/command_other.go index 5209a14fb..b8fd4b866 100644 --- a/vendor/github.com/containerd/go-runc/command_other.go +++ b/vendor/github.com/containerd/go-runc/command_other.go @@ -20,6 +20,7 @@ package runc import ( "context" + "os" "os/exec" ) @@ -28,5 +29,7 @@ func (r *Runc) command(context context.Context, args ...string) *exec.Cmd { if command == "" { command = DefaultCommand } - return exec.CommandContext(context, command, append(r.args(), args...)...) + cmd := exec.CommandContext(context, command, append(r.args(), args...)...) + cmd.Env = os.Environ() + return cmd }