Skip to content

Commit

Permalink
Add skaffold inspect command (#5765)
Browse files Browse the repository at this point in the history
* Add `skaffold inspect` command

* add tests
  • Loading branch information
gsquared94 authored May 3, 2021
1 parent 91c4480 commit d14f34d
Show file tree
Hide file tree
Showing 12 changed files with 361 additions and 9 deletions.
1 change: 1 addition & 0 deletions cmd/skaffold/app/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ func NewSkaffoldCommand(out, err io.Writer) *cobra.Command {

rootCmd.AddCommand(NewCmdGeneratePipeline())
rootCmd.AddCommand(NewCmdSurvey())
rootCmd.AddCommand(NewCmdInspect())

templates.ActsAsRootCommand(rootCmd, nil, groups...)
rootCmd.PersistentFlags().StringVarP(&v, "verbosity", "v", constants.DefaultLogLevel.String(), "Log level (debug, info, warn, error, fatal, panic)")
Expand Down
13 changes: 13 additions & 0 deletions cmd/skaffold/app/cmd/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ type Builder interface {
Hidden() Builder
ExactArgs(argCount int, action func(context.Context, io.Writer, []string) error) *cobra.Command
NoArgs(action func(context.Context, io.Writer) error) *cobra.Command
WithCommands(cmds ...*cobra.Command) *cobra.Command
WithPersistentFlagAdder(adder func(*pflag.FlagSet)) Builder
}

type builder struct {
Expand Down Expand Up @@ -94,7 +96,11 @@ func (b *builder) WithFlags(flags []*Flag) Builder {
b.cmd.PreRun = func(cmd *cobra.Command, args []string) {
ResetFlagDefaults(cmd, flags)
}
return b
}

func (b *builder) WithPersistentFlagAdder(adder func(*pflag.FlagSet)) Builder {
adder(b.cmd.PersistentFlags())
return b
}

Expand Down Expand Up @@ -144,3 +150,10 @@ func (b *builder) WithArgs(f cobra.PositionalArgs, action func(context.Context,
}
return &b.cmd
}

func (b *builder) WithCommands(cmds ...*cobra.Command) *cobra.Command {
for _, c := range cmds {
b.cmd.AddCommand(c)
}
return &b.cmd
}
2 changes: 1 addition & 1 deletion cmd/skaffold/app/cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
func NewCmdConfig() *cobra.Command {
cmd := &cobra.Command{
Use: "config",
Short: "Interact with the Skaffold configuration",
Short: "Interact with the global skaffold config file (defaults to `$HOME/.skaffold/config`)",
}

cmd.AddCommand(NewCmdSet())
Expand Down
42 changes: 42 additions & 0 deletions cmd/skaffold/app/cmd/inspect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
Copyright 2021 The Skaffold Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmd

import (
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

var inspectFlags = struct {
fileName string
outFormat string
}{
fileName: "skaffold.yaml",
}

func NewCmdInspect() *cobra.Command {
return NewCmd("inspect").
WithDescription("Helper commands for Cloud Code IDEs to interact with and modify skaffold configuration files.").
WithPersistentFlagAdder(cmdInspectFlags).
Hidden().
WithCommands(cmdModules())
}

func cmdInspectFlags(f *pflag.FlagSet) {
f.StringVarP(&inspectFlags.fileName, "filename", "f", "skaffold.yaml", "Path to the local Skaffold config file. Defaults to `skaffold.yaml`")
f.StringVarP(&inspectFlags.outFormat, "format", "o", "json", "Output format. One of: json(default)")
}
43 changes: 43 additions & 0 deletions cmd/skaffold/app/cmd/inspect_modules.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
Copyright 2021 The Skaffold Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmd

import (
"context"
"io"

"github.com/spf13/cobra"

"github.com/GoogleContainerTools/skaffold/pkg/skaffold/inspect"
)

func cmdModules() *cobra.Command {
return NewCmd("modules").
WithDescription("Interact with configuration modules").
WithCommands(cmdModulesList())
}

func cmdModulesList() *cobra.Command {
return NewCmd("list").
WithExample("Get list of modules", "skaffold inspect modules list --format json").
WithDescription("Print the list of module names that can be invoked with the --module flag in other skaffold commands.").
NoArgs(listModules)
}

func listModules(ctx context.Context, out io.Writer) error {
return inspect.PrintModulesList(ctx, out, inspect.Options{Filename: inspectFlags.fileName, OutFormat: inspectFlags.outFormat})
}
4 changes: 2 additions & 2 deletions docs/content/en/docs/references/cli/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ Getting started with a new project:
Other Commands:
completion Output shell completion for the given shell (bash or zsh)
config Interact with the Skaffold configuration
config Interact with the global skaffold config file (defaults to `$HOME/.skaffold/config`)
credits Export third party notices to given path (./skaffold-credits by default)
diagnose Run a diagnostic on Skaffold
schema List and print json schemas used to validate skaffold.yaml configuration
Expand Down Expand Up @@ -268,7 +268,7 @@ Use "skaffold options" for a list of global command-line options (applies to all

### skaffold config

Interact with the Skaffold configuration
Interact with the global skaffold config file (defaults to `$HOME/.skaffold/config`)

```
Expand Down
54 changes: 54 additions & 0 deletions pkg/skaffold/inspect/modules.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
Copyright 2021 The Skaffold Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package inspect

import (
"context"
"io"

"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/parser"
)

var (
getConfigSetFunc = parser.GetConfigSet
)

type moduleList struct {
Modules []moduleEntry `json:"modules"`
}

type moduleEntry struct {
Name string `json:"name"`
Path string `json:"path"`
}

func PrintModulesList(ctx context.Context, out io.Writer, opts Options) error {
formatter := getOutputFormatter(out, opts.OutFormat)
cfgs, err := getConfigSetFunc(config.SkaffoldOptions{ConfigurationFile: opts.Filename})
if err != nil {
return formatter.WriteErr(err)
}

l := &moduleList{}
for _, c := range cfgs {
if c.Metadata.Name != "" {
l.Modules = append(l.Modules, moduleEntry{Name: c.Metadata.Name, Path: c.SourceFile})
}
}
return formatter.Write(l)
}
71 changes: 71 additions & 0 deletions pkg/skaffold/inspect/modules_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
Copyright 2021 The Skaffold Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package inspect

import (
"bytes"
"context"
"errors"
"fmt"
"testing"

"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/parser"
sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/errors"
v1 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v1"
"github.com/GoogleContainerTools/skaffold/testutil"
)

func TestPrintModulesList(t *testing.T) {
tests := []struct {
description string
configSet parser.SkaffoldConfigSet
err error
expected string
}{
{
description: "print modules",
configSet: parser.SkaffoldConfigSet{
&parser.SkaffoldConfigEntry{SkaffoldConfig: &v1.SkaffoldConfig{Metadata: v1.Metadata{Name: "cfg1"}}, SourceFile: "path/to/cfg1"},
&parser.SkaffoldConfigEntry{SkaffoldConfig: &v1.SkaffoldConfig{Metadata: v1.Metadata{Name: "cfg2"}}, SourceFile: "path/to/cfg2"},
},
expected: `{"modules":[{"name":"cfg1","path":"path/to/cfg1"},{"name":"cfg2","path":"path/to/cfg2"}]}` + "\n",
},
{
description: "actionable error",
err: sErrors.MainConfigFileNotFoundErr("path/to/skaffold.yaml", fmt.Errorf("failed to read file : %q", "skaffold.yaml")),
expected: `{"errorCode":"CONFIG_FILE_NOT_FOUND_ERR","errorMessage":"unable to find configuration file \"path/to/skaffold.yaml\": failed to read file : \"skaffold.yaml\". Check that the specified configuration file exists at \"path/to/skaffold.yaml\"."}` + "\n",
},
{
description: "generic error",
err: errors.New("some error occurred"),
expected: `{"errorCode":"UNKNOWN_ERROR","errorMessage":"some error occurred"}` + "\n",
},
}

for _, test := range tests {
testutil.Run(t, test.description, func(t *testutil.T) {
t.Override(&getConfigSetFunc, func(config.SkaffoldOptions) (parser.SkaffoldConfigSet, error) {
return test.configSet, test.err
})
var buf bytes.Buffer
err := PrintModulesList(context.Background(), &buf, Options{OutFormat: "json"})
t.CheckNoError(err)
t.CheckDeepEqual(test.expected, buf.String())
})
}
}
60 changes: 60 additions & 0 deletions pkg/skaffold/inspect/output.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
Copyright 2021 The Skaffold Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package inspect

import (
"encoding/json"
"errors"
"io"

sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/errors"
"github.com/GoogleContainerTools/skaffold/proto/v1"
)

type formatter interface {
Write(interface{}) error
WriteErr(error) error
}

func getOutputFormatter(out io.Writer, _ string) formatter {
// TODO: implement other output formatters. Currently only JSON is implemented
return jsonFormatter{out: out}
}

type jsonFormatter struct {
out io.Writer
}

func (j jsonFormatter) Write(data interface{}) error {
return json.NewEncoder(j.out).Encode(data)
}

type jsonErrorOutput struct {
ErrorCode string `json:"errorCode"`
ErrorMessage string `json:"errorMessage"`
}

func (j jsonFormatter) WriteErr(err error) error {
var sErr sErrors.Error
var jsonErr jsonErrorOutput
if errors.As(err, &sErr) {
jsonErr = jsonErrorOutput{ErrorCode: sErr.StatusCode().String(), ErrorMessage: sErr.Error()}
} else {
jsonErr = jsonErrorOutput{ErrorCode: proto.StatusCode_UNKNOWN_ERROR.String(), ErrorMessage: err.Error()}
}
return json.NewEncoder(j.out).Encode(jsonErr)
}
25 changes: 25 additions & 0 deletions pkg/skaffold/inspect/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
Copyright 2021 The Skaffold Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package inspect

// Options holds flag values for the various `skaffold inspect` commands
type Options struct {
// Filename is the `skaffold.yaml` file path
Filename string
// OutFormat is the output format. One of: json
OutFormat string
}
Loading

0 comments on commit d14f34d

Please sign in to comment.