diff --git a/command/build/command_test.go b/command/build/command_test.go index c2f1cfb03..433f0ef46 100644 --- a/command/build/command_test.go +++ b/command/build/command_test.go @@ -10,8 +10,8 @@ import ( func testEnvironment() packer.Environment { config := packer.DefaultEnvironmentConfig() config.Ui = &packer.ReaderWriterUi{ - new(bytes.Buffer), - new(bytes.Buffer), + Reader: new(bytes.Buffer), + Writer: new(bytes.Buffer), } env, err := packer.NewEnvironment(config) diff --git a/packer/environment.go b/packer/environment.go index c4586fd44..35a5e5ecc 100644 --- a/packer/environment.go +++ b/packer/environment.go @@ -68,7 +68,11 @@ type EnvironmentConfig struct { func DefaultEnvironmentConfig() *EnvironmentConfig { config := &EnvironmentConfig{} config.Commands = make([]string, 0) - config.Ui = &ReaderWriterUi{os.Stdin, os.Stdout} + config.Ui = &ReaderWriterUi{ + Reader: os.Stdin, + Writer: os.Stdout, + } + return config } diff --git a/packer/environment_test.go b/packer/environment_test.go index 16bf11854..be9e7d28f 100644 --- a/packer/environment_test.go +++ b/packer/environment_test.go @@ -13,8 +13,8 @@ import ( func testEnvironment() Environment { config := DefaultEnvironmentConfig() config.Ui = &ReaderWriterUi{ - new(bytes.Buffer), - new(bytes.Buffer), + Reader: new(bytes.Buffer), + Writer: new(bytes.Buffer), } env, err := NewEnvironment(config) @@ -292,7 +292,10 @@ func TestEnvironmentProvisioner_Error(t *testing.T) { func TestEnvironment_SettingUi(t *testing.T) { assert := asserts.NewTestingAsserts(t, true) - ui := &ReaderWriterUi{new(bytes.Buffer), new(bytes.Buffer)} + ui := &ReaderWriterUi{ + Reader: new(bytes.Buffer), + Writer: new(bytes.Buffer), + } config := &EnvironmentConfig{} config.Ui = ui diff --git a/packer/rpc/ui.go b/packer/rpc/ui.go index 02d79d7e0..5e7df0afe 100644 --- a/packer/rpc/ui.go +++ b/packer/rpc/ui.go @@ -17,6 +17,15 @@ type UiServer struct { ui packer.Ui } +func (u *Ui) Ask(query string) string { + var result string + if err := u.client.Call("Ui.Ask", query, &result); err != nil { + panic(err) + } + + return result +} + func (u *Ui) Error(message string) { if err := u.client.Call("Ui.Error", message, new(interface{})); err != nil { panic(err) @@ -35,6 +44,11 @@ func (u *Ui) Say(message string) { } } +func (u *UiServer) Ask(query string, reply *string) error { + *reply = u.ui.Ask(query) + return nil +} + func (u *UiServer) Error(message *string, reply *interface{}) error { u.ui.Error(*message) diff --git a/packer/rpc/ui_test.go b/packer/rpc/ui_test.go index ce877d8fe..384b959f2 100644 --- a/packer/rpc/ui_test.go +++ b/packer/rpc/ui_test.go @@ -7,6 +7,8 @@ import ( ) type testUi struct { + askCalled bool + askQuery string errorCalled bool errorMessage string messageCalled bool @@ -15,6 +17,12 @@ type testUi struct { sayMessage string } +func (u *testUi) Ask(query string) string { + u.askCalled = true + u.askQuery = query + return "foo" +} + func (u *testUi) Error(message string) { u.errorCalled = true u.errorMessage = message @@ -50,6 +58,11 @@ func TestUiRPC(t *testing.T) { uiClient := &Ui{client} // Basic error and say tests + result := uiClient.Ask("query") + assert.True(ui.askCalled, "ask should be called") + assert.Equal(ui.askQuery, "query", "should be correct") + assert.Equal(result, "foo", "should have correct result") + uiClient.Error("message") assert.Equal(ui.errorMessage, "message", "message should be correct") diff --git a/packer/ui.go b/packer/ui.go index e32faf8b8..c702b09f5 100644 --- a/packer/ui.go +++ b/packer/ui.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "log" + "sync" ) type UiColor uint @@ -21,6 +22,7 @@ const ( // world. This sort of control allows us to strictly control how output // is formatted and various levels of output. type Ui interface { + Ask(string) string Say(string) Message(string) Error(string) @@ -46,6 +48,11 @@ type PrefixedUi struct { type ReaderWriterUi struct { Reader io.Reader Writer io.Writer + l sync.Mutex +} + +func (u *ColoredUi) Ask(query string) string { + return u.Ui.Ask(u.colorize(query, u.Color, true)) } func (u *ColoredUi) Say(message string) { @@ -74,6 +81,10 @@ func (u *ColoredUi) colorize(message string, color UiColor, bold bool) string { return fmt.Sprintf("\033[%d;%d;40m%s\033[0m", attr, color, message) } +func (u *PrefixedUi) Ask(query string) string { + return u.Ui.Ask(fmt.Sprintf("%s: %s", u.SayPrefix, query)) +} + func (u *PrefixedUi) Say(message string) { u.Ui.Say(fmt.Sprintf("%s: %s", u.SayPrefix, message)) } @@ -86,7 +97,29 @@ func (u *PrefixedUi) Error(message string) { u.Ui.Error(fmt.Sprintf("%s: %s", u.SayPrefix, message)) } +func (rw *ReaderWriterUi) Ask(query string) string { + rw.l.Lock() + defer rw.l.Unlock() + + log.Printf("ui: ask: %s", query) + if query != "" { + if _, err := fmt.Fprint(rw.Writer, query+" "); err != nil { + panic(err) + } + } + + var line string + if _, err := fmt.Fscanln(rw.Reader, &line); err != nil { + panic(err) + } + + return line +} + func (rw *ReaderWriterUi) Say(message string) { + rw.l.Lock() + defer rw.l.Unlock() + log.Printf("ui: %s", message) _, err := fmt.Fprint(rw.Writer, message+"\n") if err != nil { @@ -95,6 +128,9 @@ func (rw *ReaderWriterUi) Say(message string) { } func (rw *ReaderWriterUi) Message(message string) { + rw.l.Lock() + defer rw.l.Unlock() + log.Printf("ui: %s", message) _, err := fmt.Fprintf(rw.Writer, message+"\n") if err != nil { @@ -103,6 +139,9 @@ func (rw *ReaderWriterUi) Message(message string) { } func (rw *ReaderWriterUi) Error(message string) { + rw.l.Lock() + defer rw.l.Unlock() + log.Printf("ui error: %s", message) _, err := fmt.Fprint(rw.Writer, message+"\n") if err != nil { diff --git a/packer/ui_test.go b/packer/ui_test.go index 8978f6fc1..bc3191a86 100644 --- a/packer/ui_test.go +++ b/packer/ui_test.go @@ -8,8 +8,8 @@ import ( func testUi() *ReaderWriterUi { return &ReaderWriterUi{ - new(bytes.Buffer), - new(bytes.Buffer), + Reader: new(bytes.Buffer), + Writer: new(bytes.Buffer), } }