integration-cli: Add secret/config templating tests

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
This commit is contained in:
Aaron Lehmann 2017-06-15 12:06:08 -07:00 committed by Brian Goff
parent c5df7235f6
commit cdd2e6efdb
3 changed files with 382 additions and 0 deletions

View File

@ -1,8 +1,10 @@
package config
import (
"bytes"
"sort"
"testing"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
@ -10,6 +12,7 @@ import (
"github.com/docker/docker/client"
"github.com/docker/docker/integration/internal/swarm"
"github.com/docker/docker/internal/testutil"
"github.com/docker/docker/pkg/stdcopy"
"github.com/gotestyourself/gotestyourself/skip"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -188,3 +191,130 @@ func TestConfigsUpdate(t *testing.T) {
err = client.ConfigUpdate(ctx, configID, insp.Version, insp.Spec)
testutil.ErrorContains(t, err, "only updates to Labels are allowed")
}
func TestTemplatedConfig(t *testing.T) {
d := swarm.NewSwarm(t, testEnv)
defer d.Stop(t)
ctx := context.Background()
client := swarm.GetClient(t, d)
referencedSecretSpec := swarmtypes.SecretSpec{
Annotations: swarmtypes.Annotations{
Name: "referencedsecret",
},
Data: []byte("this is a secret"),
}
referencedSecret, err := client.SecretCreate(ctx, referencedSecretSpec)
assert.NoError(t, err)
referencedConfigSpec := swarmtypes.ConfigSpec{
Annotations: swarmtypes.Annotations{
Name: "referencedconfig",
},
Data: []byte("this is a config"),
}
referencedConfig, err := client.ConfigCreate(ctx, referencedConfigSpec)
assert.NoError(t, err)
configSpec := swarmtypes.ConfigSpec{
Annotations: swarmtypes.Annotations{
Name: "templated_config",
},
Templating: &swarmtypes.Driver{
Name: "golang",
},
Data: []byte("SERVICE_NAME={{.Service.Name}}\n" +
"{{secret \"referencedsecrettarget\"}}\n" +
"{{config \"referencedconfigtarget\"}}\n"),
}
templatedConfig, err := client.ConfigCreate(ctx, configSpec)
assert.NoError(t, err)
serviceID := swarm.CreateService(t, d,
swarm.ServiceWithConfig(
&swarmtypes.ConfigReference{
File: &swarmtypes.ConfigReferenceFileTarget{
Name: "/templated_config",
UID: "0",
GID: "0",
Mode: 0600,
},
ConfigID: templatedConfig.ID,
ConfigName: "templated_config",
},
),
swarm.ServiceWithConfig(
&swarmtypes.ConfigReference{
File: &swarmtypes.ConfigReferenceFileTarget{
Name: "referencedconfigtarget",
UID: "0",
GID: "0",
Mode: 0600,
},
ConfigID: referencedConfig.ID,
ConfigName: "referencedconfig",
},
),
swarm.ServiceWithSecret(
&swarmtypes.SecretReference{
File: &swarmtypes.SecretReferenceFileTarget{
Name: "referencedsecrettarget",
UID: "0",
GID: "0",
Mode: 0600,
},
SecretID: referencedSecret.ID,
SecretName: "referencedsecret",
},
),
swarm.ServiceWithName("svc"),
)
var tasks []swarmtypes.Task
waitAndAssert(t, 60*time.Second, func(t *testing.T) bool {
tasks = swarm.GetRunningTasks(t, d, serviceID)
return len(tasks) > 0
})
task := tasks[0]
waitAndAssert(t, 60*time.Second, func(t *testing.T) bool {
if task.NodeID == "" || (task.Status.ContainerStatus == nil || task.Status.ContainerStatus.ContainerID == "") {
task, _, _ = client.TaskInspectWithRaw(context.Background(), task.ID)
}
return task.NodeID != "" && task.Status.ContainerStatus != nil && task.Status.ContainerStatus.ContainerID != ""
})
attach := swarm.ExecTask(t, d, task, types.ExecConfig{
Cmd: []string{"/bin/cat", "/templated_config"},
AttachStdout: true,
AttachStderr: true,
})
buf := bytes.NewBuffer(nil)
_, err = stdcopy.StdCopy(buf, buf, attach.Reader)
require.NoError(t, err)
expect := "SERVICE_NAME=svc\n" +
"this is a secret\n" +
"this is a config\n"
assert.Equal(t, expect, buf.String())
}
func waitAndAssert(t *testing.T, timeout time.Duration, f func(*testing.T) bool) {
t.Helper()
after := time.After(timeout)
for {
select {
case <-after:
t.Fatalf("timed out waiting for condition")
default:
}
if f(t) {
return
}
time.Sleep(100 * time.Millisecond)
}
}

View File

@ -1,10 +1,14 @@
package swarm
import (
"context"
"fmt"
"testing"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
swarmtypes "github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/client"
"github.com/docker/docker/integration-cli/daemon"
"github.com/docker/docker/internal/test/environment"
"github.com/stretchr/testify/require"
@ -34,3 +38,121 @@ func NewSwarm(t *testing.T, testEnv *environment.Execution) *daemon.Swarm {
require.NoError(t, d.Init(swarmtypes.InitRequest{}))
return d
}
// ServiceSpecOpt is used with `CreateService` to pass in service spec modifiers
type ServiceSpecOpt func(*swarmtypes.ServiceSpec)
// CreateService creates a service on the passed in swarm daemon.
func CreateService(t *testing.T, d *daemon.Swarm, opts ...ServiceSpecOpt) string {
spec := defaultServiceSpec()
for _, o := range opts {
o(&spec)
}
client := GetClient(t, d)
resp, err := client.ServiceCreate(context.Background(), spec, types.ServiceCreateOptions{})
require.NoError(t, err, "error creating service")
return resp.ID
}
func defaultServiceSpec() swarmtypes.ServiceSpec {
var spec swarmtypes.ServiceSpec
ServiceWithImage("busybox:latest")(&spec)
ServiceWithCommand([]string{"/bin/top"})(&spec)
ServiceWithReplicas(1)(&spec)
return spec
}
// ServiceWithImage sets the image to use for the service
func ServiceWithImage(image string) func(*swarmtypes.ServiceSpec) {
return func(spec *swarmtypes.ServiceSpec) {
ensureContainerSpec(spec)
spec.TaskTemplate.ContainerSpec.Image = image
}
}
// ServiceWithCommand sets the command to use for the service
func ServiceWithCommand(cmd []string) ServiceSpecOpt {
return func(spec *swarmtypes.ServiceSpec) {
ensureContainerSpec(spec)
spec.TaskTemplate.ContainerSpec.Command = cmd
}
}
// ServiceWithConfig adds the config reference to the service
func ServiceWithConfig(configRef *swarmtypes.ConfigReference) ServiceSpecOpt {
return func(spec *swarmtypes.ServiceSpec) {
ensureContainerSpec(spec)
spec.TaskTemplate.ContainerSpec.Configs = append(spec.TaskTemplate.ContainerSpec.Configs, configRef)
}
}
// ServiceWithSecret adds the secret reference to the service
func ServiceWithSecret(secretRef *swarmtypes.SecretReference) ServiceSpecOpt {
return func(spec *swarmtypes.ServiceSpec) {
ensureContainerSpec(spec)
spec.TaskTemplate.ContainerSpec.Secrets = append(spec.TaskTemplate.ContainerSpec.Secrets, secretRef)
}
}
// ServiceWithReplicas sets the replicas for the service
func ServiceWithReplicas(n uint64) ServiceSpecOpt {
return func(spec *swarmtypes.ServiceSpec) {
spec.Mode = swarmtypes.ServiceMode{
Replicated: &swarmtypes.ReplicatedService{
Replicas: &n,
},
}
}
}
// ServiceWithName sets the name of the service
func ServiceWithName(name string) ServiceSpecOpt {
return func(spec *swarmtypes.ServiceSpec) {
spec.Annotations.Name = name
}
}
// GetRunningTasks gets the list of running tasks for a service
func GetRunningTasks(t *testing.T, d *daemon.Swarm, serviceID string) []swarmtypes.Task {
client := GetClient(t, d)
filterArgs := filters.NewArgs()
filterArgs.Add("desired-state", "running")
filterArgs.Add("service", serviceID)
options := types.TaskListOptions{
Filters: filterArgs,
}
tasks, err := client.TaskList(context.Background(), options)
require.NoError(t, err)
return tasks
}
// ExecTask runs the passed in exec config on the given task
func ExecTask(t *testing.T, d *daemon.Swarm, task swarmtypes.Task, config types.ExecConfig) types.HijackedResponse {
client := GetClient(t, d)
ctx := context.Background()
resp, err := client.ContainerExecCreate(ctx, task.Status.ContainerStatus.ContainerID, config)
require.NoError(t, err, "error creating exec")
startCheck := types.ExecStartCheck{}
attach, err := client.ContainerExecAttach(ctx, resp.ID, startCheck)
require.NoError(t, err, "error attaching to exec")
return attach
}
func ensureContainerSpec(spec *swarmtypes.ServiceSpec) {
if spec.TaskTemplate.ContainerSpec == nil {
spec.TaskTemplate.ContainerSpec = &swarmtypes.ContainerSpec{}
}
}
// GetClient creates a new client for the passed in swarm daemon.
func GetClient(t *testing.T, d *daemon.Swarm) client.APIClient {
client, err := client.NewClientWithOpts(client.WithHost((d.Sock())))
require.NoError(t, err)
return client
}

View File

@ -1,8 +1,10 @@
package secret
import (
"bytes"
"sort"
"testing"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
@ -10,6 +12,7 @@ import (
"github.com/docker/docker/client"
"github.com/docker/docker/integration/internal/swarm"
"github.com/docker/docker/internal/testutil"
"github.com/docker/docker/pkg/stdcopy"
"github.com/gotestyourself/gotestyourself/skip"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -232,3 +235,130 @@ func TestSecretsUpdate(t *testing.T) {
err = client.SecretUpdate(ctx, secretID, insp.Version, insp.Spec)
testutil.ErrorContains(t, err, "only updates to Labels are allowed")
}
func TestTemplatedSecret(t *testing.T) {
d := swarm.NewSwarm(t, testEnv)
defer d.Stop(t)
ctx := context.Background()
client := swarm.GetClient(t, d)
referencedSecretSpec := swarmtypes.SecretSpec{
Annotations: swarmtypes.Annotations{
Name: "referencedsecret",
},
Data: []byte("this is a secret"),
}
referencedSecret, err := client.SecretCreate(ctx, referencedSecretSpec)
assert.NoError(t, err)
referencedConfigSpec := swarmtypes.ConfigSpec{
Annotations: swarmtypes.Annotations{
Name: "referencedconfig",
},
Data: []byte("this is a config"),
}
referencedConfig, err := client.ConfigCreate(ctx, referencedConfigSpec)
assert.NoError(t, err)
secretSpec := swarmtypes.SecretSpec{
Annotations: swarmtypes.Annotations{
Name: "templated_secret",
},
Templating: &swarmtypes.Driver{
Name: "golang",
},
Data: []byte("SERVICE_NAME={{.Service.Name}}\n" +
"{{secret \"referencedsecrettarget\"}}\n" +
"{{config \"referencedconfigtarget\"}}\n"),
}
templatedSecret, err := client.SecretCreate(ctx, secretSpec)
assert.NoError(t, err)
serviceID := swarm.CreateService(t, d,
swarm.ServiceWithSecret(
&swarmtypes.SecretReference{
File: &swarmtypes.SecretReferenceFileTarget{
Name: "templated_secret",
UID: "0",
GID: "0",
Mode: 0600,
},
SecretID: templatedSecret.ID,
SecretName: "templated_secret",
},
),
swarm.ServiceWithConfig(
&swarmtypes.ConfigReference{
File: &swarmtypes.ConfigReferenceFileTarget{
Name: "referencedconfigtarget",
UID: "0",
GID: "0",
Mode: 0600,
},
ConfigID: referencedConfig.ID,
ConfigName: "referencedconfig",
},
),
swarm.ServiceWithSecret(
&swarmtypes.SecretReference{
File: &swarmtypes.SecretReferenceFileTarget{
Name: "referencedsecrettarget",
UID: "0",
GID: "0",
Mode: 0600,
},
SecretID: referencedSecret.ID,
SecretName: "referencedsecret",
},
),
swarm.ServiceWithName("svc"),
)
var tasks []swarmtypes.Task
waitAndAssert(t, 60*time.Second, func(t *testing.T) bool {
tasks = swarm.GetRunningTasks(t, d, serviceID)
return len(tasks) > 0
})
task := tasks[0]
waitAndAssert(t, 60*time.Second, func(t *testing.T) bool {
if task.NodeID == "" || (task.Status.ContainerStatus == nil || task.Status.ContainerStatus.ContainerID == "") {
task, _, _ = client.TaskInspectWithRaw(context.Background(), task.ID)
}
return task.NodeID != "" && task.Status.ContainerStatus != nil && task.Status.ContainerStatus.ContainerID != ""
})
attach := swarm.ExecTask(t, d, task, types.ExecConfig{
Cmd: []string{"/bin/cat", "/run/secrets/templated_secret"},
AttachStdout: true,
AttachStderr: true,
})
buf := bytes.NewBuffer(nil)
_, err = stdcopy.StdCopy(buf, buf, attach.Reader)
require.NoError(t, err)
expect := "SERVICE_NAME=svc\n" +
"this is a secret\n" +
"this is a config\n"
assert.Equal(t, expect, buf.String())
}
func waitAndAssert(t *testing.T, timeout time.Duration, f func(*testing.T) bool) {
t.Helper()
after := time.After(timeout)
for {
select {
case <-after:
t.Fatalf("timed out waiting for condition")
default:
}
if f(t) {
return
}
time.Sleep(100 * time.Millisecond)
}
}