Skip to content

Commit

Permalink
Add suggestions in oc status
Browse files Browse the repository at this point in the history
  • Loading branch information
0xmichalis committed Dec 10, 2015
1 parent 8f61adf commit 73597be
Show file tree
Hide file tree
Showing 13 changed files with 179 additions and 89 deletions.
2 changes: 2 additions & 0 deletions contrib/completions/bash/oc
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,8 @@ _oc_status()

flags+=("--output=")
two_word_flags+=("-o")
flags+=("--verbose")
flags+=("-v")
flags+=("--alsologtostderr")
flags+=("--api-version=")
flags+=("--boot-id-file=")
Expand Down
2 changes: 2 additions & 0 deletions contrib/completions/bash/openshift
Original file line number Diff line number Diff line change
Expand Up @@ -5336,6 +5336,8 @@ _openshift_cli_status()

flags+=("--output=")
two_word_flags+=("-o")
flags+=("--verbose")
flags+=("-v")
flags+=("--alsologtostderr")
flags+=("--api-version=")
flags+=("--boot-id-file=")
Expand Down
8 changes: 7 additions & 1 deletion docs/generated/oc_by_example_content.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -959,8 +959,14 @@ Show an overview of the current project
[options="nowrap"]
----
# Show an overview of the current project
# See an overview of the current project.
$ oc status
# Export the overview of the current project in an svg file.
$ oc status -o dot | dot -T svg -o project.svg
# See an overview of the current project including details for any identified issues.
$ oc status -v
----
====

Expand Down
12 changes: 12 additions & 0 deletions pkg/api/graph/interfaces.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package graph

import (
"fmt"

"github.com/gonum/graph"
)

Expand All @@ -15,8 +17,12 @@ type Marker struct {
Severity Severity
// Key is a short string to identify this message
Key string

// Message is a human-readable string that describes what is interesting
Message string
// Suggestion is a human-readable string that holds advice for resolving this
// marker.
Suggestion Suggestion
}

// Severity indicates how important this problem is.
Expand Down Expand Up @@ -97,3 +103,9 @@ func (m ByKey) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
func (m ByKey) Less(i, j int) bool {
return m[i].Key < m[j].Key
}

type Suggestion string

func (s Suggestion) String() string {
return fmt.Sprintf("try: %s", string(s))
}
14 changes: 7 additions & 7 deletions pkg/build/graph/analysis/bc.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import (
)

const (
MissingRequiredRegistryWarning = "MissingRequiredRegistry"
MissingImageStreamWarning = "MissingImageStream"
CyclicBuildConfigWarning = "CyclicBuildConfig"
MissingRequiredRegistryErr = "MissingRequiredRegistry"
MissingImageStreamErr = "MissingImageStream"
CyclicBuildConfigWarning = "CyclicBuildConfig"
)

// FindUnpushableBuildConfigs checks all build configs that will output to an IST backed by an ImageStream and checks to make sure their builds can push.
Expand All @@ -35,8 +35,8 @@ bc:
Node: bcNode,
RelatedNodes: []graph.Node{istNode},

Severity: osgraph.WarningSeverity,
Key: MissingImageStreamWarning,
Severity: osgraph.ErrorSeverity,
Key: MissingImageStreamErr,
Message: fmt.Sprintf("%s is pushing to %s that is using %s, but that image stream does not exist.",
bcNode.(*buildgraph.BuildConfigNode).ResourceString(), istNode.(*imagegraph.ImageStreamTagNode).ResourceString(), imageStreamNode.ResourceString()),
})
Expand All @@ -49,8 +49,8 @@ bc:
Node: bcNode,
RelatedNodes: []graph.Node{istNode},

Severity: osgraph.WarningSeverity,
Key: MissingRequiredRegistryWarning,
Severity: osgraph.ErrorSeverity,
Key: MissingRequiredRegistryErr,
Message: fmt.Sprintf("%s is pushing to %s that is using %s, but the administrator has not configured the integrated Docker registry. (oadm registry)",
bcNode.(*buildgraph.BuildConfigNode).ResourceString(), istNode.(*imagegraph.ImageStreamTagNode).ResourceString(), imageStreamNode.ResourceString()),
})
Expand Down
4 changes: 2 additions & 2 deletions pkg/build/graph/analysis/bc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func TestUnpushableBuild(t *testing.T) {
t.Fatalf("expected %v, got %v", e, a)
}

