Lots of stuff, too early for meaningful commit messages

UI, command dispatch
This commit is contained in:
Mitchell Hashimoto 2013-03-24 14:03:53 -07:00
parent 817822abab
commit db1c11fff5
8 changed files with 165 additions and 36 deletions

1
.gitignore vendored
View File

@ -1,2 +1 @@
/bin
/packer

View File

@ -1,6 +1,6 @@
all:
@mkdir -p bin/
go get -a
go get -d
go build -a -o bin/packer
.PHONY: all

View File

@ -11,11 +11,12 @@ type Builder struct {
config config
}
func (b *Builder) ConfigInterface() interface{} {
return &b.config
}
func (*Builder) Prepare() {
}
func (*Builder) Build() {
}
func (*Builder) Destroy() {
func (b *Builder) Build() {
}

24
example.json Normal file
View File

@ -0,0 +1,24 @@
{
"name": "my-custom-image",
"builders": [
{
"type": "amazon-ebs",
"region": "us-east-1",
"source": "ami-de0d9eb7"
}
],
"provisioners": [
{
"type": "shell",
"path": "script.sh"
}
],
"outputs": [
{
"type": "vagrant"
}
]
}

View File

@ -1,44 +1,40 @@
// This is the main package for the `packer` application.
package main
import "github.com/mitchellh/packer/builder/amazon"
import "github.com/mitchellh/packer/packer"
import "os"
// A command is a runnable sub-command of the `packer` application.
// When `packer` is called with the proper subcommand, this will be
// called.
//
// The mapping of command names to command interfaces is in the
// Environment struct.
type Command interface {
Run(args []string)
}
// The environment struct contains all the state necessary for a single
// instance of Packer.
//
// It is *not* a singleton, but generally a single environment is created
// when Packer starts running to represent that Packer run. Technically,
// if you're building a custom Packer binary, you could instantiate multiple
// environments and run them in parallel.
type Environment struct {
commands map[string]Command
}
type Template struct {
type RawTemplate struct {
Name string
Builders map[string]interface{} `toml:"builder"`
Provisioners map[string]interface{} `toml:"provision"`
Outputs map[string]interface{} `toml:"output"`
Builders []map[string]interface{}
Provisioners []map[string]interface{}
Outputs []map[string]interface{}
}
type Builder interface {
ConfigInterface() interface{}
Prepare()
Build()
Destroy()
}
type Build interface {
Hook(name string)
}
func main() {
var builder Builder
builder = &amazon.Builder{}
builder.Build()
env := packer.NewEnvironment()
os.Exit(env.Cli(os.Args[1:]))
/*
file, _ := ioutil.ReadFile("example.json")
var tpl RawTemplate
json.Unmarshal(file, &tpl)
fmt.Printf("%#v\n", tpl)
builderType, ok := tpl.Builders[0]["type"].(Build)
if !ok {
panic("OH NOES")
}
fmt.Printf("TYPE: %v\n", builderType)
*/
}

75
packer/environment.go Normal file
View File

@ -0,0 +1,75 @@
// The packer package contains the core components of Packer.
package packer
import "os"
// A command is a runnable sub-command of the `packer` application.
// When `packer` is called with the proper subcommand, this will be
// called.
//
// The mapping of command names to command interfaces is in the
// Environment struct.
type Command interface {
Run(env *Environment, args []string) int
}
// The environment struct contains all the state necessary for a single
// instance of Packer.
//
// It is *not* a singleton, but generally a single environment is created
// when Packer starts running to represent that Packer run. Technically,
// if you're building a custom Packer binary, you could instantiate multiple
// environments and run them in parallel.
type Environment struct {
command map[string]Command
ui Ui
}
// This creates a new environment
func NewEnvironment() *Environment {
env := &Environment{}
env.command = make(map[string]Command)
env.command["version"] = new(versionCommand)
env.ui = &ReaderWriterUi{ os.Stdin, os.Stdout }
return env
}
// Executes a command as if it was typed on the command-line interface.
// The return value is the exit code of the command.
func (e *Environment) Cli(args []string) int {
if len(args) == 0 {
e.PrintHelp()
return 1
}
command, ok := e.command[args[0]]
if !ok {
// The command was not found. In this case, let's go through
// the arguments and see if the user is requesting the version.
for _, arg := range args {
if arg == "--version" || arg == "-v" {
command = e.command["version"]
break
}
}
// If we still don't have a command, show the help.
if command == nil {
e.PrintHelp()
return 1
}
}
return command.Run(e, args)
}
// Returns the UI for the environment. The UI is the interface that should
// be used for all communication with the outside world.
func (e *Environment) Ui() Ui {
return e.ui
}
// Prints the CLI help to the UI.
func (e *Environment) PrintHelp() {
e.ui.Say("Bad.\n")
}

22
packer/ui.go Normal file
View File

@ -0,0 +1,22 @@
package packer
import "fmt"
import "io"
// The Ui interface handles all communication for Packer with the outside
// world. This sort of control allows us to strictly control how output
// is formatted and various levels of output.
type Ui interface {
Say(format string, a ...interface{})
}
// The ReaderWriterUi is a UI that writes and reads from standard Go
// io.Reader and io.Writer.
type ReaderWriterUi struct {
Reader io.Reader
Writer io.Writer
}
func (rw *ReaderWriterUi) Say(format string, a ...interface{}) {
fmt.Fprintf(rw.Writer, format, a...)
}

12
packer/version.go Normal file
View File

@ -0,0 +1,12 @@
package packer
// The version of packer.
const Version = "0.1.0.dev"
type versionCommand byte
// Implement the Command interface by simply showing the version
func (versionCommand) Run(env *Environment, args[] string) int {
env.Ui().Say("Packer v%v\n", Version)
return 0
}