Merge pull request #12350 from hashicorp/b-state-push-stdin
command/state-push: support pushing from stdin
This commit is contained in:
commit
ee985a606c
|
@ -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
|
||||
f, err := os.Open(args[0])
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
sourceState, err := terraform.ReadState(f)
|
||||
f.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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
Loading…
Reference in New Issue