Переглянути джерело

Merge pull request #46887 from thaJeztah/minimum_api_version

daemon: raise default minimum API version to v1.24
Sebastiaan van Stijn 1 рік тому
батько
коміт
6af9204938

+ 0 - 6
api/common_unix.go

@@ -1,6 +0,0 @@
-//go:build !windows
-
-package api // import "github.com/docker/docker/api"
-
-// MinVersion represents Minimum REST API version supported
-const MinVersion = "1.12"

+ 0 - 8
api/common_windows.go

@@ -1,8 +0,0 @@
-package api // import "github.com/docker/docker/api"
-
-// MinVersion represents Minimum REST API version supported
-// Technically the first daemon API version released on Windows is v1.25 in
-// engine version 1.13. However, some clients are explicitly using downlevel
-// APIs (e.g. docker-compose v2.1 file format) and that is just too restrictive.
-// Hence also allowing 1.24 on Windows.
-const MinVersion string = "1.24"

+ 2 - 1
api/server/server_test.go

@@ -15,7 +15,8 @@ import (
 func TestMiddlewares(t *testing.T) {
 	srv := &Server{}
 
-	srv.UseMiddleware(middleware.NewVersionMiddleware("0.1omega2", api.DefaultVersion, api.MinVersion))
+	const apiMinVersion = "1.12"
+	srv.UseMiddleware(middleware.NewVersionMiddleware("0.1omega2", api.DefaultVersion, apiMinVersion))
 
 	req, _ := http.NewRequest(http.MethodGet, "/containers/json", nil)
 	resp := httptest.NewRecorder()

+ 8 - 1
client/client.go

@@ -90,6 +90,13 @@ import (
 // [Go stdlib]: https://github.com/golang/go/blob/6244b1946bc2101b01955468f1be502dbadd6807/src/net/http/transport.go#L558-L569
 const DummyHost = "api.moby.localhost"
 
+// fallbackAPIVersion is the version to fallback to if API-version negotiation
+// fails. This version is the highest version of the API before API-version
+// negotiation was introduced. If negotiation fails (or no API version was
+// included in the API response), we assume the API server uses the most
+// recent version before negotiation was introduced.
+const fallbackAPIVersion = "1.24"
+
 // Client is the API client that performs all operations
 // against a docker server.
 type Client struct {
@@ -329,7 +336,7 @@ func (cli *Client) NegotiateAPIVersionPing(pingResponse types.Ping) {
 func (cli *Client) negotiateAPIVersionPing(pingResponse types.Ping) {
 	// default to the latest version before versioning headers existed
 	if pingResponse.APIVersion == "" {
-		pingResponse.APIVersion = "1.24"
+		pingResponse.APIVersion = fallbackAPIVersion
 	}
 
 	// if the client is not initialized with a version, start with the latest supported version

+ 15 - 1
cmd/dockerd/daemon.go

@@ -520,6 +520,20 @@ func loadDaemonCliConfig(opts *daemonOptions) (*config.Config, error) {
 	conf.LogLevel = opts.LogLevel
 	conf.LogFormat = log.OutputFormat(opts.LogFormat)
 
+	// The DOCKER_MIN_API_VERSION env-var allows overriding the minimum API
+	// version provided by the daemon within constraints of the minimum and
+	// maximum (current) supported API versions.
+	//
+	// API versions older than [config.defaultMinAPIVersion] are deprecated and
+	// to be removed in a future release. The "DOCKER_MIN_API_VERSION" env-var
+	// should only be used for exceptional cases.
+	if ver := os.Getenv("DOCKER_MIN_API_VERSION"); ver != "" {
+		if err := config.ValidateMinAPIVersion(ver); err != nil {
+			return nil, errors.Wrap(err, "invalid DOCKER_MIN_API_VERSION")
+		}
+		conf.MinAPIVersion = ver
+	}
+
 	if flags.Changed(FlagTLS) {
 		conf.TLS = &opts.TLS
 	}
@@ -689,7 +703,7 @@ func initMiddlewares(s *apiserver.Server, cfg *config.Config, pluginStore plugin
 	exp := middleware.NewExperimentalMiddleware(cfg.Experimental)
 	s.UseMiddleware(exp)
 
-	vm := middleware.NewVersionMiddleware(v, api.DefaultVersion, api.MinVersion)
+	vm := middleware.NewVersionMiddleware(v, api.DefaultVersion, cfg.MinAPIVersion)
 	s.UseMiddleware(vm)
 
 	if cfg.CorsHeaders != "" {

+ 41 - 5
daemon/config/config.go

@@ -10,16 +10,17 @@ import (
 	"os"
 	"strings"
 
-	"golang.org/x/text/encoding"
-	"golang.org/x/text/encoding/unicode"
-	"golang.org/x/text/transform"
-
 	"github.com/containerd/log"
+	"github.com/docker/docker/api"
+	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/registry"
 	"github.com/imdario/mergo"
 	"github.com/pkg/errors"
 	"github.com/spf13/pflag"
+	"golang.org/x/text/encoding"
+	"golang.org/x/text/encoding/unicode"
+	"golang.org/x/text/transform"
 )
 
 const (
@@ -53,7 +54,11 @@ const (
 	DefaultContainersNamespace = "moby"
 	// DefaultPluginNamespace is the name of the default containerd namespace used for plugins.
 	DefaultPluginNamespace = "plugins.moby"
-
+	// defaultMinAPIVersion is the minimum API version supported by the API.
+	// This version can be overridden through the "DOCKER_MIN_API_VERSION"
+	// environment variable. The minimum allowed version is determined
+	// by [minAPIVersion].
+	defaultMinAPIVersion = "1.24"
 	// SeccompProfileDefault is the built-in default seccomp profile.
 	SeccompProfileDefault = "builtin"
 	// SeccompProfileUnconfined is a special profile name for seccomp to use an
@@ -247,6 +252,17 @@ type CommonConfig struct {
 
 	// CDISpecDirs is a list of directories in which CDI specifications can be found.
 	CDISpecDirs []string `json:"cdi-spec-dirs,omitempty"`
+
+	// The minimum API version provided by the daemon. Defaults to [defaultMinAPIVersion].
+	//
+	// The DOCKER_MIN_API_VERSION allows overriding the minimum API version within
+	// constraints of the minimum and maximum (current) supported API versions.
+	//
+	// API versions older than [defaultMinAPIVersion] are deprecated and
+	// to be removed in a future release. The "DOCKER_MIN_API_VERSION" env
+	// var should only be used for exceptional cases, and the MinAPIVersion
+	// field is therefore not included in the JSON representation.
+	MinAPIVersion string `json:"-"`
 }
 
 // Proxies holds the proxies that are configured for the daemon.
@@ -290,6 +306,7 @@ func New() (*Config, error) {
 			ContainerdNamespace:       DefaultContainersNamespace,
 			ContainerdPluginNamespace: DefaultPluginNamespace,
 			DefaultRuntime:            StockRuntimeName,
+			MinAPIVersion:             defaultMinAPIVersion,
 		},
 	}
 
@@ -583,6 +600,25 @@ func findConfigurationConflicts(config map[string]interface{}, flags *pflag.Flag
 	return nil
 }
 
+// ValidateMinAPIVersion verifies if the given API version is within the
+// range supported by the daemon. It is used to validate a custom minimum
+// API version set through DOCKER_MIN_API_VERSION.
+func ValidateMinAPIVersion(ver string) error {
+	if ver == "" {
+		return errors.New(`value is empty`)
+	}
+	if strings.EqualFold(ver[0:1], "v") {
+		return errors.New(`API version must be provided without "v" prefix`)
+	}
+	if versions.LessThan(ver, minAPIVersion) {
+		return errors.Errorf(`minimum supported API version is %s: %s`, minAPIVersion, ver)
+	}
+	if versions.GreaterThan(ver, api.DefaultVersion) {
+		return errors.Errorf(`maximum supported API version is %s: %s`, api.DefaultVersion, ver)
+	}
+	return nil
+}
+
 // Validate validates some specific configs.
 // such as config.DNS, config.Labels, config.DNSSearch,
 // as well as config.MaxConcurrentDownloads, config.MaxConcurrentUploads and config.MaxDownloadAttempts.

+ 3 - 0
daemon/config/config_linux.go

@@ -29,6 +29,9 @@ const (
 	// StockRuntimeName is the reserved name/alias used to represent the
 	// OCI runtime being shipped with the docker daemon package.
 	StockRuntimeName = "runc"
+
+	// minAPIVersion represents Minimum REST API version supported
+	minAPIVersion = "1.12"
 )
 
 // BridgeConfig stores all the parameters for both the bridge driver and the default bridge network.

+ 57 - 0
daemon/config/config_test.go

@@ -8,6 +8,7 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/docker/docker/api"
 	"github.com/docker/docker/libnetwork/ipamutils"
 	"github.com/docker/docker/opts"
 	"github.com/google/go-cmp/cmp"
@@ -502,6 +503,62 @@ func TestValidateConfiguration(t *testing.T) {
 	}
 }
 
+func TestValidateMinAPIVersion(t *testing.T) {
+	t.Parallel()
+	tests := []struct {
+		doc         string
+		input       string
+		expectedErr string
+	}{
+		{
+			doc:         "empty",
+			expectedErr: "value is empty",
+		},
+		{
+			doc:         "with prefix",
+			input:       "v1.43",
+			expectedErr: `API version must be provided without "v" prefix`,
+		},
+		{
+			doc:         "major only",
+			input:       "1",
+			expectedErr: `minimum supported API version is`,
+		},
+		{
+			doc:         "too low",
+			input:       "1.0",
+			expectedErr: `minimum supported API version is`,
+		},
+		{
+			doc:         "minor too high",
+			input:       "1.99",
+			expectedErr: `maximum supported API version is`,
+		},
+		{
+			doc:         "major too high",
+			input:       "9.0",
+			expectedErr: `maximum supported API version is`,
+		},
+		{
+			doc:   "current version",
+			input: api.DefaultVersion,
+		},
+	}
+
+	for _, tc := range tests {
+		tc := tc
+		t.Run(tc.doc, func(t *testing.T) {
+			err := ValidateMinAPIVersion(tc.input)
+			if tc.expectedErr != "" {
+				assert.Check(t, is.ErrorContains(err, tc.expectedErr))
+			} else {
+				assert.Check(t, err)
+			}
+		})
+	}
+
+}
+
 func TestConfigInvalidDNS(t *testing.T) {
 	tests := []struct {
 		doc         string

+ 7 - 0
daemon/config/config_windows.go

@@ -13,6 +13,13 @@ const (
 	// default value. On Windows keep this empty so the value is auto-detected
 	// based on other options.
 	StockRuntimeName = ""
+
+	// minAPIVersion represents Minimum REST API version supported
+	// Technically the first daemon API version released on Windows is v1.25 in
+	// engine version 1.13. However, some clients are explicitly using downlevel
+	// APIs (e.g. docker-compose v2.1 file format) and that is just too restrictive.
+	// Hence also allowing 1.24 on Windows.
+	minAPIVersion string = "1.24"
 )
 
 // BridgeConfig is meant to store all the parameters for both the bridge driver and the default bridge network. On

+ 2 - 2
daemon/info.go

@@ -113,7 +113,7 @@ func (daemon *Daemon) SystemVersion(ctx context.Context) (types.Version, error)
 				Details: map[string]string{
 					"GitCommit":     dockerversion.GitCommit,
 					"ApiVersion":    api.DefaultVersion,
-					"MinAPIVersion": api.MinVersion,
+					"MinAPIVersion": cfg.MinAPIVersion,
 					"GoVersion":     runtime.Version(),
 					"Os":            runtime.GOOS,
 					"Arch":          runtime.GOARCH,
@@ -128,7 +128,7 @@ func (daemon *Daemon) SystemVersion(ctx context.Context) (types.Version, error)
 		Version:       dockerversion.Version,
 		GitCommit:     dockerversion.GitCommit,
 		APIVersion:    api.DefaultVersion,
-		MinAPIVersion: api.MinVersion,
+		MinAPIVersion: cfg.MinAPIVersion,
 		GoVersion:     runtime.Version(),
 		Os:            runtime.GOOS,
 		Arch:          runtime.GOARCH,

+ 3 - 0
hack/make/.integration-daemon-start

@@ -46,6 +46,9 @@ export DOCKER_ALLOW_SCHEMA1_PUSH_DONOTUSE=1
 export DOCKER_GRAPHDRIVER=${DOCKER_GRAPHDRIVER:-vfs}
 export DOCKER_USERLANDPROXY=${DOCKER_USERLANDPROXY:-true}
 
+# Allow testing old API versions
+export DOCKER_MIN_API_VERSION=1.12
+
 # example usage: DOCKER_STORAGE_OPTS="dm.basesize=20G,dm.loopdatasize=200G"
 storage_params=""
 if [ -n "$DOCKER_STORAGE_OPTS" ]; then