diff --git a/cli/command/engine/activate.go b/cli/command/engine/activate.go index 1e8b745929..1c37e06ebf 100644 --- a/cli/command/engine/activate.go +++ b/cli/command/engine/activate.go @@ -12,6 +12,7 @@ import ( "github.com/docker/licensing/model" "github.com/pkg/errors" "github.com/spf13/cobra" + "golang.org/x/sys/unix" ) type activateOptions struct { @@ -67,6 +68,9 @@ https://hub.docker.com/ then specify the file with the '--license' flag. } func runActivate(cli command.Cli, options activateOptions) error { + if unix.Geteuid() != 0 { + return errors.New("must be privileged to activate engine") + } ctx := context.Background() client, err := cli.NewContainerizedEngineClient(options.sockPath) if err != nil { diff --git a/cli/command/engine/check.go b/cli/command/engine/check.go index 0ba19cc0e6..86d4243dca 100644 --- a/cli/command/engine/check.go +++ b/cli/command/engine/check.go @@ -1,15 +1,14 @@ package engine import ( - "context" "fmt" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/command/formatter" clitypes "github.com/docker/cli/types" "github.com/pkg/errors" "github.com/spf13/cobra" + "golang.org/x/sys/unix" ) const ( @@ -50,67 +49,63 @@ func newCheckForUpdatesCommand(dockerCli command.Cli) *cobra.Command { } func runCheck(dockerCli command.Cli, options checkOptions) error { - ctx := context.Background() - client, err := dockerCli.NewContainerizedEngineClient(options.sockPath) - if err != nil { - return errors.Wrap(err, "unable to access local containerd") - } - defer client.Close() - currentOpts, err := client.GetCurrentEngineVersion(ctx) - if err != nil { - return err + if unix.Geteuid() != 0 { + return errors.New("must be privileged to activate engine") } - // override with user provided prefix if specified - if options.registryPrefix != "" { - currentOpts.RegistryPrefix = options.registryPrefix - } - imageName := currentOpts.RegistryPrefix + "/" + currentOpts.EngineImage - currentVersion := currentOpts.EngineVersion - versions, err := client.GetEngineVersions(ctx, dockerCli.RegistryClient(false), currentVersion, imageName) - if err != nil { - return err - } + /* + ctx := context.Background() + client, err := dockerCli.NewContainerizedEngineClient(options.sockPath) + if err != nil { + return errors.Wrap(err, "unable to access local containerd") + } + defer client.Close() + versions, err := client.GetEngineVersions(ctx, dockerCli.RegistryClient(false), currentVersion, imageName) + if err != nil { + return err + } - availUpdates := []clitypes.Update{ - {Type: "current", Version: currentVersion}, - } - if len(versions.Patches) > 0 { - availUpdates = append(availUpdates, - processVersions( - currentVersion, - "patch", - options.preReleases, - versions.Patches)...) - } - if options.upgrades { - availUpdates = append(availUpdates, - processVersions( - currentVersion, - "upgrade", - options.preReleases, - versions.Upgrades)...) - } - if options.downgrades { - availUpdates = append(availUpdates, - processVersions( - currentVersion, - "downgrade", - options.preReleases, - versions.Downgrades)...) - } + availUpdates := []clitypes.Update{ + {Type: "current", Version: currentVersion}, + } + if len(versions.Patches) > 0 { + availUpdates = append(availUpdates, + processVersions( + currentVersion, + "patch", + options.preReleases, + versions.Patches)...) + } + if options.upgrades { + availUpdates = append(availUpdates, + processVersions( + currentVersion, + "upgrade", + options.preReleases, + versions.Upgrades)...) + } + if options.downgrades { + availUpdates = append(availUpdates, + processVersions( + currentVersion, + "downgrade", + options.preReleases, + versions.Downgrades)...) + } - format := options.format - if len(format) == 0 { - format = formatter.TableFormatKey - } + format := options.format + if len(format) == 0 { + format = formatter.TableFormatKey + } - updatesCtx := formatter.Context{ - Output: dockerCli.Out(), - Format: formatter.NewUpdatesFormat(format, options.quiet), - Trunc: false, - } - return formatter.UpdatesWrite(updatesCtx, availUpdates) + updatesCtx := formatter.Context{ + Output: dockerCli.Out(), + Format: formatter.NewUpdatesFormat(format, options.quiet), + Trunc: false, + } + return formatter.UpdatesWrite(updatesCtx, availUpdates) + */ + return nil } func processVersions(currentVersion, verType string, diff --git a/cli/command/engine/cmd.go b/cli/command/engine/cmd.go index 7c4ab76b70..66e56631f0 100644 --- a/cli/command/engine/cmd.go +++ b/cli/command/engine/cmd.go @@ -15,11 +15,9 @@ func NewEngineCommand(dockerCli command.Cli) *cobra.Command { RunE: command.ShowHelp(dockerCli.Err()), } cmd.AddCommand( - newInitCommand(dockerCli), newActivateCommand(dockerCli), newCheckForUpdatesCommand(dockerCli), newUpdateCommand(dockerCli), - newRmCommand(dockerCli), ) return cmd } diff --git a/cli/command/engine/rm.go b/cli/command/engine/rm.go deleted file mode 100644 index c27e9d2136..0000000000 --- a/cli/command/engine/rm.go +++ /dev/null @@ -1,49 +0,0 @@ -package engine - -import ( - "context" - - "github.com/docker/cli/cli" - "github.com/docker/cli/cli/command" - "github.com/pkg/errors" - "github.com/spf13/cobra" -) - -// TODO - consider adding a "purge" flag that also removes -// configuration files and the docker root dir. - -type rmOptions struct { - sockPath string -} - -func newRmCommand(dockerCli command.Cli) *cobra.Command { - var options rmOptions - cmd := &cobra.Command{ - Use: "rm [OPTIONS]", - Short: "Remove the local engine", - Long: `This command will remove the local engine running on containerd. - -No state files will be removed from the host filesystem. -`, - Args: cli.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - return runRm(dockerCli, options) - }, - Annotations: map[string]string{"experimentalCLI": ""}, - } - flags := cmd.Flags() - flags.StringVar(&options.sockPath, "containerd", "", "override default location of containerd endpoint") - - return cmd -} - -func runRm(dockerCli command.Cli, options rmOptions) error { - ctx := context.Background() - client, err := dockerCli.NewContainerizedEngineClient(options.sockPath) - if err != nil { - return errors.Wrap(err, "unable to access local containerd") - } - defer client.Close() - - return client.RemoveEngine(ctx) -} diff --git a/cli/command/engine/rm_test.go b/cli/command/engine/rm_test.go deleted file mode 100644 index 25d3d290f4..0000000000 --- a/cli/command/engine/rm_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package engine - -import ( - "fmt" - "testing" - - clitypes "github.com/docker/cli/types" - "gotest.tools/assert" -) - -func TestRmNoContainerd(t *testing.T) { - testCli.SetContainerizedEngineClient( - func(string) (clitypes.ContainerizedClient, error) { - return nil, fmt.Errorf("some error") - }, - ) - cmd := newRmCommand(testCli) - cmd.SilenceUsage = true - cmd.SilenceErrors = true - err := cmd.Execute() - assert.ErrorContains(t, err, "unable to access local containerd") -} - -func TestRmHappy(t *testing.T) { - testCli.SetContainerizedEngineClient( - func(string) (clitypes.ContainerizedClient, error) { - return &fakeContainerizedEngineClient{}, nil - }, - ) - cmd := newRmCommand(testCli) - err := cmd.Execute() - assert.NilError(t, err) -} diff --git a/cli/command/engine/update.go b/cli/command/engine/update.go index ad42f323ef..73f3638629 100644 --- a/cli/command/engine/update.go +++ b/cli/command/engine/update.go @@ -8,6 +8,7 @@ import ( "github.com/docker/cli/cli/command" "github.com/pkg/errors" "github.com/spf13/cobra" + "golang.org/x/sys/unix" ) func newUpdateCommand(dockerCli command.Cli) *cobra.Command { @@ -32,6 +33,9 @@ func newUpdateCommand(dockerCli command.Cli) *cobra.Command { } func runUpdate(dockerCli command.Cli, options extendedEngineInitOptions) error { + if unix.Geteuid() != 0 { + return errors.New("must be privileged to activate engine") + } ctx := context.Background() client, err := dockerCli.NewContainerizedEngineClient(options.sockPath) if err != nil { @@ -39,15 +43,11 @@ func runUpdate(dockerCli command.Cli, options extendedEngineInitOptions) error { } defer client.Close() if options.EngineImage == "" || options.RegistryPrefix == "" { - currentOpts, err := client.GetCurrentEngineVersion(ctx) - if err != nil { - return err - } if options.EngineImage == "" { - options.EngineImage = currentOpts.EngineImage + options.EngineImage = "docker/engine-community" } if options.RegistryPrefix == "" { - options.RegistryPrefix = currentOpts.RegistryPrefix + options.RegistryPrefix = "docker.io" } } authConfig, err := getRegistryAuth(dockerCli, options.RegistryPrefix) diff --git a/internal/containerizedengine/types.go b/internal/containerizedengine/types.go index d86a9c3338..8268e1e4fa 100644 --- a/internal/containerizedengine/types.go +++ b/internal/containerizedengine/types.go @@ -13,10 +13,7 @@ import ( const ( containerdSockPath = "/run/containerd/containerd.sock" engineContainerName = "dockerd" - engineNamespace = "docker" - - // Used to signal the containerd-proxy if it should manage - proxyLabel = "com.docker/containerd-proxy.scope" + engineNamespace = "com.docker" ) var ( @@ -82,4 +79,5 @@ type containerdClient interface { Close() error ContentStore() content.Store ContainerService() containers.Store + Install(context.Context, containerd.Image, ...containerd.InstallOpts) error } diff --git a/internal/containerizedengine/update.go b/internal/containerizedengine/update.go index 0150688372..d4626b96b6 100644 --- a/internal/containerizedengine/update.go +++ b/internal/containerizedengine/update.go @@ -3,73 +3,20 @@ package containerizedengine import ( "context" "fmt" - "path" - "strings" + "github.com/containerd/containerd" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/namespaces" - "github.com/docker/cli/internal/pkg/containerized" clitypes "github.com/docker/cli/types" - "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/pkg/errors" ) -// GetCurrentEngineVersion determines the current type of engine (image) and version -func (c *baseClient) GetCurrentEngineVersion(ctx context.Context) (clitypes.EngineInitOptions, error) { - ctx = namespaces.WithNamespace(ctx, engineNamespace) - ret := clitypes.EngineInitOptions{} - currentEngine := clitypes.CommunityEngineImage - engine, err := c.GetEngine(ctx) - if err != nil { - if err == ErrEngineNotPresent { - return ret, errors.Wrap(err, "failed to find existing engine") - } - return ret, err - } - imageName, err := c.getEngineImage(engine) - if err != nil { - return ret, err - } - distributionRef, err := reference.ParseNormalizedNamed(imageName) - if err != nil { - return ret, errors.Wrapf(err, "failed to parse image name: %s", imageName) - } - - if strings.Contains(distributionRef.Name(), clitypes.EnterpriseEngineImage) { - currentEngine = clitypes.EnterpriseEngineImage - } - taggedRef, ok := distributionRef.(reference.NamedTagged) - if !ok { - return ret, ErrEngineImageMissingTag - } - ret.EngineImage = currentEngine - ret.EngineVersion = taggedRef.Tag() - ret.RegistryPrefix = reference.Domain(taggedRef) + "/" + path.Dir(reference.Path(taggedRef)) - return ret, nil -} - // ActivateEngine will switch the image from the CE to EE image func (c *baseClient) ActivateEngine(ctx context.Context, opts clitypes.EngineInitOptions, out clitypes.OutStream, authConfig *types.AuthConfig, healthfn func(context.Context) error) error { - // set the proxy scope to "ee" for activate flows - opts.Scope = "ee" - ctx = namespaces.WithNamespace(ctx, engineNamespace) - - // If version is unspecified, use the existing engine version - if opts.EngineVersion == "" { - currentOpts, err := c.GetCurrentEngineVersion(ctx) - if err != nil { - return err - } - opts.EngineVersion = currentOpts.EngineVersion - if currentOpts.EngineImage == clitypes.EnterpriseEngineImage { - // This is a "no-op" activation so the only change would be the license - don't update the engine itself - return nil - } - } return c.DoUpdate(ctx, opts, out, authConfig, healthfn) } @@ -101,31 +48,5 @@ func (c *baseClient) DoUpdate(ctx context.Context, opts clitypes.EngineInitOptio return errors.Wrapf(err, "unable to check for image %s", imageName) } } - - // Gather information about the existing engine so we can recreate it - engine, err := c.GetEngine(ctx) - if err != nil { - if err == ErrEngineNotPresent { - return errors.Wrap(err, "unable to find existing engine - please use init") - } - return err - } - - // TODO verify the image has changed and don't update if nothing has changed - - err = containerized.AtomicImageUpdate(ctx, engine, image, func() error { - ctx, cancel := context.WithTimeout(ctx, engineWaitTimeout) - defer cancel() - return c.waitForEngine(ctx, out, healthfn) - }) - if err == nil && opts.Scope != "" { - var labels map[string]string - labels, err = engine.Labels(ctx) - if err != nil { - return err - } - labels[proxyLabel] = opts.Scope - _, err = engine.SetLabels(ctx, labels) - } - return err + return c.cclient.Install(ctx, image, containerd.WithInstallReplace, containerd.WithInstallPath("/usr")) } diff --git a/types/types.go b/types/types.go index 0a376e0579..cec01377dd 100644 --- a/types/types.go +++ b/types/types.go @@ -37,8 +37,6 @@ type ContainerizedClient interface { authConfig *types.AuthConfig, healthfn func(context.Context) error) error GetEngineVersions(ctx context.Context, registryClient registryclient.RegistryClient, currentVersion, imageName string) (AvailableVersions, error) - GetCurrentEngineVersion(ctx context.Context) (EngineInitOptions, error) - RemoveEngine(ctx context.Context) error } // EngineInitOptions contains the configuration settings @@ -48,7 +46,6 @@ type EngineInitOptions struct { EngineImage string EngineVersion string ConfigFile string - Scope string } // AvailableVersions groups the available versions which were discovered diff --git a/vendor.conf b/vendor.conf index 42a795916e..7a84ad4fdd 100755 --- a/vendor.conf +++ b/vendor.conf @@ -3,7 +3,7 @@ github.com/asaskevich/govalidator f9ffefc3facfbe0caee3fea233cbb6e8208f4541 github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109 github.com/beorn7/perks 3a771d992973f24aa725d07868b467d1ddfceafb github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23 -github.com/containerd/containerd v1.2.0-beta.2 +github.com/containerd/containerd bb0f83ab6eec47c3316bb763d5c20a82c7750c31 github.com/containerd/continuity d8fb8589b0e8e85b8c8bbaa8840226d0dfeb7371 github.com/containerd/fifo 3d5202a github.com/containerd/typeurl f694355 diff --git a/vendor/github.com/containerd/containerd/README.md b/vendor/github.com/containerd/containerd/README.md index d068fa3afd..d3e4aa4357 100644 --- a/vendor/github.com/containerd/containerd/README.md +++ b/vendor/github.com/containerd/containerd/README.md @@ -2,6 +2,7 @@ [![GoDoc](https://godoc.org/github.com/containerd/containerd?status.svg)](https://godoc.org/github.com/containerd/containerd) [![Build Status](https://travis-ci.org/containerd/containerd.svg?branch=master)](https://travis-ci.org/containerd/containerd) +[![Windows Build Status](https://ci.appveyor.com/api/projects/status/github/containerd/containerd?branch=master&svg=true)](https://ci.appveyor.com/project/mlaventure/containerd-3g73f?branch=master) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fcontainerd%2Fcontainerd.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fcontainerd%2Fcontainerd?ref=badge_shield) [![Go Report Card](https://goreportcard.com/badge/github.com/containerd/containerd)](https://goreportcard.com/report/github.com/containerd/containerd) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1271/badge)](https://bestpractices.coreinfrastructure.org/projects/1271) diff --git a/vendor/github.com/containerd/containerd/api/services/events/v1/events.pb.go b/vendor/github.com/containerd/containerd/api/services/events/v1/events.pb.go index 0173f394ea..d6a7b38a87 100644 --- a/vendor/github.com/containerd/containerd/api/services/events/v1/events.pb.go +++ b/vendor/github.com/containerd/containerd/api/services/events/v1/events.pb.go @@ -141,7 +141,7 @@ type EventsClient interface { // Forward sends an event that has already been packaged into an envelope // with a timestamp and namespace. // - // This is useful if earlier timestamping is required or when fowarding on + // This is useful if earlier timestamping is required or when forwarding on // behalf of another component, namespace or publisher. Forward(ctx context.Context, in *ForwardRequest, opts ...grpc.CallOption) (*google_protobuf2.Empty, error) // Subscribe to a stream of events, possibly returning only that match any @@ -223,7 +223,7 @@ type EventsServer interface { // Forward sends an event that has already been packaged into an envelope // with a timestamp and namespace. // - // This is useful if earlier timestamping is required or when fowarding on + // This is useful if earlier timestamping is required or when forwarding on // behalf of another component, namespace or publisher. Forward(context.Context, *ForwardRequest) (*google_protobuf2.Empty, error) // Subscribe to a stream of events, possibly returning only that match any diff --git a/vendor/github.com/containerd/containerd/api/services/events/v1/events.proto b/vendor/github.com/containerd/containerd/api/services/events/v1/events.proto index 58f2dadeb5..1959c8e395 100644 --- a/vendor/github.com/containerd/containerd/api/services/events/v1/events.proto +++ b/vendor/github.com/containerd/containerd/api/services/events/v1/events.proto @@ -20,7 +20,7 @@ service Events { // Forward sends an event that has already been packaged into an envelope // with a timestamp and namespace. // - // This is useful if earlier timestamping is required or when fowarding on + // This is useful if earlier timestamping is required or when forwarding on // behalf of another component, namespace or publisher. rpc Forward(ForwardRequest) returns (google.protobuf.Empty); diff --git a/vendor/github.com/containerd/containerd/cio/io.go b/vendor/github.com/containerd/containerd/cio/io.go index 10ad36ba87..a9c6d2b156 100644 --- a/vendor/github.com/containerd/containerd/cio/io.go +++ b/vendor/github.com/containerd/containerd/cio/io.go @@ -141,6 +141,15 @@ func NewCreator(opts ...Opt) Creator { if err != nil { return nil, err } + if streams.Stdin == nil { + fifos.Stdin = "" + } + if streams.Stdout == nil { + fifos.Stdout = "" + } + if streams.Stderr == nil { + fifos.Stderr = "" + } return copyIO(fifos, streams) } } diff --git a/vendor/github.com/containerd/containerd/container_opts_unix.go b/vendor/github.com/containerd/containerd/container_opts_unix.go index a4935b2b45..c0622f67fe 100644 --- a/vendor/github.com/containerd/containerd/container_opts_unix.go +++ b/vendor/github.com/containerd/containerd/container_opts_unix.go @@ -20,25 +20,21 @@ package containerd import ( "context" - "encoding/json" "fmt" "os" "path/filepath" "syscall" - "github.com/containerd/containerd/api/types" "github.com/containerd/containerd/containers" "github.com/containerd/containerd/content" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" "github.com/containerd/containerd/mount" "github.com/containerd/containerd/platforms" - "github.com/containerd/containerd/runtime/linux/runctypes" "github.com/gogo/protobuf/proto" protobuf "github.com/gogo/protobuf/types" "github.com/opencontainers/image-spec/identity" "github.com/opencontainers/image-spec/specs-go/v1" - ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" ) @@ -105,44 +101,6 @@ func WithCheckpoint(im Image, snapshotKey string) NewContainerOpts { } } -// WithTaskCheckpoint allows a task to be created with live runtime and memory data from a -// previous checkpoint. Additional software such as CRIU may be required to -// restore a task from a checkpoint -func WithTaskCheckpoint(im Image) NewTaskOpts { - return func(ctx context.Context, c *Client, info *TaskInfo) error { - desc := im.Target() - id := desc.Digest - index, err := decodeIndex(ctx, c.ContentStore(), desc) - if err != nil { - return err - } - for _, m := range index.Manifests { - if m.MediaType == images.MediaTypeContainerd1Checkpoint { - info.Checkpoint = &types.Descriptor{ - MediaType: m.MediaType, - Size_: m.Size, - Digest: m.Digest, - } - return nil - } - } - return fmt.Errorf("checkpoint not found in index %s", id) - } -} - -func decodeIndex(ctx context.Context, store content.Provider, desc ocispec.Descriptor) (*v1.Index, error) { - var index v1.Index - p, err := content.ReadBlob(ctx, store, desc) - if err != nil { - return nil, err - } - if err := json.Unmarshal(p, &index); err != nil { - return nil, err - } - - return &index, nil -} - // WithRemappedSnapshot creates a new snapshot and remaps the uid/gid for the // filesystem to be used by a container with user namespaces func WithRemappedSnapshot(id string, i Image, uid, gid uint32) NewContainerOpts { @@ -221,19 +179,3 @@ func incrementFS(root string, uidInc, gidInc uint32) filepath.WalkFunc { return os.Lchown(path, u, g) } } - -// WithNoPivotRoot instructs the runtime not to you pivot_root -func WithNoPivotRoot(_ context.Context, _ *Client, info *TaskInfo) error { - if info.Options == nil { - info.Options = &runctypes.CreateOptions{ - NoPivotRoot: true, - } - return nil - } - copts, ok := info.Options.(*runctypes.CreateOptions) - if !ok { - return errors.New("invalid options type, expected runctypes.CreateOptions") - } - copts.NoPivotRoot = true - return nil -} diff --git a/vendor/github.com/containerd/containerd/events/exchange/exchange.go b/vendor/github.com/containerd/containerd/events/exchange/exchange.go index 51c760f045..95d21b7df6 100644 --- a/vendor/github.com/containerd/containerd/events/exchange/exchange.go +++ b/vendor/github.com/containerd/containerd/events/exchange/exchange.go @@ -52,7 +52,7 @@ var _ events.Subscriber = &Exchange{} // Forward accepts an envelope to be direcly distributed on the exchange. // -// This is useful when an event is forwaded on behalf of another namespace or +// This is useful when an event is forwarded on behalf of another namespace or // when the event is propagated on behalf of another publisher. func (e *Exchange) Forward(ctx context.Context, envelope *events.Envelope) (err error) { if err := validateEnvelope(envelope); err != nil { diff --git a/vendor/github.com/containerd/containerd/export.go b/vendor/github.com/containerd/containerd/export.go new file mode 100644 index 0000000000..7aac309ba0 --- /dev/null +++ b/vendor/github.com/containerd/containerd/export.go @@ -0,0 +1,57 @@ +/* + 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 + +import ( + "context" + "io" + + "github.com/containerd/containerd/images" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +type exportOpts struct { +} + +// ExportOpt allows the caller to specify export-specific options +type ExportOpt func(c *exportOpts) error + +func resolveExportOpt(opts ...ExportOpt) (exportOpts, error) { + var eopts exportOpts + for _, o := range opts { + if err := o(&eopts); err != nil { + return eopts, err + } + } + return eopts, nil +} + +// Export exports an image to a Tar stream. +// OCI format is used by default. +// It is up to caller to put "org.opencontainers.image.ref.name" annotation to desc. +// TODO(AkihiroSuda): support exporting multiple descriptors at once to a single archive stream. +func (c *Client) Export(ctx context.Context, exporter images.Exporter, desc ocispec.Descriptor, opts ...ExportOpt) (io.ReadCloser, error) { + _, err := resolveExportOpt(opts...) // unused now + if err != nil { + return nil, err + } + pr, pw := io.Pipe() + go func() { + pw.CloseWithError(exporter.Export(ctx, c.ContentStore(), desc, pw)) + }() + return pr, nil +} diff --git a/vendor/github.com/containerd/containerd/import.go b/vendor/github.com/containerd/containerd/import.go index e4ac00cee3..7a69f1d45a 100644 --- a/vendor/github.com/containerd/containerd/import.go +++ b/vendor/github.com/containerd/containerd/import.go @@ -22,7 +22,6 @@ import ( "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" - ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) type importOpts struct { @@ -84,35 +83,3 @@ func (c *Client) Import(ctx context.Context, importer images.Importer, reader io } return images, nil } - -type exportOpts struct { -} - -// ExportOpt allows the caller to specify export-specific options -type ExportOpt func(c *exportOpts) error - -func resolveExportOpt(opts ...ExportOpt) (exportOpts, error) { - var eopts exportOpts - for _, o := range opts { - if err := o(&eopts); err != nil { - return eopts, err - } - } - return eopts, nil -} - -// Export exports an image to a Tar stream. -// OCI format is used by default. -// It is up to caller to put "org.opencontainers.image.ref.name" annotation to desc. -// TODO(AkihiroSuda): support exporting multiple descriptors at once to a single archive stream. -func (c *Client) Export(ctx context.Context, exporter images.Exporter, desc ocispec.Descriptor, opts ...ExportOpt) (io.ReadCloser, error) { - _, err := resolveExportOpt(opts...) // unused now - if err != nil { - return nil, err - } - pr, pw := io.Pipe() - go func() { - pw.CloseWithError(exporter.Export(ctx, c.ContentStore(), desc, pw)) - }() - return pr, nil -} diff --git a/vendor/github.com/containerd/containerd/install.go b/vendor/github.com/containerd/containerd/install.go index 2aa8b03946..5e4c6a2c8d 100644 --- a/vendor/github.com/containerd/containerd/install.go +++ b/vendor/github.com/containerd/containerd/install.go @@ -33,25 +33,14 @@ import ( // Install a binary image into the opt service func (c *Client) Install(ctx context.Context, image Image, opts ...InstallOpts) error { - resp, err := c.IntrospectionService().Plugins(ctx, &introspectionapi.PluginsRequest{ - Filters: []string{ - "id==opt", - }, - }) - if err != nil { - return err - } - if len(resp.Plugins) != 1 { - return errors.New("opt service not enabled") - } - path := resp.Plugins[0].Exports["path"] - if path == "" { - return errors.New("opt path not exported") - } var config InstallConfig for _, o := range opts { o(&config) } + path, err := c.getInstallPath(ctx, config) + if err != nil { + return err + } var ( cs = image.ContentStore() platform = platforms.Default() @@ -89,3 +78,25 @@ func (c *Client) Install(ctx context.Context, image Image, opts ...InstallOpts) } return nil } + +func (c *Client) getInstallPath(ctx context.Context, config InstallConfig) (string, error) { + if config.Path != "" { + return config.Path, nil + } + resp, err := c.IntrospectionService().Plugins(ctx, &introspectionapi.PluginsRequest{ + Filters: []string{ + "id==opt", + }, + }) + if err != nil { + return "", err + } + if len(resp.Plugins) != 1 { + return "", errors.New("opt service not enabled") + } + path := resp.Plugins[0].Exports["path"] + if path == "" { + return "", errors.New("opt path not exported") + } + return path, nil +} diff --git a/vendor/github.com/containerd/containerd/install_opts.go b/vendor/github.com/containerd/containerd/install_opts.go index b11e7f3d6d..b0c9213cb2 100644 --- a/vendor/github.com/containerd/containerd/install_opts.go +++ b/vendor/github.com/containerd/containerd/install_opts.go @@ -25,6 +25,8 @@ type InstallConfig struct { Libs bool // Replace will overwrite existing binaries or libs in the opt directory Replace bool + // Path to install libs and binaries to + Path string } // WithInstallLibs installs libs from the image @@ -36,3 +38,10 @@ func WithInstallLibs(c *InstallConfig) { func WithInstallReplace(c *InstallConfig) { c.Replace = true } + +// WithInstallPath sets the optional install path +func WithInstallPath(path string) InstallOpts { + return func(c *InstallConfig) { + c.Path = path + } +} diff --git a/vendor/github.com/containerd/containerd/mount/mount_windows.go b/vendor/github.com/containerd/containerd/mount/mount_windows.go index f7c97894b4..5de25c4e0a 100644 --- a/vendor/github.com/containerd/containerd/mount/mount_windows.go +++ b/vendor/github.com/containerd/containerd/mount/mount_windows.go @@ -32,6 +32,10 @@ var ( // Mount to the provided target func (m *Mount) Mount(target string) error { + if m.Type != "windows-layer" { + return errors.Errorf("invalid windows mount type: '%s'", m.Type) + } + home, layerID := filepath.Split(m.Source) parentLayerPaths, err := m.GetParentPaths() diff --git a/vendor/github.com/containerd/containerd/oci/spec.go b/vendor/github.com/containerd/containerd/oci/spec.go index ffd0bffca0..6fb31e454c 100644 --- a/vendor/github.com/containerd/containerd/oci/spec.go +++ b/vendor/github.com/containerd/containerd/oci/spec.go @@ -18,11 +18,27 @@ package oci import ( "context" + "path/filepath" + "runtime" + + "github.com/containerd/containerd/namespaces" + "github.com/containerd/containerd/platforms" "github.com/containerd/containerd/containers" specs "github.com/opencontainers/runtime-spec/specs-go" ) +const ( + rwm = "rwm" + defaultRootfsPath = "rootfs" +) + +var ( + defaultUnixEnv = []string{ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + } +) + // Spec is a type alias to the OCI runtime spec to allow third part SpecOpts // to be created without the "issues" with go vendoring and package imports type Spec = specs.Spec @@ -30,12 +46,36 @@ type Spec = specs.Spec // GenerateSpec will generate a default spec from the provided image // for use as a containerd container func GenerateSpec(ctx context.Context, client Client, c *containers.Container, opts ...SpecOpts) (*Spec, error) { - s, err := createDefaultSpec(ctx, c.ID) - if err != nil { + return GenerateSpecWithPlatform(ctx, client, platforms.DefaultString(), c, opts...) +} + +// GenerateSpecWithPlatform will generate a default spec from the provided image +// for use as a containerd container in the platform requested. +func GenerateSpecWithPlatform(ctx context.Context, client Client, platform string, c *containers.Container, opts ...SpecOpts) (*Spec, error) { + var s Spec + if err := generateDefaultSpecWithPlatform(ctx, platform, c.ID, &s); err != nil { return nil, err } - return s, ApplyOpts(ctx, client, c, s, opts...) + return &s, ApplyOpts(ctx, client, c, &s, opts...) +} + +func generateDefaultSpecWithPlatform(ctx context.Context, platform, id string, s *Spec) error { + plat, err := platforms.Parse(platform) + if err != nil { + return err + } + + if plat.OS == "windows" { + err = populateDefaultWindowsSpec(ctx, s, id) + } else { + err = populateDefaultUnixSpec(ctx, s, id) + if err == nil && runtime.GOOS == "windows" { + // To run LCOW we have a Linux and Windows section. Add an empty one now. + s.Windows = &specs.Windows{} + } + } + return err } // ApplyOpts applys the options to the given spec, injecting data from the @@ -50,7 +90,173 @@ func ApplyOpts(ctx context.Context, client Client, c *containers.Container, s *S return nil } -func createDefaultSpec(ctx context.Context, id string) (*Spec, error) { - var s Spec - return &s, populateDefaultSpec(ctx, &s, id) +func defaultUnixCaps() []string { + return []string{ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE", + } +} + +func defaultUnixNamespaces() []specs.LinuxNamespace { + return []specs.LinuxNamespace{ + { + Type: specs.PIDNamespace, + }, + { + Type: specs.IPCNamespace, + }, + { + Type: specs.UTSNamespace, + }, + { + Type: specs.MountNamespace, + }, + { + Type: specs.NetworkNamespace, + }, + } +} + +func populateDefaultUnixSpec(ctx context.Context, s *Spec, id string) error { + ns, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return err + } + + *s = Spec{ + Version: specs.Version, + Root: &specs.Root{ + Path: defaultRootfsPath, + }, + Process: &specs.Process{ + Env: defaultUnixEnv, + Cwd: "/", + NoNewPrivileges: true, + User: specs.User{ + UID: 0, + GID: 0, + }, + Capabilities: &specs.LinuxCapabilities{ + Bounding: defaultUnixCaps(), + Permitted: defaultUnixCaps(), + Inheritable: defaultUnixCaps(), + Effective: defaultUnixCaps(), + }, + Rlimits: []specs.POSIXRlimit{ + { + Type: "RLIMIT_NOFILE", + Hard: uint64(1024), + Soft: uint64(1024), + }, + }, + }, + Mounts: []specs.Mount{ + { + Destination: "/proc", + Type: "proc", + Source: "proc", + }, + { + Destination: "/dev", + Type: "tmpfs", + Source: "tmpfs", + Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, + }, + { + Destination: "/dev/pts", + Type: "devpts", + Source: "devpts", + Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"}, + }, + { + Destination: "/dev/shm", + Type: "tmpfs", + Source: "shm", + Options: []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"}, + }, + { + Destination: "/dev/mqueue", + Type: "mqueue", + Source: "mqueue", + Options: []string{"nosuid", "noexec", "nodev"}, + }, + { + Destination: "/sys", + Type: "sysfs", + Source: "sysfs", + Options: []string{"nosuid", "noexec", "nodev", "ro"}, + }, + { + Destination: "/run", + Type: "tmpfs", + Source: "tmpfs", + Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, + }, + }, + Linux: &specs.Linux{ + MaskedPaths: []string{ + "/proc/acpi", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/timer_list", + "/proc/timer_stats", + "/proc/sched_debug", + "/sys/firmware", + "/proc/scsi", + }, + ReadonlyPaths: []string{ + "/proc/asound", + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger", + }, + CgroupsPath: filepath.Join("/", ns, id), + Resources: &specs.LinuxResources{ + Devices: []specs.LinuxDeviceCgroup{ + { + Allow: false, + Access: rwm, + }, + }, + }, + Namespaces: defaultUnixNamespaces(), + }, + } + return nil +} + +func populateDefaultWindowsSpec(ctx context.Context, s *Spec, id string) error { + *s = Spec{ + Version: specs.Version, + Root: &specs.Root{}, + Process: &specs.Process{ + Cwd: `C:\`, + ConsoleSize: &specs.Box{ + Width: 80, + Height: 20, + }, + }, + Windows: &specs.Windows{ + IgnoreFlushesDuringBoot: true, + Network: &specs.WindowsNetwork{ + AllowUnqualifiedDNSQuery: true, + }, + }, + } + return nil } diff --git a/vendor/github.com/containerd/containerd/oci/spec_opts.go b/vendor/github.com/containerd/containerd/oci/spec_opts.go index fd2cfb0392..973ac7e152 100644 --- a/vendor/github.com/containerd/containerd/oci/spec_opts.go +++ b/vendor/github.com/containerd/containerd/oci/spec_opts.go @@ -19,12 +19,25 @@ package oci import ( "context" "encoding/json" + "fmt" "io/ioutil" + "os" + "path/filepath" + "strconv" "strings" "github.com/containerd/containerd/containers" + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/images" + "github.com/containerd/containerd/mount" + "github.com/containerd/containerd/namespaces" + "github.com/containerd/containerd/platforms" + "github.com/containerd/continuity/fs" + "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/opencontainers/runc/libcontainer/user" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" + "github.com/syndtr/gocapability/capability" ) // SpecOpts sets spec specific information to a newly generated OCI spec @@ -49,13 +62,45 @@ func setProcess(s *Spec) { } } +// setRoot sets Root to empty if unset +func setRoot(s *Spec) { + if s.Root == nil { + s.Root = &specs.Root{} + } +} + +// setLinux sets Linux to empty if unset +func setLinux(s *Spec) { + if s.Linux == nil { + s.Linux = &specs.Linux{} + } +} + +// setCapabilities sets Linux Capabilities to empty if unset +func setCapabilities(s *Spec) { + setProcess(s) + if s.Process.Capabilities == nil { + s.Process.Capabilities = &specs.LinuxCapabilities{} + } +} + // WithDefaultSpec returns a SpecOpts that will populate the spec with default // values. // // Use as the first option to clear the spec, then apply options afterwards. func WithDefaultSpec() SpecOpts { return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error { - return populateDefaultSpec(ctx, s, c.ID) + return generateDefaultSpecWithPlatform(ctx, platforms.DefaultString(), c.ID, s) + } +} + +// WithDefaultSpecForPlatform returns a SpecOpts that will populate the spec +// with default values for a given platform. +// +// Use as the first option to clear the spec, then apply options afterwards. +func WithDefaultSpecForPlatform(platform string) SpecOpts { + return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error { + return generateDefaultSpecWithPlatform(ctx, platform, c.ID, s) } } @@ -81,32 +126,6 @@ func WithSpecFromFile(filename string) SpecOpts { } } -// WithProcessArgs replaces the args on the generated spec -func WithProcessArgs(args ...string) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setProcess(s) - s.Process.Args = args - return nil - } -} - -// WithProcessCwd replaces the current working directory on the generated spec -func WithProcessCwd(cwd string) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setProcess(s) - s.Process.Cwd = cwd - return nil - } -} - -// WithHostname sets the container's hostname -func WithHostname(name string) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - s.Hostname = name - return nil - } -} - // WithEnv appends environment variables func WithEnv(environmentVariables []string) SpecOpts { return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { @@ -118,14 +137,6 @@ func WithEnv(environmentVariables []string) SpecOpts { } } -// WithMounts appends mounts -func WithMounts(mounts []specs.Mount) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - s.Mounts = append(s.Mounts, mounts...) - return nil - } -} - // replaceOrAppendEnvValues returns the defaults with the overrides either // replaced by env key or appended to the list func replaceOrAppendEnvValues(defaults, overrides []string) []string { @@ -163,3 +174,821 @@ func replaceOrAppendEnvValues(defaults, overrides []string) []string { return defaults } + +// WithProcessArgs replaces the args on the generated spec +func WithProcessArgs(args ...string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setProcess(s) + s.Process.Args = args + return nil + } +} + +// WithProcessCwd replaces the current working directory on the generated spec +func WithProcessCwd(cwd string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setProcess(s) + s.Process.Cwd = cwd + return nil + } +} + +// WithTTY sets the information on the spec as well as the environment variables for +// using a TTY +func WithTTY(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setProcess(s) + s.Process.Terminal = true + if s.Linux != nil { + s.Process.Env = append(s.Process.Env, "TERM=xterm") + } + + return nil +} + +// WithTTYSize sets the information on the spec as well as the environment variables for +// using a TTY +func WithTTYSize(width, height int) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setProcess(s) + if s.Process.ConsoleSize == nil { + s.Process.ConsoleSize = &specs.Box{} + } + s.Process.ConsoleSize.Width = uint(width) + s.Process.ConsoleSize.Height = uint(height) + return nil + } +} + +// WithHostname sets the container's hostname +func WithHostname(name string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + s.Hostname = name + return nil + } +} + +// WithMounts appends mounts +func WithMounts(mounts []specs.Mount) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + s.Mounts = append(s.Mounts, mounts...) + return nil + } +} + +// WithHostNamespace allows a task to run inside the host's linux namespace +func WithHostNamespace(ns specs.LinuxNamespaceType) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setLinux(s) + for i, n := range s.Linux.Namespaces { + if n.Type == ns { + s.Linux.Namespaces = append(s.Linux.Namespaces[:i], s.Linux.Namespaces[i+1:]...) + return nil + } + } + return nil + } +} + +// WithLinuxNamespace uses the passed in namespace for the spec. If a namespace of the same type already exists in the +// spec, the existing namespace is replaced by the one provided. +func WithLinuxNamespace(ns specs.LinuxNamespace) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setLinux(s) + for i, n := range s.Linux.Namespaces { + if n.Type == ns.Type { + before := s.Linux.Namespaces[:i] + after := s.Linux.Namespaces[i+1:] + s.Linux.Namespaces = append(before, ns) + s.Linux.Namespaces = append(s.Linux.Namespaces, after...) + return nil + } + } + s.Linux.Namespaces = append(s.Linux.Namespaces, ns) + return nil + } +} + +// WithImageConfig configures the spec to from the configuration of an Image +func WithImageConfig(image Image) SpecOpts { + return WithImageConfigArgs(image, nil) +} + +// WithImageConfigArgs configures the spec to from the configuration of an Image with additional args that +// replaces the CMD of the image +func WithImageConfigArgs(image Image, args []string) SpecOpts { + return func(ctx context.Context, client Client, c *containers.Container, s *Spec) error { + ic, err := image.Config(ctx) + if err != nil { + return err + } + var ( + ociimage v1.Image + config v1.ImageConfig + ) + switch ic.MediaType { + case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config: + p, err := content.ReadBlob(ctx, image.ContentStore(), ic) + if err != nil { + return err + } + + if err := json.Unmarshal(p, &ociimage); err != nil { + return err + } + config = ociimage.Config + default: + return fmt.Errorf("unknown image config media type %s", ic.MediaType) + } + + setProcess(s) + if s.Linux != nil { + s.Process.Env = append(s.Process.Env, config.Env...) + cmd := config.Cmd + if len(args) > 0 { + cmd = args + } + s.Process.Args = append(config.Entrypoint, cmd...) + + cwd := config.WorkingDir + if cwd == "" { + cwd = "/" + } + s.Process.Cwd = cwd + if config.User != "" { + return WithUser(config.User)(ctx, client, c, s) + } + } else if s.Windows != nil { + s.Process.Env = config.Env + s.Process.Args = append(config.Entrypoint, config.Cmd...) + s.Process.User = specs.User{ + Username: config.User, + } + } else { + return errors.New("spec does not contain Linux or Windows section") + } + return nil + } +} + +// WithRootFSPath specifies unmanaged rootfs path. +func WithRootFSPath(path string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setRoot(s) + s.Root.Path = path + // Entrypoint is not set here (it's up to caller) + return nil + } +} + +// WithRootFSReadonly sets specs.Root.Readonly to true +func WithRootFSReadonly() SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setRoot(s) + s.Root.Readonly = true + return nil + } +} + +// WithNoNewPrivileges sets no_new_privileges on the process for the container +func WithNoNewPrivileges(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setProcess(s) + s.Process.NoNewPrivileges = true + return nil +} + +// WithHostHostsFile bind-mounts the host's /etc/hosts into the container as readonly +func WithHostHostsFile(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + s.Mounts = append(s.Mounts, specs.Mount{ + Destination: "/etc/hosts", + Type: "bind", + Source: "/etc/hosts", + Options: []string{"rbind", "ro"}, + }) + return nil +} + +// WithHostResolvconf bind-mounts the host's /etc/resolv.conf into the container as readonly +func WithHostResolvconf(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + s.Mounts = append(s.Mounts, specs.Mount{ + Destination: "/etc/resolv.conf", + Type: "bind", + Source: "/etc/resolv.conf", + Options: []string{"rbind", "ro"}, + }) + return nil +} + +// WithHostLocaltime bind-mounts the host's /etc/localtime into the container as readonly +func WithHostLocaltime(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + s.Mounts = append(s.Mounts, specs.Mount{ + Destination: "/etc/localtime", + Type: "bind", + Source: "/etc/localtime", + Options: []string{"rbind", "ro"}, + }) + return nil +} + +// WithUserNamespace sets the uid and gid mappings for the task +// this can be called multiple times to add more mappings to the generated spec +func WithUserNamespace(container, host, size uint32) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + var hasUserns bool + setLinux(s) + for _, ns := range s.Linux.Namespaces { + if ns.Type == specs.UserNamespace { + hasUserns = true + break + } + } + if !hasUserns { + s.Linux.Namespaces = append(s.Linux.Namespaces, specs.LinuxNamespace{ + Type: specs.UserNamespace, + }) + } + mapping := specs.LinuxIDMapping{ + ContainerID: container, + HostID: host, + Size: size, + } + s.Linux.UIDMappings = append(s.Linux.UIDMappings, mapping) + s.Linux.GIDMappings = append(s.Linux.GIDMappings, mapping) + return nil + } +} + +// WithCgroup sets the container's cgroup path +func WithCgroup(path string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setLinux(s) + s.Linux.CgroupsPath = path + return nil + } +} + +// WithNamespacedCgroup uses the namespace set on the context to create a +// root directory for containers in the cgroup with the id as the subcgroup +func WithNamespacedCgroup() SpecOpts { + return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error { + namespace, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return err + } + setLinux(s) + s.Linux.CgroupsPath = filepath.Join("/", namespace, c.ID) + return nil + } +} + +// WithUser sets the user to be used within the container. +// It accepts a valid user string in OCI Image Spec v1.0.0: +// user, uid, user:group, uid:gid, uid:group, user:gid +func WithUser(userstr string) SpecOpts { + return func(ctx context.Context, client Client, c *containers.Container, s *Spec) error { + setProcess(s) + parts := strings.Split(userstr, ":") + switch len(parts) { + case 1: + v, err := strconv.Atoi(parts[0]) + if err != nil { + // if we cannot parse as a uint they try to see if it is a username + return WithUsername(userstr)(ctx, client, c, s) + } + return WithUserID(uint32(v))(ctx, client, c, s) + case 2: + var ( + username string + groupname string + ) + var uid, gid uint32 + v, err := strconv.Atoi(parts[0]) + if err != nil { + username = parts[0] + } else { + uid = uint32(v) + } + if v, err = strconv.Atoi(parts[1]); err != nil { + groupname = parts[1] + } else { + gid = uint32(v) + } + if username == "" && groupname == "" { + s.Process.User.UID, s.Process.User.GID = uid, gid + return nil + } + f := func(root string) error { + if username != "" { + uid, _, err = getUIDGIDFromPath(root, func(u user.User) bool { + return u.Name == username + }) + if err != nil { + return err + } + } + if groupname != "" { + gid, err = getGIDFromPath(root, func(g user.Group) bool { + return g.Name == groupname + }) + if err != nil { + return err + } + } + s.Process.User.UID, s.Process.User.GID = uid, gid + return nil + } + if c.Snapshotter == "" && c.SnapshotKey == "" { + if !isRootfsAbs(s.Root.Path) { + return errors.New("rootfs absolute path is required") + } + return f(s.Root.Path) + } + if c.Snapshotter == "" { + return errors.New("no snapshotter set for container") + } + if c.SnapshotKey == "" { + return errors.New("rootfs snapshot not created for container") + } + snapshotter := client.SnapshotService(c.Snapshotter) + mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey) + if err != nil { + return err + } + return mount.WithTempMount(ctx, mounts, f) + default: + return fmt.Errorf("invalid USER value %s", userstr) + } + } +} + +// WithUIDGID allows the UID and GID for the Process to be set +func WithUIDGID(uid, gid uint32) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setProcess(s) + s.Process.User.UID = uid + s.Process.User.GID = gid + return nil + } +} + +// WithUserID sets the correct UID and GID for the container based +// on the image's /etc/passwd contents. If /etc/passwd does not exist, +// or uid is not found in /etc/passwd, it sets the requested uid, +// additionally sets the gid to 0, and does not return an error. +func WithUserID(uid uint32) SpecOpts { + return func(ctx context.Context, client Client, c *containers.Container, s *Spec) (err error) { + setProcess(s) + if c.Snapshotter == "" && c.SnapshotKey == "" { + if !isRootfsAbs(s.Root.Path) { + return errors.Errorf("rootfs absolute path is required") + } + uuid, ugid, err := getUIDGIDFromPath(s.Root.Path, func(u user.User) bool { + return u.Uid == int(uid) + }) + if err != nil { + if os.IsNotExist(err) || err == errNoUsersFound { + s.Process.User.UID, s.Process.User.GID = uid, 0 + return nil + } + return err + } + s.Process.User.UID, s.Process.User.GID = uuid, ugid + return nil + + } + if c.Snapshotter == "" { + return errors.Errorf("no snapshotter set for container") + } + if c.SnapshotKey == "" { + return errors.Errorf("rootfs snapshot not created for container") + } + snapshotter := client.SnapshotService(c.Snapshotter) + mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey) + if err != nil { + return err + } + return mount.WithTempMount(ctx, mounts, func(root string) error { + uuid, ugid, err := getUIDGIDFromPath(root, func(u user.User) bool { + return u.Uid == int(uid) + }) + if err != nil { + if os.IsNotExist(err) || err == errNoUsersFound { + s.Process.User.UID, s.Process.User.GID = uid, 0 + return nil + } + return err + } + s.Process.User.UID, s.Process.User.GID = uuid, ugid + return nil + }) + } +} + +// WithUsername sets the correct UID and GID for the container +// based on the the image's /etc/passwd contents. If /etc/passwd +// does not exist, or the username is not found in /etc/passwd, +// it returns error. +func WithUsername(username string) SpecOpts { + return func(ctx context.Context, client Client, c *containers.Container, s *Spec) (err error) { + setProcess(s) + if s.Linux != nil { + if c.Snapshotter == "" && c.SnapshotKey == "" { + if !isRootfsAbs(s.Root.Path) { + return errors.Errorf("rootfs absolute path is required") + } + uid, gid, err := getUIDGIDFromPath(s.Root.Path, func(u user.User) bool { + return u.Name == username + }) + if err != nil { + return err + } + s.Process.User.UID, s.Process.User.GID = uid, gid + return nil + } + if c.Snapshotter == "" { + return errors.Errorf("no snapshotter set for container") + } + if c.SnapshotKey == "" { + return errors.Errorf("rootfs snapshot not created for container") + } + snapshotter := client.SnapshotService(c.Snapshotter) + mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey) + if err != nil { + return err + } + return mount.WithTempMount(ctx, mounts, func(root string) error { + uid, gid, err := getUIDGIDFromPath(root, func(u user.User) bool { + return u.Name == username + }) + if err != nil { + return err + } + s.Process.User.UID, s.Process.User.GID = uid, gid + return nil + }) + } else if s.Windows != nil { + s.Process.User.Username = username + } else { + return errors.New("spec does not contain Linux or Windows section") + } + return nil + } +} + +// WithAdditionalGIDs sets the OCI spec's additionalGids array to any additional groups listed +// for a particular user in the /etc/groups file of the image's root filesystem +func WithAdditionalGIDs(username string) SpecOpts { + return func(ctx context.Context, client Client, c *containers.Container, s *Spec) (err error) { + setProcess(s) + if c.Snapshotter == "" && c.SnapshotKey == "" { + if !isRootfsAbs(s.Root.Path) { + return errors.Errorf("rootfs absolute path is required") + } + gids, err := getSupplementalGroupsFromPath(s.Root.Path, func(g user.Group) bool { + // we only want supplemental groups + if g.Name == username { + return false + } + for _, entry := range g.List { + if entry == username { + return true + } + } + return false + }) + if err != nil { + return err + } + s.Process.User.AdditionalGids = gids + return nil + } + if c.Snapshotter == "" { + return errors.Errorf("no snapshotter set for container") + } + if c.SnapshotKey == "" { + return errors.Errorf("rootfs snapshot not created for container") + } + snapshotter := client.SnapshotService(c.Snapshotter) + mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey) + if err != nil { + return err + } + return mount.WithTempMount(ctx, mounts, func(root string) error { + gids, err := getSupplementalGroupsFromPath(root, func(g user.Group) bool { + // we only want supplemental groups + if g.Name == username { + return false + } + for _, entry := range g.List { + if entry == username { + return true + } + } + return false + }) + if err != nil { + return err + } + s.Process.User.AdditionalGids = gids + return nil + }) + } +} + +// WithCapabilities sets Linux capabilities on the process +func WithCapabilities(caps []string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setCapabilities(s) + + s.Process.Capabilities.Bounding = caps + s.Process.Capabilities.Effective = caps + s.Process.Capabilities.Permitted = caps + s.Process.Capabilities.Inheritable = caps + + return nil + } +} + +// WithAllCapabilities sets all linux capabilities for the process +var WithAllCapabilities = WithCapabilities(getAllCapabilities()) + +func getAllCapabilities() []string { + last := capability.CAP_LAST_CAP + // hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap + if last == capability.Cap(63) { + last = capability.CAP_BLOCK_SUSPEND + } + var caps []string + for _, cap := range capability.List() { + if cap > last { + continue + } + caps = append(caps, "CAP_"+strings.ToUpper(cap.String())) + } + return caps +} + +// WithAmbientCapabilities set the Linux ambient capabilities for the process +// Ambient capabilities should only be set for non-root users or the caller should +// understand how these capabilities are used and set +func WithAmbientCapabilities(caps []string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setCapabilities(s) + + s.Process.Capabilities.Ambient = caps + return nil + } +} + +var errNoUsersFound = errors.New("no users found") + +func getUIDGIDFromPath(root string, filter func(user.User) bool) (uid, gid uint32, err error) { + ppath, err := fs.RootPath(root, "/etc/passwd") + if err != nil { + return 0, 0, err + } + users, err := user.ParsePasswdFileFilter(ppath, filter) + if err != nil { + return 0, 0, err + } + if len(users) == 0 { + return 0, 0, errNoUsersFound + } + u := users[0] + return uint32(u.Uid), uint32(u.Gid), nil +} + +var errNoGroupsFound = errors.New("no groups found") + +func getGIDFromPath(root string, filter func(user.Group) bool) (gid uint32, err error) { + gpath, err := fs.RootPath(root, "/etc/group") + if err != nil { + return 0, err + } + groups, err := user.ParseGroupFileFilter(gpath, filter) + if err != nil { + return 0, err + } + if len(groups) == 0 { + return 0, errNoGroupsFound + } + g := groups[0] + return uint32(g.Gid), nil +} + +func getSupplementalGroupsFromPath(root string, filter func(user.Group) bool) ([]uint32, error) { + gpath, err := fs.RootPath(root, "/etc/group") + if err != nil { + return []uint32{}, err + } + groups, err := user.ParseGroupFileFilter(gpath, filter) + if err != nil { + return []uint32{}, err + } + if len(groups) == 0 { + // if there are no additional groups; just return an empty set + return []uint32{}, nil + } + addlGids := []uint32{} + for _, grp := range groups { + addlGids = append(addlGids, uint32(grp.Gid)) + } + return addlGids, nil +} + +func isRootfsAbs(root string) bool { + return filepath.IsAbs(root) +} + +// WithMaskedPaths sets the masked paths option +func WithMaskedPaths(paths []string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setLinux(s) + s.Linux.MaskedPaths = paths + return nil + } +} + +// WithReadonlyPaths sets the read only paths option +func WithReadonlyPaths(paths []string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setLinux(s) + s.Linux.ReadonlyPaths = paths + return nil + } +} + +// WithWriteableSysfs makes any sysfs mounts writeable +func WithWriteableSysfs(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + for i, m := range s.Mounts { + if m.Type == "sysfs" { + var options []string + for _, o := range m.Options { + if o == "ro" { + o = "rw" + } + options = append(options, o) + } + s.Mounts[i].Options = options + } + } + return nil +} + +// WithWriteableCgroupfs makes any cgroup mounts writeable +func WithWriteableCgroupfs(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + for i, m := range s.Mounts { + if m.Type == "cgroup" { + var options []string + for _, o := range m.Options { + if o == "ro" { + o = "rw" + } + options = append(options, o) + } + s.Mounts[i].Options = options + } + } + return nil +} + +// WithSelinuxLabel sets the process SELinux label +func WithSelinuxLabel(label string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setProcess(s) + s.Process.SelinuxLabel = label + return nil + } +} + +// WithApparmorProfile sets the Apparmor profile for the process +func WithApparmorProfile(profile string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setProcess(s) + s.Process.ApparmorProfile = profile + return nil + } +} + +// WithSeccompUnconfined clears the seccomp profile +func WithSeccompUnconfined(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setLinux(s) + s.Linux.Seccomp = nil + return nil +} + +// WithParentCgroupDevices uses the default cgroup setup to inherit the container's parent cgroup's +// allowed and denied devices +func WithParentCgroupDevices(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setLinux(s) + if s.Linux.Resources == nil { + s.Linux.Resources = &specs.LinuxResources{} + } + s.Linux.Resources.Devices = nil + return nil +} + +// WithDefaultUnixDevices adds the default devices for unix such as /dev/null, /dev/random to +// the container's resource cgroup spec +func WithDefaultUnixDevices(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setLinux(s) + if s.Linux.Resources == nil { + s.Linux.Resources = &specs.LinuxResources{} + } + intptr := func(i int64) *int64 { + return &i + } + s.Linux.Resources.Devices = append(s.Linux.Resources.Devices, []specs.LinuxDeviceCgroup{ + { + // "/dev/null", + Type: "c", + Major: intptr(1), + Minor: intptr(3), + Access: rwm, + Allow: true, + }, + { + // "/dev/random", + Type: "c", + Major: intptr(1), + Minor: intptr(8), + Access: rwm, + Allow: true, + }, + { + // "/dev/full", + Type: "c", + Major: intptr(1), + Minor: intptr(7), + Access: rwm, + Allow: true, + }, + { + // "/dev/tty", + Type: "c", + Major: intptr(5), + Minor: intptr(0), + Access: rwm, + Allow: true, + }, + { + // "/dev/zero", + Type: "c", + Major: intptr(1), + Minor: intptr(5), + Access: rwm, + Allow: true, + }, + { + // "/dev/urandom", + Type: "c", + Major: intptr(1), + Minor: intptr(9), + Access: rwm, + Allow: true, + }, + { + // "/dev/console", + Type: "c", + Major: intptr(5), + Minor: intptr(1), + Access: rwm, + Allow: true, + }, + // /dev/pts/ - pts namespaces are "coming soon" + { + Type: "c", + Major: intptr(136), + Access: rwm, + Allow: true, + }, + { + Type: "c", + Major: intptr(5), + Minor: intptr(2), + Access: rwm, + Allow: true, + }, + { + // tuntap + Type: "c", + Major: intptr(10), + Minor: intptr(200), + Access: rwm, + Allow: true, + }, + }...) + return nil +} + +// WithPrivileged sets up options for a privileged container +// TODO(justincormack) device handling +var WithPrivileged = Compose( + WithAllCapabilities, + WithMaskedPaths(nil), + WithReadonlyPaths(nil), + WithWriteableSysfs, + WithWriteableCgroupfs, + WithSelinuxLabel(""), + WithApparmorProfile(""), + WithSeccompUnconfined, +) diff --git a/vendor/github.com/containerd/containerd/oci/spec_opts_unix.go b/vendor/github.com/containerd/containerd/oci/spec_opts_unix.go deleted file mode 100644 index 592da651d2..0000000000 --- a/vendor/github.com/containerd/containerd/oci/spec_opts_unix.go +++ /dev/null @@ -1,733 +0,0 @@ -// +build !windows - -/* - 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 oci - -import ( - "context" - "encoding/json" - "fmt" - "os" - "path/filepath" - "strconv" - "strings" - - "github.com/containerd/containerd/containers" - "github.com/containerd/containerd/content" - "github.com/containerd/containerd/images" - "github.com/containerd/containerd/mount" - "github.com/containerd/containerd/namespaces" - "github.com/containerd/continuity/fs" - "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/opencontainers/runc/libcontainer/user" - specs "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" - "github.com/syndtr/gocapability/capability" -) - -// WithTTY sets the information on the spec as well as the environment variables for -// using a TTY -func WithTTY(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setProcess(s) - s.Process.Terminal = true - s.Process.Env = append(s.Process.Env, "TERM=xterm") - return nil -} - -// setRoot sets Root to empty if unset -func setRoot(s *Spec) { - if s.Root == nil { - s.Root = &specs.Root{} - } -} - -// setLinux sets Linux to empty if unset -func setLinux(s *Spec) { - if s.Linux == nil { - s.Linux = &specs.Linux{} - } -} - -// setCapabilities sets Linux Capabilities to empty if unset -func setCapabilities(s *Spec) { - setProcess(s) - if s.Process.Capabilities == nil { - s.Process.Capabilities = &specs.LinuxCapabilities{} - } -} - -// WithHostNamespace allows a task to run inside the host's linux namespace -func WithHostNamespace(ns specs.LinuxNamespaceType) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setLinux(s) - for i, n := range s.Linux.Namespaces { - if n.Type == ns { - s.Linux.Namespaces = append(s.Linux.Namespaces[:i], s.Linux.Namespaces[i+1:]...) - return nil - } - } - return nil - } -} - -// WithLinuxNamespace uses the passed in namespace for the spec. If a namespace of the same type already exists in the -// spec, the existing namespace is replaced by the one provided. -func WithLinuxNamespace(ns specs.LinuxNamespace) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setLinux(s) - for i, n := range s.Linux.Namespaces { - if n.Type == ns.Type { - before := s.Linux.Namespaces[:i] - after := s.Linux.Namespaces[i+1:] - s.Linux.Namespaces = append(before, ns) - s.Linux.Namespaces = append(s.Linux.Namespaces, after...) - return nil - } - } - s.Linux.Namespaces = append(s.Linux.Namespaces, ns) - return nil - } -} - -// WithImageConfig configures the spec to from the configuration of an Image -func WithImageConfig(image Image) SpecOpts { - return WithImageConfigArgs(image, nil) -} - -// WithImageConfigArgs configures the spec to from the configuration of an Image with additional args that -// replaces the CMD of the image -func WithImageConfigArgs(image Image, args []string) SpecOpts { - return func(ctx context.Context, client Client, c *containers.Container, s *Spec) error { - ic, err := image.Config(ctx) - if err != nil { - return err - } - var ( - ociimage v1.Image - config v1.ImageConfig - ) - switch ic.MediaType { - case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config: - p, err := content.ReadBlob(ctx, image.ContentStore(), ic) - if err != nil { - return err - } - - if err := json.Unmarshal(p, &ociimage); err != nil { - return err - } - config = ociimage.Config - default: - return fmt.Errorf("unknown image config media type %s", ic.MediaType) - } - - setProcess(s) - s.Process.Env = append(s.Process.Env, config.Env...) - cmd := config.Cmd - if len(args) > 0 { - cmd = args - } - s.Process.Args = append(config.Entrypoint, cmd...) - - cwd := config.WorkingDir - if cwd == "" { - cwd = "/" - } - s.Process.Cwd = cwd - if config.User != "" { - return WithUser(config.User)(ctx, client, c, s) - } - return nil - } -} - -// WithRootFSPath specifies unmanaged rootfs path. -func WithRootFSPath(path string) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setRoot(s) - s.Root.Path = path - // Entrypoint is not set here (it's up to caller) - return nil - } -} - -// WithRootFSReadonly sets specs.Root.Readonly to true -func WithRootFSReadonly() SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setRoot(s) - s.Root.Readonly = true - return nil - } -} - -// WithNoNewPrivileges sets no_new_privileges on the process for the container -func WithNoNewPrivileges(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setProcess(s) - s.Process.NoNewPrivileges = true - return nil -} - -// WithHostHostsFile bind-mounts the host's /etc/hosts into the container as readonly -func WithHostHostsFile(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - s.Mounts = append(s.Mounts, specs.Mount{ - Destination: "/etc/hosts", - Type: "bind", - Source: "/etc/hosts", - Options: []string{"rbind", "ro"}, - }) - return nil -} - -// WithHostResolvconf bind-mounts the host's /etc/resolv.conf into the container as readonly -func WithHostResolvconf(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - s.Mounts = append(s.Mounts, specs.Mount{ - Destination: "/etc/resolv.conf", - Type: "bind", - Source: "/etc/resolv.conf", - Options: []string{"rbind", "ro"}, - }) - return nil -} - -// WithHostLocaltime bind-mounts the host's /etc/localtime into the container as readonly -func WithHostLocaltime(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - s.Mounts = append(s.Mounts, specs.Mount{ - Destination: "/etc/localtime", - Type: "bind", - Source: "/etc/localtime", - Options: []string{"rbind", "ro"}, - }) - return nil -} - -// WithUserNamespace sets the uid and gid mappings for the task -// this can be called multiple times to add more mappings to the generated spec -func WithUserNamespace(container, host, size uint32) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - var hasUserns bool - setLinux(s) - for _, ns := range s.Linux.Namespaces { - if ns.Type == specs.UserNamespace { - hasUserns = true - break - } - } - if !hasUserns { - s.Linux.Namespaces = append(s.Linux.Namespaces, specs.LinuxNamespace{ - Type: specs.UserNamespace, - }) - } - mapping := specs.LinuxIDMapping{ - ContainerID: container, - HostID: host, - Size: size, - } - s.Linux.UIDMappings = append(s.Linux.UIDMappings, mapping) - s.Linux.GIDMappings = append(s.Linux.GIDMappings, mapping) - return nil - } -} - -// WithCgroup sets the container's cgroup path -func WithCgroup(path string) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setLinux(s) - s.Linux.CgroupsPath = path - return nil - } -} - -// WithNamespacedCgroup uses the namespace set on the context to create a -// root directory for containers in the cgroup with the id as the subcgroup -func WithNamespacedCgroup() SpecOpts { - return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error { - namespace, err := namespaces.NamespaceRequired(ctx) - if err != nil { - return err - } - setLinux(s) - s.Linux.CgroupsPath = filepath.Join("/", namespace, c.ID) - return nil - } -} - -// WithUser sets the user to be used within the container. -// It accepts a valid user string in OCI Image Spec v1.0.0: -// user, uid, user:group, uid:gid, uid:group, user:gid -func WithUser(userstr string) SpecOpts { - return func(ctx context.Context, client Client, c *containers.Container, s *Spec) error { - setProcess(s) - parts := strings.Split(userstr, ":") - switch len(parts) { - case 1: - v, err := strconv.Atoi(parts[0]) - if err != nil { - // if we cannot parse as a uint they try to see if it is a username - return WithUsername(userstr)(ctx, client, c, s) - } - return WithUserID(uint32(v))(ctx, client, c, s) - case 2: - var ( - username string - groupname string - ) - var uid, gid uint32 - v, err := strconv.Atoi(parts[0]) - if err != nil { - username = parts[0] - } else { - uid = uint32(v) - } - if v, err = strconv.Atoi(parts[1]); err != nil { - groupname = parts[1] - } else { - gid = uint32(v) - } - if username == "" && groupname == "" { - s.Process.User.UID, s.Process.User.GID = uid, gid - return nil - } - f := func(root string) error { - if username != "" { - uid, _, err = getUIDGIDFromPath(root, func(u user.User) bool { - return u.Name == username - }) - if err != nil { - return err - } - } - if groupname != "" { - gid, err = getGIDFromPath(root, func(g user.Group) bool { - return g.Name == groupname - }) - if err != nil { - return err - } - } - s.Process.User.UID, s.Process.User.GID = uid, gid - return nil - } - if c.Snapshotter == "" && c.SnapshotKey == "" { - if !isRootfsAbs(s.Root.Path) { - return errors.New("rootfs absolute path is required") - } - return f(s.Root.Path) - } - if c.Snapshotter == "" { - return errors.New("no snapshotter set for container") - } - if c.SnapshotKey == "" { - return errors.New("rootfs snapshot not created for container") - } - snapshotter := client.SnapshotService(c.Snapshotter) - mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey) - if err != nil { - return err - } - return mount.WithTempMount(ctx, mounts, f) - default: - return fmt.Errorf("invalid USER value %s", userstr) - } - } -} - -// WithUIDGID allows the UID and GID for the Process to be set -func WithUIDGID(uid, gid uint32) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setProcess(s) - s.Process.User.UID = uid - s.Process.User.GID = gid - return nil - } -} - -// WithUserID sets the correct UID and GID for the container based -// on the image's /etc/passwd contents. If /etc/passwd does not exist, -// or uid is not found in /etc/passwd, it sets the requested uid, -// additionally sets the gid to 0, and does not return an error. -func WithUserID(uid uint32) SpecOpts { - return func(ctx context.Context, client Client, c *containers.Container, s *Spec) (err error) { - setProcess(s) - if c.Snapshotter == "" && c.SnapshotKey == "" { - if !isRootfsAbs(s.Root.Path) { - return errors.Errorf("rootfs absolute path is required") - } - uuid, ugid, err := getUIDGIDFromPath(s.Root.Path, func(u user.User) bool { - return u.Uid == int(uid) - }) - if err != nil { - if os.IsNotExist(err) || err == errNoUsersFound { - s.Process.User.UID, s.Process.User.GID = uid, 0 - return nil - } - return err - } - s.Process.User.UID, s.Process.User.GID = uuid, ugid - return nil - - } - if c.Snapshotter == "" { - return errors.Errorf("no snapshotter set for container") - } - if c.SnapshotKey == "" { - return errors.Errorf("rootfs snapshot not created for container") - } - snapshotter := client.SnapshotService(c.Snapshotter) - mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey) - if err != nil { - return err - } - return mount.WithTempMount(ctx, mounts, func(root string) error { - uuid, ugid, err := getUIDGIDFromPath(root, func(u user.User) bool { - return u.Uid == int(uid) - }) - if err != nil { - if os.IsNotExist(err) || err == errNoUsersFound { - s.Process.User.UID, s.Process.User.GID = uid, 0 - return nil - } - return err - } - s.Process.User.UID, s.Process.User.GID = uuid, ugid - return nil - }) - } -} - -// WithUsername sets the correct UID and GID for the container -// based on the the image's /etc/passwd contents. If /etc/passwd -// does not exist, or the username is not found in /etc/passwd, -// it returns error. -func WithUsername(username string) SpecOpts { - return func(ctx context.Context, client Client, c *containers.Container, s *Spec) (err error) { - setProcess(s) - if c.Snapshotter == "" && c.SnapshotKey == "" { - if !isRootfsAbs(s.Root.Path) { - return errors.Errorf("rootfs absolute path is required") - } - uid, gid, err := getUIDGIDFromPath(s.Root.Path, func(u user.User) bool { - return u.Name == username - }) - if err != nil { - return err - } - s.Process.User.UID, s.Process.User.GID = uid, gid - return nil - } - if c.Snapshotter == "" { - return errors.Errorf("no snapshotter set for container") - } - if c.SnapshotKey == "" { - return errors.Errorf("rootfs snapshot not created for container") - } - snapshotter := client.SnapshotService(c.Snapshotter) - mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey) - if err != nil { - return err - } - return mount.WithTempMount(ctx, mounts, func(root string) error { - uid, gid, err := getUIDGIDFromPath(root, func(u user.User) bool { - return u.Name == username - }) - if err != nil { - return err - } - s.Process.User.UID, s.Process.User.GID = uid, gid - return nil - }) - } -} - -// WithCapabilities sets Linux capabilities on the process -func WithCapabilities(caps []string) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setCapabilities(s) - - s.Process.Capabilities.Bounding = caps - s.Process.Capabilities.Effective = caps - s.Process.Capabilities.Permitted = caps - s.Process.Capabilities.Inheritable = caps - - return nil - } -} - -// WithAllCapabilities sets all linux capabilities for the process -var WithAllCapabilities = WithCapabilities(getAllCapabilities()) - -func getAllCapabilities() []string { - last := capability.CAP_LAST_CAP - // hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap - if last == capability.Cap(63) { - last = capability.CAP_BLOCK_SUSPEND - } - var caps []string - for _, cap := range capability.List() { - if cap > last { - continue - } - caps = append(caps, "CAP_"+strings.ToUpper(cap.String())) - } - return caps -} - -// WithAmbientCapabilities set the Linux ambient capabilities for the process -// Ambient capabilities should only be set for non-root users or the caller should -// understand how these capabilities are used and set -func WithAmbientCapabilities(caps []string) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setCapabilities(s) - - s.Process.Capabilities.Ambient = caps - return nil - } -} - -var errNoUsersFound = errors.New("no users found") - -func getUIDGIDFromPath(root string, filter func(user.User) bool) (uid, gid uint32, err error) { - ppath, err := fs.RootPath(root, "/etc/passwd") - if err != nil { - return 0, 0, err - } - users, err := user.ParsePasswdFileFilter(ppath, filter) - if err != nil { - return 0, 0, err - } - if len(users) == 0 { - return 0, 0, errNoUsersFound - } - u := users[0] - return uint32(u.Uid), uint32(u.Gid), nil -} - -var errNoGroupsFound = errors.New("no groups found") - -func getGIDFromPath(root string, filter func(user.Group) bool) (gid uint32, err error) { - gpath, err := fs.RootPath(root, "/etc/group") - if err != nil { - return 0, err - } - groups, err := user.ParseGroupFileFilter(gpath, filter) - if err != nil { - return 0, err - } - if len(groups) == 0 { - return 0, errNoGroupsFound - } - g := groups[0] - return uint32(g.Gid), nil -} - -func isRootfsAbs(root string) bool { - return filepath.IsAbs(root) -} - -// WithMaskedPaths sets the masked paths option -func WithMaskedPaths(paths []string) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setLinux(s) - s.Linux.MaskedPaths = paths - return nil - } -} - -// WithReadonlyPaths sets the read only paths option -func WithReadonlyPaths(paths []string) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setLinux(s) - s.Linux.ReadonlyPaths = paths - return nil - } -} - -// WithWriteableSysfs makes any sysfs mounts writeable -func WithWriteableSysfs(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - for i, m := range s.Mounts { - if m.Type == "sysfs" { - var options []string - for _, o := range m.Options { - if o == "ro" { - o = "rw" - } - options = append(options, o) - } - s.Mounts[i].Options = options - } - } - return nil -} - -// WithWriteableCgroupfs makes any cgroup mounts writeable -func WithWriteableCgroupfs(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - for i, m := range s.Mounts { - if m.Type == "cgroup" { - var options []string - for _, o := range m.Options { - if o == "ro" { - o = "rw" - } - options = append(options, o) - } - s.Mounts[i].Options = options - } - } - return nil -} - -// WithSelinuxLabel sets the process SELinux label -func WithSelinuxLabel(label string) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setProcess(s) - s.Process.SelinuxLabel = label - return nil - } -} - -// WithApparmorProfile sets the Apparmor profile for the process -func WithApparmorProfile(profile string) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setProcess(s) - s.Process.ApparmorProfile = profile - return nil - } -} - -// WithSeccompUnconfined clears the seccomp profile -func WithSeccompUnconfined(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setLinux(s) - s.Linux.Seccomp = nil - return nil -} - -// WithParentCgroupDevices uses the default cgroup setup to inherit the container's parent cgroup's -// allowed and denied devices -func WithParentCgroupDevices(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setLinux(s) - if s.Linux.Resources == nil { - s.Linux.Resources = &specs.LinuxResources{} - } - s.Linux.Resources.Devices = nil - return nil -} - -// WithDefaultUnixDevices adds the default devices for unix such as /dev/null, /dev/random to -// the container's resource cgroup spec -func WithDefaultUnixDevices(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setLinux(s) - if s.Linux.Resources == nil { - s.Linux.Resources = &specs.LinuxResources{} - } - intptr := func(i int64) *int64 { - return &i - } - s.Linux.Resources.Devices = append(s.Linux.Resources.Devices, []specs.LinuxDeviceCgroup{ - { - // "/dev/null", - Type: "c", - Major: intptr(1), - Minor: intptr(3), - Access: rwm, - Allow: true, - }, - { - // "/dev/random", - Type: "c", - Major: intptr(1), - Minor: intptr(8), - Access: rwm, - Allow: true, - }, - { - // "/dev/full", - Type: "c", - Major: intptr(1), - Minor: intptr(7), - Access: rwm, - Allow: true, - }, - { - // "/dev/tty", - Type: "c", - Major: intptr(5), - Minor: intptr(0), - Access: rwm, - Allow: true, - }, - { - // "/dev/zero", - Type: "c", - Major: intptr(1), - Minor: intptr(5), - Access: rwm, - Allow: true, - }, - { - // "/dev/urandom", - Type: "c", - Major: intptr(1), - Minor: intptr(9), - Access: rwm, - Allow: true, - }, - { - // "/dev/console", - Type: "c", - Major: intptr(5), - Minor: intptr(1), - Access: rwm, - Allow: true, - }, - // /dev/pts/ - pts namespaces are "coming soon" - { - Type: "c", - Major: intptr(136), - Access: rwm, - Allow: true, - }, - { - Type: "c", - Major: intptr(5), - Minor: intptr(2), - Access: rwm, - Allow: true, - }, - { - // tuntap - Type: "c", - Major: intptr(10), - Minor: intptr(200), - Access: rwm, - Allow: true, - }, - }...) - return nil -} - -// WithPrivileged sets up options for a privileged container -// TODO(justincormack) device handling -var WithPrivileged = Compose( - WithAllCapabilities, - WithMaskedPaths(nil), - WithReadonlyPaths(nil), - WithWriteableSysfs, - WithWriteableCgroupfs, - WithSelinuxLabel(""), - WithApparmorProfile(""), - WithSeccompUnconfined, -) diff --git a/vendor/github.com/containerd/containerd/oci/spec_opts_windows.go b/vendor/github.com/containerd/containerd/oci/spec_opts_windows.go deleted file mode 100644 index 3688a582d2..0000000000 --- a/vendor/github.com/containerd/containerd/oci/spec_opts_windows.go +++ /dev/null @@ -1,89 +0,0 @@ -// +build windows - -/* - 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 oci - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/containerd/containerd/containers" - "github.com/containerd/containerd/content" - "github.com/containerd/containerd/images" - "github.com/opencontainers/image-spec/specs-go/v1" - specs "github.com/opencontainers/runtime-spec/specs-go" -) - -// WithImageConfig configures the spec to from the configuration of an Image -func WithImageConfig(image Image) SpecOpts { - return func(ctx context.Context, client Client, _ *containers.Container, s *Spec) error { - setProcess(s) - ic, err := image.Config(ctx) - if err != nil { - return err - } - var ( - ociimage v1.Image - config v1.ImageConfig - ) - switch ic.MediaType { - case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config: - p, err := content.ReadBlob(ctx, image.ContentStore(), ic) - if err != nil { - return err - } - if err := json.Unmarshal(p, &ociimage); err != nil { - return err - } - config = ociimage.Config - default: - return fmt.Errorf("unknown image config media type %s", ic.MediaType) - } - s.Process.Env = config.Env - s.Process.Args = append(config.Entrypoint, config.Cmd...) - s.Process.User = specs.User{ - Username: config.User, - } - return nil - } -} - -// WithTTY sets the information on the spec as well as the environment variables for -// using a TTY -func WithTTY(width, height int) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setProcess(s) - s.Process.Terminal = true - if s.Process.ConsoleSize == nil { - s.Process.ConsoleSize = &specs.Box{} - } - s.Process.ConsoleSize.Width = uint(width) - s.Process.ConsoleSize.Height = uint(height) - return nil - } -} - -// WithUsername sets the username on the process -func WithUsername(username string) SpecOpts { - return func(ctx context.Context, client Client, c *containers.Container, s *Spec) error { - setProcess(s) - s.Process.User.Username = username - return nil - } -} diff --git a/vendor/github.com/containerd/containerd/oci/spec_unix.go b/vendor/github.com/containerd/containerd/oci/spec_unix.go deleted file mode 100644 index cb69434cba..0000000000 --- a/vendor/github.com/containerd/containerd/oci/spec_unix.go +++ /dev/null @@ -1,188 +0,0 @@ -// +build !windows - -/* - 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 oci - -import ( - "context" - "path/filepath" - - "github.com/containerd/containerd/namespaces" - specs "github.com/opencontainers/runtime-spec/specs-go" -) - -const ( - rwm = "rwm" - defaultRootfsPath = "rootfs" -) - -var ( - defaultEnv = []string{ - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - } -) - -func defaultCaps() []string { - return []string{ - "CAP_CHOWN", - "CAP_DAC_OVERRIDE", - "CAP_FSETID", - "CAP_FOWNER", - "CAP_MKNOD", - "CAP_NET_RAW", - "CAP_SETGID", - "CAP_SETUID", - "CAP_SETFCAP", - "CAP_SETPCAP", - "CAP_NET_BIND_SERVICE", - "CAP_SYS_CHROOT", - "CAP_KILL", - "CAP_AUDIT_WRITE", - } -} - -func defaultNamespaces() []specs.LinuxNamespace { - return []specs.LinuxNamespace{ - { - Type: specs.PIDNamespace, - }, - { - Type: specs.IPCNamespace, - }, - { - Type: specs.UTSNamespace, - }, - { - Type: specs.MountNamespace, - }, - { - Type: specs.NetworkNamespace, - }, - } -} - -func populateDefaultSpec(ctx context.Context, s *Spec, id string) error { - ns, err := namespaces.NamespaceRequired(ctx) - if err != nil { - return err - } - - *s = Spec{ - Version: specs.Version, - Root: &specs.Root{ - Path: defaultRootfsPath, - }, - Process: &specs.Process{ - Env: defaultEnv, - Cwd: "/", - NoNewPrivileges: true, - User: specs.User{ - UID: 0, - GID: 0, - }, - Capabilities: &specs.LinuxCapabilities{ - Bounding: defaultCaps(), - Permitted: defaultCaps(), - Inheritable: defaultCaps(), - Effective: defaultCaps(), - }, - Rlimits: []specs.POSIXRlimit{ - { - Type: "RLIMIT_NOFILE", - Hard: uint64(1024), - Soft: uint64(1024), - }, - }, - }, - Mounts: []specs.Mount{ - { - Destination: "/proc", - Type: "proc", - Source: "proc", - }, - { - Destination: "/dev", - Type: "tmpfs", - Source: "tmpfs", - Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, - }, - { - Destination: "/dev/pts", - Type: "devpts", - Source: "devpts", - Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"}, - }, - { - Destination: "/dev/shm", - Type: "tmpfs", - Source: "shm", - Options: []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"}, - }, - { - Destination: "/dev/mqueue", - Type: "mqueue", - Source: "mqueue", - Options: []string{"nosuid", "noexec", "nodev"}, - }, - { - Destination: "/sys", - Type: "sysfs", - Source: "sysfs", - Options: []string{"nosuid", "noexec", "nodev", "ro"}, - }, - { - Destination: "/run", - Type: "tmpfs", - Source: "tmpfs", - Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, - }, - }, - Linux: &specs.Linux{ - MaskedPaths: []string{ - "/proc/acpi", - "/proc/kcore", - "/proc/keys", - "/proc/latency_stats", - "/proc/timer_list", - "/proc/timer_stats", - "/proc/sched_debug", - "/sys/firmware", - "/proc/scsi", - }, - ReadonlyPaths: []string{ - "/proc/asound", - "/proc/bus", - "/proc/fs", - "/proc/irq", - "/proc/sys", - "/proc/sysrq-trigger", - }, - CgroupsPath: filepath.Join("/", ns, id), - Resources: &specs.LinuxResources{ - Devices: []specs.LinuxDeviceCgroup{ - { - Allow: false, - Access: rwm, - }, - }, - }, - Namespaces: defaultNamespaces(), - }, - } - return nil -} diff --git a/vendor/github.com/containerd/containerd/platforms/defaults.go b/vendor/github.com/containerd/containerd/platforms/defaults.go index b8d9c8277f..a14d80e58c 100644 --- a/vendor/github.com/containerd/containerd/platforms/defaults.go +++ b/vendor/github.com/containerd/containerd/platforms/defaults.go @@ -22,11 +22,6 @@ import ( specs "github.com/opencontainers/image-spec/specs-go/v1" ) -// Default returns the default matcher for the platform. -func Default() MatchComparer { - return Only(DefaultSpec()) -} - // DefaultString returns the default string specifier for the platform. func DefaultString() string { return Format(DefaultSpec()) diff --git a/vendor/github.com/containerd/containerd/oci/spec_windows.go b/vendor/github.com/containerd/containerd/platforms/defaults_unix.go similarity index 54% rename from vendor/github.com/containerd/containerd/oci/spec_windows.go rename to vendor/github.com/containerd/containerd/platforms/defaults_unix.go index d0236585dc..e8a7d5ffa0 100644 --- a/vendor/github.com/containerd/containerd/oci/spec_windows.go +++ b/vendor/github.com/containerd/containerd/platforms/defaults_unix.go @@ -1,3 +1,5 @@ +// +build !windows + /* Copyright The containerd Authors. @@ -14,31 +16,9 @@ limitations under the License. */ -package oci +package platforms -import ( - "context" - - specs "github.com/opencontainers/runtime-spec/specs-go" -) - -func populateDefaultSpec(ctx context.Context, s *Spec, id string) error { - *s = Spec{ - Version: specs.Version, - Root: &specs.Root{}, - Process: &specs.Process{ - Cwd: `C:\`, - ConsoleSize: &specs.Box{ - Width: 80, - Height: 20, - }, - }, - Windows: &specs.Windows{ - IgnoreFlushesDuringBoot: true, - Network: &specs.WindowsNetwork{ - AllowUnqualifiedDNSQuery: true, - }, - }, - } - return nil +// Default returns the default matcher for the platform. +func Default() MatchComparer { + return Only(DefaultSpec()) } diff --git a/vendor/github.com/containerd/containerd/task_opts_windows.go b/vendor/github.com/containerd/containerd/platforms/defaults_windows.go similarity index 63% rename from vendor/github.com/containerd/containerd/task_opts_windows.go rename to vendor/github.com/containerd/containerd/platforms/defaults_windows.go index 60836bc8fa..0defbd36c0 100644 --- a/vendor/github.com/containerd/containerd/task_opts_windows.go +++ b/vendor/github.com/containerd/containerd/platforms/defaults_windows.go @@ -1,3 +1,5 @@ +// +build windows + /* Copyright The containerd Authors. @@ -14,18 +16,16 @@ limitations under the License. */ -package containerd +package platforms import ( - "context" - - specs "github.com/opencontainers/runtime-spec/specs-go" + specs "github.com/opencontainers/image-spec/specs-go/v1" ) -// WithResources sets the provided resources on the spec for task updates -func WithResources(resources *specs.WindowsResources) UpdateTaskOpts { - return func(ctx context.Context, client *Client, r *UpdateTaskInfo) error { - r.Resources = resources - return nil - } +// Default returns the default matcher for the platform. +func Default() MatchComparer { + return Ordered(DefaultSpec(), specs.Platform{ + OS: "linux", + Architecture: "amd64", + }) } diff --git a/vendor/github.com/containerd/containerd/remotes/docker/fetcher.go b/vendor/github.com/containerd/containerd/remotes/docker/fetcher.go index 1509e696cd..4a2ce3c393 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/fetcher.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/fetcher.go @@ -117,7 +117,7 @@ func (r dockerFetcher) open(ctx context.Context, u, mediatype string, offset int } } else { // TODO: Should any cases where use of content range - // without the proper header be considerd? + // without the proper header be considered? // 206 responses? // Discard up to offset diff --git a/vendor/github.com/containerd/containerd/remotes/docker/httpreadseeker.go b/vendor/github.com/containerd/containerd/remotes/docker/httpreadseeker.go index 5a77789537..9175b6a7a4 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/httpreadseeker.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/httpreadseeker.go @@ -134,7 +134,7 @@ func (hrs *httpReadSeeker) reader() (io.Reader, error) { // There is an edge case here where offset == size of the content. If // we seek, we will probably get an error for content that cannot be // sought (?). In that case, we should err on committing the content, - // as the length is already satisified but we just return the empty + // as the length is already satisfied but we just return the empty // reader instead. hrs.rc = ioutil.NopCloser(bytes.NewReader([]byte{})) diff --git a/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go b/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go index 3155d6ec35..45ac1933fd 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go @@ -272,8 +272,14 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro return err } - // TODO: Check if blob -> diff id mapping already exists - // TODO: Check if blob empty label exists + reuse, err := c.reuseLabelBlobState(ctx, desc) + if err != nil { + return err + } + + if reuse { + return nil + } ra, err := c.contentStore.ReaderAt(ctx, desc) if err != nil { @@ -343,6 +349,17 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro state := calc.State() + cinfo := content.Info{ + Digest: desc.Digest, + Labels: map[string]string{ + "containerd.io/uncompressed": state.diffID.String(), + }, + } + + if _, err := c.contentStore.Update(ctx, cinfo, "labels.containerd.io/uncompressed"); err != nil { + return errors.Wrap(err, "failed to update uncompressed label") + } + c.mu.Lock() c.blobMap[desc.Digest] = state c.layerBlobs[state.diffID] = desc @@ -351,6 +368,40 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro return nil } +func (c *Converter) reuseLabelBlobState(ctx context.Context, desc ocispec.Descriptor) (bool, error) { + cinfo, err := c.contentStore.Info(ctx, desc.Digest) + if err != nil { + return false, errors.Wrap(err, "failed to get blob info") + } + desc.Size = cinfo.Size + + diffID, ok := cinfo.Labels["containerd.io/uncompressed"] + if !ok { + return false, nil + } + + bState := blobState{empty: false} + + if bState.diffID, err = digest.Parse(diffID); err != nil { + log.G(ctx).WithField("id", desc.Digest).Warnf("failed to parse digest from label containerd.io/uncompressed: %v", diffID) + return false, nil + } + + // NOTE: there is no need to read header to get compression method + // because there are only two kinds of methods. + if bState.diffID == desc.Digest { + desc.MediaType = images.MediaTypeDockerSchema2Layer + } else { + desc.MediaType = images.MediaTypeDockerSchema2LayerGzip + } + + c.mu.Lock() + c.blobMap[desc.Digest] = bState + c.layerBlobs[bState.diffID] = desc + c.mu.Unlock() + return true, nil +} + func (c *Converter) schema1ManifestHistory() ([]ocispec.History, []digest.Digest, error) { if c.pulledManifest == nil { return nil, nil, errors.New("missing schema 1 manifest for conversion") diff --git a/vendor/github.com/containerd/containerd/sys/socket_unix.go b/vendor/github.com/containerd/containerd/sys/socket_unix.go index 0dbca0e333..90fa55c482 100644 --- a/vendor/github.com/containerd/containerd/sys/socket_unix.go +++ b/vendor/github.com/containerd/containerd/sys/socket_unix.go @@ -42,7 +42,7 @@ func CreateUnixSocket(path string) (net.Listener, error) { return net.Listen("unix", path) } -// GetLocalListener returns a listerner out of a unix socket. +// GetLocalListener returns a listener out of a unix socket. func GetLocalListener(path string, uid, gid int) (net.Listener, error) { // Ensure parent directory is created if err := mkdirAs(filepath.Dir(path), uid, gid); err != nil { diff --git a/vendor/github.com/containerd/containerd/task_opts.go b/vendor/github.com/containerd/containerd/task_opts.go index 9e998a3498..ce861ea51d 100644 --- a/vendor/github.com/containerd/containerd/task_opts.go +++ b/vendor/github.com/containerd/containerd/task_opts.go @@ -18,10 +18,18 @@ package containerd import ( "context" + "encoding/json" + "fmt" "syscall" + "github.com/containerd/containerd/api/types" + "github.com/containerd/containerd/content" "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/images" "github.com/containerd/containerd/mount" + imagespec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" ) // NewTaskOpts allows the caller to set options on a new task @@ -35,6 +43,44 @@ func WithRootFS(mounts []mount.Mount) NewTaskOpts { } } +// WithTaskCheckpoint allows a task to be created with live runtime and memory data from a +// previous checkpoint. Additional software such as CRIU may be required to +// restore a task from a checkpoint +func WithTaskCheckpoint(im Image) NewTaskOpts { + return func(ctx context.Context, c *Client, info *TaskInfo) error { + desc := im.Target() + id := desc.Digest + index, err := decodeIndex(ctx, c.ContentStore(), desc) + if err != nil { + return err + } + for _, m := range index.Manifests { + if m.MediaType == images.MediaTypeContainerd1Checkpoint { + info.Checkpoint = &types.Descriptor{ + MediaType: m.MediaType, + Size_: m.Size, + Digest: m.Digest, + } + return nil + } + } + return fmt.Errorf("checkpoint not found in index %s", id) + } +} + +func decodeIndex(ctx context.Context, store content.Provider, desc imagespec.Descriptor) (*imagespec.Index, error) { + var index imagespec.Index + p, err := content.ReadBlob(ctx, store, desc) + if err != nil { + return nil, err + } + if err := json.Unmarshal(p, &index); err != nil { + return nil, err + } + + return &index, nil +} + // WithCheckpointName sets the image name for the checkpoint func WithCheckpointName(name string) CheckpointTaskOpts { return func(r *CheckpointTaskInfo) error { @@ -92,3 +138,19 @@ func WithKillExecID(execID string) KillOpts { return nil } } + +// WithResources sets the provided resources for task updates. Resources must be +// either a *specs.LinuxResources or a *specs.WindowsResources +func WithResources(resources interface{}) UpdateTaskOpts { + return func(ctx context.Context, client *Client, r *UpdateTaskInfo) error { + switch resources.(type) { + case *specs.LinuxResources: + case *specs.WindowsResources: + default: + return errors.New("WithResources requires a *specs.LinuxResources or *specs.WindowsResources") + } + + r.Resources = resources + return nil + } +} diff --git a/vendor/github.com/containerd/containerd/task_opts_linux.go b/vendor/github.com/containerd/containerd/task_opts_unix.go similarity index 72% rename from vendor/github.com/containerd/containerd/task_opts_linux.go rename to vendor/github.com/containerd/containerd/task_opts_unix.go index 551cb996c1..f8652be3bc 100644 --- a/vendor/github.com/containerd/containerd/task_opts_linux.go +++ b/vendor/github.com/containerd/containerd/task_opts_unix.go @@ -1,3 +1,5 @@ +// +build !windows + /* Copyright The containerd Authors. @@ -18,20 +20,11 @@ package containerd import ( "context" - "errors" "github.com/containerd/containerd/runtime/linux/runctypes" - "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" ) -// WithResources sets the provided resources for task updates -func WithResources(resources *specs.LinuxResources) UpdateTaskOpts { - return func(ctx context.Context, client *Client, r *UpdateTaskInfo) error { - r.Resources = resources - return nil - } -} - // WithNoNewKeyring causes tasks not to be created with a new keyring for secret storage. // There is an upper limit on the number of keyrings in a linux system func WithNoNewKeyring(ctx context.Context, c *Client, ti *TaskInfo) error { @@ -46,3 +39,19 @@ func WithNoNewKeyring(ctx context.Context, c *Client, ti *TaskInfo) error { opts.NoNewKeyring = true return nil } + +// WithNoPivotRoot instructs the runtime not to you pivot_root +func WithNoPivotRoot(_ context.Context, _ *Client, info *TaskInfo) error { + if info.Options == nil { + info.Options = &runctypes.CreateOptions{ + NoPivotRoot: true, + } + return nil + } + opts, ok := info.Options.(*runctypes.CreateOptions) + if !ok { + return errors.New("invalid options type, expected runctypes.CreateOptions") + } + opts.NoPivotRoot = true + return nil +} diff --git a/vendor/github.com/containerd/containerd/vendor.conf b/vendor/github.com/containerd/containerd/vendor.conf index 094a1bed5a..657cd20aec 100644 --- a/vendor/github.com/containerd/containerd/vendor.conf +++ b/vendor/github.com/containerd/containerd/vendor.conf @@ -1,10 +1,10 @@ -github.com/containerd/go-runc acb7c88cac264acca9b5eae187a117f4d77a1292 +github.com/containerd/go-runc 5a6d9f37cfa36b15efba46dc7ea349fa9b7143c3 github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23 github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2 github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40 github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c github.com/containerd/btrfs 2e1aa0ddf94f91fa282b6ed87c23bf0d64911244 -github.com/containerd/continuity d3c23511c1bf5851696cba83143d9cbcd666869b +github.com/containerd/continuity f44b615e492bdfb371aae2f76ec694d9da1db537 github.com/coreos/go-systemd 48702e0da86bd25e76cfef347e2adeb434a0d0a6 github.com/docker/go-metrics 4ea375f7759c82740c893fc030bc37088d2ec098 github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9 @@ -71,7 +71,7 @@ github.com/xeipuuv/gojsonschema 1d523034197ff1f222f6429836dd36a2457a1874 golang.org/x/crypto 49796115aa4b964c318aad4f3084fdb41e9aa067 golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631 gopkg.in/inf.v0 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 -gopkg.in/yaml.v2 53feefa2559fb8dfa8d81baad31be332c97d6c77 +gopkg.in/yaml.v2 v2.2.1 k8s.io/api 9e5ffd1f1320950b238cfce291b926411f0af722 k8s.io/apimachinery ed135c5b96450fd24e5e981c708114fbbd950697 k8s.io/apiserver a90e3a95c2e91b944bfca8225c4e0d12e42a9eb5 @@ -85,4 +85,4 @@ github.com/mistifyio/go-zfs 166add352731e515512690329794ee593f1aaff2 github.com/pborman/uuid c65b2f87fee37d1c7854c9164a450713c28d50cd # aufs dependencies -github.com/containerd/aufs a7fbd554da7a9eafbe5a460a421313a9fd18d988 +github.com/containerd/aufs ffa39970e26ad01d81f540b21e65f9c1841a5f92