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

Commit

Permalink
Merge pull request #715 from rumpl/feat-run-labels
Browse files Browse the repository at this point in the history
Feat run labels
  • Loading branch information
jcsirot authored Oct 31, 2019
2 parents cbf819c + 903f3c0 commit 3e2545d
Show file tree
Hide file tree
Showing 10 changed files with 164 additions and 11 deletions.
25 changes: 25 additions & 0 deletions cmd/cnab-run/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ func installAction(instanceName string) error {
if err != nil {
return err
}
if err = addLabels(rendered); err != nil {
return err
}
addAppLabels(rendered, instanceName)

if err := os.Chdir(app.Path); err != nil {
return err
}
Expand Down Expand Up @@ -87,6 +91,27 @@ func getBundleImageMap() (map[string]bundle.Image, error) {
return result, nil
}

func addLabels(rendered *composetypes.Config) error {
args, err := ioutil.ReadFile(internal.DockerArgsPath)
if err != nil {
return err
}
var a packager.DockerAppArgs
if err := json.Unmarshal(args, &a); err != nil {
return err
}
for k, v := range a.Labels {
for i, service := range rendered.Services {
if service.Labels == nil {
service.Labels = map[string]string{}
}
service.Labels[k] = v
rendered.Services[i] = service
}
}
return nil
}

func addAppLabels(rendered *composetypes.Config, instanceName string) {
for i, service := range rendered.Services {
if service.Labels == nil {
Expand Down
24 changes: 24 additions & 0 deletions e2e/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,30 @@ func TestRunOnlyOne(t *testing.T) {
})
}

func TestRunWithLabels(t *testing.T) {
runWithDindSwarmAndRegistry(t, func(info dindSwarmAndRegistryInfo) {
cmd := info.configuredCmd

contextPath := filepath.Join("testdata", "simple")
cmd.Command = dockerCli.Command("app", "build", "--tag", "myapp", contextPath)
icmd.RunCmd(cmd).Assert(t, icmd.Success)

cmd.Command = dockerCli.Command("app", "run", "myapp", "--name", "myapp", "--label", "label.key=labelValue")
icmd.RunCmd(cmd).Assert(t, icmd.Success)

services := []string{
"myapp_db", "myapp_web", "myapp_api",
}
for _, service := range services {
cmd.Command = dockerCli.Command("inspect", service)
icmd.RunCmd(cmd).Assert(t, icmd.Expected{
ExitCode: 0,
Out: `"label.key": "labelValue"`,
})
}
})
}

func TestDockerAppLifecycle(t *testing.T) {
t.Run("withBindMounts", func(t *testing.T) {
testDockerAppLifecycle(t, true)
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/image/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func printImageIDs(dockerCli command.Cli, refs []pkg) error {
}
fmt.Fprintln(&buf, stringid.TruncateID(id.String()))
}
fmt.Fprintf(dockerCli.Out(), buf.String())
fmt.Fprint(dockerCli.Out(), buf.String())
return nil
}

Expand Down
6 changes: 0 additions & 6 deletions internal/commands/image/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,6 @@ import (
"github.com/docker/distribution/reference"
)

type mockRef string

func (ref mockRef) String() string {
return string(ref)
}

type bundleStoreStubForListCmd struct {
refMap map[reference.Reference]*bundle.Bundle
// in order to keep the reference in the same order between tests
Expand Down
23 changes: 23 additions & 0 deletions internal/commands/parameters.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package commands

import (
"encoding/json"
"fmt"
"io"
"os"
"strings"

"github.com/deislabs/cnab-go/bundle"
"github.com/docker/app/internal"
"github.com/docker/app/internal/packager"
"github.com/docker/app/internal/store"
"github.com/docker/app/types/parameters"
cliopts "github.com/docker/cli/opts"
Expand Down Expand Up @@ -45,6 +47,27 @@ func withCommandLineParameters(overrides []string) mergeBundleOpt {
}
}

func withLabels(labels []string) mergeBundleOpt {
return func(c *mergeBundleConfig) error {
for _, l := range labels {
if strings.HasPrefix(l, internal.Namespace) {
return errors.Errorf("labels cannot start with %q", internal.Namespace)
}
}
l := packager.DockerAppArgs{
Labels: cliopts.ConvertKVStringsToMap(labels),
}
out, err := json.Marshal(l)
if err != nil {
return err
}
if _, ok := c.bundle.Parameters[internal.ParameterArgs]; ok {
c.params[internal.ParameterArgs] = string(out)
}
return nil
}
}

func withSendRegistryAuth(sendRegistryAuth bool) mergeBundleOpt {
return func(c *mergeBundleConfig) error {
if _, ok := c.bundle.Definitions[internal.ParameterShareRegistryCredsName]; ok {
Expand Down
41 changes: 41 additions & 0 deletions internal/commands/parameters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ package commands

import (
"bytes"
"encoding/json"
"fmt"
"strings"
"testing"

"github.com/deislabs/cnab-go/bundle"
"github.com/deislabs/cnab-go/bundle/definition"
"github.com/deislabs/cnab-go/claim"
"github.com/docker/app/internal"
"github.com/docker/app/internal/packager"
"github.com/docker/app/internal/store"
"gotest.tools/assert"
"gotest.tools/assert/cmp"
Expand Down Expand Up @@ -264,3 +267,41 @@ func TestMergeBundleParameters(t *testing.T) {
assert.ErrorContains(t, err, "invalid value for parameter")
})
}

func TestLabels(t *testing.T) {
expected := packager.DockerAppArgs{
Labels: map[string]string{
"label": "value",
},
}
expectedStr, err := json.Marshal(expected)
assert.NilError(t, err)

labels := []string{
"label=value",
}
op := withLabels(labels)

config := &mergeBundleConfig{
bundle: &bundle.Bundle{
Parameters: map[string]bundle.Parameter{
internal.ParameterArgs: {},
},
},
params: map[string]string{},
}
err = op(config)
assert.NilError(t, err)
fmt.Println(config.params)
l := config.params[internal.ParameterArgs]
assert.Equal(t, l, string(expectedStr))
}

func TestInvalidLabels(t *testing.T) {
labels := []string{
"com.docker.app.label=value",
}
op := withLabels(labels)
err := op(&mergeBundleConfig{})
assert.ErrorContains(t, err, fmt.Sprintf("labels cannot start with %q", internal.Namespace))
}
11 changes: 7 additions & 4 deletions internal/commands/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type runOptions struct {
kubeNamespace string
stackName string
cnabBundle string
labels []string
}

const longDescription = `Run an App from an App image.`
Expand Down Expand Up @@ -59,10 +60,11 @@ func runCmd(dockerCli command.Cli) *cobra.Command {
}
opts.parametersOptions.addFlags(cmd.Flags())
opts.credentialOptions.addFlags(cmd.Flags())
cmd.Flags().StringVar(&opts.orchestrator, "orchestrator", "", "Orchestrator to run on (swarm, kubernetes)")
cmd.Flags().StringVar(&opts.kubeNamespace, "namespace", "default", "Kubernetes namespace in which to run the App")
cmd.Flags().StringVar(&opts.stackName, "name", "", "Name of the running App")
cmd.Flags().StringVar(&opts.cnabBundle, "cnab-bundle-json", "", "Run a CNAB bundle instead of a Docker App image")
cmd.Flags().StringVar(&opts.orchestrator, "orchestrator", "", "Orchestrator to install on (swarm, kubernetes)")
cmd.Flags().StringVar(&opts.kubeNamespace, "namespace", "default", "Kubernetes namespace to install into")
cmd.Flags().StringVar(&opts.stackName, "name", "", "Assign a name to the installation")
cmd.Flags().StringVar(&opts.cnabBundle, "cnab-bundle-json", "", "Run a CNAB bundle instead of a Docker App")
cmd.Flags().StringArrayVar(&opts.labels, "label", nil, "Label to add to services")

return cmd
}
Expand Down Expand Up @@ -130,6 +132,7 @@ func runBundle(dockerCli command.Cli, bndl *bundle.Bundle, opts runOptions, ref
if err := mergeBundleParameters(installation,
withFileParameters(opts.parametersFiles),
withCommandLineParameters(opts.overrides),
withLabels(opts.labels),
withOrchestratorParameters(opts.orchestrator, opts.kubeNamespace),
withSendRegistryAuth(opts.sendRegistryAuth),
); err != nil {
Expand Down
4 changes: 4 additions & 0 deletions internal/names.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ const (
ParameterRenderFormatName = Namespace + "render-format"
// ParameterInspectFormatName is the name of the parameter containing the inspect format
ParameterInspectFormatName = Namespace + "inspect-format"
// ParameterArgs is the name of the parameter containing labels to be applied to service containers
ParameterArgs = Namespace + "args"
// ParameterShareRegistryCredsName is the name of the parameter which indicates if credentials should be shared
ParameterShareRegistryCredsName = Namespace + "share-registry-creds"

Expand All @@ -68,6 +70,8 @@ const (
// the inspect output format.
DockerInspectFormatEnvVar = "DOCKER_INSPECT_FORMAT"

DockerArgsPath = "/cnab/app/args.json"

// CustomDockerAppName is the custom variable set by Docker App to
// save custom informations
CustomDockerAppName = "com.docker.app"
Expand Down
23 changes: 23 additions & 0 deletions internal/packager/cnab.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,24 @@ type DockerAppCustom struct {
Payload json.RawMessage `json:"payload,omitempty"`
}

// DockerAppArgs represent the object passed to the invocation image
// by Docker App.
type DockerAppArgs struct {
// Labels are the labels to add to containers on run
Labels map[string]string `json:"labels,omitempty"`
}

// ToCNAB creates a CNAB bundle from an app package
func ToCNAB(app *types.App, invocationImageName string) (*bundle.Bundle, error) {
mapping := ExtractCNABParameterMapping(app.Parameters())
flatParameters := app.Parameters().Flatten()
definitions := definition.Definitions{
internal.ParameterArgs: {
Type: "string",
Default: "",
Title: "Arguments",
Description: "Arguments that are passed by file to the invocation image",
},
internal.ParameterOrchestratorName: {
Type: "string",
Enum: []interface{}{
Expand Down Expand Up @@ -73,6 +86,16 @@ func ToCNAB(app *types.App, invocationImageName string) (*bundle.Bundle, error)
},
}
parameters := map[string]bundle.Parameter{
internal.ParameterArgs: {
Destination: &bundle.Location{
Path: internal.DockerArgsPath,
},
ApplyTo: []string{
"install",
"upgrade",
},
Definition: internal.ParameterArgs,
},
internal.ParameterOrchestratorName: {
Destination: &bundle.Location{
EnvironmentVariable: internal.DockerStackOrchestratorEnvVar,
Expand Down
16 changes: 16 additions & 0 deletions internal/packager/testdata/bundle-json.golden
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@
"io.cnab.status": {}
},
"parameters": {
"com.docker.app.args": {
"definition": "com.docker.app.args",
"applyTo": [
"install",
"upgrade"
],
"destination": {
"path": "/cnab/app/args.json"
}
},
"com.docker.app.inspect-format": {
"definition": "com.docker.app.inspect-format",
"applyTo": [
Expand Down Expand Up @@ -115,6 +125,12 @@
}
},
"definitions": {
"com.docker.app.args": {
"default": "",
"description": "Arguments that are passed by file to the invocation image",
"title": "Arguments",
"type": "string"
},
"com.docker.app.inspect-format": {
"default": "json",
"description": "Output format for the inspect command",
Expand Down

0 comments on commit 3e2545d

Please sign in to comment.