cli/compose/schema: make version: "3" equivalent to "3.x" (latest)

Previously, `version: "3"` was equivalent to `version: "3.0"`, which
caused confusion for many users, as they expected it to be "3.x".

docker-compose and docker compose (v2) have adopted the compose-spec
(https://compose-spec.io), which no longer has a version field in
the compose file, and always picks the "latest" supported version.

This changes how `docker stack` interprets "major" version numbers
specified in compose-files:

When only the major version ("3") is specified, it is now equivalent
to "3.x" (latest supported v3 schema).

Compose-files that specify both major and minor version (e.g. "3.0"
or "3.1") continue to use the existing behavior; validation is down-
graded to the specified version and will produce an error if options
are used that are not supported in that schema version. This allows
users to locally verify that a composse-file does not use options
that are not supported in the intended deployment environment (for
example if the deploy environment only supports older versions of
the schema).

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2021-08-16 14:13:07 +02:00
parent 8667ccd112
commit 60c3836365
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
3 changed files with 5 additions and 6 deletions

View File

@ -180,7 +180,7 @@ func strPtr(val string) *string {
} }
var sampleConfig = types.Config{ var sampleConfig = types.Config{
Version: "3.0", Version: "3.10",
Services: []types.ServiceConfig{ Services: []types.ServiceConfig{
{ {
Name: "foo", Name: "foo",

View File

@ -40,7 +40,8 @@ func init() {
} }
// Version returns the version of the config, defaulting to the latest "3.x" // Version returns the version of the config, defaulting to the latest "3.x"
// version (3.10). // version (3.10). If only the major version "3" is specified, it is used as
// version "3.x" and returns the default version (latest 3.x).
func Version(config map[string]interface{}) string { func Version(config map[string]interface{}) string {
version, ok := config[versionField] version, ok := config[versionField]
if !ok { if !ok {
@ -51,10 +52,8 @@ func Version(config map[string]interface{}) string {
func normalizeVersion(version string) string { func normalizeVersion(version string) string {
switch version { switch version {
case "": case "", "3":
return defaultVersion return defaultVersion
case "3":
return "3.0"
default: default:
return version return version
} }

View File

@ -88,7 +88,6 @@ func TestValidateCredentialSpecs(t *testing.T) {
version string version string
expectedErr string expectedErr string
}{ }{
{version: "3", expectedErr: "credential_spec"},
{version: "3.0", expectedErr: "credential_spec"}, {version: "3.0", expectedErr: "credential_spec"},
{version: "3.1", expectedErr: "credential_spec"}, {version: "3.1", expectedErr: "credential_spec"},
{version: "3.2", expectedErr: "credential_spec"}, {version: "3.2", expectedErr: "credential_spec"},
@ -100,6 +99,7 @@ func TestValidateCredentialSpecs(t *testing.T) {
{version: "3.8"}, {version: "3.8"},
{version: "3.9"}, {version: "3.9"},
{version: "3.10"}, {version: "3.10"},
{version: "3"},
{version: ""}, {version: ""},
} }