Support for rollback config in compose 3.7

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
This commit is contained in:
Vincent Demeester 2018-05-29 11:37:51 +02:00
parent df6e38b81a
commit 4e6e5d583c
No known key found for this signature in database
GPG Key ID: 083CC6FD6EB699A3
9 changed files with 192 additions and 51 deletions

View File

@ -162,6 +162,7 @@ func Service(
EndpointSpec: endpoint,
Mode: mode,
UpdateConfig: convertUpdateConfig(service.Deploy.UpdateConfig),
RollbackConfig: convertUpdateConfig(service.Deploy.RollbackConfig),
}
// add an image label to serviceSpec

View File

@ -550,3 +550,17 @@ func (c *fakeClient) ConfigList(ctx context.Context, options types.ConfigListOpt
}
return []swarm.Config{}, nil
}
func TestConvertUpdateConfigParallelism(t *testing.T) {
parallel := uint64(4)
// test default behavior
updateConfig := convertUpdateConfig(&composetypes.UpdateConfig{})
assert.Check(t, is.Equal(uint64(1), updateConfig.Parallelism))
// Non default value
updateConfig = convertUpdateConfig(&composetypes.UpdateConfig{
Parallelism: &parallel,
})
assert.Check(t, is.Equal(parallel, updateConfig.Parallelism))
}

View File

@ -1,4 +1,4 @@
version: "3.6"
version: "3.7"
services:
foo:
@ -39,6 +39,13 @@ services:
mode: replicated
replicas: 6
labels: [FOO=BAR]
rollback_config:
parallelism: 3
delay: 10s
failure_action: continue
monitor: 60s
max_failure_ratio: 0.3
order: start-first
update_config:
parallelism: 3
delay: 10s

View File

@ -10,7 +10,7 @@ import (
func fullExampleConfig(workingDir, homeDir string) *types.Config {
return &types.Config{
Version: "3.6",
Version: "3.7",
Services: services(workingDir, homeDir),
Networks: networks(),
Volumes: volumes(),
@ -41,6 +41,14 @@ func services(workingDir, homeDir string) []types.ServiceConfig {
Mode: "replicated",
Replicas: uint64Ptr(6),
Labels: map[string]string{"FOO": "BAR"},
RollbackConfig: &types.UpdateConfig{
Parallelism: uint64Ptr(3),
Delay: time.Duration(10 * time.Second),
FailureAction: "continue",
Monitor: time.Duration(60 * time.Second),
MaxFailureRatio: 0.3,
Order: "start-first",
},
UpdateConfig: &types.UpdateConfig{
Parallelism: uint64Ptr(3),
Delay: time.Duration(10 * time.Second),
@ -393,7 +401,7 @@ func volumes() map[string]types.VolumeConfig {
}
func fullExampleYAML(workingDir string) string {
return fmt.Sprintf(`version: "3.6"
return fmt.Sprintf(`version: "3.7"
services:
foo:
build:
@ -436,6 +444,13 @@ services:
monitor: 1m0s
max_failure_ratio: 0.3
order: start-first
rollback_config:
parallelism: 3
delay: 10s
failure_action: continue
monitor: 1m0s
max_failure_ratio: 0.3
order: start-first
resources:
limits:
cpus: "0.001"

View File

@ -19,7 +19,7 @@ func TestMarshallConfig(t *testing.T) {
assert.Check(t, is.Equal(expected, string(actual)))
// Make sure the expected still
dict, err := ParseYAML([]byte("version: '3.6'\n" + expected))
dict, err := ParseYAML([]byte("version: '3.7'\n" + expected))
assert.NilError(t, err)
_, err = Load(buildConfigDetails(dict, map[string]string{}))
assert.NilError(t, err)

View File

@ -467,44 +467,44 @@ DoTuq9lAU9Q4O1xV/59X/w8AAP//zRo7vm9CAAA=
"/data/config_schema_v3.7.json": {
local: "data/config_schema_v3.7.json",
size: 17007,
size: 17540,
modtime: 1518458244,
compressed: `
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
`,
},

View File

@ -348,6 +348,20 @@
"endpoint_mode": {"type": "string"},
"replicas": {"type": "integer"},
"labels": {"$ref": "#/definitions/list_or_dict"},
"rollback_config": {
"type": "object",
"properties": {
"parallelism": {"type": "integer"},
"delay": {"type": "string", "format": "duration"},
"failure_action": {"type": "string"},
"monitor": {"type": "string", "format": "duration"},
"max_failure_ratio": {"type": "number"},
"order": {"type": "string", "enum": [
"start-first", "stop-first"
]}
},
"additionalProperties": false
},
"update_config": {
"type": "object",
"properties": {

View File

@ -114,3 +114,92 @@ func TestValidateIsolation(t *testing.T) {
}
assert.NilError(t, Validate(config, "3.5"))
}
func TestValidateRollbackConfig(t *testing.T) {
config := dict{
"version": "3.4",
"services": dict{
"foo": dict{
"image": "busybox",
"deploy": dict{
"rollback_config": dict{
"parallelism": 1,
},
},
},
},
}
assert.NilError(t, Validate(config, "3.7"))
}
func TestValidateRollbackConfigWithOrder(t *testing.T) {
config := dict{
"version": "3.4",
"services": dict{
"foo": dict{
"image": "busybox",
"deploy": dict{
"rollback_config": dict{
"parallelism": 1,
"order": "start-first",
},
},
},
},
}
assert.NilError(t, Validate(config, "3.7"))
}
func TestValidateRollbackConfigWithUpdateConfig(t *testing.T) {
config := dict{
"version": "3.4",
"services": dict{
"foo": dict{
"image": "busybox",
"deploy": dict{
"update_config": dict{
"parallelism": 1,
"order": "start-first",
},
"rollback_config": dict{
"parallelism": 1,
"order": "start-first",
},
},
},
},
}
assert.NilError(t, Validate(config, "3.7"))
}
func TestValidateRollbackConfigWithUpdateConfigFull(t *testing.T) {
config := dict{
"version": "3.4",
"services": dict{
"foo": dict{
"image": "busybox",
"deploy": dict{
"update_config": dict{
"parallelism": 1,
"order": "start-first",
"delay": "10s",
"failure_action": "pause",
"monitor": "10s",
},
"rollback_config": dict{
"parallelism": 1,
"order": "start-first",
"delay": "10s",
"failure_action": "pause",
"monitor": "10s",
},
},
},
},
}
assert.NilError(t, Validate(config, "3.7"))
}

View File

@ -194,6 +194,7 @@ type DeployConfig struct {
Replicas *uint64 `yaml:",omitempty"`
Labels Labels `yaml:",omitempty"`
UpdateConfig *UpdateConfig `mapstructure:"update_config" yaml:"update_config,omitempty"`
RollbackConfig *UpdateConfig `mapstructure:"rollback_config" yaml:"rollback_config,omitempty"`
Resources Resources `yaml:",omitempty"`
RestartPolicy *RestartPolicy `mapstructure:"restart_policy" yaml:"restart_policy,omitempty"`
Placement Placement `yaml:",omitempty"`