mirror of https://github.com/docker/cli.git
304 lines
8.2 KiB
Go
304 lines
8.2 KiB
Go
package swarm
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/docker/cli/internal/test"
|
|
"github.com/docker/docker/api/types/swarm"
|
|
"gotest.tools/v3/assert"
|
|
is "gotest.tools/v3/assert/cmp"
|
|
)
|
|
|
|
const (
|
|
cert = `
|
|
-----BEGIN CERTIFICATE-----
|
|
MIIBuDCCAV4CCQDOqUYOWdqMdjAKBggqhkjOPQQDAzBjMQswCQYDVQQGEwJVUzEL
|
|
MAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDzANBgNVBAoMBkRv
|
|
Y2tlcjEPMA0GA1UECwwGRG9ja2VyMQ0wCwYDVQQDDARUZXN0MCAXDTE4MDcwMjIx
|
|
MjkxOFoYDzMwMTcxMTAyMjEyOTE4WjBjMQswCQYDVQQGEwJVUzELMAkGA1UECAwC
|
|
Q0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDzANBgNVBAoMBkRvY2tlcjEPMA0G
|
|
A1UECwwGRG9ja2VyMQ0wCwYDVQQDDARUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0D
|
|
AQcDQgAEgvvZl5Vqpr1e+g5IhoU6TZHgRau+BZETVFTmqyWYajA/mooRQ1MZTozu
|
|
s9ZZZA8tzUhIqS36gsFuyIZ4YiAlyjAKBggqhkjOPQQDAwNIADBFAiBQ7pCPQrj8
|
|
8zaItMf0pk8j1NU5XrFqFEZICzvjzUJQBAIhAKq2gFwoTn8KH+cAAXZpAGJPmOsT
|
|
zsBT8gBAOHhNA6/2
|
|
-----END CERTIFICATE-----`
|
|
key = `
|
|
-----BEGIN EC PRIVATE KEY-----
|
|
MHcCAQEEICyheZpw70pbgO4hEuwhZTETWyTpNJmJ3TyFaWT6WTRkoAoGCCqGSM49
|
|
AwEHoUQDQgAEgvvZl5Vqpr1e+g5IhoU6TZHgRau+BZETVFTmqyWYajA/mooRQ1MZ
|
|
Tozus9ZZZA8tzUhIqS36gsFuyIZ4YiAlyg==
|
|
-----END EC PRIVATE KEY-----`
|
|
)
|
|
|
|
func swarmSpecWithFullCAConfig() *swarm.Spec {
|
|
return &swarm.Spec{
|
|
CAConfig: swarm.CAConfig{
|
|
SigningCACert: "cacert",
|
|
SigningCAKey: "cakey",
|
|
ForceRotate: 1,
|
|
NodeCertExpiry: time.Duration(200),
|
|
ExternalCAs: []*swarm.ExternalCA{
|
|
{
|
|
URL: "https://example.com/ca",
|
|
Protocol: swarm.ExternalCAProtocolCFSSL,
|
|
CACert: "excacert",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestDisplayTrustRootNoRoot(t *testing.T) {
|
|
buffer := new(bytes.Buffer)
|
|
err := displayTrustRoot(buffer, swarm.Swarm{})
|
|
assert.Error(t, err, "No CA information available")
|
|
}
|
|
|
|
type invalidCATestCases struct {
|
|
args []string
|
|
errorMsg string
|
|
}
|
|
|
|
func writeFile(data string) (string, error) {
|
|
tmpfile, err := os.CreateTemp("", "testfile")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
_, err = tmpfile.Write([]byte(data))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return tmpfile.Name(), tmpfile.Close()
|
|
}
|
|
|
|
func TestDisplayTrustRootInvalidFlags(t *testing.T) {
|
|
// we need an actual PEMfile to test
|
|
tmpfile, err := writeFile(cert)
|
|
assert.NilError(t, err)
|
|
t.Cleanup(func() { _ = os.Remove(tmpfile) })
|
|
|
|
errorTestCases := []invalidCATestCases{
|
|
{
|
|
args: []string{"--ca-cert=" + tmpfile},
|
|
errorMsg: "flag requires the `--rotate` flag to update the CA",
|
|
},
|
|
{
|
|
args: []string{"--ca-key=" + tmpfile},
|
|
errorMsg: "flag requires the `--rotate` flag to update the CA",
|
|
},
|
|
{ // to make sure we're not erroring because we didn't provide a CA key along with the CA cert
|
|
args: []string{
|
|
"--ca-cert=" + tmpfile,
|
|
"--ca-key=" + tmpfile,
|
|
},
|
|
errorMsg: "flag requires the `--rotate` flag to update the CA",
|
|
},
|
|
{
|
|
args: []string{"--cert-expiry=2160h0m0s"},
|
|
errorMsg: "flag requires the `--rotate` flag to update the CA",
|
|
},
|
|
{
|
|
args: []string{"--external-ca=protocol=cfssl,url=https://some.example.com/https/url"},
|
|
errorMsg: "flag requires the `--rotate` flag to update the CA",
|
|
},
|
|
{ // to make sure we're not erroring because we didn't provide a CA cert and external CA
|
|
args: []string{
|
|
"--ca-cert=" + tmpfile,
|
|
"--external-ca=protocol=cfssl,url=https://some.example.com/https/url",
|
|
},
|
|
errorMsg: "flag requires the `--rotate` flag to update the CA",
|
|
},
|
|
{
|
|
args: []string{
|
|
"--rotate",
|
|
"--external-ca=protocol=cfssl,url=https://some.example.com/https/url",
|
|
},
|
|
errorMsg: "rotating to an external CA requires the `--ca-cert` flag to specify the external CA's cert - " +
|
|
"to add an external CA with the current root CA certificate, use the `update` command instead",
|
|
},
|
|
{
|
|
args: []string{
|
|
"--rotate",
|
|
"--ca-cert=" + tmpfile,
|
|
},
|
|
errorMsg: "the --ca-cert flag requires that a --ca-key flag and/or --external-ca flag be provided as well",
|
|
},
|
|
}
|
|
|
|
for _, testCase := range errorTestCases {
|
|
cmd := newCACommand(
|
|
test.NewFakeCli(&fakeClient{
|
|
swarmInspectFunc: func() (swarm.Swarm, error) {
|
|
return swarm.Swarm{
|
|
ClusterInfo: swarm.ClusterInfo{
|
|
TLSInfo: swarm.TLSInfo{
|
|
TrustRoot: "root",
|
|
},
|
|
},
|
|
}, nil
|
|
},
|
|
}))
|
|
assert.Check(t, cmd.Flags().Parse(testCase.args))
|
|
cmd.SetOut(io.Discard)
|
|
cmd.SetErr(io.Discard)
|
|
assert.ErrorContains(t, cmd.Execute(), testCase.errorMsg)
|
|
}
|
|
}
|
|
|
|
func TestDisplayTrustRoot(t *testing.T) {
|
|
buffer := new(bytes.Buffer)
|
|
trustRoot := "trustme"
|
|
err := displayTrustRoot(buffer, swarm.Swarm{
|
|
ClusterInfo: swarm.ClusterInfo{
|
|
TLSInfo: swarm.TLSInfo{TrustRoot: trustRoot},
|
|
},
|
|
})
|
|
assert.NilError(t, err)
|
|
assert.Check(t, is.Equal(trustRoot+"\n", buffer.String()))
|
|
}
|
|
|
|
type swarmUpdateRecorder struct {
|
|
spec swarm.Spec
|
|
}
|
|
|
|
func (s *swarmUpdateRecorder) swarmUpdate(sp swarm.Spec, _ swarm.UpdateFlags) error {
|
|
s.spec = sp
|
|
return nil
|
|
}
|
|
|
|
func swarmInspectFuncWithFullCAConfig() (swarm.Swarm, error) {
|
|
return swarm.Swarm{
|
|
ClusterInfo: swarm.ClusterInfo{
|
|
Spec: *swarmSpecWithFullCAConfig(),
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func TestUpdateSwarmSpecDefaultRotate(t *testing.T) {
|
|
s := &swarmUpdateRecorder{}
|
|
cli := test.NewFakeCli(&fakeClient{
|
|
swarmInspectFunc: swarmInspectFuncWithFullCAConfig,
|
|
swarmUpdateFunc: s.swarmUpdate,
|
|
})
|
|
cmd := newCACommand(cli)
|
|
cmd.SetArgs([]string{"--rotate", "--detach"})
|
|
cmd.SetOut(cli.OutBuffer())
|
|
assert.NilError(t, cmd.Execute())
|
|
|
|
expected := swarmSpecWithFullCAConfig()
|
|
expected.CAConfig.ForceRotate = 2
|
|
expected.CAConfig.SigningCACert = ""
|
|
expected.CAConfig.SigningCAKey = ""
|
|
assert.Check(t, is.DeepEqual(*expected, s.spec))
|
|
}
|
|
|
|
func TestUpdateSwarmSpecCertAndKey(t *testing.T) {
|
|
certfile, err := writeFile(cert)
|
|
assert.NilError(t, err)
|
|
defer os.Remove(certfile)
|
|
|
|
keyfile, err := writeFile(key)
|
|
assert.NilError(t, err)
|
|
defer os.Remove(keyfile)
|
|
|
|
s := &swarmUpdateRecorder{}
|
|
cli := test.NewFakeCli(&fakeClient{
|
|
swarmInspectFunc: swarmInspectFuncWithFullCAConfig,
|
|
swarmUpdateFunc: s.swarmUpdate,
|
|
})
|
|
cmd := newCACommand(cli)
|
|
cmd.SetArgs([]string{
|
|
"--rotate",
|
|
"--detach",
|
|
"--ca-cert=" + certfile,
|
|
"--ca-key=" + keyfile,
|
|
"--cert-expiry=3m",
|
|
})
|
|
cmd.SetOut(cli.OutBuffer())
|
|
assert.NilError(t, cmd.Execute())
|
|
|
|
expected := swarmSpecWithFullCAConfig()
|
|
expected.CAConfig.SigningCACert = cert
|
|
expected.CAConfig.SigningCAKey = key
|
|
expected.CAConfig.NodeCertExpiry = 3 * time.Minute
|
|
assert.Check(t, is.DeepEqual(*expected, s.spec))
|
|
}
|
|
|
|
func TestUpdateSwarmSpecCertAndExternalCA(t *testing.T) {
|
|
certfile, err := writeFile(cert)
|
|
assert.NilError(t, err)
|
|
defer os.Remove(certfile)
|
|
|
|
s := &swarmUpdateRecorder{}
|
|
cli := test.NewFakeCli(&fakeClient{
|
|
swarmInspectFunc: swarmInspectFuncWithFullCAConfig,
|
|
swarmUpdateFunc: s.swarmUpdate,
|
|
})
|
|
cmd := newCACommand(cli)
|
|
cmd.SetArgs([]string{
|
|
"--rotate",
|
|
"--detach",
|
|
"--ca-cert=" + certfile,
|
|
"--external-ca=protocol=cfssl,url=https://some.external.ca.example.com",
|
|
})
|
|
cmd.SetOut(cli.OutBuffer())
|
|
assert.NilError(t, cmd.Execute())
|
|
|
|
expected := swarmSpecWithFullCAConfig()
|
|
expected.CAConfig.SigningCACert = cert
|
|
expected.CAConfig.SigningCAKey = ""
|
|
expected.CAConfig.ExternalCAs = []*swarm.ExternalCA{
|
|
{
|
|
Protocol: swarm.ExternalCAProtocolCFSSL,
|
|
URL: "https://some.external.ca.example.com",
|
|
CACert: cert,
|
|
Options: make(map[string]string),
|
|
},
|
|
}
|
|
assert.Check(t, is.DeepEqual(*expected, s.spec))
|
|
}
|
|
|
|
func TestUpdateSwarmSpecCertAndKeyAndExternalCA(t *testing.T) {
|
|
certfile, err := writeFile(cert)
|
|
assert.NilError(t, err)
|
|
defer os.Remove(certfile)
|
|
|
|
keyfile, err := writeFile(key)
|
|
assert.NilError(t, err)
|
|
defer os.Remove(keyfile)
|
|
|
|
s := &swarmUpdateRecorder{}
|
|
cli := test.NewFakeCli(&fakeClient{
|
|
swarmInspectFunc: swarmInspectFuncWithFullCAConfig,
|
|
swarmUpdateFunc: s.swarmUpdate,
|
|
})
|
|
cmd := newCACommand(cli)
|
|
cmd.SetArgs([]string{
|
|
"--rotate",
|
|
"--detach",
|
|
"--ca-cert=" + certfile,
|
|
"--ca-key=" + keyfile,
|
|
"--external-ca=protocol=cfssl,url=https://some.external.ca.example.com",
|
|
})
|
|
cmd.SetOut(cli.OutBuffer())
|
|
assert.NilError(t, cmd.Execute())
|
|
|
|
expected := swarmSpecWithFullCAConfig()
|
|
expected.CAConfig.SigningCACert = cert
|
|
expected.CAConfig.SigningCAKey = key
|
|
expected.CAConfig.ExternalCAs = []*swarm.ExternalCA{
|
|
{
|
|
Protocol: swarm.ExternalCAProtocolCFSSL,
|
|
URL: "https://some.external.ca.example.com",
|
|
CACert: cert,
|
|
Options: make(map[string]string),
|
|
},
|
|
}
|
|
assert.Check(t, is.DeepEqual(*expected, s.spec))
|
|
}
|