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 }