Skip to content

Commit

Permalink
Add support for using Configs as CredentialSpecs in services
Browse files Browse the repository at this point in the history
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
  • Loading branch information
thaJeztah committed Feb 2, 2019
1 parent 2a48f18 commit e126e9f
Show file tree
Hide file tree
Showing 9 changed files with 187 additions and 63 deletions.
4 changes: 3 additions & 1 deletion cli/command/service/opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,12 +331,14 @@ func (c *credentialSpecOpt) Set(value string) error {
c.source = value
c.value = &swarm.CredentialSpec{}
switch {
case strings.HasPrefix(value, "config://"):
c.value.Config = strings.TrimPrefix(value, "config://")
case strings.HasPrefix(value, "file://"):
c.value.File = strings.TrimPrefix(value, "file://")
case strings.HasPrefix(value, "registry://"):
c.value.Registry = strings.TrimPrefix(value, "registry://")
default:
return errors.New("Invalid credential spec - value must be prefixed file:// or registry:// followed by a value")
return errors.New(`invalid credential spec: value must be prefixed with "config://", "file://", or "registry://"`)
}

return nil
Expand Down
55 changes: 55 additions & 0 deletions cli/command/service/opts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,61 @@ import (
is "gotest.tools/assert/cmp"
)

func TestCredentialSpecOpt(t *testing.T) {
tests := []struct {
name string
in string
value swarm.CredentialSpec
expectedErr string
}{
{
name: "empty",
in: "",
value: swarm.CredentialSpec{},
expectedErr: `invalid credential spec: value must be prefixed with "config://", "file://", or "registry://"`,
},
{
name: "no-prefix",
in: "noprefix",
value: swarm.CredentialSpec{},
expectedErr: `invalid credential spec: value must be prefixed with "config://", "file://", or "registry://"`,
},
{
name: "config",
in: "config://0bt9dmxjvjiqermk6xrop3ekq",
value: swarm.CredentialSpec{Config: "0bt9dmxjvjiqermk6xrop3ekq"},
},
{
name: "file",
in: "file://somefile.json",
value: swarm.CredentialSpec{File: "somefile.json"},
},
{
name: "registry",
in: "registry://testing",
value: swarm.CredentialSpec{Registry: "testing"},
},
}

for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
var cs credentialSpecOpt

err := cs.Set(tc.in)

if tc.expectedErr != "" {
assert.Error(t, err, tc.expectedErr)
} else {
assert.NilError(t, err)
}

assert.Equal(t, cs.String(), tc.in)
assert.DeepEqual(t, cs.Value(), &tc.value)
})
}
}

