-
Notifications
You must be signed in to change notification settings - Fork 286
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #982 from buildpacks/924-default-builder
Add pack config default-builder subcommand Signed-off-by: David Freilich <dfreilich@vmware.com>
- Loading branch information
Showing
11 changed files
with
389 additions
and
87 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package commands | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/pkg/errors" | ||
"github.com/spf13/cobra" | ||
|
||
"github.com/buildpacks/pack/internal/config" | ||
"github.com/buildpacks/pack/internal/style" | ||
"github.com/buildpacks/pack/logging" | ||
) | ||
|
||
var suggestedBuilderString = "For suggested builders, run `pack builder suggest`." | ||
|
||
func ConfigDefaultBuilder(logger logging.Logger, cfg config.Config, cfgPath string, client PackClient) *cobra.Command { | ||
var unset bool | ||
|
||
cmd := &cobra.Command{ | ||
Use: "default-builder", | ||
Args: cobra.MaximumNArgs(1), | ||
Short: "List, set and unset the default builder used by other commands", | ||
Long: "List, set, and unset the default builder used by other commands.\n\n" + | ||
"* To list your default builder, run `pack config default-builder`.\n" + | ||
"* To set your default builder, run `pack config default-builder <builder-name>`.\n" + | ||
"* To unset your default builder, run `pack config default-builder --unset`.\n\n" + | ||
suggestedBuilderString, | ||
Example: "pack config default-builder cnbs/sample-builder:bionic", | ||
RunE: logError(logger, func(cmd *cobra.Command, args []string) error { | ||
switch { | ||
case unset: | ||
if cfg.DefaultBuilder == "" { | ||
logger.Info("No default builder was set") | ||
} else { | ||
oldBuilder := cfg.DefaultBuilder | ||
cfg.DefaultBuilder = "" | ||
if err := config.Write(cfg, cfgPath); err != nil { | ||
return errors.Wrapf(err, "failed to write to config at %s", cfgPath) | ||
} | ||
logger.Infof("Successfully unset default builder %s", style.Symbol(oldBuilder)) | ||
} | ||
case len(args) == 0: | ||
if cfg.DefaultBuilder != "" { | ||
logger.Infof("The current default builder is %s", style.Symbol(cfg.DefaultBuilder)) | ||
} else { | ||
logger.Infof("No default builder is set. \n\n%s", suggestedBuilderString) | ||
} | ||
return nil | ||
default: | ||
imageName := args[0] | ||
if err := validateBuilderExists(logger, imageName, client); err != nil { | ||
return errors.Wrapf(err, "validating that builder %s exists", style.Symbol(imageName)) | ||
} | ||
|
||
cfg.DefaultBuilder = imageName | ||
if err := config.Write(cfg, cfgPath); err != nil { | ||
return errors.Wrapf(err, "failed to write to config at %s", cfgPath) | ||
} | ||
logger.Infof("Builder %s is now the default builder", style.Symbol(imageName)) | ||
} | ||
|
||
return nil | ||
}), | ||
} | ||
|
||
cmd.Flags().BoolVarP(&unset, "unset", "u", false, "Unset the current default builder") | ||
AddHelpFlag(cmd, "config default-builder") | ||
return cmd | ||
} | ||
|
||
func validateBuilderExists(logger logging.Logger, imageName string, client PackClient) error { | ||
logger.Debug("Verifying local image...") | ||
info, err := client.InspectBuilder(imageName, true) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if info == nil { | ||
logger.Debug("Verifying remote image...") | ||
info, err := client.InspectBuilder(imageName, false) | ||
if err != nil { | ||
return errors.Wrapf(err, "failed to inspect remote image %s", style.Symbol(imageName)) | ||
} | ||
|
||
if info == nil { | ||
return fmt.Errorf("builder %s not found", style.Symbol(imageName)) | ||
} | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
package commands_test | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/golang/mock/gomock" | ||
"github.com/heroku/color" | ||
"github.com/sclevine/spec" | ||
"github.com/sclevine/spec/report" | ||
"github.com/spf13/cobra" | ||
|
||
"github.com/buildpacks/pack" | ||
"github.com/buildpacks/pack/internal/commands" | ||
"github.com/buildpacks/pack/internal/commands/testmocks" | ||
"github.com/buildpacks/pack/internal/config" | ||
ilogging "github.com/buildpacks/pack/internal/logging" | ||
"github.com/buildpacks/pack/internal/style" | ||
"github.com/buildpacks/pack/logging" | ||
h "github.com/buildpacks/pack/testhelpers" | ||
) | ||
|
||
func TestConfigDefaultBuilder(t *testing.T) { | ||
color.Disable(true) | ||
defer color.Disable(false) | ||
spec.Run(t, "ConfigDefaultBuilderCommand", testConfigDefaultBuilder, spec.Random(), spec.Report(report.Terminal{})) | ||
} | ||
|
||
func testConfigDefaultBuilder(t *testing.T, when spec.G, it spec.S) { | ||
var ( | ||
cmd *cobra.Command | ||
logger logging.Logger | ||
outBuf bytes.Buffer | ||
mockController *gomock.Controller | ||
mockClient *testmocks.MockPackClient | ||
tempPackHome string | ||
configPath string | ||
) | ||
|
||
it.Before(func() { | ||
var err error | ||
|
||
mockController = gomock.NewController(t) | ||
mockClient = testmocks.NewMockPackClient(mockController) | ||
logger = ilogging.NewLogWithWriters(&outBuf, &outBuf) | ||
tempPackHome, err = ioutil.TempDir("", "pack-home") | ||
h.AssertNil(t, err) | ||
configPath = filepath.Join(tempPackHome, "config.toml") | ||
cmd = commands.ConfigDefaultBuilder(logger, config.Config{}, configPath, mockClient) | ||
}) | ||
|
||
it.After(func() { | ||
mockController.Finish() | ||
h.AssertNil(t, os.RemoveAll(tempPackHome)) | ||
}) | ||
|
||
when("#ConfigDefaultBuilder", func() { | ||
when("no args", func() { | ||
it("lists current default builder if one is set", func() { | ||
cmd = commands.ConfigDefaultBuilder(logger, config.Config{DefaultBuilder: "some/builder"}, configPath, mockClient) | ||
cmd.SetArgs([]string{}) | ||
h.AssertNil(t, cmd.Execute()) | ||
h.AssertContains(t, outBuf.String(), "some/builder") | ||
}) | ||
|
||
it("suggests setting a builder if none is set", func() { | ||
cmd.SetArgs([]string{}) | ||
h.AssertNil(t, cmd.Execute()) | ||
h.AssertContains(t, outBuf.String(), "No default builder is set.") | ||
h.AssertContains(t, outBuf.String(), "run `pack builder suggest`") | ||
}) | ||
}) | ||
|
||
when("unset", func() { | ||
it("unsets current default builder", func() { | ||
cfg := config.Config{DefaultBuilder: "some/builder"} | ||
h.AssertNil(t, config.Write(cfg, configPath)) | ||
cmd = commands.ConfigDefaultBuilder(logger, cfg, configPath, mockClient) | ||
cmd.SetArgs([]string{"--unset"}) | ||
err := cmd.Execute() | ||
h.AssertNil(t, err) | ||
cfg, err = config.Read(configPath) | ||
h.AssertNil(t, err) | ||
h.AssertEq(t, cfg.DefaultBuilder, "") | ||
h.AssertContains(t, outBuf.String(), fmt.Sprintf("Successfully unset default builder %s", style.Symbol("some/builder"))) | ||
}) | ||
|
||
it("clarifies if no builder was set", func() { | ||
cmd.SetArgs([]string{"--unset"}) | ||
h.AssertNil(t, cmd.Execute()) | ||
h.AssertContains(t, outBuf.String(), "No default builder was set") | ||
}) | ||
|
||
it("gives clear error if unable to write to config", func() { | ||
h.AssertNil(t, ioutil.WriteFile(configPath, []byte("some-data"), 0001)) | ||
cmd = commands.ConfigDefaultBuilder(logger, config.Config{DefaultBuilder: "some/builder"}, configPath, mockClient) | ||
cmd.SetArgs([]string{"--unset"}) | ||
err := cmd.Execute() | ||
h.AssertError(t, err, "failed to write to config at "+configPath) | ||
}) | ||
}) | ||
|
||
when("set", func() { | ||
when("valid builder is provider", func() { | ||
when("in local", func() { | ||
var imageName = "some/image" | ||
|
||
it("sets default builder", func() { | ||
mockClient.EXPECT().InspectBuilder(imageName, true).Return(&pack.BuilderInfo{ | ||
Stack: "test.stack.id", | ||
}, nil) | ||
|
||
cmd.SetArgs([]string{imageName}) | ||
h.AssertNil(t, cmd.Execute()) | ||
h.AssertContains(t, outBuf.String(), fmt.Sprintf("Builder '%s' is now the default builder", imageName)) | ||
|
||
cfg, err := config.Read(configPath) | ||
h.AssertNil(t, err) | ||
h.AssertEq(t, cfg.DefaultBuilder, "some/image") | ||
}) | ||
|
||
it("gives clear error if unable to write to config", func() { | ||
h.AssertNil(t, ioutil.WriteFile(configPath, []byte("some-data"), 0001)) | ||
mockClient.EXPECT().InspectBuilder(imageName, true).Return(&pack.BuilderInfo{ | ||
Stack: "test.stack.id", | ||
}, nil) | ||
cmd = commands.ConfigDefaultBuilder(logger, config.Config{}, configPath, mockClient) | ||
cmd.SetArgs([]string{imageName}) | ||
err := cmd.Execute() | ||
h.AssertError(t, err, "failed to write to config at "+configPath) | ||
}) | ||
}) | ||
|
||
when("in remote", func() { | ||
it("sets default builder", func() { | ||
imageName := "some/image" | ||
|
||
localCall := mockClient.EXPECT().InspectBuilder(imageName, true).Return(nil, nil) | ||
|
||
mockClient.EXPECT().InspectBuilder(imageName, false).Return(&pack.BuilderInfo{ | ||
Stack: "test.stack.id", | ||
}, nil).After(localCall) | ||
|
||
cmd.SetArgs([]string{imageName}) | ||
h.AssertNil(t, cmd.Execute()) | ||
h.AssertContains(t, outBuf.String(), fmt.Sprintf("Builder '%s' is now the default builder", imageName)) | ||
}) | ||
|
||
it("gives clear error if unable to inspect remote image", func() { | ||
imageName := "some/image" | ||
|
||
localCall := mockClient.EXPECT().InspectBuilder(imageName, true).Return(nil, nil) | ||
|
||
mockClient.EXPECT().InspectBuilder(imageName, false).Return(&pack.BuilderInfo{ | ||
Stack: "test.stack.id", | ||
}, pack.SoftError{}).After(localCall) | ||
|
||
cmd.SetArgs([]string{imageName}) | ||
err := cmd.Execute() | ||
h.AssertError(t, err, fmt.Sprintf("failed to inspect remote image %s", style.Symbol(imageName))) | ||
}) | ||
}) | ||
}) | ||
|
||
when("invalid builder is provided", func() { | ||
it("error is presented", func() { | ||
imageName := "nonbuilder/image" | ||
|
||
mockClient.EXPECT().InspectBuilder(imageName, true).Return( | ||
nil, | ||
fmt.Errorf("failed to inspect image %s", imageName)) | ||
|
||
cmd.SetArgs([]string{imageName}) | ||
|
||
h.AssertNotNil(t, cmd.Execute()) | ||
h.AssertContains(t, outBuf.String(), fmt.Sprintf("validating that builder %s exists", style.Symbol("nonbuilder/image"))) | ||
}) | ||
}) | ||
|
||
when("non-existent builder is provided", func() { | ||
it("error is present", func() { | ||
imageName := "nonexisting/image" | ||
|
||
localCall := mockClient.EXPECT().InspectBuilder(imageName, true).Return( | ||
nil, | ||
nil) | ||
|
||
mockClient.EXPECT().InspectBuilder(imageName, false).Return( | ||
nil, | ||
nil).After(localCall) | ||
|
||
cmd.SetArgs([]string{imageName}) | ||
|
||
h.AssertNotNil(t, cmd.Execute()) | ||
h.AssertContains(t, outBuf.String(), "builder 'nonexisting/image' not found") | ||
}) | ||
}) | ||
}) | ||
}) | ||
} |
Oops, something went wrong.