Skip to content
This repository has been archived by the owner on Nov 5, 2021. It is now read-only.

Commit

Permalink
Unit tests for service spec creation
Browse files Browse the repository at this point in the history
Signed-off-by: Marcelo Pereira <[email protected]>
  • Loading branch information
MarcPer authored and Marcelo committed Jul 9, 2018
1 parent 4d19113 commit d7be830
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 51 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
sudo: required

services:
- docker
- docker
before_install:
- docker pull golang:1.9.2

script:
- make test
- make docker
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.PHONY: test

linux:
CGO_ENABLED=0 GOOS=linux go build -a -ldflags "-s -w" -installsuffix cgo -o ./jaas
Expand All @@ -7,3 +8,6 @@ darwin:

docker:
docker build -t alexellis2/jaas:latest .

test:
go test ./...
117 changes: 67 additions & 50 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ var (
verbose bool
)

type clientInterface interface {
client.CommonAPIClient
}

func init() {
rootCmd.AddCommand(runCmd)

Expand Down Expand Up @@ -94,7 +98,6 @@ func runTask(taskRequest TaskRequest) error {
var err error
c, err = client.NewEnvClient()
if err != nil {

return fmt.Errorf("is the Docker Daemon running? Error: %s", err.Error())
}

Expand All @@ -116,13 +119,7 @@ func runTask(taskRequest TaskRequest) error {
}
}

spec := makeSpec(taskRequest.Image, taskRequest.EnvVars)
if len(taskRequest.Networks) > 0 {
nets := []swarm.NetworkAttachmentConfig{
swarm.NetworkAttachmentConfig{Target: taskRequest.Networks[0]},
}
spec.Networks = nets
}
spec := makeServiceSpec(taskRequest, c)

createOptions := types.ServiceCreateOptions{}

Expand All @@ -131,16 +128,6 @@ func runTask(taskRequest TaskRequest) error {
fmt.Println("Using RegistryAuth")
}

placement := &swarm.Placement{}
if len(taskRequest.Constraints) > 0 {
placement.Constraints = taskRequest.Constraints
spec.TaskTemplate.Placement = placement
}

if len(taskRequest.Command) > 0 {
spec.TaskTemplate.ContainerSpec.Command = strings.Split(taskRequest.Command, " ")
}

if len(taskRequest.EnvFiles) > 0 {
for _, file := range taskRequest.EnvFiles {
envs, err := readEnvs(file)
Expand All @@ -155,10 +142,66 @@ func runTask(taskRequest TaskRequest) error {
}
}

createResponse, _ := c.ServiceCreate(context.Background(), spec, createOptions)
opts := types.ServiceInspectOptions{InsertDefaults: true}

service, _, _ := c.ServiceInspectWithRaw(context.Background(), createResponse.ID, opts)
fmt.Printf("Service created: %s (%s)\n", service.Spec.Name, createResponse.ID)

taskExitCode := pollTask(c, createResponse.ID, timeoutVal, taskRequest.ShowLogs, taskRequest.RemoveService)
os.Exit(taskExitCode)
return nil
}

func makeServiceSpec(tr TaskRequest, c clientInterface) swarm.ServiceSpec {
max := uint64(1)
spec := swarm.ServiceSpec{
TaskTemplate: swarm.TaskSpec{
RestartPolicy: &swarm.RestartPolicy{
MaxAttempts: &max,
Condition: swarm.RestartPolicyConditionNone,
},
ContainerSpec: &swarm.ContainerSpec{
Image: tr.Image,
Env: tr.EnvVars,
},
},
}
attachNetworks(&spec, tr.Networks)
setConstraints(&spec, tr.Constraints)
setCommand(&spec, tr.Command)
attachMounts(&spec, tr.Mounts)
attachSecrets(&spec, tr.Secrets, c)
return spec
}

func attachNetworks(spec *swarm.ServiceSpec, networks []string) {
nets := []swarm.NetworkAttachmentConfig{}
for _, n := range networks {
nets = append(nets, swarm.NetworkAttachmentConfig{Target: n})
}

spec.Networks = nets
}

func setConstraints(spec *swarm.ServiceSpec, constraints []string) {
if len(constraints) > 0 {
placement := &swarm.Placement{Constraints: constraints}
spec.TaskTemplate.Placement = placement
}
}

func setCommand(spec *swarm.ServiceSpec, command string) {
if len(command) > 0 {
spec.TaskTemplate.ContainerSpec.Command = strings.Split(command, " ")
}
}

func attachMounts(spec *swarm.ServiceSpec, mounts []string) {
spec.TaskTemplate.ContainerSpec.Mounts = []mount.Mount{}
for _, bindMount := range taskRequest.Mounts {
for _, bindMount := range mounts {
parts := strings.Split(bindMount, "=")
if len(parts) < 2 || len(parts) > 2 {
if len(parts) != 2 {
fmt.Fprintf(os.Stderr, "Bind-mounts must be specified as: src=dest, i.e. --mount /home/alex/tmp/=/tmp/\n")
os.Exit(1)
}
Expand All @@ -172,11 +215,13 @@ func runTask(taskRequest TaskRequest) error {
spec.TaskTemplate.ContainerSpec.Mounts = append(spec.TaskTemplate.ContainerSpec.Mounts, mountVal)
}
}
}

secretList, err := c.SecretList(context.Background(), types.SecretListOptions{})
func attachSecrets(spec *swarm.ServiceSpec, secrets []string, c clientInterface) {
secretList, _ := c.SecretList(context.Background(), types.SecretListOptions{})

spec.TaskTemplate.ContainerSpec.Secrets = []*swarm.SecretReference{}
for _, serviceSecret := range taskRequest.Secrets {
for _, serviceSecret := range secrets {
var secretID string
for _, s := range secretList {
if serviceSecret == s.Spec.Annotations.Name {
Expand All @@ -202,34 +247,6 @@ func runTask(taskRequest TaskRequest) error {

spec.TaskTemplate.ContainerSpec.Secrets = append(spec.TaskTemplate.ContainerSpec.Secrets, &secretVal)
}

createResponse, _ := c.ServiceCreate(context.Background(), spec, createOptions)
opts := types.ServiceInspectOptions{InsertDefaults: true}

service, _, _ := c.ServiceInspectWithRaw(context.Background(), createResponse.ID, opts)
fmt.Printf("Service created: %s (%s)\n", service.Spec.Name, createResponse.ID)

taskExitCode := pollTask(c, createResponse.ID, timeoutVal, taskRequest.ShowLogs, taskRequest.RemoveService)
os.Exit(taskExitCode)
return nil
}

func makeSpec(image string, envVars []string) swarm.ServiceSpec {
max := uint64(1)

spec := swarm.ServiceSpec{
TaskTemplate: swarm.TaskSpec{
RestartPolicy: &swarm.RestartPolicy{
MaxAttempts: &max,
Condition: swarm.RestartPolicyConditionNone,
},
ContainerSpec: &swarm.ContainerSpec{
Image: image,
Env: envVars,
},
},
}
return spec
}

func readEnvs(file string) ([]string, error) {
Expand Down
108 changes: 108 additions & 0 deletions cmd/run_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright (c) Alex Ellis 2017-2018. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

package cmd

import (
"context"
"reflect"
"strconv"
"testing"

"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/client"
)

var validRequest = TaskRequest{
Image: "input-image",
Networks: []string{"net1", "net2"},
Constraints: []string{"node.id=2ivku8v2gvtg4", "engine.labels.operatingsystem==ubuntu 14.04"},
EnvVars: []string{"ev1=val1", "ev2=val2"},
Mounts: []string{"hostVol1=taskVol1", "hostVol2=taskVol2"},
EnvFiles: []string{".env1", ".env2"},
Secrets: []string{"secret1", "secret2"},
ShowLogs: true,
Timeout: "12",
RemoveService: true,
RegistryAuth: "true",
Command: "echo 'some output'",
}

type fakeClient struct {
client.CommonAPIClient
secrets []string
}

func (fk fakeClient) SecretList(ctx context.Context, sopt types.SecretListOptions) ([]swarm.Secret, error) {
slist := []swarm.Secret{}
for i, secret := range fk.secrets {
a := swarm.Annotations{Name: secret}
sspec := swarm.SecretSpec{Annotations: a}
s := swarm.Secret{
ID: strconv.Itoa(i),
Meta: swarm.Meta{},
Spec: sspec,
}

slist = append(slist, s)
}
return slist, nil
}

func newClient(secrets []string) fakeClient {
return fakeClient{secrets: secrets}
}

func TestMakeServiceSpecValid(t *testing.T) {
c := newClient([]string{"secret1", "secret2", "secret3"})
spec := makeServiceSpec(validRequest, c)
if spec.TaskTemplate.ContainerSpec.Image != "input-image" {
t.Errorf("Container spec image should be %s, was %s", validRequest.Image, spec.TaskTemplate.ContainerSpec.Image)
}

// Test networks
networkTargets := []string{}
for _, n := range spec.Networks {
networkTargets = append(networkTargets, n.Target)
}
if !reflect.DeepEqual(networkTargets, []string{"net1", "net2"}) {
t.Errorf("Container spec networks should be %s, was %s", validRequest.Networks, networkTargets)
}

// Test env variables
if !reflect.DeepEqual(spec.TaskTemplate.ContainerSpec.Env, []string{"ev1=val1", "ev2=val2"}) {
t.Errorf("Container spec env should be %s, was %s", validRequest.EnvVars, spec.TaskTemplate.ContainerSpec.Env)
}

// Test mounts
expectedMounts := []mount.Mount{
{Source: "hostVol1", Target: "taskVol1"},
{Source: "hostVol2", Target: "taskVol2"},
}
if !reflect.DeepEqual(spec.TaskTemplate.ContainerSpec.Mounts, expectedMounts) {
t.Error("Container spec mounts should include:")
for _, m := range expectedMounts {
t.Errorf("{Source: %s, Target: %s}", m.Source, m.Target)
}
t.Error("But contained instead:")
for _, m := range spec.TaskTemplate.ContainerSpec.Mounts {
t.Errorf("{Source: %s, Target: %s}", m.Source, m.Target)
}
}

// Test secrets
secretNames := []string{}
for _, s := range spec.TaskTemplate.ContainerSpec.Secrets {
secretNames = append(secretNames, s.SecretName)
}
if !reflect.DeepEqual(secretNames, []string{"secret1", "secret2"}) {
t.Errorf("Container spec secrets should be %s, was %s", validRequest.Secrets, secretNames)
}

// Test command
if !reflect.DeepEqual(spec.TaskTemplate.ContainerSpec.Command, []string{"echo", "'some", "output'"}) {
t.Errorf("Container spec command should be %s, was %s", []string{"echo", "'some", "output'"}, spec.TaskTemplate.ContainerSpec.Command)
}
}

0 comments on commit d7be830

Please sign in to comment.