github.com/nevins-b/terraform@v0.3.8-0.20170215184714-bbae22007d5a/command/plan_test.go (about) 1 package command 2 3 import ( 4 "bytes" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "reflect" 9 "strings" 10 "testing" 11 12 "github.com/hashicorp/terraform/helper/copy" 13 "github.com/hashicorp/terraform/terraform" 14 "github.com/mitchellh/cli" 15 ) 16 17 func TestPlan(t *testing.T) { 18 cwd, err := os.Getwd() 19 if err != nil { 20 t.Fatalf("err: %s", err) 21 } 22 if err := os.Chdir(testFixturePath("plan")); err != nil { 23 t.Fatalf("err: %s", err) 24 } 25 defer os.Chdir(cwd) 26 27 p := testProvider() 28 ui := new(cli.MockUi) 29 c := &PlanCommand{ 30 Meta: Meta{ 31 ContextOpts: testCtxConfig(p), 32 Ui: ui, 33 }, 34 } 35 36 args := []string{} 37 if code := c.Run(args); code != 0 { 38 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 39 } 40 } 41 42 func TestPlan_lockedState(t *testing.T) { 43 cwd, err := os.Getwd() 44 if err != nil { 45 t.Fatalf("err: %s", err) 46 } 47 48 testPath := testFixturePath("plan") 49 unlock, err := testLockState("./testdata", filepath.Join(testPath, DefaultStateFilename)) 50 if err != nil { 51 t.Fatal(err) 52 } 53 defer unlock() 54 55 if err := os.Chdir(testPath); err != nil { 56 t.Fatalf("err: %s", err) 57 } 58 defer os.Chdir(cwd) 59 60 p := testProvider() 61 ui := new(cli.MockUi) 62 c := &PlanCommand{ 63 Meta: Meta{ 64 ContextOpts: testCtxConfig(p), 65 Ui: ui, 66 }, 67 } 68 69 args := []string{} 70 if code := c.Run(args); code == 0 { 71 t.Fatal("expected error") 72 } 73 74 output := ui.ErrorWriter.String() 75 if !strings.Contains(output, "locked") { 76 t.Fatal("command output does not look like a lock error:", output) 77 } 78 } 79 80 func TestPlan_plan(t *testing.T) { 81 tmp, cwd := testCwd(t) 82 defer testFixCwd(t, tmp, cwd) 83 84 planPath := testPlanFile(t, &terraform.Plan{ 85 Module: testModule(t, "apply"), 86 }) 87 88 p := testProvider() 89 ui := new(cli.MockUi) 90 c := &PlanCommand{ 91 Meta: Meta{ 92 ContextOpts: testCtxConfig(p), 93 Ui: ui, 94 }, 95 } 96 97 args := []string{planPath} 98 if code := c.Run(args); code != 0 { 99 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 100 } 101 102 if p.RefreshCalled { 103 t.Fatal("refresh should not be called") 104 } 105 } 106 107 func TestPlan_destroy(t *testing.T) { 108 originalState := &terraform.State{ 109 Modules: []*terraform.ModuleState{ 110 &terraform.ModuleState{ 111 Path: []string{"root"}, 112 Resources: map[string]*terraform.ResourceState{ 113 "test_instance.foo": &terraform.ResourceState{ 114 Type: "test_instance", 115 Primary: &terraform.InstanceState{ 116 ID: "bar", 117 }, 118 }, 119 }, 120 }, 121 }, 122 } 123 124 outPath := testTempFile(t) 125 statePath := testStateFile(t, originalState) 126 127 p := testProvider() 128 ui := new(cli.MockUi) 129 c := &PlanCommand{ 130 Meta: Meta{ 131 ContextOpts: testCtxConfig(p), 132 Ui: ui, 133 }, 134 } 135 136 args := []string{ 137 "-destroy", 138 "-out", outPath, 139 "-state", statePath, 140 testFixturePath("plan"), 141 } 142 if code := c.Run(args); code != 0 { 143 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 144 } 145 146 if !p.RefreshCalled { 147 t.Fatal("refresh should be called") 148 } 149 150 plan := testReadPlan(t, outPath) 151 for _, m := range plan.Diff.Modules { 152 for _, r := range m.Resources { 153 if !r.Destroy { 154 t.Fatalf("bad: %#v", r) 155 } 156 } 157 } 158 } 159 160 func TestPlan_noState(t *testing.T) { 161 tmp, cwd := testCwd(t) 162 defer testFixCwd(t, tmp, cwd) 163 164 p := testProvider() 165 ui := new(cli.MockUi) 166 c := &PlanCommand{ 167 Meta: Meta{ 168 ContextOpts: testCtxConfig(p), 169 Ui: ui, 170 }, 171 } 172 173 args := []string{ 174 testFixturePath("plan"), 175 } 176 if code := c.Run(args); code != 0 { 177 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 178 } 179 180 // Verify that refresh was called 181 if p.RefreshCalled { 182 t.Fatal("refresh should not be called") 183 } 184 185 // Verify that the provider was called with the existing state 186 actual := strings.TrimSpace(p.DiffState.String()) 187 expected := strings.TrimSpace(testPlanNoStateStr) 188 if actual != expected { 189 t.Fatalf("bad:\n\n%s", actual) 190 } 191 } 192 193 func TestPlan_outPath(t *testing.T) { 194 tmp, cwd := testCwd(t) 195 defer testFixCwd(t, tmp, cwd) 196 197 tf, err := ioutil.TempFile("", "tf") 198 if err != nil { 199 t.Fatalf("err: %s", err) 200 } 201 outPath := tf.Name() 202 os.Remove(tf.Name()) 203 204 p := testProvider() 205 ui := new(cli.MockUi) 206 c := &PlanCommand{ 207 Meta: Meta{ 208 ContextOpts: testCtxConfig(p), 209 Ui: ui, 210 }, 211 } 212 213 p.DiffReturn = &terraform.InstanceDiff{ 214 Destroy: true, 215 } 216 217 args := []string{ 218 "-out", outPath, 219 testFixturePath("plan"), 220 } 221 if code := c.Run(args); code != 0 { 222 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 223 } 224 225 f, err := os.Open(outPath) 226 if err != nil { 227 t.Fatalf("err: %s", err) 228 } 229 defer f.Close() 230 231 if _, err := terraform.ReadPlan(f); err != nil { 232 t.Fatalf("err: %s", err) 233 } 234 } 235 236 func TestPlan_outPathNoChange(t *testing.T) { 237 originalState := &terraform.State{ 238 Modules: []*terraform.ModuleState{ 239 &terraform.ModuleState{ 240 Path: []string{"root"}, 241 Resources: map[string]*terraform.ResourceState{ 242 "test_instance.foo": &terraform.ResourceState{ 243 Type: "test_instance", 244 Primary: &terraform.InstanceState{ 245 ID: "bar", 246 }, 247 }, 248 }, 249 }, 250 }, 251 } 252 statePath := testStateFile(t, originalState) 253 254 tf, err := ioutil.TempFile("", "tf") 255 if err != nil { 256 t.Fatalf("err: %s", err) 257 } 258 outPath := tf.Name() 259 os.Remove(tf.Name()) 260 261 p := testProvider() 262 ui := new(cli.MockUi) 263 c := &PlanCommand{ 264 Meta: Meta{ 265 ContextOpts: testCtxConfig(p), 266 Ui: ui, 267 }, 268 } 269 270 args := []string{ 271 "-out", outPath, 272 "-state", statePath, 273 testFixturePath("plan"), 274 } 275 if code := c.Run(args); code != 0 { 276 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 277 } 278 279 plan := testReadPlan(t, outPath) 280 if !plan.Diff.Empty() { 281 t.Fatalf("Expected empty plan to be written to plan file, got: %s", plan) 282 } 283 } 284 285 // When using "-out" with a backend, the plan should encode the backend config 286 func TestPlan_outBackend(t *testing.T) { 287 // Create a temporary working directory that is empty 288 td := tempDir(t) 289 copy.CopyDir(testFixturePath("plan-out-backend"), td) 290 defer os.RemoveAll(td) 291 defer testChdir(t, td)() 292 293 // Our state 294 originalState := &terraform.State{ 295 Modules: []*terraform.ModuleState{ 296 &terraform.ModuleState{ 297 Path: []string{"root"}, 298 Resources: map[string]*terraform.ResourceState{ 299 "test_instance.foo": &terraform.ResourceState{ 300 Type: "test_instance", 301 Primary: &terraform.InstanceState{ 302 ID: "bar", 303 }, 304 }, 305 }, 306 }, 307 }, 308 } 309 originalState.Init() 310 311 // Setup our backend state 312 dataState, srv := testBackendState(t, originalState, 200) 313 defer srv.Close() 314 testStateFileRemote(t, dataState) 315 316 outPath := "foo" 317 p := testProvider() 318 ui := new(cli.MockUi) 319 c := &PlanCommand{ 320 Meta: Meta{ 321 ContextOpts: testCtxConfig(p), 322 Ui: ui, 323 }, 324 } 325 326 args := []string{ 327 "-out", outPath, 328 } 329 if code := c.Run(args); code != 0 { 330 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 331 } 332 333 plan := testReadPlan(t, outPath) 334 if !plan.Diff.Empty() { 335 t.Fatalf("Expected empty plan to be written to plan file, got: %s", plan) 336 } 337 338 if plan.Backend.Empty() { 339 t.Fatal("should have backend info") 340 } 341 if !reflect.DeepEqual(plan.Backend, dataState.Backend) { 342 t.Fatalf("bad: %#v", plan.Backend) 343 } 344 } 345 346 // When using "-out" with a legacy remote state, the plan should encode 347 // the backend config 348 func TestPlan_outBackendLegacy(t *testing.T) { 349 // Create a temporary working directory that is empty 350 td := tempDir(t) 351 copy.CopyDir(testFixturePath("plan-out-backend-legacy"), td) 352 defer os.RemoveAll(td) 353 defer testChdir(t, td)() 354 355 // Our state 356 originalState := &terraform.State{ 357 Modules: []*terraform.ModuleState{ 358 &terraform.ModuleState{ 359 Path: []string{"root"}, 360 Resources: map[string]*terraform.ResourceState{ 361 "test_instance.foo": &terraform.ResourceState{ 362 Type: "test_instance", 363 Primary: &terraform.InstanceState{ 364 ID: "bar", 365 }, 366 }, 367 }, 368 }, 369 }, 370 } 371 originalState.Init() 372 373 // Setup our legacy state 374 remoteState, srv := testRemoteState(t, originalState, 200) 375 defer srv.Close() 376 dataState := terraform.NewState() 377 dataState.Remote = remoteState 378 testStateFileRemote(t, dataState) 379 380 outPath := "foo" 381 p := testProvider() 382 ui := new(cli.MockUi) 383 c := &PlanCommand{ 384 Meta: Meta{ 385 ContextOpts: testCtxConfig(p), 386 Ui: ui, 387 }, 388 } 389 390 args := []string{ 391 "-out", outPath, 392 } 393 if code := c.Run(args); code != 0 { 394 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 395 } 396 397 plan := testReadPlan(t, outPath) 398 if !plan.Diff.Empty() { 399 t.Fatalf("Expected empty plan to be written to plan file, got: %s", plan) 400 } 401 402 if plan.State.Remote.Empty() { 403 t.Fatal("should have remote info") 404 } 405 } 406 407 func TestPlan_refresh(t *testing.T) { 408 tmp, cwd := testCwd(t) 409 defer testFixCwd(t, tmp, cwd) 410 411 p := testProvider() 412 ui := new(cli.MockUi) 413 c := &PlanCommand{ 414 Meta: Meta{ 415 ContextOpts: testCtxConfig(p), 416 Ui: ui, 417 }, 418 } 419 420 args := []string{ 421 "-refresh=false", 422 testFixturePath("plan"), 423 } 424 if code := c.Run(args); code != 0 { 425 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 426 } 427 428 if p.RefreshCalled { 429 t.Fatal("refresh should not be called") 430 } 431 } 432 433 func TestPlan_state(t *testing.T) { 434 // Write out some prior state 435 tf, err := ioutil.TempFile("", "tf") 436 if err != nil { 437 t.Fatalf("err: %s", err) 438 } 439 statePath := tf.Name() 440 defer os.Remove(tf.Name()) 441 442 originalState := testState() 443 err = terraform.WriteState(originalState, tf) 444 tf.Close() 445 if err != nil { 446 t.Fatalf("err: %s", err) 447 } 448 449 p := testProvider() 450 ui := new(cli.MockUi) 451 c := &PlanCommand{ 452 Meta: Meta{ 453 ContextOpts: testCtxConfig(p), 454 Ui: ui, 455 }, 456 } 457 458 args := []string{ 459 "-state", statePath, 460 testFixturePath("plan"), 461 } 462 if code := c.Run(args); code != 0 { 463 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 464 } 465 466 // Verify that the provider was called with the existing state 467 actual := strings.TrimSpace(p.DiffState.String()) 468 expected := strings.TrimSpace(testPlanStateStr) 469 if actual != expected { 470 t.Fatalf("bad:\n\n%s", actual) 471 } 472 } 473 474 func TestPlan_stateDefault(t *testing.T) { 475 originalState := testState() 476 477 // Write the state file in a temporary directory with the 478 // default filename. 479 td, err := ioutil.TempDir("", "tf") 480 if err != nil { 481 t.Fatalf("err: %s", err) 482 } 483 statePath := filepath.Join(td, DefaultStateFilename) 484 485 f, err := os.Create(statePath) 486 if err != nil { 487 t.Fatalf("err: %s", err) 488 } 489 err = terraform.WriteState(originalState, f) 490 f.Close() 491 if err != nil { 492 t.Fatalf("err: %s", err) 493 } 494 495 // Change to that directory 496 cwd, err := os.Getwd() 497 if err != nil { 498 t.Fatalf("err: %s", err) 499 } 500 if err := os.Chdir(filepath.Dir(statePath)); err != nil { 501 t.Fatalf("err: %s", err) 502 } 503 defer os.Chdir(cwd) 504 505 p := testProvider() 506 ui := new(cli.MockUi) 507 c := &PlanCommand{ 508 Meta: Meta{ 509 ContextOpts: testCtxConfig(p), 510 Ui: ui, 511 }, 512 } 513 514 args := []string{ 515 testFixturePath("plan"), 516 } 517 if code := c.Run(args); code != 0 { 518 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 519 } 520 521 // Verify that the provider was called with the existing state 522 actual := strings.TrimSpace(p.DiffState.String()) 523 expected := strings.TrimSpace(testPlanStateDefaultStr) 524 if actual != expected { 525 t.Fatalf("bad:\n\n%s", actual) 526 } 527 } 528 529 func TestPlan_stateFuture(t *testing.T) { 530 originalState := testState() 531 originalState.TFVersion = "99.99.99" 532 statePath := testStateFile(t, originalState) 533 534 p := testProvider() 535 ui := new(cli.MockUi) 536 c := &PlanCommand{ 537 Meta: Meta{ 538 ContextOpts: testCtxConfig(p), 539 Ui: ui, 540 }, 541 } 542 543 args := []string{ 544 "-state", statePath, 545 testFixturePath("plan"), 546 } 547 if code := c.Run(args); code == 0 { 548 t.Fatal("should fail") 549 } 550 551 f, err := os.Open(statePath) 552 if err != nil { 553 t.Fatalf("err: %s", err) 554 } 555 556 newState, err := terraform.ReadState(f) 557 f.Close() 558 if err != nil { 559 t.Fatalf("err: %s", err) 560 } 561 562 if !newState.Equal(originalState) { 563 t.Fatalf("bad: %#v", newState) 564 } 565 if newState.TFVersion != originalState.TFVersion { 566 t.Fatalf("bad: %#v", newState) 567 } 568 } 569 570 func TestPlan_statePast(t *testing.T) { 571 originalState := testState() 572 originalState.TFVersion = "0.1.0" 573 statePath := testStateFile(t, originalState) 574 575 p := testProvider() 576 ui := new(cli.MockUi) 577 c := &PlanCommand{ 578 Meta: Meta{ 579 ContextOpts: testCtxConfig(p), 580 Ui: ui, 581 }, 582 } 583 584 args := []string{ 585 "-state", statePath, 586 testFixturePath("plan"), 587 } 588 if code := c.Run(args); code != 0 { 589 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 590 } 591 } 592 593 func TestPlan_validate(t *testing.T) { 594 // This is triggered by not asking for input so we have to set this to false 595 test = false 596 defer func() { test = true }() 597 598 cwd, err := os.Getwd() 599 if err != nil { 600 t.Fatalf("err: %s", err) 601 } 602 if err := os.Chdir(testFixturePath("plan-invalid")); err != nil { 603 t.Fatalf("err: %s", err) 604 } 605 defer os.Chdir(cwd) 606 607 p := testProvider() 608 ui := new(cli.MockUi) 609 c := &PlanCommand{ 610 Meta: Meta{ 611 ContextOpts: testCtxConfig(p), 612 Ui: ui, 613 }, 614 } 615 616 args := []string{} 617 if code := c.Run(args); code != 1 { 618 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 619 } 620 621 actual := ui.ErrorWriter.String() 622 if !strings.Contains(actual, "cannot be computed") { 623 t.Fatalf("bad: %s", actual) 624 } 625 } 626 627 func TestPlan_vars(t *testing.T) { 628 tmp, cwd := testCwd(t) 629 defer testFixCwd(t, tmp, cwd) 630 631 p := testProvider() 632 ui := new(cli.MockUi) 633 c := &PlanCommand{ 634 Meta: Meta{ 635 ContextOpts: testCtxConfig(p), 636 Ui: ui, 637 }, 638 } 639 640 actual := "" 641 p.DiffFn = func( 642 info *terraform.InstanceInfo, 643 s *terraform.InstanceState, 644 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 645 if v, ok := c.Config["value"]; ok { 646 actual = v.(string) 647 } 648 649 return nil, nil 650 } 651 652 args := []string{ 653 "-var", "foo=bar", 654 testFixturePath("plan-vars"), 655 } 656 if code := c.Run(args); code != 0 { 657 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 658 } 659 660 if actual != "bar" { 661 t.Fatal("didn't work") 662 } 663 } 664 665 func TestPlan_varsUnset(t *testing.T) { 666 tmp, cwd := testCwd(t) 667 defer testFixCwd(t, tmp, cwd) 668 669 // Disable test mode so input would be asked 670 test = false 671 defer func() { test = true }() 672 673 defaultInputReader = bytes.NewBufferString("bar\n") 674 675 p := testProvider() 676 ui := new(cli.MockUi) 677 c := &PlanCommand{ 678 Meta: Meta{ 679 ContextOpts: testCtxConfig(p), 680 Ui: ui, 681 }, 682 } 683 684 args := []string{ 685 testFixturePath("plan-vars"), 686 } 687 if code := c.Run(args); code != 0 { 688 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 689 } 690 } 691 692 func TestPlan_varFile(t *testing.T) { 693 tmp, cwd := testCwd(t) 694 defer testFixCwd(t, tmp, cwd) 695 696 varFilePath := testTempFile(t) 697 if err := ioutil.WriteFile(varFilePath, []byte(planVarFile), 0644); err != nil { 698 t.Fatalf("err: %s", err) 699 } 700 701 p := testProvider() 702 ui := new(cli.MockUi) 703 c := &PlanCommand{ 704 Meta: Meta{ 705 ContextOpts: testCtxConfig(p), 706 Ui: ui, 707 }, 708 } 709 710 actual := "" 711 p.DiffFn = func( 712 info *terraform.InstanceInfo, 713 s *terraform.InstanceState, 714 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 715 if v, ok := c.Config["value"]; ok { 716 actual = v.(string) 717 } 718 719 return nil, nil 720 } 721 722 args := []string{ 723 "-var-file", varFilePath, 724 testFixturePath("plan-vars"), 725 } 726 if code := c.Run(args); code != 0 { 727 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 728 } 729 730 if actual != "bar" { 731 t.Fatal("didn't work") 732 } 733 } 734 735 func TestPlan_varFileDefault(t *testing.T) { 736 varFileDir := testTempDir(t) 737 varFilePath := filepath.Join(varFileDir, "terraform.tfvars") 738 if err := ioutil.WriteFile(varFilePath, []byte(planVarFile), 0644); err != nil { 739 t.Fatalf("err: %s", err) 740 } 741 742 cwd, err := os.Getwd() 743 if err != nil { 744 t.Fatalf("err: %s", err) 745 } 746 if err := os.Chdir(varFileDir); err != nil { 747 t.Fatalf("err: %s", err) 748 } 749 defer os.Chdir(cwd) 750 751 p := testProvider() 752 ui := new(cli.MockUi) 753 c := &PlanCommand{ 754 Meta: Meta{ 755 ContextOpts: testCtxConfig(p), 756 Ui: ui, 757 }, 758 } 759 760 actual := "" 761 p.DiffFn = func( 762 info *terraform.InstanceInfo, 763 s *terraform.InstanceState, 764 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 765 if v, ok := c.Config["value"]; ok { 766 actual = v.(string) 767 } 768 769 return nil, nil 770 } 771 772 args := []string{ 773 testFixturePath("plan-vars"), 774 } 775 if code := c.Run(args); code != 0 { 776 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 777 } 778 779 if actual != "bar" { 780 t.Fatal("didn't work") 781 } 782 } 783 784 func TestPlan_detailedExitcode(t *testing.T) { 785 cwd, err := os.Getwd() 786 if err != nil { 787 t.Fatalf("err: %s", err) 788 } 789 if err := os.Chdir(testFixturePath("plan")); err != nil { 790 t.Fatalf("err: %s", err) 791 } 792 defer os.Chdir(cwd) 793 794 p := testProvider() 795 ui := new(cli.MockUi) 796 c := &PlanCommand{ 797 Meta: Meta{ 798 ContextOpts: testCtxConfig(p), 799 Ui: ui, 800 }, 801 } 802 803 args := []string{"-detailed-exitcode"} 804 if code := c.Run(args); code != 2 { 805 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 806 } 807 } 808 809 func TestPlan_detailedExitcode_emptyDiff(t *testing.T) { 810 cwd, err := os.Getwd() 811 if err != nil { 812 t.Fatalf("err: %s", err) 813 } 814 if err := os.Chdir(testFixturePath("plan-emptydiff")); err != nil { 815 t.Fatalf("err: %s", err) 816 } 817 defer os.Chdir(cwd) 818 819 p := testProvider() 820 ui := new(cli.MockUi) 821 c := &PlanCommand{ 822 Meta: Meta{ 823 ContextOpts: testCtxConfig(p), 824 Ui: ui, 825 }, 826 } 827 828 args := []string{"-detailed-exitcode"} 829 if code := c.Run(args); code != 0 { 830 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 831 } 832 } 833 834 const planVarFile = ` 835 foo = "bar" 836 ` 837 838 const testPlanNoStateStr = ` 839 <not created> 840 ` 841 842 const testPlanStateStr = ` 843 ID = bar 844 Tainted = false 845 ` 846 847 const testPlanStateDefaultStr = ` 848 ID = bar 849 Tainted = false 850 `