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
|
||||
}
|
||||
|
||||
// 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
|
||||
// the expected state string.
|
||||
func testStateOutput(t *testing.T, path string, expected string) {
|
||||
|
|
|
@ -3,6 +3,7 @@ package command
|
|||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
@ -12,6 +13,11 @@ import (
|
|||
|
||||
type PushCommand struct {
|
||||
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 {
|
||||
|
@ -64,7 +70,7 @@ func (c *PushCommand) Run(args []string) int {
|
|||
}
|
||||
|
||||
// Build the context based on the arguments given
|
||||
_, planned, err := c.Context(contextOpts{
|
||||
ctx, planned, err := c.Context(contextOpts{
|
||||
Path: configPath,
|
||||
StatePath: c.Meta.statePath,
|
||||
})
|
||||
|
@ -79,6 +85,13 @@ func (c *PushCommand) Run(args []string) int {
|
|||
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
|
||||
// by default according to VCS rules but forcing the data directory.
|
||||
archiveOpts := &archive.ArchiveOpts{
|
||||
|
@ -92,7 +105,7 @@ func (c *PushCommand) Run(args []string) int {
|
|||
filepath.Join(c.DataDir(), "modules"))
|
||||
}
|
||||
|
||||
_, err = archive.CreateArchive(configPath, archiveOpts)
|
||||
archiveR, err := archive.CreateArchive(configPath, archiveOpts)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf(
|
||||
"An error has occurred while archiving the module for uploading:\n"+
|
||||
|
@ -100,6 +113,13 @@ func (c *PushCommand) Run(args []string) int {
|
|||
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
|
||||
}
|
||||
|
||||
|
@ -126,3 +146,31 @@ Options:
|
|||
func (c *PushCommand) Synopsis() string {
|
||||
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
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"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) {
|
||||
tmp, cwd := testCwd(t)
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
<<<<<<< Updated upstream
|
||||
=======
|
||||
// Variables will return the mapping of variables that were defined
|
||||
// for this Context. If Input was called, this mapping may be different
|
||||
// than what was given.
|
||||
|
@ -390,7 +388,6 @@ func (c *Context) SetVariable(k, v string) {
|
|||
c.variables[k] = v
|
||||
}
|
||||
|
||||
>>>>>>> Stashed changes
|
||||
func (c *Context) acquireRun() chan<- struct{} {
|
||||
c.l.Lock()
|
||||
defer c.l.Unlock()
|
||||
|
|
Loading…
Reference in New Issue