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 #518 from silvin-lubecki/status-display-bundle-infos
Browse files Browse the repository at this point in the history
Status display bundle infos
  • Loading branch information
silvin-lubecki committed Apr 26, 2019
2 parents 7b03132 + 2ed726a commit 137c8cd
Show file tree
Hide file tree
Showing 10 changed files with 184 additions and 9 deletions.
18 changes: 13 additions & 5 deletions e2e/cnab_test.go
Expand Up @@ -20,15 +20,23 @@ func TestCallCustomStatusAction(t *testing.T) {
cnab string
}{
{
name: "validCustomStatusAction",
name: "validCustomDockerStatusAction",
exitCode: 0,
expectedOutput: "Status action",
cnab: "cnab-with-status",
expectedOutput: "com.docker.app.status",
cnab: "cnab-with-docker-status",
},
{
name: "validCustomStandardStatusAction",
exitCode: 0,
expectedOutput: "io.cnab.status",
cnab: "cnab-with-standard-status",
},
// A CNAB bundle without standard or docker status action still can output
// some informations about the installation.
{
name: "missingCustomStatusAction",
exitCode: 1,
expectedOutput: "status failed: action not defined for bundle",
exitCode: 0,
expectedOutput: "Name: missingCustomStatusAction",
cnab: "cnab-without-status",
},
}
Expand Down
24 changes: 24 additions & 0 deletions e2e/commands_test.go
Expand Up @@ -352,6 +352,30 @@ func testDockerAppLifecycle(t *testing.T, useBindMount bool) {
cmd.Command = dockerCli.Command("app", "status", appName)
checkContains(t, icmd.RunCmd(cmd).Assert(t, icmd.Success).Combined(),
[]string{
`INSTALLATION
------------
Name: TestDockerAppLifecycle_.*
Created: .*
Modified: .*
Revision: .*
Last Action: install
Result: SUCCESS
Orchestrator: swarm
APPLICATION
-----------
Name: simple
Version: 1.1.0-beta1
Reference:.*
PARAMETERS
----------
api_host: example.com
static_subdir: data/static
web_port: 8082
STATUS
------`,
fmt.Sprintf("[[:alnum:]]+ %s_db replicated [0-1]/1 postgres:9.3", appName),
fmt.Sprintf(`[[:alnum:]]+ %s_web replicated [0-1]/1 nginx:latest \*:8082->80/tcp`, appName),
fmt.Sprintf("[[:alnum:]]+ %s_api replicated [0-1]/1 python:3.6", appName),
Expand Down
@@ -1,10 +1,10 @@
{
"name": "cnab-with-status",
"name": "cnab-with-docker-status",
"version": "0.1.0",
"invocationImages": [
{
"imageType": "docker",
"image": "e2e/cnab-with-status:v0.1.0"
"image": "e2e/cnab-with-docker-status:v0.1.0"
}
],
"actions": {
Expand Down
15 changes: 15 additions & 0 deletions e2e/testdata/cnab-with-standard-status/bundle.json
@@ -0,0 +1,15 @@
{
"name": "cnab-with-standard-status",
"version": "0.1.0",
"invocationImages": [
{
"imageType": "docker",
"image": "e2e/cnab-with-standard-status:v0.1.0"
}
],
"actions": {
"io.cnab.status": {
"modifies": false
}
}
}
23 changes: 23 additions & 0 deletions e2e/testdata/cnab-with-standard-status/cnab/app/run
@@ -0,0 +1,23 @@
#!/bin/sh

action=$CNAB_ACTION
name=$CNAB_INSTALLATION_NAME

case $action in
install)
echo "Install action"
;;
uninstall)
echo "uninstall action"
;;
upgrade)
echo "Upgrade action"
;;
io.cnab.status)
echo "Status action"
;;
*)
echo "No action for $action"
;;
esac
echo "Action $action complete for $name"
7 changes: 7 additions & 0 deletions e2e/testdata/cnab-with-standard-status/cnab/build/Dockerfile
@@ -0,0 +1,7 @@
ARG ALPINE_VERSION=3.9.2

FROM alpine:${ALPINE_VERSION}

COPY cnab/app/run /cnab/app/run

CMD /cnab/app/run
2 changes: 1 addition & 1 deletion internal/commands/install.go
Expand Up @@ -136,7 +136,7 @@ func runInstall(dockerCli command.Cli, appname string, opts installOptions) erro
// so any installation needs a clean uninstallation.
err2 := installationStore.Store(installation)
if err != nil {
return fmt.Errorf("Installation failed: %s", errBuf)
return fmt.Errorf("Installation failed: %s\n%s", errBuf, err)
}
if err2 != nil {
return err2
Expand Down
100 changes: 99 additions & 1 deletion internal/commands/status.go
Expand Up @@ -2,15 +2,31 @@ package commands

import (
"fmt"
"io"
"os"
"sort"
"strings"
"text/tabwriter"
"time"

"github.com/deislabs/duffle/pkg/action"
"github.com/deislabs/duffle/pkg/credentials"
"github.com/docker/app/internal"
"github.com/docker/app/internal/store"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
units "github.com/docker/go-units"
"github.com/spf13/cobra"
)

var (
knownStatusActions = []string{
internal.ActionStatusName,
// TODO: Extract this constant to the cnab-go library
"io.cnab.status",
}
)

func statusCmd(dockerCli command.Cli) *cobra.Command {
var opts credentialOptions

Expand Down Expand Up @@ -42,6 +58,14 @@ func runStatus(dockerCli command.Cli, installationName string, opts credentialOp
if err != nil {
return err
}
displayInstallationStatus(os.Stdout, installation)

// Check if the bundle knows the docker app status action, if not just exit without error.
statusAction := resolveStatusAction(installation)
if statusAction == "" {
return nil
}

bind, err := requiredClaimBindMount(installation.Claim, opts.targetContext, dockerCli)
if err != nil {
return err
Expand All @@ -62,12 +86,86 @@ func runStatus(dockerCli command.Cli, installationName string, opts credentialOp
if err := credentials.Validate(creds, installation.Bundle.Credentials); err != nil {
return err
}
printHeader(os.Stdout, "STATUS")
status := &action.RunCustom{
Action: internal.ActionStatusName,
Action: statusAction,
Driver: driverImpl,
}
if err := status.Run(&installation.Claim, creds, dockerCli.Out()); err != nil {
return fmt.Errorf("status failed: %s\n%s", err, errBuf)
}
return nil
}

func displayInstallationStatus(w io.Writer, installation *store.Installation) {
printHeader(w, "INSTALLATION")
tab := tabwriter.NewWriter(w, 0, 0, 1, ' ', 0)
printValue(tab, "Name", installation.Name)
printValue(tab, "Created", units.HumanDuration(time.Since(installation.Created)))
printValue(tab, "Modified", units.HumanDuration(time.Since(installation.Modified)))
printValue(tab, "Revision", installation.Revision)
printValue(tab, "Last Action", installation.Result.Action)
printValue(tab, "Result", strings.ToUpper(installation.Result.Status))
if o, ok := installation.Parameters[internal.ParameterOrchestratorName]; ok {
orchestrator := fmt.Sprintf("%v", o)
if orchestrator == "" {
orchestrator = string(command.OrchestratorSwarm)
}
printValue(tab, "Orchestrator", orchestrator)
if kubeNamespace, ok := installation.Parameters[internal.ParameterKubernetesNamespaceName]; ok && orchestrator == string(command.OrchestratorKubernetes) {
printValue(tab, "Kubernetes namespace", fmt.Sprintf("%v", kubeNamespace))
}
}

tab.Flush()
fmt.Fprintln(w)

printHeader(w, "APPLICATION")
tab = tabwriter.NewWriter(w, 0, 0, 1, ' ', 0)
printValue(tab, "Name", installation.Bundle.Name)
printValue(tab, "Version", installation.Bundle.Version)
printValue(tab, "Reference", installation.Reference)
tab.Flush()
fmt.Fprintln(w)

if len(installation.Parameters) > 0 {
printHeader(w, "PARAMETERS")
tab = tabwriter.NewWriter(w, 0, 0, 1, ' ', 0)
params := sortParameters(installation)
for _, param := range params {
if !strings.HasPrefix(param, internal.Namespace) {
// TODO: Trim long []byte parameters, maybe add type too (string, int...)
printValue(tab, param, fmt.Sprintf("%v", installation.Parameters[param]))
}
}
tab.Flush()
fmt.Fprintln(w)
}
}

func sortParameters(installation *store.Installation) []string {
var params []string
for name := range installation.Parameters {
params = append(params, name)
}
sort.Strings(params)
return params
}

func printHeader(w io.Writer, header string) {
fmt.Fprintln(w, header)
fmt.Fprintln(w, strings.Repeat("-", len(header)))
}

func printValue(w io.Writer, key, value string) {
fmt.Fprintf(w, "%s:\t%s\n", key, value)
}

func resolveStatusAction(installation *store.Installation) string {
for _, name := range knownStatusActions {
if _, ok := installation.Bundle.Actions[name]; ok {
return name
}
}
return ""
}

0 comments on commit 137c8cd

Please sign in to comment.