12 | 12 |
tfjson "github.com/hashicorp/terraform-json"
|
13 | 13 |
)
|
14 | 14 |
|
|
15 |
const (
|
|
16 |
ConfigFileName = "terraform_plugin_test.tf"
|
|
17 |
PlanFileName = "tfplan"
|
|
18 |
)
|
|
19 |
|
15 | 20 |
// WorkingDir represents a distinct working directory that can be used for
|
16 | 21 |
// running tests. Each test should construct its own WorkingDir by calling
|
17 | 22 |
// NewWorkingDir or RequireNewWorkingDir on its package's singleton
|
|
25 | 30 |
// baseArgs is arguments that should be appended to all commands
|
26 | 31 |
baseArgs []string
|
27 | 32 |
|
28 | |
// configDir contains the singular config file generated for each test
|
29 | |
configDir string
|
30 | |
|
31 | 33 |
// tf is the instance of tfexec.Terraform used for running Terraform commands
|
32 | 34 |
tf *tfexec.Terraform
|
33 | 35 |
|
|
74 | 76 |
return wd.h
|
75 | 77 |
}
|
76 | 78 |
|
77 | |
func (wd *WorkingDir) relativeConfigDir() (string, error) {
|
78 | |
relPath, err := filepath.Rel(wd.baseDir, wd.configDir)
|
79 | |
if err != nil {
|
80 | |
return "", fmt.Errorf("Error determining relative path of configuration directory: %w", err)
|
81 | |
}
|
82 | |
return relPath, nil
|
83 | |
}
|
84 | |
|
85 | 79 |
// SetConfig sets a new configuration for the working directory.
|
86 | 80 |
//
|
87 | 81 |
// This must be called at least once before any call to Init, Plan, Apply, or
|
88 | 82 |
// Destroy to establish the configuration. Any previously-set configuration is
|
89 | 83 |
// discarded and any saved plan is cleared.
|
90 | 84 |
func (wd *WorkingDir) SetConfig(cfg string) error {
|
91 | |
// Each call to SetConfig creates a new directory under our baseDir.
|
92 | |
// We create them within so that our final cleanup step will delete them
|
93 | |
// automatically without any additional tracking.
|
94 | |
configDir, err := ioutil.TempDir(wd.baseDir, "config")
|
|
85 |
configFilename := filepath.Join(wd.baseDir, ConfigFileName)
|
|
86 |
err := ioutil.WriteFile(configFilename, []byte(cfg), 0700)
|
95 | 87 |
if err != nil {
|
96 | 88 |
return err
|
97 | 89 |
}
|
98 | |
configFilename := filepath.Join(configDir, "terraform_plugin_test.tf")
|
99 | |
err = ioutil.WriteFile(configFilename, []byte(cfg), 0700)
|
100 | |
if err != nil {
|
101 | |
return err
|
102 | |
}
|
103 | |
|
104 | |
// symlink the provider source files into the config directory
|
105 | |
// e.g. testdata
|
106 | |
err = symlinkDirectoriesOnly(wd.h.sourceDir, configDir)
|
107 | |
if err != nil {
|
108 | |
return err
|
109 | |
}
|
110 | |
|
111 | |
tf, err := tfexec.NewTerraform(configDir, wd.terraformExec)
|
112 | |
if err != nil {
|
113 | |
return err
|
114 | |
}
|
115 | 90 |
|
116 | 91 |
var mismatch *tfexec.ErrVersionMismatch
|
117 | |
err = tf.SetDisablePluginTLS(true)
|
|
92 |
err = wd.tf.SetDisablePluginTLS(true)
|
118 | 93 |
if err != nil && !errors.As(err, &mismatch) {
|
119 | 94 |
return err
|
120 | 95 |
}
|
121 | |
err = tf.SetSkipProviderVerify(true)
|
|
96 |
err = wd.tf.SetSkipProviderVerify(true)
|
122 | 97 |
if err != nil && !errors.As(err, &mismatch) {
|
123 | 98 |
return err
|
124 | 99 |
}
|
125 | 100 |
|
126 | 101 |
if p := os.Getenv("TF_ACC_LOG_PATH"); p != "" {
|
127 | |
tf.SetLogPath(p)
|
128 | |
}
|
129 | |
|
130 | |
wd.configDir = configDir
|
131 | |
wd.tf = tf
|
|
102 |
wd.tf.SetLogPath(p)
|
|
103 |
}
|
132 | 104 |
|
133 | 105 |
// Changing configuration invalidates any saved plan.
|
134 | 106 |
err = wd.ClearPlan()
|
|
153 | 125 |
// Any remote objects tracked by the state are not destroyed first, so this
|
154 | 126 |
// will leave them dangling in the remote system.
|
155 | 127 |
func (wd *WorkingDir) ClearState() error {
|
156 | |
err := os.Remove(filepath.Join(wd.configDir, "terraform.tfstate"))
|
|
128 |
err := os.Remove(filepath.Join(wd.baseDir, "terraform.tfstate"))
|
157 | 129 |
if os.IsNotExist(err) {
|
158 | 130 |
return nil
|
159 | 131 |
}
|
|
192 | 164 |
// Init runs "terraform init" for the given working directory, forcing Terraform
|
193 | 165 |
// to use the current version of the plugin under test.
|
194 | 166 |
func (wd *WorkingDir) Init() error {
|
195 | |
if wd.configDir == "" {
|
|
167 |
if _, err := os.Stat(wd.configFilename()); err != nil {
|
196 | 168 |
return fmt.Errorf("must call SetConfig before Init")
|
197 | 169 |
}
|
198 | 170 |
|
199 | 171 |
return wd.tf.Init(context.Background(), tfexec.Reattach(wd.reattachInfo))
|
|
172 |
}
|
|
173 |
|
|
174 |
func (wd *WorkingDir) configFilename() string {
|
|
175 |
return filepath.Join(wd.baseDir, ConfigFileName)
|
200 | 176 |
}
|
201 | 177 |
|
202 | 178 |
// RequireInit is a variant of Init that will fail the test via the given
|
|
210 | 186 |
}
|
211 | 187 |
|
212 | 188 |
func (wd *WorkingDir) planFilename() string {
|
213 | |
return filepath.Join(wd.configDir, "tfplan")
|
|
189 |
return filepath.Join(wd.baseDir, PlanFileName)
|
214 | 190 |
}
|
215 | 191 |
|
216 | 192 |
// CreatePlan runs "terraform plan" to create a saved plan file, which if successful
|
217 | 193 |
// will then be used for the next call to Apply.
|
218 | 194 |
func (wd *WorkingDir) CreatePlan() error {
|
219 | |
_, err := wd.tf.Plan(context.Background(), tfexec.Reattach(wd.reattachInfo), tfexec.Refresh(false), tfexec.Out("tfplan"))
|
|
195 |
_, err := wd.tf.Plan(context.Background(), tfexec.Reattach(wd.reattachInfo), tfexec.Refresh(false), tfexec.Out(PlanFileName))
|
220 | 196 |
return err
|
221 | 197 |
}
|
222 | 198 |
|
|
233 | 209 |
// CreateDestroyPlan runs "terraform plan -destroy" to create a saved plan
|
234 | 210 |
// file, which if successful will then be used for the next call to Apply.
|
235 | 211 |
func (wd *WorkingDir) CreateDestroyPlan() error {
|
236 | |
_, err := wd.tf.Plan(context.Background(), tfexec.Reattach(wd.reattachInfo), tfexec.Refresh(false), tfexec.Out("tfplan"), tfexec.Destroy(true))
|
|
212 |
_, err := wd.tf.Plan(context.Background(), tfexec.Reattach(wd.reattachInfo), tfexec.Refresh(false), tfexec.Out(PlanFileName), tfexec.Destroy(true))
|
237 | 213 |
return err
|
238 | 214 |
}
|
239 | 215 |
|
|
244 | 220 |
func (wd *WorkingDir) Apply() error {
|
245 | 221 |
args := []tfexec.ApplyOption{tfexec.Reattach(wd.reattachInfo), tfexec.Refresh(false)}
|
246 | 222 |
if wd.HasSavedPlan() {
|
247 | |
args = append(args, tfexec.DirOrPlan("tfplan"))
|
|
223 |
args = append(args, tfexec.DirOrPlan(PlanFileName))
|
248 | 224 |
}
|
249 | 225 |
|
250 | 226 |
return wd.tf.Apply(context.Background(), args...)
|
|
368 | 344 |
|
369 | 345 |
// Import runs terraform import
|
370 | 346 |
func (wd *WorkingDir) Import(resource, id string) error {
|
371 | |
return wd.tf.Import(context.Background(), resource, id, tfexec.Config(wd.configDir), tfexec.Reattach(wd.reattachInfo))
|
|
347 |
return wd.tf.Import(context.Background(), resource, id, tfexec.Config(wd.baseDir), tfexec.Reattach(wd.reattachInfo))
|
372 | 348 |
}
|
373 | 349 |
|
374 | 350 |
// RequireImport is a variant of Import that will fail the test via
|
|
383 | 359 |
|
384 | 360 |
// Refresh runs terraform refresh
|
385 | 361 |
func (wd *WorkingDir) Refresh() error {
|
386 | |
return wd.tf.Refresh(context.Background(), tfexec.Reattach(wd.reattachInfo), tfexec.State(filepath.Join(wd.configDir, "terraform.tfstate")))
|
|
362 |
return wd.tf.Refresh(context.Background(), tfexec.Reattach(wd.reattachInfo), tfexec.State(filepath.Join(wd.baseDir, "terraform.tfstate")))
|
387 | 363 |
}
|
388 | 364 |
|
389 | 365 |
// RequireRefresh is a variant of Refresh that will fail the test via
|