mirror of https://github.com/docker/cli.git
Merge pull request #1405 from dhiltgen/revamp_master
Refine how metadata dir is handled
This commit is contained in:
commit
7808348548
|
@ -3,6 +3,7 @@ package engine
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/cli/cli/command/formatter"
|
"github.com/docker/cli/cli/command/formatter"
|
||||||
|
@ -57,7 +58,7 @@ https://hub.docker.com/ then specify the file with the '--license' flag.
|
||||||
flags.StringVar(&options.licenseFile, "license", "", "License File")
|
flags.StringVar(&options.licenseFile, "license", "", "License File")
|
||||||
flags.StringVar(&options.version, "version", "", "Specify engine version (default is to use currently running version)")
|
flags.StringVar(&options.version, "version", "", "Specify engine version (default is to use currently running version)")
|
||||||
flags.StringVar(&options.registryPrefix, "registry-prefix", clitypes.RegistryPrefix, "Override the default location where engine images are pulled")
|
flags.StringVar(&options.registryPrefix, "registry-prefix", clitypes.RegistryPrefix, "Override the default location where engine images are pulled")
|
||||||
flags.StringVar(&options.image, "engine-image", clitypes.EnterpriseEngineImage, "Specify engine image")
|
flags.StringVar(&options.image, "engine-image", "", "Specify engine image")
|
||||||
flags.StringVar(&options.format, "format", "", "Pretty-print licenses using a Go template")
|
flags.StringVar(&options.format, "format", "", "Pretty-print licenses using a Go template")
|
||||||
flags.BoolVar(&options.displayOnly, "display-only", false, "only display the available licenses and exit")
|
flags.BoolVar(&options.displayOnly, "display-only", false, "only display the available licenses and exit")
|
||||||
flags.BoolVar(&options.quiet, "quiet", false, "Only display available licenses by ID")
|
flags.BoolVar(&options.quiet, "quiet", false, "Only display available licenses by ID")
|
||||||
|
@ -102,10 +103,24 @@ func runActivate(cli command.Cli, options activateOptions) error {
|
||||||
if options.displayOnly {
|
if options.displayOnly {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err = licenseutils.ApplyLicense(ctx, cli.Client(), license); err != nil {
|
dclient := cli.Client()
|
||||||
|
if err = licenseutils.ApplyLicense(ctx, dclient, license); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Short circuit if the user didn't specify a version and we're already running enterprise
|
||||||
|
if options.version == "" {
|
||||||
|
serverVersion, err := dclient.ServerVersion(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if strings.Contains(strings.ToLower(serverVersion.Platform.Name), "enterprise") {
|
||||||
|
fmt.Fprintln(cli.Out(), "Successfully activated engine license on existing enterprise engine.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
options.version = serverVersion.Version
|
||||||
|
}
|
||||||
|
|
||||||
opts := clitypes.EngineInitOptions{
|
opts := clitypes.EngineInitOptions{
|
||||||
RegistryPrefix: options.registryPrefix,
|
RegistryPrefix: options.registryPrefix,
|
||||||
EngineImage: options.image,
|
EngineImage: options.image,
|
||||||
|
|
|
@ -53,7 +53,7 @@ func TestActivateExpiredLicenseDryRun(t *testing.T) {
|
||||||
defer dir.Remove()
|
defer dir.Remove()
|
||||||
filename := dir.Join("docker.lic")
|
filename := dir.Join("docker.lic")
|
||||||
isRoot = func() bool { return true }
|
isRoot = func() bool { return true }
|
||||||
c := test.NewFakeCli(&verClient{client.Client{}, types.Version{}, nil})
|
c := test.NewFakeCli(&verClient{client.Client{}, types.Version{}, nil, types.Info{}, nil})
|
||||||
c.SetContainerizedEngineClient(
|
c.SetContainerizedEngineClient(
|
||||||
func(string) (clitypes.ContainerizedClient, error) {
|
func(string) (clitypes.ContainerizedClient, error) {
|
||||||
return &fakeContainerizedEngineClient{}, nil
|
return &fakeContainerizedEngineClient{}, nil
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
type checkOptions struct {
|
type checkOptions struct {
|
||||||
registryPrefix string
|
registryPrefix string
|
||||||
preReleases bool
|
preReleases bool
|
||||||
|
engineImage string
|
||||||
downgrades bool
|
downgrades bool
|
||||||
upgrades bool
|
upgrades bool
|
||||||
format string
|
format string
|
||||||
|
@ -38,6 +39,7 @@ func newCheckForUpdatesCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
flags.StringVar(&options.registryPrefix, "registry-prefix", clitypes.RegistryPrefix, "Override the existing location where engine images are pulled")
|
flags.StringVar(&options.registryPrefix, "registry-prefix", clitypes.RegistryPrefix, "Override the existing location where engine images are pulled")
|
||||||
flags.BoolVar(&options.downgrades, "downgrades", false, "Report downgrades (default omits older versions)")
|
flags.BoolVar(&options.downgrades, "downgrades", false, "Report downgrades (default omits older versions)")
|
||||||
flags.BoolVar(&options.preReleases, "pre-releases", false, "Include pre-release versions")
|
flags.BoolVar(&options.preReleases, "pre-releases", false, "Include pre-release versions")
|
||||||
|
flags.StringVar(&options.engineImage, "engine-image", "", "Specify engine image (default uses the same image as currently running)")
|
||||||
flags.BoolVar(&options.upgrades, "upgrades", true, "Report available upgrades")
|
flags.BoolVar(&options.upgrades, "upgrades", true, "Report available upgrades")
|
||||||
flags.StringVar(&options.format, "format", "", "Pretty-print updates using a Go template")
|
flags.StringVar(&options.format, "format", "", "Pretty-print updates using a Go template")
|
||||||
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Only display available versions")
|
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Only display available versions")
|
||||||
|
@ -57,7 +59,7 @@ func runCheck(dockerCli command.Cli, options checkOptions) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
availVersions, err := versions.GetEngineVersions(ctx, dockerCli.RegistryClient(false), options.registryPrefix, serverVersion)
|
availVersions, err := versions.GetEngineVersions(ctx, dockerCli.RegistryClient(false), options.registryPrefix, options.engineImage, serverVersion.Version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,14 +22,20 @@ var (
|
||||||
|
|
||||||
type verClient struct {
|
type verClient struct {
|
||||||
client.Client
|
client.Client
|
||||||
ver types.Version
|
ver types.Version
|
||||||
verErr error
|
verErr error
|
||||||
|
info types.Info
|
||||||
|
infoErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *verClient) ServerVersion(ctx context.Context) (types.Version, error) {
|
func (c *verClient) ServerVersion(ctx context.Context) (types.Version, error) {
|
||||||
return c.ver, c.verErr
|
return c.ver, c.verErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *verClient) Info(ctx context.Context) (types.Info, error) {
|
||||||
|
return c.info, c.infoErr
|
||||||
|
}
|
||||||
|
|
||||||
type testRegistryClient struct {
|
type testRegistryClient struct {
|
||||||
tags []string
|
tags []string
|
||||||
}
|
}
|
||||||
|
@ -53,26 +59,28 @@ func (c testRegistryClient) GetTags(ctx context.Context, ref reference.Named) ([
|
||||||
|
|
||||||
func TestCheckForUpdatesNoCurrentVersion(t *testing.T) {
|
func TestCheckForUpdatesNoCurrentVersion(t *testing.T) {
|
||||||
isRoot = func() bool { return true }
|
isRoot = func() bool { return true }
|
||||||
c := test.NewFakeCli(&verClient{client.Client{}, types.Version{}, nil})
|
c := test.NewFakeCli(&verClient{client.Client{}, types.Version{}, nil, types.Info{}, nil})
|
||||||
c.SetRegistryClient(testRegistryClient{})
|
c.SetRegistryClient(testRegistryClient{})
|
||||||
cmd := newCheckForUpdatesCommand(c)
|
cmd := newCheckForUpdatesCommand(c)
|
||||||
cmd.SilenceUsage = true
|
cmd.SilenceUsage = true
|
||||||
cmd.SilenceErrors = true
|
cmd.SilenceErrors = true
|
||||||
err := cmd.Execute()
|
err := cmd.Execute()
|
||||||
assert.ErrorContains(t, err, "alformed version")
|
assert.ErrorContains(t, err, "no such file or directory")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckForUpdatesGetEngineVersionsHappy(t *testing.T) {
|
func TestCheckForUpdatesGetEngineVersionsHappy(t *testing.T) {
|
||||||
c := test.NewFakeCli(&verClient{client.Client{}, types.Version{Version: "1.1.0"}, nil})
|
c := test.NewFakeCli(&verClient{client.Client{}, types.Version{Version: "1.1.0"}, nil, types.Info{ServerVersion: "1.1.0"}, nil})
|
||||||
c.SetRegistryClient(testRegistryClient{[]string{
|
c.SetRegistryClient(testRegistryClient{[]string{
|
||||||
"1.0.1", "1.0.2", "1.0.3-beta1",
|
"1.0.1", "1.0.2", "1.0.3-beta1",
|
||||||
"1.1.1", "1.1.2", "1.1.3-beta1",
|
"1.1.1", "1.1.2", "1.1.3-beta1",
|
||||||
"1.2.0", "2.0.0", "2.1.0-beta1",
|
"1.2.0", "2.0.0", "2.1.0-beta1",
|
||||||
}})
|
}})
|
||||||
|
|
||||||
isRoot = func() bool { return true }
|
isRoot = func() bool { return true }
|
||||||
cmd := newCheckForUpdatesCommand(c)
|
cmd := newCheckForUpdatesCommand(c)
|
||||||
cmd.Flags().Set("pre-releases", "true")
|
cmd.Flags().Set("pre-releases", "true")
|
||||||
cmd.Flags().Set("downgrades", "true")
|
cmd.Flags().Set("downgrades", "true")
|
||||||
|
cmd.Flags().Set("engine-image", "engine-community")
|
||||||
cmd.SilenceUsage = true
|
cmd.SilenceUsage = true
|
||||||
cmd.SilenceErrors = true
|
cmd.SilenceErrors = true
|
||||||
err := cmd.Execute()
|
err := cmd.Execute()
|
||||||
|
|
|
@ -25,7 +25,7 @@ func newUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
flags := cmd.Flags()
|
flags := cmd.Flags()
|
||||||
|
|
||||||
flags.StringVar(&options.EngineVersion, "version", "", "Specify engine version")
|
flags.StringVar(&options.EngineVersion, "version", "", "Specify engine version")
|
||||||
flags.StringVar(&options.EngineImage, "engine-image", "", "Specify engine image")
|
flags.StringVar(&options.EngineImage, "engine-image", "", "Specify engine image (default uses the same image as currently running)")
|
||||||
flags.StringVar(&options.RegistryPrefix, "registry-prefix", clitypes.RegistryPrefix, "Override the current location where engine images are pulled")
|
flags.StringVar(&options.RegistryPrefix, "registry-prefix", clitypes.RegistryPrefix, "Override the current location where engine images are pulled")
|
||||||
flags.StringVar(&options.sockPath, "containerd", "", "override default location of containerd endpoint")
|
flags.StringVar(&options.sockPath, "containerd", "", "override default location of containerd endpoint")
|
||||||
|
|
||||||
|
@ -46,7 +46,6 @@ func runUpdate(dockerCli command.Cli, options extendedEngineInitOptions) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := client.DoUpdate(ctx, options.EngineInitOptions, dockerCli.Out(), authConfig,
|
if err := client.DoUpdate(ctx, options.EngineInitOptions, dockerCli.Out(), authConfig,
|
||||||
func(ctx context.Context) error {
|
func(ctx context.Context) error {
|
||||||
client := dockerCli.Client()
|
client := dockerCli.Client()
|
||||||
|
|
|
@ -4,7 +4,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/cli/internal/test"
|
||||||
clitypes "github.com/docker/cli/types"
|
clitypes "github.com/docker/cli/types"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/client"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,14 +25,16 @@ func TestUpdateNoContainerd(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateHappy(t *testing.T) {
|
func TestUpdateHappy(t *testing.T) {
|
||||||
testCli.SetContainerizedEngineClient(
|
c := test.NewFakeCli(&verClient{client.Client{}, types.Version{Version: "1.1.0"}, nil, types.Info{ServerVersion: "1.1.0"}, nil})
|
||||||
|
c.SetContainerizedEngineClient(
|
||||||
func(string) (clitypes.ContainerizedClient, error) {
|
func(string) (clitypes.ContainerizedClient, error) {
|
||||||
return &fakeContainerizedEngineClient{}, nil
|
return &fakeContainerizedEngineClient{}, nil
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
cmd := newUpdateCommand(testCli)
|
cmd := newUpdateCommand(c)
|
||||||
cmd.Flags().Set("registry-prefix", clitypes.RegistryPrefix)
|
cmd.Flags().Set("registry-prefix", clitypes.RegistryPrefix)
|
||||||
cmd.Flags().Set("version", "someversion")
|
cmd.Flags().Set("version", "someversion")
|
||||||
|
cmd.Flags().Set("engine-image", "someimage")
|
||||||
err := cmd.Execute()
|
err := cmd.Execute()
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,10 +12,6 @@ import (
|
||||||
const (
|
const (
|
||||||
containerdSockPath = "/run/containerd/containerd.sock"
|
containerdSockPath = "/run/containerd/containerd.sock"
|
||||||
engineNamespace = "com.docker"
|
engineNamespace = "com.docker"
|
||||||
|
|
||||||
// runtimeMetadataName is the name of the runtime metadata file
|
|
||||||
// When stored as a label on the container it is prefixed by "com.docker."
|
|
||||||
runtimeMetadataName = "distribution_based_engine"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -51,10 +47,3 @@ type containerdClient interface {
|
||||||
Install(context.Context, containerd.Image, ...containerd.InstallOpts) error
|
Install(context.Context, containerd.Image, ...containerd.InstallOpts) error
|
||||||
Version(ctx context.Context) (containerd.Version, error)
|
Version(ctx context.Context) (containerd.Version, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RuntimeMetadata holds platform information about the daemon
|
|
||||||
type RuntimeMetadata struct {
|
|
||||||
Platform string `json:"platform"`
|
|
||||||
ContainerdMinVersion string `json:"containerd_min_version"`
|
|
||||||
Runtime string `json:"runtime"`
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,9 +4,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
|
@ -14,6 +11,7 @@ import (
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
"github.com/containerd/containerd/images"
|
"github.com/containerd/containerd/images"
|
||||||
"github.com/containerd/containerd/namespaces"
|
"github.com/containerd/containerd/namespaces"
|
||||||
|
"github.com/docker/cli/internal/versions"
|
||||||
clitypes "github.com/docker/cli/types"
|
clitypes "github.com/docker/cli/types"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
@ -26,6 +24,25 @@ import (
|
||||||
func (c *baseClient) ActivateEngine(ctx context.Context, opts clitypes.EngineInitOptions, out clitypes.OutStream,
|
func (c *baseClient) ActivateEngine(ctx context.Context, opts clitypes.EngineInitOptions, out clitypes.OutStream,
|
||||||
authConfig *types.AuthConfig, healthfn func(context.Context) error) error {
|
authConfig *types.AuthConfig, healthfn func(context.Context) error) error {
|
||||||
|
|
||||||
|
// If the user didn't specify an image, determine the correct enterprise image to use
|
||||||
|
if opts.EngineImage == "" {
|
||||||
|
localMetadata, err := versions.GetCurrentRuntimeMetadata(opts.RuntimeMetadataDir)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "unable to determine the installed engine version. Specify which engine image to update with --engine-image")
|
||||||
|
}
|
||||||
|
|
||||||
|
engineImage := localMetadata.EngineImage
|
||||||
|
if engineImage == clitypes.EnterpriseEngineImage || engineImage == clitypes.CommunityEngineImage {
|
||||||
|
opts.EngineImage = clitypes.EnterpriseEngineImage
|
||||||
|
} else {
|
||||||
|
// Chop off the standard prefix and retain any trailing OS specific image details
|
||||||
|
// e.g., engine-community-dm -> engine-enterprise-dm
|
||||||
|
engineImage = strings.TrimPrefix(engineImage, clitypes.EnterpriseEngineImage)
|
||||||
|
engineImage = strings.TrimPrefix(engineImage, clitypes.CommunityEngineImage)
|
||||||
|
opts.EngineImage = clitypes.EnterpriseEngineImage + engineImage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx = namespaces.WithNamespace(ctx, engineNamespace)
|
ctx = namespaces.WithNamespace(ctx, engineNamespace)
|
||||||
return c.DoUpdate(ctx, opts, out, authConfig, healthfn)
|
return c.DoUpdate(ctx, opts, out, authConfig, healthfn)
|
||||||
}
|
}
|
||||||
|
@ -43,19 +60,14 @@ func (c *baseClient) DoUpdate(ctx context.Context, opts clitypes.EngineInitOptio
|
||||||
// `docker engine update`
|
// `docker engine update`
|
||||||
return fmt.Errorf("pick the version you want to update to with --version")
|
return fmt.Errorf("pick the version you want to update to with --version")
|
||||||
}
|
}
|
||||||
|
var localMetadata *clitypes.RuntimeMetadata
|
||||||
localMetadata, err := c.GetCurrentRuntimeMetadata(ctx, "")
|
|
||||||
if err == nil {
|
|
||||||
if opts.EngineImage == "" {
|
|
||||||
if strings.Contains(strings.ToLower(localMetadata.Platform), "community") {
|
|
||||||
opts.EngineImage = clitypes.CommunityEngineImage
|
|
||||||
} else {
|
|
||||||
opts.EngineImage = clitypes.EnterpriseEngineImage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if opts.EngineImage == "" {
|
if opts.EngineImage == "" {
|
||||||
return fmt.Errorf("unable to determine the installed engine version. Specify which engine image to update with --engine-image set to 'engine-community' or 'engine-enterprise'")
|
var err error
|
||||||
|
localMetadata, err = versions.GetCurrentRuntimeMetadata(opts.RuntimeMetadataDir)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "unable to determine the installed engine version. Specify which engine image to update with --engine-image set to 'engine-community' or 'engine-enterprise'")
|
||||||
|
}
|
||||||
|
opts.EngineImage = localMetadata.EngineImage
|
||||||
}
|
}
|
||||||
|
|
||||||
imageName := fmt.Sprintf("%s/%s:%s", opts.RegistryPrefix, opts.EngineImage, opts.EngineVersion)
|
imageName := fmt.Sprintf("%s/%s:%s", opts.RegistryPrefix, opts.EngineImage, opts.EngineVersion)
|
||||||
|
@ -78,7 +90,6 @@ func (c *baseClient) DoUpdate(ctx context.Context, opts clitypes.EngineInitOptio
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Grab current metadata for comparison purposes
|
|
||||||
if localMetadata != nil {
|
if localMetadata != nil {
|
||||||
if localMetadata.Platform != newMetadata.Platform {
|
if localMetadata.Platform != newMetadata.Platform {
|
||||||
fmt.Fprintf(out, "\nNotice: you have switched to \"%s\". Refer to %s for update instructions.\n\n", newMetadata.Platform, getReleaseNotesURL(imageName))
|
fmt.Fprintf(out, "\nNotice: you have switched to \"%s\". Refer to %s for update instructions.\n\n", newMetadata.Platform, getReleaseNotesURL(imageName))
|
||||||
|
@ -89,50 +100,13 @@ func (c *baseClient) DoUpdate(ctx context.Context, opts clitypes.EngineInitOptio
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.WriteRuntimeMetadata("", newMetadata)
|
return versions.WriteRuntimeMetadata(opts.RuntimeMetadataDir, newMetadata)
|
||||||
}
|
|
||||||
|
|
||||||
var defaultDockerRoot = "/var/lib/docker"
|
|
||||||
|
|
||||||
// GetCurrentRuntimeMetadata loads the current daemon runtime metadata information from the local host
|
|
||||||
func (c *baseClient) GetCurrentRuntimeMetadata(_ context.Context, dockerRoot string) (*RuntimeMetadata, error) {
|
|
||||||
if dockerRoot == "" {
|
|
||||||
dockerRoot = defaultDockerRoot
|
|
||||||
}
|
|
||||||
filename := filepath.Join(dockerRoot, runtimeMetadataName+".json")
|
|
||||||
|
|
||||||
data, err := ioutil.ReadFile(filename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var res RuntimeMetadata
|
|
||||||
err = json.Unmarshal(data, &res)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrapf(err, "malformed runtime metadata file %s", filename)
|
|
||||||
}
|
|
||||||
return &res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteRuntimeMetadata stores the metadata on the local system
|
|
||||||
func (c *baseClient) WriteRuntimeMetadata(dockerRoot string, metadata *RuntimeMetadata) error {
|
|
||||||
if dockerRoot == "" {
|
|
||||||
dockerRoot = defaultDockerRoot
|
|
||||||
}
|
|
||||||
filename := filepath.Join(dockerRoot, runtimeMetadataName+".json")
|
|
||||||
|
|
||||||
data, err := json.Marshal(metadata)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Remove(filename)
|
|
||||||
return ioutil.WriteFile(filename, data, 0644)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PreflightCheck verifies the specified image is compatible with the local system before proceeding to update/activate
|
// PreflightCheck verifies the specified image is compatible with the local system before proceeding to update/activate
|
||||||
// If things look good, the RuntimeMetadata for the new image is returned and can be written out to the host
|
// If things look good, the RuntimeMetadata for the new image is returned and can be written out to the host
|
||||||
func (c *baseClient) PreflightCheck(ctx context.Context, image containerd.Image) (*RuntimeMetadata, error) {
|
func (c *baseClient) PreflightCheck(ctx context.Context, image containerd.Image) (*clitypes.RuntimeMetadata, error) {
|
||||||
var metadata RuntimeMetadata
|
var metadata clitypes.RuntimeMetadata
|
||||||
ic, err := image.Config(ctx)
|
ic, err := image.Config(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -156,9 +130,9 @@ func (c *baseClient) PreflightCheck(ctx context.Context, image containerd.Image)
|
||||||
return nil, fmt.Errorf("unknown image %s config media type %s", image.Name(), ic.MediaType)
|
return nil, fmt.Errorf("unknown image %s config media type %s", image.Name(), ic.MediaType)
|
||||||
}
|
}
|
||||||
|
|
||||||
metadataString, ok := config.Labels["com.docker."+runtimeMetadataName]
|
metadataString, ok := config.Labels["com.docker."+clitypes.RuntimeMetadataName]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("image %s does not contain runtime metadata label %s", image.Name(), runtimeMetadataName)
|
return nil, fmt.Errorf("image %s does not contain runtime metadata label %s", image.Name(), clitypes.RuntimeMetadataName)
|
||||||
}
|
}
|
||||||
err = json.Unmarshal([]byte(metadataString), &metadata)
|
err = json.Unmarshal([]byte(metadataString), &metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -6,13 +6,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
"github.com/containerd/containerd/cio"
|
"github.com/containerd/containerd/cio"
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
|
"github.com/docker/cli/internal/versions"
|
||||||
clitypes "github.com/docker/cli/types"
|
clitypes "github.com/docker/cli/types"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
@ -23,6 +23,51 @@ func healthfnHappy(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestActivateImagePermutations(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
lookedup := "not called yet"
|
||||||
|
expectedError := fmt.Errorf("expected error")
|
||||||
|
client := baseClient{
|
||||||
|
cclient: &fakeContainerdClient{
|
||||||
|
getImageFunc: func(ctx context.Context, ref string) (containerd.Image, error) {
|
||||||
|
lookedup = ref
|
||||||
|
return nil, expectedError
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tmpdir, err := ioutil.TempDir("", "enginedir")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
metadata := clitypes.RuntimeMetadata{EngineImage: clitypes.EnterpriseEngineImage}
|
||||||
|
err = versions.WriteRuntimeMetadata(tmpdir, &metadata)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
opts := clitypes.EngineInitOptions{
|
||||||
|
EngineVersion: "engineversiongoeshere",
|
||||||
|
RegistryPrefix: "registryprefixgoeshere",
|
||||||
|
ConfigFile: "/tmp/configfilegoeshere",
|
||||||
|
RuntimeMetadataDir: tmpdir,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = client.ActivateEngine(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}, healthfnHappy)
|
||||||
|
assert.ErrorContains(t, err, expectedError.Error())
|
||||||
|
assert.Equal(t, lookedup, fmt.Sprintf("%s/%s:%s", opts.RegistryPrefix, clitypes.EnterpriseEngineImage, opts.EngineVersion))
|
||||||
|
|
||||||
|
metadata = clitypes.RuntimeMetadata{EngineImage: clitypes.CommunityEngineImage}
|
||||||
|
err = versions.WriteRuntimeMetadata(tmpdir, &metadata)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
err = client.ActivateEngine(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}, healthfnHappy)
|
||||||
|
assert.ErrorContains(t, err, expectedError.Error())
|
||||||
|
assert.Equal(t, lookedup, fmt.Sprintf("%s/%s:%s", opts.RegistryPrefix, clitypes.EnterpriseEngineImage, opts.EngineVersion))
|
||||||
|
|
||||||
|
metadata = clitypes.RuntimeMetadata{EngineImage: clitypes.CommunityEngineImage + "-dm"}
|
||||||
|
err = versions.WriteRuntimeMetadata(tmpdir, &metadata)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
err = client.ActivateEngine(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}, healthfnHappy)
|
||||||
|
assert.ErrorContains(t, err, expectedError.Error())
|
||||||
|
assert.Equal(t, lookedup, fmt.Sprintf("%s/%s:%s", opts.RegistryPrefix, clitypes.EnterpriseEngineImage+"-dm", opts.EngineVersion))
|
||||||
|
}
|
||||||
|
|
||||||
func TestActivateConfigFailure(t *testing.T) {
|
func TestActivateConfigFailure(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
registryPrefix := "registryprefixgoeshere"
|
registryPrefix := "registryprefixgoeshere"
|
||||||
|
@ -55,14 +100,21 @@ func TestActivateConfigFailure(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
tmpdir, err := ioutil.TempDir("", "engindir")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
metadata := clitypes.RuntimeMetadata{EngineImage: clitypes.CommunityEngineImage}
|
||||||
|
err = versions.WriteRuntimeMetadata(tmpdir, &metadata)
|
||||||
|
assert.NilError(t, err)
|
||||||
opts := clitypes.EngineInitOptions{
|
opts := clitypes.EngineInitOptions{
|
||||||
EngineVersion: "engineversiongoeshere",
|
EngineVersion: "engineversiongoeshere",
|
||||||
RegistryPrefix: "registryprefixgoeshere",
|
RegistryPrefix: "registryprefixgoeshere",
|
||||||
ConfigFile: "/tmp/configfilegoeshere",
|
ConfigFile: "/tmp/configfilegoeshere",
|
||||||
EngineImage: clitypes.EnterpriseEngineImage,
|
EngineImage: clitypes.EnterpriseEngineImage,
|
||||||
|
RuntimeMetadataDir: tmpdir,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := client.ActivateEngine(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}, healthfnHappy)
|
err = client.ActivateEngine(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}, healthfnHappy)
|
||||||
assert.ErrorContains(t, err, "config lookup failure")
|
assert.ErrorContains(t, err, "config lookup failure")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,38 +142,60 @@ func TestActivateDoUpdateFail(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
tmpdir, err := ioutil.TempDir("", "enginedir")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
metadata := clitypes.RuntimeMetadata{EngineImage: clitypes.CommunityEngineImage}
|
||||||
|
err = versions.WriteRuntimeMetadata(tmpdir, &metadata)
|
||||||
|
assert.NilError(t, err)
|
||||||
opts := clitypes.EngineInitOptions{
|
opts := clitypes.EngineInitOptions{
|
||||||
EngineVersion: "engineversiongoeshere",
|
EngineVersion: "engineversiongoeshere",
|
||||||
RegistryPrefix: "registryprefixgoeshere",
|
RegistryPrefix: "registryprefixgoeshere",
|
||||||
ConfigFile: "/tmp/configfilegoeshere",
|
ConfigFile: "/tmp/configfilegoeshere",
|
||||||
EngineImage: clitypes.EnterpriseEngineImage,
|
EngineImage: clitypes.EnterpriseEngineImage,
|
||||||
|
RuntimeMetadataDir: tmpdir,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := client.ActivateEngine(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}, healthfnHappy)
|
err = client.ActivateEngine(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}, healthfnHappy)
|
||||||
assert.ErrorContains(t, err, "check for image")
|
assert.ErrorContains(t, err, "check for image")
|
||||||
assert.ErrorContains(t, err, "something went wrong")
|
assert.ErrorContains(t, err, "something went wrong")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDoUpdateNoVersion(t *testing.T) {
|
func TestDoUpdateNoVersion(t *testing.T) {
|
||||||
|
tmpdir, err := ioutil.TempDir("", "enginedir")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
metadata := clitypes.RuntimeMetadata{EngineImage: clitypes.EnterpriseEngineImage}
|
||||||
|
err = versions.WriteRuntimeMetadata(tmpdir, &metadata)
|
||||||
|
assert.NilError(t, err)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
opts := clitypes.EngineInitOptions{
|
opts := clitypes.EngineInitOptions{
|
||||||
EngineVersion: "",
|
EngineVersion: "",
|
||||||
RegistryPrefix: "registryprefixgoeshere",
|
RegistryPrefix: "registryprefixgoeshere",
|
||||||
ConfigFile: "/tmp/configfilegoeshere",
|
ConfigFile: "/tmp/configfilegoeshere",
|
||||||
EngineImage: clitypes.EnterpriseEngineImage,
|
EngineImage: clitypes.EnterpriseEngineImage,
|
||||||
|
RuntimeMetadataDir: tmpdir,
|
||||||
}
|
}
|
||||||
|
|
||||||
client := baseClient{}
|
client := baseClient{}
|
||||||
err := client.DoUpdate(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}, healthfnHappy)
|
err = client.DoUpdate(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}, healthfnHappy)
|
||||||
assert.ErrorContains(t, err, "pick the version you")
|
assert.ErrorContains(t, err, "pick the version you")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDoUpdateImageMiscError(t *testing.T) {
|
func TestDoUpdateImageMiscError(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
tmpdir, err := ioutil.TempDir("", "enginedir")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
metadata := clitypes.RuntimeMetadata{EngineImage: clitypes.EnterpriseEngineImage}
|
||||||
|
err = versions.WriteRuntimeMetadata(tmpdir, &metadata)
|
||||||
|
assert.NilError(t, err)
|
||||||
opts := clitypes.EngineInitOptions{
|
opts := clitypes.EngineInitOptions{
|
||||||
EngineVersion: "engineversiongoeshere",
|
EngineVersion: "engineversiongoeshere",
|
||||||
RegistryPrefix: "registryprefixgoeshere",
|
RegistryPrefix: "registryprefixgoeshere",
|
||||||
ConfigFile: "/tmp/configfilegoeshere",
|
ConfigFile: "/tmp/configfilegoeshere",
|
||||||
EngineImage: "testnamegoeshere",
|
EngineImage: "testnamegoeshere",
|
||||||
|
RuntimeMetadataDir: tmpdir,
|
||||||
}
|
}
|
||||||
client := baseClient{
|
client := baseClient{
|
||||||
cclient: &fakeContainerdClient{
|
cclient: &fakeContainerdClient{
|
||||||
|
@ -131,18 +205,26 @@ func TestDoUpdateImageMiscError(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := client.DoUpdate(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}, healthfnHappy)
|
|
||||||
|
err = client.DoUpdate(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}, healthfnHappy)
|
||||||
assert.ErrorContains(t, err, "check for image")
|
assert.ErrorContains(t, err, "check for image")
|
||||||
assert.ErrorContains(t, err, "something went wrong")
|
assert.ErrorContains(t, err, "something went wrong")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDoUpdatePullFail(t *testing.T) {
|
func TestDoUpdatePullFail(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
tmpdir, err := ioutil.TempDir("", "enginedir")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
metadata := clitypes.RuntimeMetadata{EngineImage: clitypes.EnterpriseEngineImage}
|
||||||
|
err = versions.WriteRuntimeMetadata(tmpdir, &metadata)
|
||||||
|
assert.NilError(t, err)
|
||||||
opts := clitypes.EngineInitOptions{
|
opts := clitypes.EngineInitOptions{
|
||||||
EngineVersion: "engineversiongoeshere",
|
EngineVersion: "engineversiongoeshere",
|
||||||
RegistryPrefix: "registryprefixgoeshere",
|
RegistryPrefix: "registryprefixgoeshere",
|
||||||
ConfigFile: "/tmp/configfilegoeshere",
|
ConfigFile: "/tmp/configfilegoeshere",
|
||||||
EngineImage: "testnamegoeshere",
|
EngineImage: "testnamegoeshere",
|
||||||
|
RuntimeMetadataDir: tmpdir,
|
||||||
}
|
}
|
||||||
client := baseClient{
|
client := baseClient{
|
||||||
cclient: &fakeContainerdClient{
|
cclient: &fakeContainerdClient{
|
||||||
|
@ -155,7 +237,8 @@ func TestDoUpdatePullFail(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := client.DoUpdate(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}, healthfnHappy)
|
|
||||||
|
err = client.DoUpdate(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}, healthfnHappy)
|
||||||
assert.ErrorContains(t, err, "unable to pull")
|
assert.ErrorContains(t, err, "unable to pull")
|
||||||
assert.ErrorContains(t, err, "pull failure")
|
assert.ErrorContains(t, err, "pull failure")
|
||||||
}
|
}
|
||||||
|
@ -186,78 +269,26 @@ func TestActivateDoUpdateVerifyImageName(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
tmpdir, err := ioutil.TempDir("", "enginedir")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
metadata := clitypes.RuntimeMetadata{EngineImage: clitypes.EnterpriseEngineImage}
|
||||||
|
err = versions.WriteRuntimeMetadata(tmpdir, &metadata)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
opts := clitypes.EngineInitOptions{
|
opts := clitypes.EngineInitOptions{
|
||||||
EngineVersion: "engineversiongoeshere",
|
EngineVersion: "engineversiongoeshere",
|
||||||
RegistryPrefix: "registryprefixgoeshere",
|
RegistryPrefix: "registryprefixgoeshere",
|
||||||
ConfigFile: "/tmp/configfilegoeshere",
|
EngineImage: "testnamegoeshere",
|
||||||
|
ConfigFile: "/tmp/configfilegoeshere",
|
||||||
|
RuntimeMetadataDir: tmpdir,
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpdir, err := ioutil.TempDir("", "docker-root")
|
|
||||||
assert.NilError(t, err)
|
|
||||||
defer os.RemoveAll(tmpdir)
|
|
||||||
tmpDockerRoot := defaultDockerRoot
|
|
||||||
defaultDockerRoot = tmpdir
|
|
||||||
defer func() {
|
|
||||||
defaultDockerRoot = tmpDockerRoot
|
|
||||||
}()
|
|
||||||
metadata := RuntimeMetadata{Platform: "platformgoeshere"}
|
|
||||||
err = client.WriteRuntimeMetadata(tmpdir, &metadata)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
|
|
||||||
err = client.ActivateEngine(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}, healthfnHappy)
|
err = client.ActivateEngine(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}, healthfnHappy)
|
||||||
assert.ErrorContains(t, err, "check for image")
|
assert.ErrorContains(t, err, "check for image")
|
||||||
assert.ErrorContains(t, err, "something went wrong")
|
assert.ErrorContains(t, err, "something went wrong")
|
||||||
expectedImage := fmt.Sprintf("%s/%s:%s", opts.RegistryPrefix, "engine-enterprise", opts.EngineVersion)
|
expectedImage := fmt.Sprintf("%s/%s:%s", opts.RegistryPrefix, opts.EngineImage, opts.EngineVersion)
|
||||||
assert.Assert(t, requestedImage == expectedImage, "%s != %s", requestedImage, expectedImage)
|
assert.Assert(t, requestedImage == expectedImage, "%s != %s", requestedImage, expectedImage)
|
||||||
|
|
||||||
// Redo with enterprise set
|
|
||||||
metadata = RuntimeMetadata{Platform: "Docker Engine - Enterprise"}
|
|
||||||
err = client.WriteRuntimeMetadata(tmpdir, &metadata)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
|
|
||||||
err = client.ActivateEngine(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}, healthfnHappy)
|
|
||||||
assert.ErrorContains(t, err, "check for image")
|
|
||||||
assert.ErrorContains(t, err, "something went wrong")
|
|
||||||
expectedImage = fmt.Sprintf("%s/%s:%s", opts.RegistryPrefix, "engine-enterprise", opts.EngineVersion)
|
|
||||||
assert.Assert(t, requestedImage == expectedImage, "%s != %s", requestedImage, expectedImage)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetCurrentRuntimeMetadataNotPresent(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
tmpdir, err := ioutil.TempDir("", "docker-root")
|
|
||||||
assert.NilError(t, err)
|
|
||||||
defer os.RemoveAll(tmpdir)
|
|
||||||
client := baseClient{}
|
|
||||||
_, err = client.GetCurrentRuntimeMetadata(ctx, tmpdir)
|
|
||||||
assert.ErrorType(t, err, os.IsNotExist)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetCurrentRuntimeMetadataBadJson(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
tmpdir, err := ioutil.TempDir("", "docker-root")
|
|
||||||
assert.NilError(t, err)
|
|
||||||
defer os.RemoveAll(tmpdir)
|
|
||||||
filename := filepath.Join(tmpdir, runtimeMetadataName+".json")
|
|
||||||
err = ioutil.WriteFile(filename, []byte("not json"), 0644)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
client := baseClient{}
|
|
||||||
_, err = client.GetCurrentRuntimeMetadata(ctx, tmpdir)
|
|
||||||
assert.ErrorContains(t, err, "malformed runtime metadata file")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetCurrentRuntimeMetadataHappyPath(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
tmpdir, err := ioutil.TempDir("", "docker-root")
|
|
||||||
assert.NilError(t, err)
|
|
||||||
defer os.RemoveAll(tmpdir)
|
|
||||||
client := baseClient{}
|
|
||||||
metadata := RuntimeMetadata{Platform: "platformgoeshere"}
|
|
||||||
err = client.WriteRuntimeMetadata(tmpdir, &metadata)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
|
|
||||||
res, err := client.GetCurrentRuntimeMetadata(ctx, tmpdir)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
assert.Equal(t, res.Platform, "platformgoeshere")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetReleaseNotesURL(t *testing.T) {
|
func TestGetReleaseNotesURL(t *testing.T) {
|
||||||
|
|
|
@ -2,23 +2,38 @@ package versions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
|
||||||
|
|
||||||
registryclient "github.com/docker/cli/cli/registry/client"
|
registryclient "github.com/docker/cli/cli/registry/client"
|
||||||
clitypes "github.com/docker/cli/types"
|
clitypes "github.com/docker/cli/types"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
"github.com/docker/docker/api/types"
|
|
||||||
ver "github.com/hashicorp/go-version"
|
ver "github.com/hashicorp/go-version"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// defaultRuntimeMetadataDir is the location where the metadata file is stored
|
||||||
|
defaultRuntimeMetadataDir = "/var/lib/docker-engine"
|
||||||
|
)
|
||||||
|
|
||||||
// GetEngineVersions reports the versions of the engine that are available
|
// GetEngineVersions reports the versions of the engine that are available
|
||||||
func GetEngineVersions(ctx context.Context, registryClient registryclient.RegistryClient, registryPrefix string, serverVersion types.Version) (clitypes.AvailableVersions, error) {
|
func GetEngineVersions(ctx context.Context, registryClient registryclient.RegistryClient, registryPrefix, imageName, versionString string) (clitypes.AvailableVersions, error) {
|
||||||
imageName := getEngineImage(registryPrefix, serverVersion)
|
|
||||||
imageRef, err := reference.ParseNormalizedNamed(imageName)
|
if imageName == "" {
|
||||||
|
var err error
|
||||||
|
localMetadata, err := GetCurrentRuntimeMetadata("")
|
||||||
|
if err != nil {
|
||||||
|
return clitypes.AvailableVersions{}, err
|
||||||
|
}
|
||||||
|
imageName = localMetadata.EngineImage
|
||||||
|
}
|
||||||
|
imageRef, err := reference.ParseNormalizedNamed(path.Join(registryPrefix, imageName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return clitypes.AvailableVersions{}, err
|
return clitypes.AvailableVersions{}, err
|
||||||
}
|
}
|
||||||
|
@ -28,25 +43,7 @@ func GetEngineVersions(ctx context.Context, registryClient registryclient.Regist
|
||||||
return clitypes.AvailableVersions{}, err
|
return clitypes.AvailableVersions{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseTags(tags, serverVersion.Version)
|
return parseTags(tags, versionString)
|
||||||
}
|
|
||||||
|
|
||||||
func getEngineImage(registryPrefix string, serverVersion types.Version) string {
|
|
||||||
platform := strings.ToLower(serverVersion.Platform.Name)
|
|
||||||
if platform != "" {
|
|
||||||
if strings.Contains(platform, "enterprise") {
|
|
||||||
return path.Join(registryPrefix, clitypes.EnterpriseEngineImage)
|
|
||||||
}
|
|
||||||
return path.Join(registryPrefix, clitypes.CommunityEngineImage)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO This check is only applicable for early 18.09 builds that had some packaging bugs
|
|
||||||
// and can be removed once we're no longer testing with them
|
|
||||||
if strings.Contains(serverVersion.Version, "ee") {
|
|
||||||
return path.Join(registryPrefix, clitypes.EnterpriseEngineImage)
|
|
||||||
}
|
|
||||||
|
|
||||||
return path.Join(registryPrefix, clitypes.CommunityEngineImage)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseTags(tags []string, currentVersion string) (clitypes.AvailableVersions, error) {
|
func parseTags(tags []string, currentVersion string) (clitypes.AvailableVersions, error) {
|
||||||
|
@ -93,3 +90,38 @@ func parseTags(tags []string, currentVersion string) (clitypes.AvailableVersions
|
||||||
ret.Upgrades = upgrades
|
ret.Upgrades = upgrades
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCurrentRuntimeMetadata loads the current daemon runtime metadata information from the local host
|
||||||
|
func GetCurrentRuntimeMetadata(metadataDir string) (*clitypes.RuntimeMetadata, error) {
|
||||||
|
if metadataDir == "" {
|
||||||
|
metadataDir = defaultRuntimeMetadataDir
|
||||||
|
}
|
||||||
|
filename := filepath.Join(metadataDir, clitypes.RuntimeMetadataName+".json")
|
||||||
|
|
||||||
|
data, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var res clitypes.RuntimeMetadata
|
||||||
|
err = json.Unmarshal(data, &res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "malformed runtime metadata file %s", filename)
|
||||||
|
}
|
||||||
|
return &res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteRuntimeMetadata stores the metadata on the local system
|
||||||
|
func WriteRuntimeMetadata(metadataDir string, metadata *clitypes.RuntimeMetadata) error {
|
||||||
|
if metadataDir == "" {
|
||||||
|
metadataDir = defaultRuntimeMetadataDir
|
||||||
|
}
|
||||||
|
filename := filepath.Join(metadataDir, clitypes.RuntimeMetadataName+".json")
|
||||||
|
|
||||||
|
data, err := json.Marshal(metadata)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Remove(filename)
|
||||||
|
return ioutil.WriteFile(filename, data, 0644)
|
||||||
|
}
|
||||||
|
|
|
@ -1,22 +1,15 @@
|
||||||
package versions
|
package versions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
clitypes "github.com/docker/cli/types"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetEngineVersionsBadImage(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
registryPrefix := "this is an illegal image $%^&"
|
|
||||||
currentVersion := types.Version{Version: "currentversiongoeshere"}
|
|
||||||
_, err := GetEngineVersions(ctx, nil, registryPrefix, currentVersion)
|
|
||||||
assert.ErrorContains(t, err, "invalid reference format")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseTagsSimple(t *testing.T) {
|
func TestParseTagsSimple(t *testing.T) {
|
||||||
tags := []string{"1.0.0", "1.1.2", "1.1.1", "1.2.2"}
|
tags := []string{"1.0.0", "1.1.2", "1.1.1", "1.2.2"}
|
||||||
currentVersion := "1.1.0"
|
currentVersion := "1.1.0"
|
||||||
|
@ -78,3 +71,35 @@ func TestParseBadCurrent2(t *testing.T) {
|
||||||
_, err := parseTags(tags, currentVersion)
|
_, err := parseTags(tags, currentVersion)
|
||||||
assert.ErrorContains(t, err, "failed to parse existing")
|
assert.ErrorContains(t, err, "failed to parse existing")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetCurrentRuntimeMetadataNotPresent(t *testing.T) {
|
||||||
|
tmpdir, err := ioutil.TempDir("", "docker-root")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
_, err = GetCurrentRuntimeMetadata(tmpdir)
|
||||||
|
assert.ErrorType(t, err, os.IsNotExist)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetCurrentRuntimeMetadataBadJson(t *testing.T) {
|
||||||
|
tmpdir, err := ioutil.TempDir("", "docker-root")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
filename := filepath.Join(tmpdir, clitypes.RuntimeMetadataName+".json")
|
||||||
|
err = ioutil.WriteFile(filename, []byte("not json"), 0644)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
_, err = GetCurrentRuntimeMetadata(tmpdir)
|
||||||
|
assert.ErrorContains(t, err, "malformed runtime metadata file")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetCurrentRuntimeMetadataHappyPath(t *testing.T) {
|
||||||
|
tmpdir, err := ioutil.TempDir("", "docker-root")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
metadata := clitypes.RuntimeMetadata{Platform: "platformgoeshere"}
|
||||||
|
err = WriteRuntimeMetadata(tmpdir, &metadata)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
res, err := GetCurrentRuntimeMetadata(tmpdir)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Equal(t, res.Platform, "platformgoeshere")
|
||||||
|
}
|
||||||
|
|
|
@ -20,6 +20,10 @@ const (
|
||||||
|
|
||||||
// ReleaseNotePrefix is where to point users to for release notes
|
// ReleaseNotePrefix is where to point users to for release notes
|
||||||
ReleaseNotePrefix = "https://docs.docker.com/releasenotes"
|
ReleaseNotePrefix = "https://docs.docker.com/releasenotes"
|
||||||
|
|
||||||
|
// RuntimeMetadataName is the name of the runtime metadata file
|
||||||
|
// When stored as a label on the container it is prefixed by "com.docker."
|
||||||
|
RuntimeMetadataName = "distribution_based_engine"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ContainerizedClient can be used to manage the lifecycle of
|
// ContainerizedClient can be used to manage the lifecycle of
|
||||||
|
@ -41,10 +45,11 @@ type ContainerizedClient interface {
|
||||||
// EngineInitOptions contains the configuration settings
|
// EngineInitOptions contains the configuration settings
|
||||||
// use during initialization of a containerized docker engine
|
// use during initialization of a containerized docker engine
|
||||||
type EngineInitOptions struct {
|
type EngineInitOptions struct {
|
||||||
RegistryPrefix string
|
RegistryPrefix string
|
||||||
EngineImage string
|
EngineImage string
|
||||||
EngineVersion string
|
EngineVersion string
|
||||||
ConfigFile string
|
ConfigFile string
|
||||||
|
RuntimeMetadataDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
// AvailableVersions groups the available versions which were discovered
|
// AvailableVersions groups the available versions which were discovered
|
||||||
|
@ -75,3 +80,11 @@ type OutStream interface {
|
||||||
FD() uintptr
|
FD() uintptr
|
||||||
IsTerminal() bool
|
IsTerminal() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RuntimeMetadata holds platform information about the daemon
|
||||||
|
type RuntimeMetadata struct {
|
||||||
|
Platform string `json:"platform"`
|
||||||
|
ContainerdMinVersion string `json:"containerd_min_version"`
|
||||||
|
Runtime string `json:"runtime"`
|
||||||
|
EngineImage string `json:"engine_image"`
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue