Skip to content

Commit

Permalink
Adds odo create project -o json output
Browse files Browse the repository at this point in the history
Adds machine readable output for odo create project.

To test:

```sh
odo create project -o json
```
  • Loading branch information
cdrage committed Aug 21, 2019
1 parent d01ecd5 commit 0d4696e
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 35 deletions.
34 changes: 21 additions & 13 deletions pkg/log/status.go
Expand Up @@ -118,12 +118,16 @@ func (s *Status) Start(status string, debug bool) {
s.status = status

// If we are in debug mode, don't spin!
if !isTerm || debug {
fmt.Fprintf(s.writer, prefixSpacing+getSpacingString()+suffixSpacing+"%s ...\n", s.status)
} else if !IsJSON() {
s.spinner.SetPrefix(prefixSpacing)
s.spinner.SetSuffix(fmt.Sprintf(suffixSpacing+"%s", s.status))
s.spinner.Start()
// In under no circumstances do we output if we're using -o json.. to
// to avoid parsing errors.
if !IsJSON() {
if !isTerm || debug {
fmt.Fprintf(s.writer, prefixSpacing+getSpacingString()+suffixSpacing+"%s ...\n", s.status)
} else {
s.spinner.SetPrefix(prefixSpacing)
s.spinner.SetSuffix(fmt.Sprintf(suffixSpacing+"%s", s.status))
s.spinner.Start()
}
}

}
Expand All @@ -138,15 +142,19 @@ func (s *Status) End(success bool) {
isTerm := IsTerminal(s.writer)
if isTerm {
s.spinner.Stop()
fmt.Fprint(s.writer, "\r")
if !IsJSON() {
fmt.Fprint(s.writer, "\r")
}
}

if success {
green := color.New(color.FgGreen).SprintFunc()
fmt.Fprintf(s.writer, prefixSpacing+"%s"+suffixSpacing+"%s [%s]\n", green(getSuccessString()), s.status, s.spinner.TimeSpent())
} else {
red := color.New(color.FgRed).SprintFunc()
fmt.Fprintf(s.writer, prefixSpacing+"%s"+suffixSpacing+"%s [%s]\n", red(getErrString()), s.status, s.spinner.TimeSpent())
if !IsJSON() {
if success {
green := color.New(color.FgGreen).SprintFunc()
fmt.Fprintf(s.writer, prefixSpacing+"%s"+suffixSpacing+"%s [%s]\n", green(getSuccessString()), s.status, s.spinner.TimeSpent())
} else {
red := color.New(color.FgRed).SprintFunc()
fmt.Fprintf(s.writer, prefixSpacing+"%s"+suffixSpacing+"%s [%s]\n", red(getErrString()), s.status, s.spinner.TimeSpent())
}
}

s.status = ""
Expand Down
54 changes: 54 additions & 0 deletions pkg/machineoutput/types.go
@@ -0,0 +1,54 @@
package machineoutput

import (
"encoding/json"
"fmt"

"github.com/openshift/odo/pkg/log"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// Kind is what kind we should use in the machine readable output
const Kind = "Error"

// APIVersion is the current API version we are using
const APIVersion = "odo.openshift.io/v1alpha1"

// Error for machine readable output error messages
type Error struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Message string `json:"message"`
}

// Success same as above, but copy-and-pasted just in case
// we change the output in the future
type Success struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Message string `json:"message"`
}

// OutputSuccess outputs a "successful" machine-readable output format in json
func OutputSuccess(machineOutput Success) {
printableOutput, err := json.Marshal(machineOutput)

// If we error out... there's no way to output it (since we disable logging when using -o json)
if err != nil {
fmt.Fprintf(log.GetStderr(), "Unable to unmarshal JSON: %s", err.Error())
} else {
fmt.Fprintf(log.GetStdout(), "%s", string(printableOutput))
}
}

// OutputError outputs a "successful" machine-readable output format in json
func OutputError(machineOutput Error) {
printableOutput, err := json.Marshal(machineOutput)

// If we error out... there's no way to output it (since we disable logging when using -o json)
if err != nil {
fmt.Fprintf(log.GetStderr(), "Unable to unmarshal JSON: %s", err.Error())
} else {
fmt.Fprintf(log.GetStdout(), "%s", string(printableOutput))
}
}
39 changes: 25 additions & 14 deletions pkg/odo/cli/project/create.go
Expand Up @@ -53,27 +53,38 @@ func (pco *ProjectCreateOptions) Validate() (err error) {

// Run runs the project create command
func (pco *ProjectCreateOptions) Run() (err error) {

successMessage := fmt.Sprintf(`Project '%s' is ready for use`, pco.projectName)

// Create the "spinner"
s := &log.Status{}

// If the --wait parameter has been passed, we add a spinner..
if pco.wait {
s := log.Spinner("Waiting for project to come up")
err = project.Create(pco.Client, pco.projectName, true)
if err != nil {
return err
} else {
s.End(true)
log.Successf(`Project '%s' is ready for use`, pco.projectName)
}
} else {
err = project.Create(pco.Client, pco.projectName, false)
if err != nil {
return err
}
s = log.Spinner("Waiting for project to come up")
defer s.End(false)
}

// Create the project & end the spinner (if there is any..)
err = project.Create(pco.Client, pco.projectName, pco.wait)
if err != nil {
return err
}
s.End(true)
log.Successf(successMessage)

// Set the current project when created. If it's json output, we output a json output error
err = project.SetCurrent(pco.Client, pco.projectName)
if err != nil {
return err
}
log.Successf("New project created and now using project : %v", pco.projectName)

// If -o json has been passed, let's output the approriate json output.
if log.IsJSON() {
project.MachineReadableSuccessOutput(pco.projectName, successMessage)
} else {
log.Successf("New project created and now using project : %v", pco.projectName)
}
return
}

Expand Down
36 changes: 31 additions & 5 deletions pkg/odo/util/cmdutils.go
Expand Up @@ -2,11 +2,13 @@ package util

import (
"fmt"
"github.com/openshift/odo/pkg/config"
"os"
"strings"
"unicode"

"github.com/openshift/odo/pkg/config"
"github.com/openshift/odo/pkg/machineoutput"

"github.com/golang/glog"
"github.com/openshift/odo/pkg/component"
"github.com/openshift/odo/pkg/log"
Expand All @@ -15,21 +17,45 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// LogErrorAndExit prints the cause of the given error and exits the code with an
// exit code of 1.
// If the context is provided, then that is printed, if not, then the cause is
// detected using errors.Cause(err)
// *If* we are using the global json parameter, we instead output the json output
func LogErrorAndExit(err error, context string, a ...interface{}) {

if err != nil {
glog.V(4).Infof("Error:\n%v", err)
if context == "" {
log.Error(errors.Cause(err))

// If it's JSON, we'll output the error
if log.IsJSON() {

// Machine readble error output
machineOutput := machineoutput.Error{
TypeMeta: metav1.TypeMeta{
Kind: machineoutput.Kind,
APIVersion: machineoutput.APIVersion,
},
Message: err.Error(),
}

// Output the error
machineoutput.OutputError(machineOutput)

} else {
log.Errorf(fmt.Sprintf("%s", strings.Title(context)), a...)
glog.V(4).Infof("Error:\n%v", err)
if context == "" {
log.Error(errors.Cause(err))
} else {
log.Errorf(fmt.Sprintf("%s", strings.Title(context)), a...)
}
}

// Always exit 1 anyways
os.Exit(1)

}
}

Expand Down
26 changes: 23 additions & 3 deletions pkg/project/project.go
Expand Up @@ -2,6 +2,7 @@ package project

import (
"github.com/openshift/odo/pkg/application"
"github.com/openshift/odo/pkg/machineoutput"
"github.com/pkg/errors"

"github.com/openshift/odo/pkg/log"
Expand Down Expand Up @@ -82,26 +83,45 @@ func Exists(client *occlient.Client, projectName string) (bool, error) {
return true, nil
}

// GetMachineReadableFormat gathers the readable format and output a Project struct
// for json to marshal
func GetMachineReadableFormat(projectName string, isActive bool, apps []string) Project {

return Project{
TypeMeta: metav1.TypeMeta{
Kind: "Project",
APIVersion: "odo.openshift.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: projectName,
Name: projectName,
Namespace: projectName,
},
Spec: ProjectSpec{
Applications: apps,
},
Status: ProjectStatus{

Active: isActive,
},
}
}

// MachineReadableSuccessOutput outputs a success output that includes
// project information and namespace
func MachineReadableSuccessOutput(projectName string, message string) {
machineOutput := machineoutput.Success{
TypeMeta: metav1.TypeMeta{
Kind: "Project",
APIVersion: "odo.openshift.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: projectName,
Namespace: projectName,
},
Message: message,
}

machineoutput.OutputSuccess(machineOutput)
}

// getMachineReadableFormatForList returns application list in machine readable format
func getMachineReadableFormatForList(projects []Project) ProjectList {
return ProjectList{
Expand Down
30 changes: 30 additions & 0 deletions tests/integration/json_test.go
Expand Up @@ -34,6 +34,36 @@ var _ = Describe("odojsonoutput", func() {
os.Unsetenv("GLOBALODOCONFIG")
})

// Test machine readable output
Context("Pass on creation: odo project create $PROJECT -o json", func() {
var projectName string
JustBeforeEach(func() {
projectName = helper.RandString(6)
})
JustAfterEach(func() {
helper.DeleteProject(projectName)
})

// odo project create foobar -o json
It("should be able to create project and show output in json format", func() {
actual := helper.CmdShouldPass("odo", "project", "create", projectName, "-o", "json")
desired := fmt.Sprintf(`{"kind":"Project","apiVersion":"odo.openshift.io/v1alpha1","metadata":{"name":"%s","namespace":"%s","creationTimestamp":null},"message":"Project '%s' is ready for use"}`, projectName, projectName, projectName)
Expect(desired).Should(MatchJSON(actual))
})

})

Context("Fail on double creation: odo project create $PROJECT -o json", func() {

// odo project create foobar -o json (x2)
It("should fail saying that there is already an existing project", func() {
actual := helper.CmdShouldFail("odo", "project", "create", project, "-o", "json")
desired := fmt.Sprintf(`{"kind":"Error","apiVersion":"odo.openshift.io/v1alpha1","metadata":{"creationTimestamp":null},"message":"unable to create new project: unable to create new project %s: project.project.openshift.io \"%s\" already exists"}`, project, project)
Expect(desired).Should(MatchJSON(actual))
})

})

Context("odo machine readable output on empty project", func() {
//https://github.com/openshift/odo/issues/1708
//odo project list -o json
Expand Down

0 comments on commit 0d4696e

Please sign in to comment.