command/state-push: support pushing from stdin

This commit is contained in:
Mitchell Hashimoto 2017-03-01 13:10:48 -08:00
parent 4f680aa5b8
commit a6b2eca613
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
3 changed files with 68 additions and 7 deletions

View File

@ -2,6 +2,7 @@ package command
import (
"fmt"
"io"
"os"
"strings"
@ -31,14 +32,28 @@ func (c *StatePushCommand) Run(args []string) int {
return 1
}
// Read the state
// Determine our reader for the input state. This is the filepath
// or stdin if "-" is given.
var r io.Reader = os.Stdin
if args[0] != "-" {
f, err := os.Open(args[0])
if err != nil {
c.Ui.Error(err.Error())
return 1
}
sourceState, err := terraform.ReadState(f)
f.Close()
// Note: we don't need to defer a Close here because we do a close
// automatically below directly after the read.
r = f
}
// Read the state
sourceState, err := terraform.ReadState(r)
if c, ok := r.(io.Closer); ok {
// Close the reader if possible right now since we're done with it.
c.Close()
}
if err != nil {
c.Ui.Error(fmt.Sprintf("Error reading source state %q: %s", args[0], err))
return 1
@ -109,6 +124,10 @@ Usage: terraform state push [options] PATH
This command works with local state (it will overwrite the local
state), but is less useful for this use case.
If PATH is "-", then this command will read the state to push from stdin.
Data from stdin is not streamed to the backend: it is loaded completely
(until pipe close), verified, and then pushed.
Options:
-force Write the state even if lineages don't match or the

View File

@ -1,10 +1,12 @@
package command
import (
"bytes"
"os"
"testing"
"github.com/hashicorp/terraform/helper/copy"
"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/cli"
)
@ -66,6 +68,42 @@ func TestStatePush_replaceMatch(t *testing.T) {
}
}
func TestStatePush_replaceMatchStdin(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
copy.CopyDir(testFixturePath("state-push-replace-match"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
expected := testStateRead(t, "replace.tfstate")
// Setup the replacement to come from stdin
var buf bytes.Buffer
if err := terraform.WriteState(expected, &buf); err != nil {
t.Fatalf("err: %s")
}
defer testStdinPipe(t, &buf)()
p := testProvider()
ui := new(cli.MockUi)
c := &StatePushCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
}
args := []string{"-"}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
actual := testStateRead(t, "local-state.tfstate")
if !actual.Equal(expected) {
t.Fatalf("bad: %#v", actual)
}
}
func TestStatePush_lineageMismatch(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)

View File

@ -22,6 +22,10 @@ Usage: `terraform state push [options] PATH`
This command will push the state specified by PATH to the currently
configured [backend](/docs/backends).
If PATH is "-" then the state data to push is read from stdin. This data
is loaded completely into memory and verified prior to being written to
the destination state.
Terraform will perform a number of safety checks to prevent you from
making changes that appear to be unsafe: