command/state-push: support pushing from stdin
This commit is contained in:
parent
4f680aa5b8
commit
a6b2eca613
|
@ -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
|
||||||
f, err := os.Open(args[0])
|
// or stdin if "-" is given.
|
||||||
if err != nil {
|
var r io.Reader = os.Stdin
|
||||||
c.Ui.Error(err.Error())
|
if args[0] != "-" {
|
||||||
return 1
|
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 {
|
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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue