Skip to content

Commit

Permalink
Add transparent init (#5186)
Browse files Browse the repository at this point in the history
* plumb io.Writer down into runContext()

* implement transparent init

* handle user not continuing and apply defaults to generated config

* add flags to hide functionality and skip confirmation

* update flag names, add test for helper function
  • Loading branch information
MarlonGamez committed Jan 20, 2021
1 parent e6b27a5 commit 9eab6c3
Show file tree
Hide file tree
Showing 21 changed files with 161 additions and 55 deletions.
2 changes: 1 addition & 1 deletion cmd/skaffold/app/cmd/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func doBuild(ctx context.Context, out io.Writer) error {
buildOut = ioutil.Discard
}

return withRunner(ctx, func(r runner.Runner, configs []*latest.SkaffoldConfig) error {
return withRunner(ctx, out, func(r runner.Runner, configs []*latest.SkaffoldConfig) error {
bRes, err := r.Build(ctx, buildOut, targetArtifacts(opts, configs))

if quietFlag || buildOutputFlag != "" {
Expand Down
12 changes: 6 additions & 6 deletions cmd/skaffold/app/cmd/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func (r *mockRunner) Stop() error {
}

func TestTagFlag(t *testing.T) {
mockCreateRunner := func(config.SkaffoldOptions) (runner.Runner, []*latest.SkaffoldConfig, error) {
mockCreateRunner := func(io.Writer, config.SkaffoldOptions) (runner.Runner, []*latest.SkaffoldConfig, error) {
return &mockRunner{}, []*latest.SkaffoldConfig{{}}, nil
}

Expand All @@ -68,7 +68,7 @@ func TestTagFlag(t *testing.T) {
}

func TestQuietFlag(t *testing.T) {
mockCreateRunner := func(config.SkaffoldOptions) (runner.Runner, []*latest.SkaffoldConfig, error) {
mockCreateRunner := func(io.Writer, config.SkaffoldOptions) (runner.Runner, []*latest.SkaffoldConfig, error) {
return &mockRunner{}, []*latest.SkaffoldConfig{{}}, nil
}

Expand Down Expand Up @@ -114,7 +114,7 @@ func TestQuietFlag(t *testing.T) {
}

func TestFileOutputFlag(t *testing.T) {
mockCreateRunner := func(config.SkaffoldOptions) (runner.Runner, []*latest.SkaffoldConfig, error) {
mockCreateRunner := func(io.Writer, config.SkaffoldOptions) (runner.Runner, []*latest.SkaffoldConfig, error) {
return &mockRunner{}, []*latest.SkaffoldConfig{{}}, nil
}

Expand Down Expand Up @@ -177,16 +177,16 @@ func TestFileOutputFlag(t *testing.T) {
}

func TestRunBuild(t *testing.T) {
errRunner := func(config.SkaffoldOptions) (runner.Runner, []*latest.SkaffoldConfig, error) {
errRunner := func(io.Writer, config.SkaffoldOptions) (runner.Runner, []*latest.SkaffoldConfig, error) {
return nil, nil, errors.New("some error")
}
mockCreateRunner := func(config.SkaffoldOptions) (runner.Runner, []*latest.SkaffoldConfig, error) {
mockCreateRunner := func(io.Writer, config.SkaffoldOptions) (runner.Runner, []*latest.SkaffoldConfig, error) {
return &mockRunner{}, []*latest.SkaffoldConfig{{}}, nil
}

tests := []struct {
description string
mock func(config.SkaffoldOptions) (runner.Runner, []*latest.SkaffoldConfig, error)
mock func(io.Writer, config.SkaffoldOptions) (runner.Runner, []*latest.SkaffoldConfig, error)
shouldErr bool
}{
{
Expand Down
2 changes: 1 addition & 1 deletion cmd/skaffold/app/cmd/debug_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func TestNewCmdDebug(t *testing.T) {
func TestDebugIndependentFromDev(t *testing.T) {
mockRunner := &mockDevRunner{}
testutil.Run(t, "DevDebug", func(t *testutil.T) {
t.Override(&createRunner, func(config.SkaffoldOptions) (runner.Runner, []*latest.SkaffoldConfig, error) {
t.Override(&createRunner, func(io.Writer, config.SkaffoldOptions) (runner.Runner, []*latest.SkaffoldConfig, error) {
return mockRunner, []*latest.SkaffoldConfig{{}}, nil
})
t.Override(&opts, config.SkaffoldOptions{})
Expand Down
2 changes: 1 addition & 1 deletion cmd/skaffold/app/cmd/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func NewCmdDelete() *cobra.Command {
}

func doDelete(ctx context.Context, out io.Writer) error {
return withRunner(ctx, func(r runner.Runner, _ []*latest.SkaffoldConfig) error {
return withRunner(ctx, out, func(r runner.Runner, _ []*latest.SkaffoldConfig) error {
return r.Cleanup(ctx, out)
})
}
2 changes: 1 addition & 1 deletion cmd/skaffold/app/cmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func NewCmdDeploy() *cobra.Command {
}

func doDeploy(ctx context.Context, out io.Writer) error {
return withRunner(ctx, func(r runner.Runner, configs []*latest.SkaffoldConfig) error {
return withRunner(ctx, out, func(r runner.Runner, configs []*latest.SkaffoldConfig) error {
if opts.SkipRender {
return r.DeployAndLog(ctx, out, []build.Artifact{})
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/skaffold/app/cmd/dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func runDev(ctx context.Context, out io.Writer) error {
case <-ctx.Done():
return nil
default:
err := withRunner(ctx, func(r runner.Runner, configs []*latest.SkaffoldConfig) error {
err := withRunner(ctx, out, func(r runner.Runner, configs []*latest.SkaffoldConfig) error {
var artifacts []*latest.Artifact
for _, cfg := range configs {
artifacts = append(artifacts, cfg.Build.Artifacts...)
Expand Down
4 changes: 2 additions & 2 deletions cmd/skaffold/app/cmd/dev_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func TestDoDev(t *testing.T) {
hasDeployed: test.hasDeployed,
errDev: context.Canceled,
}
t.Override(&createRunner, func(config.SkaffoldOptions) (runner.Runner, []*latest.SkaffoldConfig, error) {
t.Override(&createRunner, func(io.Writer, config.SkaffoldOptions) (runner.Runner, []*latest.SkaffoldConfig, error) {
return mockRunner, []*latest.SkaffoldConfig{{}}, nil
})
t.Override(&opts, config.SkaffoldOptions{
Expand Down Expand Up @@ -145,7 +145,7 @@ func TestDevConfigChange(t *testing.T) {
testutil.Run(t, "test config change", func(t *testutil.T) {
mockRunner := &mockConfigChangeRunner{}

t.Override(&createRunner, func(config.SkaffoldOptions) (runner.Runner, []*latest.SkaffoldConfig, error) {
t.Override(&createRunner, func(io.Writer, config.SkaffoldOptions) (runner.Runner, []*latest.SkaffoldConfig, error) {
return mockRunner, []*latest.SkaffoldConfig{{}}, nil
})
t.Override(&opts, config.SkaffoldOptions{
Expand Down
2 changes: 1 addition & 1 deletion cmd/skaffold/app/cmd/diagnose.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func NewCmdDiagnose() *cobra.Command {
}

func doDiagnose(ctx context.Context, out io.Writer) error {
runCtx, configs, err := runContext(opts)
runCtx, configs, err := runContext(out, opts)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/skaffold/app/cmd/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func NewCmdFilter() *cobra.Command {
// runFilter loads the Kubernetes manifests from stdin and applies the debug transformations.
// Unlike `skaffold debug`, this filtering affects all images and not just the built artifacts.
func runFilter(ctx context.Context, out io.Writer, debuggingFilters bool, buildArtifacts []build.Artifact) error {
return withRunner(ctx, func(r runner.Runner, configs []*latest.SkaffoldConfig) error {
return withRunner(ctx, out, func(r runner.Runner, configs []*latest.SkaffoldConfig) error {
manifestList, err := manifest.Load(os.Stdin)
if err != nil {
return fmt.Errorf("loading manifests: %w", err)
Expand Down
18 changes: 18 additions & 0 deletions cmd/skaffold/app/cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,24 @@ var flagRegistry = []Flag{
FlagAddMethod: "Var",
DefinedOn: []string{"test", "deploy"},
},
{
Name: "auto-create-config",
Usage: "If true, skaffold will try to create a config for the user's run if it doesn't find one",
Value: &opts.AutoCreateConfig,
DefValue: true,
FlagAddMethod: "BoolVar",
DefinedOn: []string{"debug", "dev", "run"},
IsEnum: true,
},
{
Name: "assume-yes",
Usage: "If true, skaffold will skip yes/no confirmation from the user and default to yes",
Value: &opts.AssumeYes,
DefValue: false,
FlagAddMethod: "BoolVar",
DefinedOn: []string{"debug", "dev", "run"},
IsEnum: true,
},
}

func methodNameByType(v reflect.Value) string {
Expand Down
2 changes: 1 addition & 1 deletion cmd/skaffold/app/cmd/generate_pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func NewCmdGeneratePipeline() *cobra.Command {
}

func doGeneratePipeline(ctx context.Context, out io.Writer) error {
return withRunner(ctx, func(r runner.Runner, configs []*latest.SkaffoldConfig) error {
return withRunner(ctx, out, func(r runner.Runner, configs []*latest.SkaffoldConfig) error {
if err := r.GeneratePipeline(ctx, out, configs, configFiles, "pipeline.yaml"); err != nil {
return fmt.Errorf("generating : %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/skaffold/app/cmd/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func doRender(ctx context.Context, out io.Writer) error {
buildOut = out
}

return withRunner(ctx, func(r runner.Runner, configs []*latest.SkaffoldConfig) error {
return withRunner(ctx, out, func(r runner.Runner, configs []*latest.SkaffoldConfig) error {
var bRes []build.Artifact

if renderFromBuildOutputFile.String() != "" {
Expand Down
2 changes: 1 addition & 1 deletion cmd/skaffold/app/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func NewCmdRun() *cobra.Command {
}

func doRun(ctx context.Context, out io.Writer) error {
return withRunner(ctx, func(r runner.Runner, configs []*latest.SkaffoldConfig) error {
return withRunner(ctx, out, func(r runner.Runner, configs []*latest.SkaffoldConfig) error {
bRes, err := r.Build(ctx, out, targetArtifacts(opts, configs))
if err != nil {
return fmt.Errorf("failed to build: %w", err)
Expand Down
2 changes: 1 addition & 1 deletion cmd/skaffold/app/cmd/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func (r *mockRunRunner) DeployAndLog(context.Context, io.Writer, []build.Artifac
func TestBuildImageFlag(t *testing.T) {
testutil.Run(t, "", func(t *testutil.T) {
mockRunner := &mockRunRunner{}
t.Override(&createRunner, func(config.SkaffoldOptions) (runner.Runner, []*latest.SkaffoldConfig, error) {
t.Override(&createRunner, func(io.Writer, config.SkaffoldOptions) (runner.Runner, []*latest.SkaffoldConfig, error) {
return mockRunner, []*latest.SkaffoldConfig{{
Pipeline: latest.Pipeline{
Build: latest.BuildConfig{
Expand Down
93 changes: 61 additions & 32 deletions cmd/skaffold/app/cmd/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@ import (
"context"
"errors"
"fmt"
"io"
"os"

"github.com/sirupsen/logrus"

"github.com/GoogleContainerTools/skaffold/pkg/skaffold/color"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/errors"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/event"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/initializer"
initConfig "github.com/GoogleContainerTools/skaffold/pkg/skaffold/initializer/config"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/instrumentation"
kubectx "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/context"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner"
Expand All @@ -42,8 +46,8 @@ import (
// For tests
var createRunner = createNewRunner

func withRunner(ctx context.Context, action func(runner.Runner, []*latest.SkaffoldConfig) error) error {
runner, config, err := createRunner(opts)
func withRunner(ctx context.Context, out io.Writer, action func(runner.Runner, []*latest.SkaffoldConfig) error) error {
runner, config, err := createRunner(out, opts)
sErrors.SetSkaffoldOptions(opts)
if err != nil {
return err
Expand All @@ -55,8 +59,8 @@ func withRunner(ctx context.Context, action func(runner.Runner, []*latest.Skaffo
}

// createNewRunner creates a Runner and returns the SkaffoldConfig associated with it.
func createNewRunner(opts config.SkaffoldOptions) (runner.Runner, []*latest.SkaffoldConfig, error) {
runCtx, configs, err := runContext(opts)
func createNewRunner(out io.Writer, opts config.SkaffoldOptions) (runner.Runner, []*latest.SkaffoldConfig, error) {
runCtx, configs, err := runContext(out, opts)
if err != nil {
return nil, nil, err
}
Expand All @@ -71,10 +75,61 @@ func createNewRunner(opts config.SkaffoldOptions) (runner.Runner, []*latest.Skaf
return runner, configs, nil
}

func runContext(opts config.SkaffoldOptions) (*runcontext.RunContext, []*latest.SkaffoldConfig, error) {
func runContext(out io.Writer, opts config.SkaffoldOptions) (*runcontext.RunContext, []*latest.SkaffoldConfig, error) {
configs, pipelines, err := skaffoldConfig(out, opts)
if err != nil {
return nil, nil, err
}

// TODO: Should support per-config kubecontext. Right now we constrain all configs to define the same kubecontext.
kubectx.ConfigureKubeConfig(opts.KubeConfig, opts.KubeContext, configs[0].Deploy.KubeContext)

if err := validation.Process(configs); err != nil {
return nil, nil, fmt.Errorf("invalid skaffold config: %w", err)
}

runCtx, err := runcontext.GetRunContext(opts, pipelines)
if err != nil {
return nil, nil, fmt.Errorf("getting run context: %w", err)
}

if err := validation.ProcessWithRunContext(runCtx); err != nil {
return nil, nil, fmt.Errorf("invalid skaffold config: %w", err)
}

return runCtx, configs, nil
}

func setDefaultDeployer(configs []util.VersionedConfig) bool {
// set the default deployer only if no deployer is explicitly specified in any config
for _, cfg := range configs {
if cfg.(*latest.SkaffoldConfig).Deploy.DeployType != (latest.DeployType{}) {
return false
}
}
return true
}

// skaffoldConfig will try to parse the given opts.ConfigurationFile. If not found, it will try to automatically generate a config for the user
func skaffoldConfig(out io.Writer, opts config.SkaffoldOptions) ([]*latest.SkaffoldConfig, []latest.Pipeline, error) {
parsed, err := schema.ParseConfigAndUpgrade(opts.ConfigurationFile, latest.Version)
if err != nil {
if os.IsNotExist(errors.Unwrap(err)) {
if opts.AutoCreateConfig && initializer.ValidCmd(opts) {
color.Default.Fprintf(out, "Skaffold config file %s not found - Trying to create one for you...\n", opts.ConfigurationFile)
config, err := initializer.Transparent(context.Background(), out, initConfig.Config{Opts: opts})
if err != nil {
return nil, nil, fmt.Errorf("unable to generate skaffold config file automatically - try running `skaffold init`: %w", err)
}
if config == nil {
return nil, nil, fmt.Errorf("unable to generate skaffold config file automatically - try running `skaffold init`: action cancelled by user")
}

defaults.Set(config, true)

return []*latest.SkaffoldConfig{config}, []latest.Pipeline{config.Pipeline}, nil
}

return nil, nil, fmt.Errorf("skaffold config file %s not found - check your current working directory, or try running `skaffold init`", opts.ConfigurationFile)
}

Expand Down Expand Up @@ -105,33 +160,7 @@ func runContext(opts config.SkaffoldOptions) (*runcontext.RunContext, []*latest.
configs = append(configs, config)
}

// TODO: Should support per-config kubecontext. Right now we constrain all configs to define the same kubecontext.
kubectx.ConfigureKubeConfig(opts.KubeConfig, opts.KubeContext, configs[0].Deploy.KubeContext)

if err := validation.Process(configs); err != nil {
return nil, nil, fmt.Errorf("invalid skaffold config: %w", err)
}

runCtx, err := runcontext.GetRunContext(opts, pipelines)
if err != nil {
return nil, nil, fmt.Errorf("getting run context: %w", err)
}

if err := validation.ProcessWithRunContext(runCtx); err != nil {
return nil, nil, fmt.Errorf("invalid skaffold config: %w", err)
}

return runCtx, configs, nil
}

func setDefaultDeployer(configs []util.VersionedConfig) bool {
// set the default deployer only if no deployer is explicitly specified in any config
for _, cfg := range configs {
if cfg.(*latest.SkaffoldConfig).Deploy.DeployType != (latest.DeployType{}) {
return false
}
}
return true
return configs, pipelines, nil
}

func warnIfUpdateIsAvailable() {
Expand Down
3 changes: 2 additions & 1 deletion cmd/skaffold/app/cmd/runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package cmd

import (
"fmt"
"io/ioutil"
"testing"

"github.com/blang/semver"
Expand Down Expand Up @@ -98,7 +99,7 @@ func TestCreateNewRunner(t *testing.T) {
Write("skaffold.yaml", fmt.Sprintf("apiVersion: %s\nkind: Config\n%s", latest.Version, test.config)).
Chdir()

_, _, err := createNewRunner(test.options)
_, _, err := createNewRunner(ioutil.Discard, test.options)

t.CheckError(test.shouldErr, err)
if test.expectedError != "" {
Expand Down
2 changes: 1 addition & 1 deletion cmd/skaffold/app/cmd/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func NewCmdTest() *cobra.Command {
}

func doTest(ctx context.Context, out io.Writer) error {
return withRunner(ctx, func(r runner.Runner, configs []*latest.SkaffoldConfig) error {
return withRunner(ctx, out, func(r runner.Runner, configs []*latest.SkaffoldConfig) error {
var artifacts []*latest.Artifact
for _, c := range configs {
artifacts = append(artifacts, c.Build.Artifacts...)
Expand Down

0 comments on commit 9eab6c3

Please sign in to comment.