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 ( import (
"fmt" "fmt"
"io"
"os" "os"
"strings" "strings"
@ -31,14 +32,28 @@ func (c *StatePushCommand) Run(args []string) int {
return 1 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]) f, err := os.Open(args[0])
if err != nil { if err != nil {
c.Ui.Error(err.Error()) c.Ui.Error(err.Error())
return 1 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 { if err != nil {
c.Ui.Error(fmt.Sprintf("Error reading source state %q: %s", args[0], err)) c.Ui.Error(fmt.Sprintf("Error reading source state %q: %s", args[0], err))
return 1 return 1
@ -109,6 +124,10 @@ Usage: terraform state push [options] PATH
This command works with local state (it will overwrite the local This command works with local state (it will overwrite the local
state), but is less useful for this use case. 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: Options:
-force Write the state even if lineages don't match or the -force Write the state even if lineages don't match or the

View File

@ -1,10 +1,12 @@
package command package command
import ( import (
"bytes"
"os" "os"
"testing" "testing"
"github.com/hashicorp/terraform/helper/copy" "github.com/hashicorp/terraform/helper/copy"
"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/cli" "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) { func TestStatePush_lineageMismatch(t *testing.T) {
// Create a temporary working directory that is empty // Create a temporary working directory that is empty
td := tempDir(t) 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 This command will push the state specified by PATH to the currently
configured [backend](/docs/backends). 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 Terraform will perform a number of safety checks to prevent you from
making changes that appear to be unsafe: making changes that appear to be unsafe: