From 1af0434a2b66b8144c0f9acdb8d9a8babc6cf59f Mon Sep 17 00:00:00 2001 From: Ying Li Date: Mon, 2 Jul 2018 17:33:43 -0700 Subject: [PATCH] Allow setting --external-ca to an empty string, to allow for removing all external CAs entirely. This will help for instance if rotating from a fully external CA to an internal CA (if the CA's cert and key are already in the swarm for instance). Signed-off-by: Ying Li --- cli/command/swarm/opts.go | 14 ++++- cli/command/swarm/opts_test.go | 111 +++++++++++++++++++++++++++------ 2 files changed, 104 insertions(+), 21 deletions(-) diff --git a/cli/command/swarm/opts.go b/cli/command/swarm/opts.go index b3baba273ea6..4d6781953542 100644 --- a/cli/command/swarm/opts.go +++ b/cli/command/swarm/opts.go @@ -4,6 +4,7 @@ import ( "encoding/csv" "encoding/pem" "fmt" + "io" "io/ioutil" "strings" "time" @@ -96,7 +97,9 @@ func (m *ExternalCAOption) Set(value string) error { return err } - m.values = append(m.values, parsed) + if parsed != nil { + m.values = append(m.values, parsed) + } return nil } @@ -109,8 +112,10 @@ func (m *ExternalCAOption) Type() string { func (m *ExternalCAOption) String() string { externalCAs := []string{} for _, externalCA := range m.values { - repr := fmt.Sprintf("%s: %s", externalCA.Protocol, externalCA.URL) - externalCAs = append(externalCAs, repr) + if externalCA != nil { + repr := fmt.Sprintf("%s: %s", externalCA.Protocol, externalCA.URL) + externalCAs = append(externalCAs, repr) + } } return strings.Join(externalCAs, ", ") } @@ -158,6 +163,9 @@ func (p *PEMFile) Contents() string { func parseExternalCA(caSpec string) (*swarm.ExternalCA, error) { csvReader := csv.NewReader(strings.NewReader(caSpec)) fields, err := csvReader.Read() + if err == io.EOF { + return nil, nil + } if err != nil { return nil, err } diff --git a/cli/command/swarm/opts_test.go b/cli/command/swarm/opts_test.go index 6382d2a02ae8..4337dd5f40ca 100644 --- a/cli/command/swarm/opts_test.go +++ b/cli/command/swarm/opts_test.go @@ -3,6 +3,7 @@ package swarm import ( "testing" + "github.com/docker/docker/api/types/swarm" "gotest.tools/assert" is "gotest.tools/assert/cmp" ) @@ -42,10 +43,6 @@ func TestExternalCAOptionErrors(t *testing.T) { externalCA string expectedError string }{ - { - externalCA: "", - expectedError: "EOF", - }, { externalCA: "anything", expectedError: "invalid field 'anything' must be a key=value pair", @@ -71,34 +68,112 @@ func TestExternalCAOptionErrors(t *testing.T) { func TestExternalCAOption(t *testing.T) { testCases := []struct { - externalCA string - expected string + externalCAs []string + expected []*swarm.ExternalCA + expectedString string }{ { - externalCA: "protocol=cfssl,url=anything", - expected: "cfssl: anything", + externalCAs: []string{""}, + expected: nil, + expectedString: "", + }, + { + externalCAs: []string{"protocol=cfssl,url=anything"}, + expected: []*swarm.ExternalCA{ + { + Protocol: swarm.ExternalCAProtocolCFSSL, + URL: "anything", + Options: make(map[string]string), + }, + }, + expectedString: "cfssl: anything", + }, + { + externalCAs: []string{"protocol=CFSSL,url=anything"}, + expected: []*swarm.ExternalCA{ + { + Protocol: swarm.ExternalCAProtocolCFSSL, + URL: "anything", + Options: make(map[string]string), + }, + }, + expectedString: "cfssl: anything", + }, + { + externalCAs: []string{"protocol=Cfssl,url=https://example.com"}, + expected: []*swarm.ExternalCA{ + { + Protocol: swarm.ExternalCAProtocolCFSSL, + URL: "https://example.com", + Options: make(map[string]string), + }, + }, + expectedString: "cfssl: https://example.com", }, { - externalCA: "protocol=CFSSL,url=anything", - expected: "cfssl: anything", + externalCAs: []string{"protocol=Cfssl,url=https://example.com,foo=bar"}, + expected: []*swarm.ExternalCA{ + { + Protocol: swarm.ExternalCAProtocolCFSSL, + URL: "https://example.com", + Options: map[string]string{ + "foo": "bar", + }, + }, + }, + expectedString: "cfssl: https://example.com", }, { - externalCA: "protocol=Cfssl,url=https://example.com", - expected: "cfssl: https://example.com", + externalCAs: []string{"protocol=Cfssl,url=https://example.com,foo=bar,foo=baz"}, + expected: []*swarm.ExternalCA{ + { + Protocol: swarm.ExternalCAProtocolCFSSL, + URL: "https://example.com", + Options: map[string]string{ + "foo": "baz", + }, + }, + }, + expectedString: "cfssl: https://example.com", }, { - externalCA: "protocol=Cfssl,url=https://example.com,foo=bar", - expected: "cfssl: https://example.com", + externalCAs: []string{"", "protocol=Cfssl,url=https://example.com"}, + expected: []*swarm.ExternalCA{ + { + Protocol: swarm.ExternalCAProtocolCFSSL, + URL: "https://example.com", + Options: make(map[string]string), + }, + }, + expectedString: "cfssl: https://example.com", }, { - externalCA: "protocol=Cfssl,url=https://example.com,foo=bar,foo=baz", - expected: "cfssl: https://example.com", + externalCAs: []string{ + "protocol=Cfssl,url=https://example.com", + "protocol=Cfssl,url=https://example2.com", + }, + expected: []*swarm.ExternalCA{ + { + Protocol: swarm.ExternalCAProtocolCFSSL, + URL: "https://example.com", + Options: make(map[string]string), + }, + { + Protocol: swarm.ExternalCAProtocolCFSSL, + URL: "https://example2.com", + Options: make(map[string]string), + }, + }, + expectedString: "cfssl: https://example.com, cfssl: https://example2.com", }, } for _, tc := range testCases { opt := &ExternalCAOption{} - assert.NilError(t, opt.Set(tc.externalCA)) - assert.Check(t, is.Equal(tc.expected, opt.String())) + for _, extCA := range tc.externalCAs { + assert.NilError(t, opt.Set(extCA)) + } + assert.Check(t, is.DeepEqual(tc.expected, opt.Value())) + assert.Check(t, is.Equal(tc.expectedString, opt.String())) } }