Skip to content

Commit 2783a19

Browse files
authored
Merge pull request #2518 from crosbymichael/install
Add install support for binary images
2 parents 74b0364 + 5a47c5e commit 2783a19

15 files changed

Lines changed: 345 additions & 2 deletions

File tree

archive/tar.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ func Apply(ctx context.Context, root string, r io.Reader, opts ...ApplyOpt) (int
114114
return 0, errors.Wrap(err, "failed to apply option")
115115
}
116116
}
117+
if options.Filter == nil {
118+
options.Filter = all
119+
}
117120

118121
return apply(ctx, root, tar.NewReader(r), options)
119122
}
@@ -155,6 +158,14 @@ func applyNaive(ctx context.Context, root string, tr *tar.Reader, options ApplyO
155158
// Normalize name, for safety and for a simple is-root check
156159
hdr.Name = filepath.Clean(hdr.Name)
157160

161+
accept, err := options.Filter(hdr)
162+
if err != nil {
163+
return 0, err
164+
}
165+
if !accept {
166+
continue
167+
}
168+
158169
if skipFile(hdr) {
159170
log.G(ctx).Warnf("file %q ignored: archive may not be supported on system", hdr.Name)
160171
continue

archive/tar_opts.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,23 @@
1616

1717
package archive
1818

19+
import "archive/tar"
20+
1921
// ApplyOpt allows setting mutable archive apply properties on creation
2022
type ApplyOpt func(options *ApplyOptions) error
23+
24+
// Filter specific files from the archive
25+
type Filter func(*tar.Header) (bool, error)
26+
27+
// all allows all files
28+
func all(_ *tar.Header) (bool, error) {
29+
return true, nil
30+
}
31+
32+
// WithFilter uses the filter to select which files are to be extracted.
33+
func WithFilter(f Filter) ApplyOpt {
34+
return func(options *ApplyOptions) error {
35+
options.Filter = f
36+
return nil
37+
}
38+
}

archive/tar_opts_unix.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ package archive
2020

2121
// ApplyOptions provides additional options for an Apply operation
2222
type ApplyOptions struct {
23+
Filter Filter // Filter tar headers
2324
}

archive/tar_opts_windows.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ package archive
2222
type ApplyOptions struct {
2323
ParentLayerPaths []string // Parent layer paths used for Windows layer apply
2424
IsWindowsContainerLayer bool // True if the tar stream to be applied is a Windows Container Layer
25+
Filter Filter // Filter tar headers
2526
}
2627

2728
// WithParentLayers adds parent layers to the apply process this is required

cmd/containerd/builtins.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
_ "github.com/containerd/containerd/services/introspection"
3131
_ "github.com/containerd/containerd/services/leases"
3232
_ "github.com/containerd/containerd/services/namespaces"
33+
_ "github.com/containerd/containerd/services/opt"
3334
_ "github.com/containerd/containerd/services/snapshots"
3435
_ "github.com/containerd/containerd/services/tasks"
3536
_ "github.com/containerd/containerd/services/version"

cmd/ctr/app/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/containerd/containerd/cmd/ctr/commands/content"
2525
"github.com/containerd/containerd/cmd/ctr/commands/events"
2626
"github.com/containerd/containerd/cmd/ctr/commands/images"
27+
"github.com/containerd/containerd/cmd/ctr/commands/install"
2728
"github.com/containerd/containerd/cmd/ctr/commands/leases"
2829
namespacesCmd "github.com/containerd/containerd/cmd/ctr/commands/namespaces"
2930
"github.com/containerd/containerd/cmd/ctr/commands/plugins"
@@ -103,6 +104,7 @@ containerd CLI
103104
run.Command,
104105
snapshots.Command,
105106
tasks.Command,
107+
install.Command,
106108
}, extraCmds...)
107109
app.Before = func(context *cli.Context) error {
108110
if context.GlobalBool("debug") {
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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 install
18+
19+
import (
20+
"github.com/containerd/containerd"
21+
"github.com/containerd/containerd/cmd/ctr/commands"
22+
"github.com/urfave/cli"
23+
)
24+
25+
// Command to install binary packages
26+
var Command = cli.Command{
27+
Name: "install",
28+
Usage: "install a new package",
29+
ArgsUsage: "<ref>",
30+
Description: "install a new package",
31+
Flags: []cli.Flag{
32+
cli.BoolFlag{
33+
Name: "libs,l",
34+
Usage: "install libs from the image",
35+
},
36+
cli.BoolFlag{
37+
Name: "replace,r",
38+
Usage: "replace any binaries or libs in the opt directory",
39+
},
40+
},
41+
Action: func(context *cli.Context) error {
42+
client, ctx, cancel, err := commands.NewClient(context)
43+
if err != nil {
44+
return err
45+
}
46+
defer cancel()
47+
ref := context.Args().First()
48+
image, err := client.GetImage(ctx, ref)
49+
if err != nil {
50+
return err
51+
}
52+
var opts []containerd.InstallOpts
53+
if context.Bool("libs") {
54+
opts = append(opts, containerd.WithInstallLibs)
55+
}
56+
if context.Bool("replace") {
57+
opts = append(opts, containerd.WithInstallReplace)
58+
}
59+
return client.Install(ctx, image, opts...)
60+
},
61+
}

install.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
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+
25+
introspectionapi "github.com/containerd/containerd/api/services/introspection/v1"
26+
"github.com/containerd/containerd/archive"
27+
"github.com/containerd/containerd/archive/compression"
28+
"github.com/containerd/containerd/content"
29+
"github.com/containerd/containerd/images"
30+
"github.com/containerd/containerd/platforms"
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+
resp, err := c.IntrospectionService().Plugins(ctx, &introspectionapi.PluginsRequest{
37+
Filters: []string{
38+
"id==opt",
39+
},
40+
})
41+
if err != nil {
42+
return err
43+
}
44+
if len(resp.Plugins) != 1 {
45+
return errors.New("opt service not enabled")
46+
}
47+
path := resp.Plugins[0].Exports["path"]
48+
if path == "" {
49+
return errors.New("opt path not exported")
50+
}
51+
var config InstallConfig
52+
for _, o := range opts {
53+
o(&config)
54+
}
55+
var (
56+
cs = image.ContentStore()
57+
platform = platforms.Default()
58+
)
59+
manifest, err := images.Manifest(ctx, cs, image.Target(), platform)
60+
if err != nil {
61+
return err
62+
}
63+
for _, layer := range manifest.Layers {
64+
ra, err := cs.ReaderAt(ctx, layer)
65+
if err != nil {
66+
return err
67+
}
68+
cr := content.NewReader(ra)
69+
r, err := compression.DecompressStream(cr)
70+
if err != nil {
71+
return err
72+
}
73+
defer r.Close()
74+
if _, err := archive.Apply(ctx, path, r, archive.WithFilter(func(hdr *tar.Header) (bool, error) {
75+
d := filepath.Dir(hdr.Name)
76+
result := d == "bin"
77+
if config.Libs {
78+
result = result || d == "lib"
79+
}
80+
if result && !config.Replace {
81+
if _, err := os.Lstat(filepath.Join(path, hdr.Name)); err == nil {
82+
return false, errors.Errorf("cannot replace %s in %s", hdr.Name, path)
83+
}
84+
}
85+
return result, nil
86+
})); err != nil {
87+
return err
88+
}
89+
}
90+
return nil
91+
}

install_opts.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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+
// InstallOpts configures binary installs
20+
type InstallOpts func(*InstallConfig)
21+
22+
// InstallConfig sets the binary install configuration
23+
type InstallConfig struct {
24+
// Libs installs libs from the image
25+
Libs bool
26+
// Replace will overwrite existing binaries or libs in the opt directory
27+
Replace bool
28+
}
29+
30+
// WithInstallLibs installs libs from the image
31+
func WithInstallLibs(c *InstallConfig) {
32+
c.Libs = true
33+
}
34+
35+
// WithInstallReplace will replace existing files
36+
func WithInstallReplace(c *InstallConfig) {
37+
c.Replace = true
38+
}

services/opt/path_unix.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// +build !windows
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 opt
20+
21+
const defaultPath = "/opt/containerd"

0 commit comments

Comments
 (0)