command/push: archive, upload
This commit is contained in:
parent
fdded8ca14
commit
22087181af
|
@ -148,6 +148,27 @@ func testStateFileDefault(t *testing.T, s *terraform.State) string {
|
||||||
return DefaultStateFilename
|
return DefaultStateFilename
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// testStateFileRemote writes the state out to the remote statefile
|
||||||
|
// in the cwd. Use `testCwd` to change into a temp cwd.
|
||||||
|
func testStateFileRemote(t *testing.T, s *terraform.State) string {
|
||||||
|
path := filepath.Join(DefaultDataDir, DefaultStateFilename)
|
||||||
|
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
if err := terraform.WriteState(s, f); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
// testStateOutput tests that the state at the given path contains
|
// testStateOutput tests that the state at the given path contains
|
||||||
// the expected state string.
|
// the expected state string.
|
||||||
func testStateOutput(t *testing.T, path string, expected string) {
|
func testStateOutput(t *testing.T, path string, expected string) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package command
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -12,6 +13,11 @@ import (
|
||||||
|
|
||||||
type PushCommand struct {
|
type PushCommand struct {
|
||||||
Meta
|
Meta
|
||||||
|
|
||||||
|
// client is the client to use for the actual push operations.
|
||||||
|
// If this isn't set, then the Atlas client is used. This should
|
||||||
|
// really only be set for testing reasons (and is hence not exported).
|
||||||
|
client pushClient
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PushCommand) Run(args []string) int {
|
func (c *PushCommand) Run(args []string) int {
|
||||||
|
@ -64,7 +70,7 @@ func (c *PushCommand) Run(args []string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the context based on the arguments given
|
// Build the context based on the arguments given
|
||||||
_, planned, err := c.Context(contextOpts{
|
ctx, planned, err := c.Context(contextOpts{
|
||||||
Path: configPath,
|
Path: configPath,
|
||||||
StatePath: c.Meta.statePath,
|
StatePath: c.Meta.statePath,
|
||||||
})
|
})
|
||||||
|
@ -79,6 +85,13 @@ func (c *PushCommand) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ask for input
|
||||||
|
if err := ctx.Input(c.InputMode()); err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf(
|
||||||
|
"Error while asking for variable input:\n\n%s", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
// Build the archiving options, which includes everything it can
|
// Build the archiving options, which includes everything it can
|
||||||
// by default according to VCS rules but forcing the data directory.
|
// by default according to VCS rules but forcing the data directory.
|
||||||
archiveOpts := &archive.ArchiveOpts{
|
archiveOpts := &archive.ArchiveOpts{
|
||||||
|
@ -92,7 +105,7 @@ func (c *PushCommand) Run(args []string) int {
|
||||||
filepath.Join(c.DataDir(), "modules"))
|
filepath.Join(c.DataDir(), "modules"))
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = archive.CreateArchive(configPath, archiveOpts)
|
archiveR, err := archive.CreateArchive(configPath, archiveOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf(
|
c.Ui.Error(fmt.Sprintf(
|
||||||
"An error has occurred while archiving the module for uploading:\n"+
|
"An error has occurred while archiving the module for uploading:\n"+
|
||||||
|
@ -100,6 +113,13 @@ func (c *PushCommand) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Upsert!
|
||||||
|
if err := c.client.Upsert(archiveR, archiveR.Size); err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf(
|
||||||
|
"An error occurred while uploading the module:\n\n%s", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,3 +146,31 @@ Options:
|
||||||
func (c *PushCommand) Synopsis() string {
|
func (c *PushCommand) Synopsis() string {
|
||||||
return "Upload this Terraform module to Atlas to run"
|
return "Upload this Terraform module to Atlas to run"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pushClient is implementd internally to control where pushes go. This is
|
||||||
|
// either to Atlas or a mock for testing.
|
||||||
|
type pushClient interface {
|
||||||
|
Upsert(io.Reader, int64) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockPushClient struct {
|
||||||
|
File string
|
||||||
|
|
||||||
|
UpsertCalled bool
|
||||||
|
UpsertError error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *mockPushClient) Upsert(data io.Reader, size int64) error {
|
||||||
|
f, err := os.Create(c.File)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
if _, err := io.CopyN(f, data, size); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.UpsertCalled = true
|
||||||
|
return c.UpsertError
|
||||||
|
}
|
||||||
|
|
|
@ -1,12 +1,61 @@
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"compress/gzip"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestPush_good(t *testing.T) {
|
||||||
|
tmp, cwd := testCwd(t)
|
||||||
|
defer testFixCwd(t, tmp, cwd)
|
||||||
|
|
||||||
|
// Create remote state file, this should be pulled
|
||||||
|
conf, srv := testRemoteState(t, testState(), 200)
|
||||||
|
defer srv.Close()
|
||||||
|
|
||||||
|
// Persist local remote state
|
||||||
|
s := terraform.NewState()
|
||||||
|
s.Serial = 5
|
||||||
|
s.Remote = conf
|
||||||
|
testStateFileRemote(t, s)
|
||||||
|
|
||||||
|
// Path where the archive will be "uploaded" to
|
||||||
|
archivePath := testTempFile(t)
|
||||||
|
defer os.Remove(archivePath)
|
||||||
|
|
||||||
|
client := &mockPushClient{File: archivePath}
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
c := &PushCommand{
|
||||||
|
Meta: Meta{
|
||||||
|
ContextOpts: testCtxConfig(testProvider()),
|
||||||
|
Ui: ui,
|
||||||
|
},
|
||||||
|
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{
|
||||||
|
testFixturePath("push"),
|
||||||
|
}
|
||||||
|
if code := c.Run(args); code != 0 {
|
||||||
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := testArchiveStr(t, archivePath)
|
||||||
|
expected := []string{}
|
||||||
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
|
t.Fatalf("bad: %#v", actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestPush_noState(t *testing.T) {
|
func TestPush_noState(t *testing.T) {
|
||||||
tmp, cwd := testCwd(t)
|
tmp, cwd := testCwd(t)
|
||||||
defer testFixCwd(t, tmp, cwd)
|
defer testFixCwd(t, tmp, cwd)
|
||||||
|
@ -57,3 +106,70 @@ func TestPush_noRemoteState(t *testing.T) {
|
||||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPush_plan(t *testing.T) {
|
||||||
|
tmp, cwd := testCwd(t)
|
||||||
|
defer testFixCwd(t, tmp, cwd)
|
||||||
|
|
||||||
|
// Create remote state file, this should be pulled
|
||||||
|
conf, srv := testRemoteState(t, testState(), 200)
|
||||||
|
defer srv.Close()
|
||||||
|
|
||||||
|
// Persist local remote state
|
||||||
|
s := terraform.NewState()
|
||||||
|
s.Serial = 5
|
||||||
|
s.Remote = conf
|
||||||
|
testStateFileRemote(t, s)
|
||||||
|
|
||||||
|
// Create a plan
|
||||||
|
planPath := testPlanFile(t, &terraform.Plan{
|
||||||
|
Module: testModule(t, "apply"),
|
||||||
|
})
|
||||||
|
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
c := &PushCommand{
|
||||||
|
Meta: Meta{
|
||||||
|
ContextOpts: testCtxConfig(testProvider()),
|
||||||
|
Ui: ui,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{planPath}
|
||||||
|
if code := c.Run(args); code != 1 {
|
||||||
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testArchiveStr(t *testing.T, path string) []string {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
// Ungzip
|
||||||
|
gzipR, err := gzip.NewReader(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accumulator
|
||||||
|
result := make([]string, 0, 10)
|
||||||
|
|
||||||
|
// Untar
|
||||||
|
tarR := tar.NewReader(gzipR)
|
||||||
|
for {
|
||||||
|
header, err := tarR.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, header.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(result)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
resource "aws_instance" "foo" {}
|
|
@ -376,8 +376,6 @@ func (c *Context) Validate() ([]string, []error) {
|
||||||
return walker.ValidationWarnings, rerrs.Errors
|
return walker.ValidationWarnings, rerrs.Errors
|
||||||
}
|
}
|
||||||
|
|
||||||
<<<<<<< Updated upstream
|
|
||||||
=======
|
|
||||||
// Variables will return the mapping of variables that were defined
|
// Variables will return the mapping of variables that were defined
|
||||||
// for this Context. If Input was called, this mapping may be different
|
// for this Context. If Input was called, this mapping may be different
|
||||||
// than what was given.
|
// than what was given.
|
||||||
|
@ -390,7 +388,6 @@ func (c *Context) SetVariable(k, v string) {
|
||||||
c.variables[k] = v
|
c.variables[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
>>>>>>> Stashed changes
|
|
||||||
func (c *Context) acquireRun() chan<- struct{} {
|
func (c *Context) acquireRun() chan<- struct{} {
|
||||||
c.l.Lock()
|
c.l.Lock()
|
||||||
defer c.l.Unlock()
|
defer c.l.Unlock()
|
||||||
|
|
Loading…
Reference in New Issue