Merge pull request #46887 from thaJeztah/minimum_api_version

daemon: raise default minimum API version to v1.24
This commit is contained in:
Sebastiaan van Stijn 2023-12-06 00:15:03 +01:00 committed by GitHub
commit 6af9204938
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 138 additions and 24 deletions

View file

@ -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"

View file

@ -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"

View file

@ -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()

View file

@ -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

View file

@ -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 != "" {

View file

@ -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.

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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