github.com/hugorut/terraform@v1.1.3/src/command/state_replace_provider_test.go (about) 1 package command 2 3 import ( 4 "bytes" 5 "path/filepath" 6 "strings" 7 "testing" 8 9 "github.com/mitchellh/cli" 10 11 "github.com/hugorut/terraform/src/addrs" 12 "github.com/hugorut/terraform/src/states" 13 ) 14 15 func TestStateReplaceProvider(t *testing.T) { 16 state := states.BuildState(func(s *states.SyncState) { 17 s.SetResourceInstanceCurrent( 18 addrs.Resource{ 19 Mode: addrs.ManagedResourceMode, 20 Type: "aws_instance", 21 Name: "alpha", 22 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 23 &states.ResourceInstanceObjectSrc{ 24 AttrsJSON: []byte(`{"id":"alpha","foo":"value","bar":"value"}`), 25 Status: states.ObjectReady, 26 }, 27 addrs.AbsProviderConfig{ 28 Provider: addrs.NewDefaultProvider("aws"), 29 Module: addrs.RootModule, 30 }, 31 ) 32 s.SetResourceInstanceCurrent( 33 addrs.Resource{ 34 Mode: addrs.ManagedResourceMode, 35 Type: "aws_instance", 36 Name: "beta", 37 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 38 &states.ResourceInstanceObjectSrc{ 39 AttrsJSON: []byte(`{"id":"beta","foo":"value","bar":"value"}`), 40 Status: states.ObjectReady, 41 }, 42 addrs.AbsProviderConfig{ 43 Provider: addrs.NewDefaultProvider("aws"), 44 Module: addrs.RootModule, 45 }, 46 ) 47 s.SetResourceInstanceCurrent( 48 addrs.Resource{ 49 Mode: addrs.ManagedResourceMode, 50 Type: "azurerm_virtual_machine", 51 Name: "gamma", 52 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 53 &states.ResourceInstanceObjectSrc{ 54 AttrsJSON: []byte(`{"id":"gamma","baz":"value"}`), 55 Status: states.ObjectReady, 56 }, 57 addrs.AbsProviderConfig{ 58 Provider: addrs.NewLegacyProvider("azurerm"), 59 Module: addrs.RootModule, 60 }, 61 ) 62 }) 63 64 t.Run("happy path", func(t *testing.T) { 65 statePath := testStateFile(t, state) 66 67 ui := new(cli.MockUi) 68 view, _ := testView(t) 69 c := &StateReplaceProviderCommand{ 70 StateMeta{ 71 Meta: Meta{ 72 Ui: ui, 73 View: view, 74 }, 75 }, 76 } 77 78 inputBuf := &bytes.Buffer{} 79 ui.InputReader = inputBuf 80 inputBuf.WriteString("yes\n") 81 82 args := []string{ 83 "-state", statePath, 84 "hashicorp/aws", 85 "acmecorp/aws", 86 } 87 if code := c.Run(args); code != 0 { 88 t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String()) 89 } 90 91 testStateOutput(t, statePath, testStateReplaceProviderOutput) 92 93 backups := testStateBackups(t, filepath.Dir(statePath)) 94 if len(backups) != 1 { 95 t.Fatalf("unexpected backups: %#v", backups) 96 } 97 testStateOutput(t, backups[0], testStateReplaceProviderOutputOriginal) 98 }) 99 100 t.Run("auto approve", func(t *testing.T) { 101 statePath := testStateFile(t, state) 102 103 ui := new(cli.MockUi) 104 view, _ := testView(t) 105 c := &StateReplaceProviderCommand{ 106 StateMeta{ 107 Meta: Meta{ 108 Ui: ui, 109 View: view, 110 }, 111 }, 112 } 113 114 inputBuf := &bytes.Buffer{} 115 ui.InputReader = inputBuf 116 117 args := []string{ 118 "-state", statePath, 119 "-auto-approve", 120 "hashicorp/aws", 121 "acmecorp/aws", 122 } 123 if code := c.Run(args); code != 0 { 124 t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String()) 125 } 126 127 testStateOutput(t, statePath, testStateReplaceProviderOutput) 128 129 backups := testStateBackups(t, filepath.Dir(statePath)) 130 if len(backups) != 1 { 131 t.Fatalf("unexpected backups: %#v", backups) 132 } 133 testStateOutput(t, backups[0], testStateReplaceProviderOutputOriginal) 134 }) 135 136 t.Run("cancel at approval step", func(t *testing.T) { 137 statePath := testStateFile(t, state) 138 139 ui := new(cli.MockUi) 140 view, _ := testView(t) 141 c := &StateReplaceProviderCommand{ 142 StateMeta{ 143 Meta: Meta{ 144 Ui: ui, 145 View: view, 146 }, 147 }, 148 } 149 150 inputBuf := &bytes.Buffer{} 151 ui.InputReader = inputBuf 152 inputBuf.WriteString("no\n") 153 154 args := []string{ 155 "-state", statePath, 156 "hashicorp/aws", 157 "acmecorp/aws", 158 } 159 if code := c.Run(args); code != 0 { 160 t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String()) 161 } 162 163 testStateOutput(t, statePath, testStateReplaceProviderOutputOriginal) 164 165 backups := testStateBackups(t, filepath.Dir(statePath)) 166 if len(backups) != 0 { 167 t.Fatalf("unexpected backups: %#v", backups) 168 } 169 }) 170 171 t.Run("no matching provider found", func(t *testing.T) { 172 statePath := testStateFile(t, state) 173 174 ui := new(cli.MockUi) 175 view, _ := testView(t) 176 c := &StateReplaceProviderCommand{ 177 StateMeta{ 178 Meta: Meta{ 179 Ui: ui, 180 View: view, 181 }, 182 }, 183 } 184 185 args := []string{ 186 "-state", statePath, 187 "hashicorp/google", 188 "acmecorp/google", 189 } 190 if code := c.Run(args); code != 0 { 191 t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String()) 192 } 193 194 testStateOutput(t, statePath, testStateReplaceProviderOutputOriginal) 195 196 backups := testStateBackups(t, filepath.Dir(statePath)) 197 if len(backups) != 0 { 198 t.Fatalf("unexpected backups: %#v", backups) 199 } 200 }) 201 202 t.Run("invalid flags", func(t *testing.T) { 203 ui := new(cli.MockUi) 204 view, _ := testView(t) 205 c := &StateReplaceProviderCommand{ 206 StateMeta{ 207 Meta: Meta{ 208 Ui: ui, 209 View: view, 210 }, 211 }, 212 } 213 214 args := []string{ 215 "-invalid", 216 "hashicorp/google", 217 "acmecorp/google", 218 } 219 if code := c.Run(args); code == 0 { 220 t.Fatalf("successful exit; want error") 221 } 222 223 if got, want := ui.ErrorWriter.String(), "Error parsing command-line flags"; !strings.Contains(got, want) { 224 t.Fatalf("missing expected error message\nwant: %s\nfull output:\n%s", want, got) 225 } 226 }) 227 228 t.Run("wrong number of arguments", func(t *testing.T) { 229 ui := new(cli.MockUi) 230 view, _ := testView(t) 231 c := &StateReplaceProviderCommand{ 232 StateMeta{ 233 Meta: Meta{ 234 Ui: ui, 235 View: view, 236 }, 237 }, 238 } 239 240 args := []string{"a", "b", "c", "d"} 241 if code := c.Run(args); code == 0 { 242 t.Fatalf("successful exit; want error") 243 } 244 245 if got, want := ui.ErrorWriter.String(), "Exactly two arguments expected"; !strings.Contains(got, want) { 246 t.Fatalf("missing expected error message\nwant: %s\nfull output:\n%s", want, got) 247 } 248 }) 249 250 t.Run("invalid provider strings", func(t *testing.T) { 251 ui := new(cli.MockUi) 252 view, _ := testView(t) 253 c := &StateReplaceProviderCommand{ 254 StateMeta{ 255 Meta: Meta{ 256 Ui: ui, 257 View: view, 258 }, 259 }, 260 } 261 262 args := []string{ 263 "hashicorp/google_cloud", 264 "-/-/google", 265 } 266 if code := c.Run(args); code == 0 { 267 t.Fatalf("successful exit; want error") 268 } 269 270 got := ui.ErrorWriter.String() 271 msgs := []string{ 272 `Invalid "from" provider "hashicorp/google_cloud"`, 273 "Invalid provider type", 274 `Invalid "to" provider "-/-/google"`, 275 "Invalid provider source hostname", 276 } 277 for _, msg := range msgs { 278 if !strings.Contains(got, msg) { 279 t.Errorf("missing expected error message\nwant: %s\nfull output:\n%s", msg, got) 280 } 281 } 282 }) 283 } 284 285 func TestStateReplaceProvider_docs(t *testing.T) { 286 c := &StateReplaceProviderCommand{} 287 288 if got, want := c.Help(), "Usage: terraform [global options] state replace-provider"; !strings.Contains(got, want) { 289 t.Fatalf("unexpected help text\nwant: %s\nfull output:\n%s", want, got) 290 } 291 292 if got, want := c.Synopsis(), "Replace provider in the state"; got != want { 293 t.Fatalf("unexpected synopsis\nwant: %s\nfull output:\n%s", want, got) 294 } 295 } 296 297 const testStateReplaceProviderOutputOriginal = ` 298 aws_instance.alpha: 299 ID = alpha 300 provider = provider["registry.terraform.io/hashicorp/aws"] 301 bar = value 302 foo = value 303 aws_instance.beta: 304 ID = beta 305 provider = provider["registry.terraform.io/hashicorp/aws"] 306 bar = value 307 foo = value 308 azurerm_virtual_machine.gamma: 309 ID = gamma 310 provider = provider["registry.terraform.io/-/azurerm"] 311 baz = value 312 ` 313 314 const testStateReplaceProviderOutput = ` 315 aws_instance.alpha: 316 ID = alpha 317 provider = provider["registry.terraform.io/acmecorp/aws"] 318 bar = value 319 foo = value 320 aws_instance.beta: 321 ID = beta 322 provider = provider["registry.terraform.io/acmecorp/aws"] 323 bar = value 324 foo = value 325 azurerm_virtual_machine.gamma: 326 ID = gamma 327 provider = provider["registry.terraform.io/-/azurerm"] 328 baz = value 329 `