Skip to content

Commit

Permalink
Add "add-stack" command
Browse files Browse the repository at this point in the history
WHEN I run pack add-stack my.custom.stack --run-image my-org/run --build-image my-org/build
THEN a stack will be added to the config.toml file in the following format
AND return the following message to the user "<stack ID> successfully added."

[buildpacks#34]
  • Loading branch information
dgodd committed Oct 4, 2018
1 parent b1d0c29 commit 03ab0d3
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 8 deletions.
29 changes: 29 additions & 0 deletions acceptance/acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"testing"
"time"

"github.com/BurntSushi/toml"
"github.com/google/go-cmp/cmp"
"github.com/sclevine/spec"
"github.com/sclevine/spec/report"
Expand Down Expand Up @@ -246,6 +247,34 @@ func testPack(t *testing.T, when spec.G, it spec.S) {
}
})
}, spec.Parallel(), spec.Report(report.Terminal{}))

when("add-stack", func() {
it("adds a custom stack to ~/.pack/config.toml", func() {
cmd := exec.Command(pack, "add-stack", "my.custom.stack", "--run-image", "my-org/run", "--build-image", "my-org/build")
cmd.Env = append(os.Environ(), "HOME="+homeDir)
output, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("add-stack command failed: %s: %s", output, err)
}

assertEq(t, string(output), "my.custom.stack successfully added\n")

var config struct {
Stacks []struct {
ID string `toml:"id"`
BuildImages []string `toml:"build-images"`
RunImages []string `toml:"run-images"`
} `toml:"stacks"`
}
_, err = toml.DecodeFile(filepath.Join(homeDir, ".pack", "config.toml"), &config)
assertNil(t, err)

stack := config.Stacks[len(config.Stacks)-1]
assertEq(t, stack.ID, "my.custom.stack")
assertEq(t, stack.BuildImages, []string{"my-org/build"})
assertEq(t, stack.RunImages, []string{"my-org/run"})
})
}, spec.Parallel(), spec.Report(report.Terminal{}))
}

func run(t *testing.T, cmd *exec.Cmd) string {
Expand Down
32 changes: 32 additions & 0 deletions cmd/pack/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"fmt"
"log"
"os"
"path/filepath"
Expand All @@ -18,10 +19,12 @@ import (
func main() {
buildCmd := buildCommand()
createBuilderCmd := createBuilderCommand()
addStackCmd := addStackCommand()

rootCmd := &cobra.Command{Use: "pack"}
rootCmd.AddCommand(buildCmd)
rootCmd.AddCommand(createBuilderCmd)
rootCmd.AddCommand(addStackCmd)
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
Expand Down Expand Up @@ -90,3 +93,32 @@ func createBuilderCommand() *cobra.Command {
createBuilderCommand.Flags().StringVarP(&flags.StackID, "stack", "s", "", "stack ID")
return createBuilderCommand
}

func addStackCommand() *cobra.Command {
flags := struct {
BuildImage string
RunImage string
}{}
addStackCommand := &cobra.Command{
Use: "add-stack <stack-name> --run-image=<name> --build-image=<name>",
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := config.New(filepath.Join(os.Getenv("HOME"), ".pack"))
if err != nil {
return err
}
if err := cfg.Add(config.Stack{
ID: args[0],
BuildImages: []string{flags.BuildImage},
RunImages: []string{flags.RunImage},
}); err != nil {
return err
}
fmt.Printf("%s successfully added\n", args[0])
return nil
},
}
addStackCommand.Flags().StringVar(&flags.BuildImage, "build-image", "", "build image to be used for bulder images built with the stack")
addStackCommand.Flags().StringVar(&flags.RunImage, "run-image", "", "run image to be used for runnable images built with the stack")
return addStackCommand
}
30 changes: 22 additions & 8 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
type Config struct {
Stacks []Stack `toml:"stacks"`
DefaultStackID string `toml:"default-stack-id"`
configPath string
}

type Stack struct {
Expand All @@ -37,20 +38,25 @@ func New(path string) (*Config, error) {
RunImages: []string{"packs/run"},
})

if err := os.MkdirAll(filepath.Dir(configPath), 0777); err != nil {
config.configPath = configPath
if err := config.save(); err != nil {
return nil, err
}
w, err := os.OpenFile(configPath, os.O_CREATE|os.O_RDWR, 0644)

return config, nil
}

func (c *Config) save() error {
if err := os.MkdirAll(filepath.Dir(c.configPath), 0777); err != nil {
return err
}
w, err := os.OpenFile(c.configPath, os.O_CREATE|os.O_RDWR, 0644)
if err != nil {
return nil, err
return err
}
defer w.Close()

if err := toml.NewEncoder(w).Encode(config); err != nil {
return nil, err
}

return config, nil
return toml.NewEncoder(w).Encode(c)
}

func previousConfig(path string) (*Config, error) {
Expand Down Expand Up @@ -84,6 +90,14 @@ func (c *Config) Get(stackID string) (*Stack, error) {
return nil, fmt.Errorf(`Missing stack: stack with id "%s" not found in pack config.toml`, stackID)
}

func (c *Config) Add(stack Stack) error {
if _, err := c.Get(stack.ID); err == nil {
return fmt.Errorf(`stack "%s" already exists`, stack.ID)
}
c.Stacks = append(c.Stacks, stack)
return c.save()
}

func ImageByRegistry(registry string, images []string) (string, error) {
if len(images) == 0 {
return "", errors.New("empty images")
Expand Down
65 changes: 65 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,71 @@ default-stack-id = "my.stack"
})
})

when("Config#Add", func() {
var subject *config.Config
it.Before(func() {
assertNil(t, ioutil.WriteFile(filepath.Join(tmpDir, "config.toml"), []byte(`
default-stack-id = "my.stack"
[[stacks]]
id = "stack-1"
[[stacks]]
id = "my.stack"
[[stacks]]
id = "stack-3"
`), 0666))
var err error
subject, err = config.New(tmpDir)
assertNil(t, err)
})

when("stack to be added is new", func() {
it("adds the stack and writes to file", func() {
err := subject.Add(config.Stack{
ID: "new-stack",
BuildImages: []string{"neworg/build"},
RunImages: []string{"neworg/run"},
})
assertNil(t, err)

stack, err := subject.Get("new-stack")
assertNil(t, err)
assertEq(t, stack.ID, "new-stack")
assertEq(t, stack.BuildImages, []string{"neworg/build"})
assertEq(t, stack.RunImages, []string{"neworg/run"})

b, err := ioutil.ReadFile(filepath.Join(tmpDir, "config.toml"))
assertNil(t, err)
assertContains(t, string(b), "new-stack")
assertContains(t, string(b), "neworg/build")
assertContains(t, string(b), "neworg/run")
})
})

when("stack to be added is already in file", func() {
it("errors and leaves file unchanged", func() {
stat, err := os.Stat(filepath.Join(tmpDir, "config.toml"))
assertNil(t, err)
origSize := stat.Size()

err = subject.Add(config.Stack{
ID: "my.stack",
BuildImages: []string{"neworg/build"},
RunImages: []string{"neworg/run"},
})
assertNotNil(t, err)
assertEq(t, err.Error(), `stack "my.stack" already exists`)

stack, err := subject.Get("my.stack")
assertNil(t, err)
assertEq(t, stack.BuildImages, []string(nil))

stat, err = os.Stat(filepath.Join(tmpDir, "config.toml"))
assertNil(t, err)
assertEq(t, stat.Size(), origSize)
})
})
})

when("ImageByRegistry", func() {
var images []string
it.Before(func() {
Expand Down

0 comments on commit 03ab0d3

Please sign in to comment.