func TestMemBytesString(t *testing.T) {
var mem opts.MemBytes = 1048576
assert.Check(t, is.Equal("1MiB", mem.String()))
Expand Down
23 changes: 19 additions & 4 deletions cli/compose/convert/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -598,11 +598,26 @@ func convertDNSConfig(DNS []string, DNSSearch []string) (*swarm.DNSConfig, error
}

func convertCredentialSpec(spec composetypes.CredentialSpecConfig) (*swarm.CredentialSpec, error) {
if spec.File == "" && spec.Registry == "" {
return nil, nil
var o []string

// Config was added in API v1.40
if spec.Config != "" {
o = append(o, `"Config"`)
}
if spec.File != "" {
o = append(o, `"File"`)
}
if spec.File != "" && spec.Registry != "" {
return nil, errors.New("Invalid credential spec - must provide one of `File` or `Registry`")
if spec.Registry != "" {
o = append(o, `"Registry"`)
}
l := len(o)
switch {
case l == 0:
return nil, nil
case l == 2:
return nil, errors.Errorf("invalid credential spec: cannot specify both %s and %s", o[0], o[1])
case l > 2:
return nil, errors.Errorf("invalid credential spec: cannot specify both %s, and %s", strings.Join(o[:l-1], ", "), o[l-1])
}
swarmCredSpec := swarm.CredentialSpec(spec)
return &swarmCredSpec, nil
Expand Down
79 changes: 57 additions & 22 deletions cli/compose/convert/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,30 +314,65 @@ func TestConvertDNSConfigSearch(t *testing.T) {
}

func TestConvertCredentialSpec(t *testing.T) {
swarmSpec, err := convertCredentialSpec(composetypes.CredentialSpecConfig{})
assert.NilError(t, err)
assert.Check(t, is.Nil(swarmSpec))

swarmSpec, err = convertCredentialSpec(composetypes.CredentialSpecConfig{
File: "/foo",
})
assert.NilError(t, err)
assert.Check(t, is.Equal(swarmSpec.File, "/foo"))
assert.Check(t, is.Equal(swarmSpec.Registry, ""))
tests := []struct {
name string
in composetypes.CredentialSpecConfig
out *swarm.CredentialSpec
expectedErr string
}{
{
name: "empty",
},
{
name: "config-and-file",
in: composetypes.CredentialSpecConfig{Config: "0bt9dmxjvjiqermk6xrop3ekq", File: "somefile.json"},
expectedErr: `invalid credential spec: cannot specify both "Config" and "File"`,
},
{
name: "config-and-registry",
in: composetypes.CredentialSpecConfig{Config: "0bt9dmxjvjiqermk6xrop3ekq", Registry: "testing"},
expectedErr: `invalid credential spec: cannot specify both "Config" and "Registry"`,
},
{
name: "file-and-registry",
in: composetypes.CredentialSpecConfig{File: "somefile.json", Registry: "testing"},
expectedErr: `invalid credential spec: cannot specify both "File" and "Registry"`,
},
{
name: "config-and-file-and-registry",
in: composetypes.CredentialSpecConfig{Config: "0bt9dmxjvjiqermk6xrop3ekq", File: "somefile.json", Registry: "testing"},
expectedErr: `invalid credential spec: cannot specify both "Config", "File", and "Registry"`,
},
{
name: "config",
in: composetypes.CredentialSpecConfig{Config: "0bt9dmxjvjiqermk6xrop3ekq"},
out: &swarm.CredentialSpec{Config: "0bt9dmxjvjiqermk6xrop3ekq"},
},
{
name: "file",
in: composetypes.CredentialSpecConfig{File: "somefile.json"},
out: &swarm.CredentialSpec{File: "somefile.json"},
},
{
name: "registry",
in: composetypes.CredentialSpecConfig{Registry: "testing"},
out: &swarm.CredentialSpec{Registry: "testing"},
},
}

swarmSpec, err = convertCredentialSpec(composetypes.CredentialSpecConfig{
Registry: "foo",
})
assert.NilError(t, err)
assert.Check(t, is.Equal(swarmSpec.File, ""))
assert.Check(t, is.Equal(swarmSpec.Registry, "foo"))
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
swarmSpec, err := convertCredentialSpec(tc.in)

swarmSpec, err = convertCredentialSpec(composetypes.CredentialSpecConfig{
File: "/asdf",
Registry: "foo",
})
assert.Check(t, is.ErrorContains(err, ""))
assert.Check(t, is.Nil(swarmSpec))
if tc.expectedErr != "" {
assert.Error(t, err, tc.expectedErr)
} else {
assert.NilError(t, err)
}
assert.DeepEqual(t, swarmSpec, tc.out)
})
}
}

func TestConvertUpdateConfigOrder(t *testing.T) {
Expand Down
14 changes: 14 additions & 0 deletions cli/compose/loader/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,20 @@ configs:
assert.Assert(t, is.Len(actual.Configs, 1))
}

func TestLoadV38(t *testing.T) {
actual, err := loadYAML(`
version: "3.8"
services:
foo:
image: busybox
credential_spec:
config: "0bt9dmxjvjiqermk6xrop3ekq"
`)
assert.NilError(t, err)
assert.Assert(t, is.Len(actual.Services, 1))
assert.Check(t, is.Equal(actual.Services[0].CredentialSpec.Config, "0bt9dmxjvjiqermk6xrop3ekq"))
}

func TestParseAndLoad(t *testing.T) {
actual, err := loadYAML(sampleYAML)
assert.NilError(t, err)
Expand Down
72 changes: 36 additions & 36 deletions cli/compose/schema/bindata.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cli/compose/schema/data/config_schema_v3.8.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@
"credential_spec": {
"type": "object",
"properties": {
"config": {"type": "string"},
"file": {"type": "string"},
"registry": {"type": "string"}
},
Expand Down
1 change: 1 addition & 0 deletions cli/compose/schema/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ func TestValidateCredentialSpecs(t *testing.T) {
{version: "3.5", expectedErr: "config"},
{version: "3.6", expectedErr: "config"},
{version: "3.7", expectedErr: "config"},
{version: "3.8"},
}

for _, tc := range tests {
Expand Down
1 change: 1 addition & 0 deletions cli/compose/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,7 @@ func (e External) MarshalJSON() ([]byte, error) {

// CredentialSpecConfig for credential spec on Windows
type CredentialSpecConfig struct {
Config string `yaml:",omitempty" json:"config,omitempty"` // Config was added in API v1.40
File string `yaml:",omitempty" json:"file,omitempty"`
Registry string `yaml:",omitempty" json:"registry,omitempty"`
}
Expand Down

0 comments on commit e126e9f

Please sign in to comment.