if got, expected := markers[0].Key, MissingRequiredRegistryWarning; got != expected {
if got, expected := markers[0].Key, MissingRequiredRegistryErr; got != expected {
t.Fatalf("expected marker key %q, got %q", expected, got)
}

Expand Down Expand Up @@ -53,7 +53,7 @@ func TestUnpushableBuild(t *testing.T) {
t.Fatalf("expected %v, got %v", e, a)
}

if got, expected := markers[0].Key, MissingImageStreamWarning; got != expected {
if got, expected := markers[0].Key, MissingImageStreamErr; got != expected {
t.Fatalf("expected marker key %q, got %q", expected, got)
}
}
Expand Down
122 changes: 77 additions & 45 deletions pkg/cmd/cli/cmd/status.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package cmd

import (
"errors"
"fmt"
"io"
"strings"

"github.com/gonum/graph/encoding/dot"
"github.com/spf13/cobra"
Expand All @@ -14,9 +14,10 @@ import (
"github.com/openshift/origin/pkg/cmd/util/clientcmd"
)

const (
StatusRecommendedName = "status"
// StatusRecommendedName is the recommended command name.
const StatusRecommendedName = "status"

const (
statusLong = `
Show a high level overview of the current project
Expand All @@ -28,36 +29,59 @@ oc describe deploymentConfig, oc describe service).
You can specify an output format of "-o dot" to have this command output the generated status
graph in DOT format that is suitable for use by the "dot" command.`

statusExample = ` # Show an overview of the current project
$ %[1]s`
statusExample = ` # See an overview of the current project.
$ %[1]s
# Export the overview of the current project in an svg file.
$ %[1]s -o dot | dot -T svg -o project.svg
# See an overview of the current project including details for any identified issues.
$ %[1]s -v`
)

// NewCmdStatus implements the OpenShift cli status command
// StatusOptions contains all the necessary options for the Openshift cli status command.
type StatusOptions struct {
namespace string
outputFormat string
describer *describe.ProjectStatusDescriber
out io.Writer
verbose bool
}

// NewCmdStatus implements the OpenShift cli status command.
func NewCmdStatus(name, fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Command {
outputFormat := ""
opts := &StatusOptions{}

cmd := &cobra.Command{
Use: "status",
Use: fmt.Sprintf("%s [-o dot | -v ]", StatusRecommendedName),
Short: "Show an overview of the current project",
Long: statusLong,
Example: fmt.Sprintf(statusExample, fullName),
Run: func(cmd *cobra.Command, args []string) {
if strings.ToLower(outputFormat) == "dot" {
cmdutil.CheckErr(RunGraph(f, out))
return
err := opts.Complete(f, cmd, args, out)
cmdutil.CheckErr(err)

if err := opts.Validate(); err != nil {
cmdutil.CheckErr(cmdutil.UsageError(cmd, err.Error()))
}

cmdutil.CheckErr(RunStatus(f, out))
err = opts.RunStatus()
cmdutil.CheckErr(err)
},
}

cmd.Flags().StringVarP(&outputFormat, "output", "o", outputFormat, "Output format. One of: dot.")
cmd.Flags().StringVarP(&opts.outputFormat, "output", "o", opts.outputFormat, "Output format. One of: dot.")
cmd.Flags().BoolVarP(&opts.verbose, "verbose", "v", opts.verbose, "See details for resolving issues.")

return cmd
}

// RunStatus contains all the necessary functionality for the OpenShift cli status command
func RunStatus(f *clientcmd.Factory, out io.Writer) error {
// Complete completes the options for the Openshift cli status command.
func (o *StatusOptions) Complete(f *clientcmd.Factory, cmd *cobra.Command, args []string, out io.Writer) error {
if len(args) > 0 {
return cmdutil.UsageError(cmd, "no arguments should be provided")
}

client, kclient, err := f.Clients()
if err != nil {
return err
Expand All @@ -72,45 +96,53 @@ func RunStatus(f *clientcmd.Factory, out io.Writer) error {
if err != nil {
return err
}
o.namespace = namespace

describer := &describe.ProjectStatusDescriber{K: kclient, C: client, Server: config.Host}
s, err := describer.Describe(namespace, "")
if err != nil {
return err
}
o.describer = &describe.ProjectStatusDescriber{K: kclient, C: client, Server: config.Host, Suggest: o.verbose}

o.out = out

fmt.Fprintf(out, s)
return nil
}

// RunGraph contains all the necessary functionality for the OpenShift cli graph command
func RunGraph(f *clientcmd.Factory, out io.Writer) error {
client, kclient, err := f.Clients()
if err != nil {
return err
// Validate validates the options for the Openshift cli status command.
func (o StatusOptions) Validate() error {
if len(o.outputFormat) != 0 && o.outputFormat != "dot" {
return fmt.Errorf("invalid output format provided: %s", o.outputFormat)
}

config, err := f.OpenShiftClientConfig.ClientConfig()
if err != nil {
return err
}

namespace, _, err := f.DefaultNamespace()
if err != nil {
return err
}

describer := &describe.ProjectStatusDescriber{K: kclient, C: client, Server: config.Host}
g, _, err := describer.MakeGraph(namespace)
if err != nil {
return err
if len(o.outputFormat) > 0 && o.verbose {
return errors.New("cannot provide suggestions when output format is dot")
}
return nil
}

data, err := dot.Marshal(g, namespace, "", " ", false)
if err != nil {
return err
// RunStatus contains all the necessary functionality for the OpenShift cli status command.
func (o StatusOptions) RunStatus() error {
var (
s string
err error
)

switch o.outputFormat {
case "":
s, err = o.describer.Describe(o.namespace, "")
if err != nil {
return err
}
case "dot":
g, _, err := o.describer.MakeGraph(o.namespace)
if err != nil {
return err
}
data, err := dot.Marshal(g, o.namespace, "", " ", false)
if err != nil {
return err
}
s = string(data)
default:
return fmt.Errorf("invalid output format provided: %s", o.outputFormat)
}

fmt.Fprintf(out, "%s", string(data))
fmt.Fprintf(o.out, s)
return nil
}
65 changes: 50 additions & 15 deletions pkg/cmd/cli/describe/projectstatus.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,10 @@ const ForbiddenListWarning = "Forbidden"

// ProjectStatusDescriber generates extended information about a Project
type ProjectStatusDescriber struct {
K kclient.Interface
C client.Interface
Server string
K kclient.Interface
C client.Interface
Server string
Suggest bool
}

func (d *ProjectStatusDescriber) MakeGraph(namespace string) (osgraph.Graph, sets.String, error) {
Expand Down Expand Up @@ -210,32 +211,66 @@ func (d *ProjectStatusDescriber) Describe(namespace, name string) (string, error

sort.Stable(osgraph.ByKey(allMarkers))
sort.Stable(osgraph.ByNodeID(allMarkers))
if errorMarkers := allMarkers.BySeverity(osgraph.ErrorSeverity); len(errorMarkers) > 0 {

errorMarkers := allMarkers.BySeverity(osgraph.ErrorSeverity)
if len(errorMarkers) > 0 {
fmt.Fprintln(out, "Errors:")
for _, marker := range errorMarkers {
fmt.Fprintln(out, indent+"* "+marker.Message)
if len(marker.Suggestion) > 0 && d.Suggest {
fmt.Fprintln(out, indent+" "+marker.Suggestion.String())
}
}
}
if warningMarkers := allMarkers.BySeverity(osgraph.WarningSeverity); len(warningMarkers) > 0 {
fmt.Fprintln(out, "Warnings:")

warningMarkers := allMarkers.BySeverity(osgraph.WarningSeverity)
if len(warningMarkers) > 0 {
if d.Suggest {
fmt.Fprintln(out, "Warnings:")
}
for _, marker := range warningMarkers {
fmt.Fprintln(out, indent+"* "+marker.Message)
if d.Suggest {
fmt.Fprintln(out, indent+"* "+marker.Message)
if len(marker.Suggestion) > 0 {
fmt.Fprintln(out, indent+" "+marker.Suggestion.String())
}
}
}
}
if infoMarkers := allMarkers.BySeverity(osgraph.InfoSeverity); len(infoMarkers) > 0 {
fmt.Fprintln(out, "Info:")
for _, marker := range infoMarkers {
fmt.Fprintln(out, indent+"* "+marker.Message)
}

// We print errors by default and warnings if -v is used. If we get none,
// this would be an extra new line.
if len(errorMarkers) != 0 || (d.Suggest && len(warningMarkers) != 0) {
fmt.Fprintln(out)
}

fmt.Fprintln(out)
errors, warnings := "", ""
if len(errorMarkers) == 1 {
errors = "1 error"
} else if len(errorMarkers) > 1 {
errors = fmt.Sprintf("%d errors", len(errorMarkers))
}
if len(warningMarkers) == 1 {
warnings = "1 warning"
} else if len(warningMarkers) > 1 {
warnings = fmt.Sprintf("%d warnings", len(warningMarkers))
}

switch {
case !d.Suggest && len(errorMarkers) > 0 && len(warningMarkers) > 0:
fmt.Fprintf(out, "%s and %s identified, use 'oc status -v' to see details.\n", errors, warnings)

if (len(services) == 0) && (len(standaloneDCs) == 0) && (len(standaloneImages) == 0) {
case !d.Suggest && len(errorMarkers) > 0:
fmt.Fprintf(out, "%s identified, use 'oc status -v' to see details.\n", errors)

case !d.Suggest && len(warningMarkers) > 0:
fmt.Fprintf(out, "%s identified, use 'oc status -v' to see details.\n", warnings)

case (len(services) == 0) && (len(standaloneDCs) == 0) && (len(standaloneImages) == 0):
fmt.Fprintln(out, "You have no services, deployment configs, or build configs.")
fmt.Fprintln(out, "Run 'oc new-app' to create an application.")

} else {
default:
fmt.Fprintln(out, "To see more, use 'oc describe <resource>/<name>'.")
fmt.Fprintln(out, "You can use 'oc get all' to see a list of other objects.")
}
Expand Down
Loading

0 comments on commit 73597be

Please sign in to comment.