github.com/r3labs/terraform@v0.8.4/command/command_test.go (about)

     1  package command
     2  
     3  import (
     4  	"flag"
     5  	"io"
     6  	"io/ioutil"
     7  	"log"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/hashicorp/go-getter"
    14  	"github.com/hashicorp/terraform/config/module"
    15  	"github.com/hashicorp/terraform/helper/logging"
    16  	"github.com/hashicorp/terraform/terraform"
    17  )
    18  
    19  // This is the directory where our test fixtures are.
    20  var fixtureDir = "./test-fixtures"
    21  
    22  func init() {
    23  	test = true
    24  
    25  	// Expand the fixture dir on init because we change the working
    26  	// directory in some tests.
    27  	var err error
    28  	fixtureDir, err = filepath.Abs(fixtureDir)
    29  	if err != nil {
    30  		panic(err)
    31  	}
    32  }
    33  
    34  func TestMain(m *testing.M) {
    35  	flag.Parse()
    36  	if testing.Verbose() {
    37  		// if we're verbose, use the logging requested by TF_LOG
    38  		logging.SetOutput()
    39  	} else {
    40  		// otherwise silence all logs
    41  		log.SetOutput(ioutil.Discard)
    42  	}
    43  
    44  	os.Exit(m.Run())
    45  }
    46  
    47  func tempDir(t *testing.T) string {
    48  	dir, err := ioutil.TempDir("", "tf")
    49  	if err != nil {
    50  		t.Fatalf("err: %s", err)
    51  	}
    52  	if err := os.RemoveAll(dir); err != nil {
    53  		t.Fatalf("err: %s", err)
    54  	}
    55  
    56  	return dir
    57  }
    58  
    59  func testFixturePath(name string) string {
    60  	return filepath.Join(fixtureDir, name)
    61  }
    62  
    63  func testCtxConfig(p terraform.ResourceProvider) *terraform.ContextOpts {
    64  	return &terraform.ContextOpts{
    65  		Providers: map[string]terraform.ResourceProviderFactory{
    66  			"test": func() (terraform.ResourceProvider, error) {
    67  				return p, nil
    68  			},
    69  		},
    70  	}
    71  }
    72  
    73  func testCtxConfigWithShell(p terraform.ResourceProvider, pr terraform.ResourceProvisioner) *terraform.ContextOpts {
    74  	return &terraform.ContextOpts{
    75  		Providers: map[string]terraform.ResourceProviderFactory{
    76  			"test": func() (terraform.ResourceProvider, error) {
    77  				return p, nil
    78  			},
    79  		},
    80  		Provisioners: map[string]terraform.ResourceProvisionerFactory{
    81  			"shell": func() (terraform.ResourceProvisioner, error) {
    82  				return pr, nil
    83  			},
    84  		},
    85  	}
    86  }
    87  
    88  func testModule(t *testing.T, name string) *module.Tree {
    89  	mod, err := module.NewTreeModule("", filepath.Join(fixtureDir, name))
    90  	if err != nil {
    91  		t.Fatalf("err: %s", err)
    92  	}
    93  
    94  	s := &getter.FolderStorage{StorageDir: tempDir(t)}
    95  	if err := mod.Load(s, module.GetModeGet); err != nil {
    96  		t.Fatalf("err: %s", err)
    97  	}
    98  
    99  	return mod
   100  }
   101  
   102  // testPlan returns a non-nil noop plan.
   103  func testPlan(t *testing.T) *terraform.Plan {
   104  	state := terraform.NewState()
   105  	state.RootModule().Outputs["foo"] = &terraform.OutputState{
   106  		Type:  "string",
   107  		Value: "foo",
   108  	}
   109  
   110  	return &terraform.Plan{
   111  		Module: testModule(t, "apply"),
   112  		State:  state,
   113  	}
   114  }
   115  
   116  func testPlanFile(t *testing.T, plan *terraform.Plan) string {
   117  	path := testTempFile(t)
   118  
   119  	f, err := os.Create(path)
   120  	if err != nil {
   121  		t.Fatalf("err: %s", err)
   122  	}
   123  	defer f.Close()
   124  
   125  	if err := terraform.WritePlan(plan, f); err != nil {
   126  		t.Fatalf("err: %s", err)
   127  	}
   128  
   129  	return path
   130  }
   131  
   132  func testReadPlan(t *testing.T, path string) *terraform.Plan {
   133  	f, err := os.Open(path)
   134  	if err != nil {
   135  		t.Fatalf("err: %s", err)
   136  	}
   137  	defer f.Close()
   138  
   139  	p, err := terraform.ReadPlan(f)
   140  	if err != nil {
   141  		t.Fatalf("err: %s", err)
   142  	}
   143  
   144  	return p
   145  }
   146  
   147  // testState returns a test State structure that we use for a lot of tests.
   148  func testState() *terraform.State {
   149  	state := &terraform.State{
   150  		Version: 2,
   151  		Modules: []*terraform.ModuleState{
   152  			&terraform.ModuleState{
   153  				Path: []string{"root"},
   154  				Resources: map[string]*terraform.ResourceState{
   155  					"test_instance.foo": &terraform.ResourceState{
   156  						Type: "test_instance",
   157  						Primary: &terraform.InstanceState{
   158  							ID: "bar",
   159  						},
   160  					},
   161  				},
   162  				Outputs: map[string]*terraform.OutputState{},
   163  			},
   164  		},
   165  	}
   166  	state.Init()
   167  	return state
   168  }
   169  
   170  func testStateFile(t *testing.T, s *terraform.State) string {
   171  	path := testTempFile(t)
   172  
   173  	f, err := os.Create(path)
   174  	if err != nil {
   175  		t.Fatalf("err: %s", err)
   176  	}
   177  	defer f.Close()
   178  
   179  	if err := terraform.WriteState(s, f); err != nil {
   180  		t.Fatalf("err: %s", err)
   181  	}
   182  
   183  	return path
   184  }
   185  
   186  // testStateFileDefault writes the state out to the default statefile
   187  // in the cwd. Use `testCwd` to change into a temp cwd.
   188  func testStateFileDefault(t *testing.T, s *terraform.State) string {
   189  	f, err := os.Create(DefaultStateFilename)
   190  	if err != nil {
   191  		t.Fatalf("err: %s", err)
   192  	}
   193  	defer f.Close()
   194  
   195  	if err := terraform.WriteState(s, f); err != nil {
   196  		t.Fatalf("err: %s", err)
   197  	}
   198  
   199  	return DefaultStateFilename
   200  }
   201  
   202  // testStateFileRemote writes the state out to the remote statefile
   203  // in the cwd. Use `testCwd` to change into a temp cwd.
   204  func testStateFileRemote(t *testing.T, s *terraform.State) string {
   205  	path := filepath.Join(DefaultDataDir, DefaultStateFilename)
   206  	if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
   207  		t.Fatalf("err: %s", err)
   208  	}
   209  
   210  	f, err := os.Create(path)
   211  	if err != nil {
   212  		t.Fatalf("err: %s", err)
   213  	}
   214  	defer f.Close()
   215  
   216  	if err := terraform.WriteState(s, f); err != nil {
   217  		t.Fatalf("err: %s", err)
   218  	}
   219  
   220  	return path
   221  }
   222  
   223  // testStateOutput tests that the state at the given path contains
   224  // the expected state string.
   225  func testStateOutput(t *testing.T, path string, expected string) {
   226  	f, err := os.Open(path)
   227  	if err != nil {
   228  		t.Fatalf("err: %s", err)
   229  	}
   230  
   231  	newState, err := terraform.ReadState(f)
   232  	f.Close()
   233  	if err != nil {
   234  		t.Fatalf("err: %s", err)
   235  	}
   236  
   237  	actual := strings.TrimSpace(newState.String())
   238  	expected = strings.TrimSpace(expected)
   239  	if actual != expected {
   240  		t.Fatalf("expected:\n%s\nactual:\n%s", expected, actual)
   241  	}
   242  }
   243  
   244  func testProvider() *terraform.MockResourceProvider {
   245  	p := new(terraform.MockResourceProvider)
   246  	p.DiffReturn = &terraform.InstanceDiff{}
   247  	p.RefreshFn = func(
   248  		info *terraform.InstanceInfo,
   249  		s *terraform.InstanceState) (*terraform.InstanceState, error) {
   250  		return s, nil
   251  	}
   252  	p.ResourcesReturn = []terraform.ResourceType{
   253  		terraform.ResourceType{
   254  			Name: "test_instance",
   255  		},
   256  	}
   257  
   258  	return p
   259  }
   260  
   261  func testTempFile(t *testing.T) string {
   262  	return filepath.Join(testTempDir(t), "state.tfstate")
   263  }
   264  
   265  func testTempDir(t *testing.T) string {
   266  	d, err := ioutil.TempDir("", "tf")
   267  	if err != nil {
   268  		t.Fatalf("err: %s", err)
   269  	}
   270  
   271  	return d
   272  }
   273  
   274  // testRename renames the path to new and returns a function to defer to
   275  // revert the rename.
   276  func testRename(t *testing.T, base, path, new string) func() {
   277  	if base != "" {
   278  		path = filepath.Join(base, path)
   279  		new = filepath.Join(base, new)
   280  	}
   281  
   282  	if err := os.Rename(path, new); err != nil {
   283  		t.Fatalf("err: %s", err)
   284  	}
   285  
   286  	return func() {
   287  		// Just re-rename and ignore the return value
   288  		testRename(t, "", new, path)
   289  	}
   290  }
   291  
   292  // testChdir changes the directory and returns a function to defer to
   293  // revert the old cwd.
   294  func testChdir(t *testing.T, new string) func() {
   295  	old, err := os.Getwd()
   296  	if err != nil {
   297  		t.Fatalf("err: %s", err)
   298  	}
   299  
   300  	if err := os.Chdir(new); err != nil {
   301  		t.Fatalf("err: %v", err)
   302  	}
   303  
   304  	return func() {
   305  		// Re-run the function ignoring the defer result
   306  		testChdir(t, old)
   307  	}
   308  }
   309  
   310  // testCwd is used to change the current working directory
   311  // into a test directory that should be remoted after
   312  func testCwd(t *testing.T) (string, string) {
   313  	tmp, err := ioutil.TempDir("", "tf")
   314  	if err != nil {
   315  		t.Fatalf("err: %v", err)
   316  	}
   317  
   318  	cwd, err := os.Getwd()
   319  	if err != nil {
   320  		t.Fatalf("err: %v", err)
   321  	}
   322  
   323  	if err := os.Chdir(tmp); err != nil {
   324  		t.Fatalf("err: %v", err)
   325  	}
   326  
   327  	return tmp, cwd
   328  }
   329  
   330  // testFixCwd is used to as a defer to testDir
   331  func testFixCwd(t *testing.T, tmp, cwd string) {
   332  	if err := os.Chdir(cwd); err != nil {
   333  		t.Fatalf("err: %v", err)
   334  	}
   335  
   336  	if err := os.RemoveAll(tmp); err != nil {
   337  		t.Fatalf("err: %v", err)
   338  	}
   339  }
   340  
   341  // testStdinPipe changes os.Stdin to be a pipe that sends the data from
   342  // the reader before closing the pipe.
   343  //
   344  // The returned function should be deferred to properly clean up and restore
   345  // the original stdin.
   346  func testStdinPipe(t *testing.T, src io.Reader) func() {
   347  	r, w, err := os.Pipe()
   348  	if err != nil {
   349  		t.Fatalf("err: %s", err)
   350  	}
   351  
   352  	// Modify stdin to point to our new pipe
   353  	old := os.Stdin
   354  	os.Stdin = r
   355  
   356  	// Copy the data from the reader to the pipe
   357  	go func() {
   358  		defer w.Close()
   359  		io.Copy(w, src)
   360  	}()
   361  
   362  	return func() {
   363  		// Close our read end
   364  		r.Close()
   365  
   366  		// Reset stdin
   367  		os.Stdin = old
   368  	}
   369  }
   370  
   371  // Modify os.Stdout to write to the given buffer. Note that this is generally
   372  // not useful since the commands are configured to write to a cli.Ui, not
   373  // Stdout directly. Commands like `console` though use the raw stdout.
   374  func testStdoutCapture(t *testing.T, dst io.Writer) func() {
   375  	r, w, err := os.Pipe()
   376  	if err != nil {
   377  		t.Fatalf("err: %s", err)
   378  	}
   379  
   380  	// Modify stdout
   381  	old := os.Stdout
   382  	os.Stdout = w
   383  
   384  	// Copy
   385  	doneCh := make(chan struct{})
   386  	go func() {
   387  		defer close(doneCh)
   388  		defer r.Close()
   389  		io.Copy(dst, r)
   390  	}()
   391  
   392  	return func() {
   393  		// Close the writer end of the pipe
   394  		w.Sync()
   395  		w.Close()
   396  
   397  		// Reset stdout
   398  		os.Stdout = old
   399  
   400  		// Wait for the data copy to complete to avoid a race reading data
   401  		<-doneCh
   402  	}
   403  }