1package acl 2 3import ( 4 "testing" 5) 6 7func TestRootACL(t *testing.T) { 8 if RootACL("allow") != AllowAll() { 9 t.Fatalf("Bad root") 10 } 11 if RootACL("deny") != DenyAll() { 12 t.Fatalf("Bad root") 13 } 14 if RootACL("manage") != ManageAll() { 15 t.Fatalf("Bad root") 16 } 17 if RootACL("foo") != nil { 18 t.Fatalf("bad root") 19 } 20} 21 22func TestStaticACL(t *testing.T) { 23 all := AllowAll() 24 if _, ok := all.(*StaticACL); !ok { 25 t.Fatalf("expected static") 26 } 27 28 none := DenyAll() 29 if _, ok := none.(*StaticACL); !ok { 30 t.Fatalf("expected static") 31 } 32 33 manage := ManageAll() 34 if _, ok := manage.(*StaticACL); !ok { 35 t.Fatalf("expected static") 36 } 37 38 if all.ACLList() { 39 t.Fatalf("should not allow") 40 } 41 if all.ACLModify() { 42 t.Fatalf("should not allow") 43 } 44 if !all.AgentRead("foobar") { 45 t.Fatalf("should allow") 46 } 47 if !all.AgentWrite("foobar") { 48 t.Fatalf("should allow") 49 } 50 if !all.EventRead("foobar") { 51 t.Fatalf("should allow") 52 } 53 if !all.EventWrite("foobar") { 54 t.Fatalf("should allow") 55 } 56 if !all.KeyRead("foobar") { 57 t.Fatalf("should allow") 58 } 59 if !all.KeyWrite("foobar", nil) { 60 t.Fatalf("should allow") 61 } 62 if !all.KeyringRead() { 63 t.Fatalf("should allow") 64 } 65 if !all.KeyringWrite() { 66 t.Fatalf("should allow") 67 } 68 if !all.NodeRead("foobar") { 69 t.Fatalf("should allow") 70 } 71 if !all.NodeWrite("foobar", nil) { 72 t.Fatalf("should allow") 73 } 74 if !all.OperatorRead() { 75 t.Fatalf("should allow") 76 } 77 if !all.OperatorWrite() { 78 t.Fatalf("should allow") 79 } 80 if !all.PreparedQueryRead("foobar") { 81 t.Fatalf("should allow") 82 } 83 if !all.PreparedQueryWrite("foobar") { 84 t.Fatalf("should allow") 85 } 86 if !all.ServiceRead("foobar") { 87 t.Fatalf("should allow") 88 } 89 if !all.ServiceWrite("foobar", nil) { 90 t.Fatalf("should allow") 91 } 92 if !all.SessionRead("foobar") { 93 t.Fatalf("should allow") 94 } 95 if !all.SessionWrite("foobar") { 96 t.Fatalf("should allow") 97 } 98 if all.Snapshot() { 99 t.Fatalf("should not allow") 100 } 101 102 if none.ACLList() { 103 t.Fatalf("should not allow") 104 } 105 if none.ACLModify() { 106 t.Fatalf("should not allow") 107 } 108 if none.AgentRead("foobar") { 109 t.Fatalf("should not allow") 110 } 111 if none.AgentWrite("foobar") { 112 t.Fatalf("should not allow") 113 } 114 if none.EventRead("foobar") { 115 t.Fatalf("should not allow") 116 } 117 if none.EventRead("") { 118 t.Fatalf("should not allow") 119 } 120 if none.EventWrite("foobar") { 121 t.Fatalf("should not allow") 122 } 123 if none.EventWrite("") { 124 t.Fatalf("should not allow") 125 } 126 if none.KeyRead("foobar") { 127 t.Fatalf("should not allow") 128 } 129 if none.KeyWrite("foobar", nil) { 130 t.Fatalf("should not allow") 131 } 132 if none.KeyringRead() { 133 t.Fatalf("should now allow") 134 } 135 if none.KeyringWrite() { 136 t.Fatalf("should not allow") 137 } 138 if none.NodeRead("foobar") { 139 t.Fatalf("should not allow") 140 } 141 if none.NodeWrite("foobar", nil) { 142 t.Fatalf("should not allow") 143 } 144 if none.OperatorRead() { 145 t.Fatalf("should now allow") 146 } 147 if none.OperatorWrite() { 148 t.Fatalf("should not allow") 149 } 150 if none.PreparedQueryRead("foobar") { 151 t.Fatalf("should not allow") 152 } 153 if none.PreparedQueryWrite("foobar") { 154 t.Fatalf("should not allow") 155 } 156 if none.ServiceRead("foobar") { 157 t.Fatalf("should not allow") 158 } 159 if none.ServiceWrite("foobar", nil) { 160 t.Fatalf("should not allow") 161 } 162 if none.SessionRead("foobar") { 163 t.Fatalf("should not allow") 164 } 165 if none.SessionWrite("foobar") { 166 t.Fatalf("should not allow") 167 } 168 if none.Snapshot() { 169 t.Fatalf("should not allow") 170 } 171 172 if !manage.ACLList() { 173 t.Fatalf("should allow") 174 } 175 if !manage.ACLModify() { 176 t.Fatalf("should allow") 177 } 178 if !manage.AgentRead("foobar") { 179 t.Fatalf("should allow") 180 } 181 if !manage.AgentWrite("foobar") { 182 t.Fatalf("should allow") 183 } 184 if !manage.EventRead("foobar") { 185 t.Fatalf("should allow") 186 } 187 if !manage.EventWrite("foobar") { 188 t.Fatalf("should allow") 189 } 190 if !manage.KeyRead("foobar") { 191 t.Fatalf("should allow") 192 } 193 if !manage.KeyWrite("foobar", nil) { 194 t.Fatalf("should allow") 195 } 196 if !manage.KeyringRead() { 197 t.Fatalf("should allow") 198 } 199 if !manage.KeyringWrite() { 200 t.Fatalf("should allow") 201 } 202 if !manage.NodeRead("foobar") { 203 t.Fatalf("should allow") 204 } 205 if !manage.NodeWrite("foobar", nil) { 206 t.Fatalf("should allow") 207 } 208 if !manage.OperatorRead() { 209 t.Fatalf("should allow") 210 } 211 if !manage.OperatorWrite() { 212 t.Fatalf("should allow") 213 } 214 if !manage.PreparedQueryRead("foobar") { 215 t.Fatalf("should allow") 216 } 217 if !manage.PreparedQueryWrite("foobar") { 218 t.Fatalf("should allow") 219 } 220 if !manage.ServiceRead("foobar") { 221 t.Fatalf("should allow") 222 } 223 if !manage.ServiceWrite("foobar", nil) { 224 t.Fatalf("should allow") 225 } 226 if !manage.SessionRead("foobar") { 227 t.Fatalf("should allow") 228 } 229 if !manage.SessionWrite("foobar") { 230 t.Fatalf("should allow") 231 } 232 if !manage.Snapshot() { 233 t.Fatalf("should allow") 234 } 235} 236 237func TestPolicyACL(t *testing.T) { 238 all := AllowAll() 239 policy := &Policy{ 240 Events: []*EventPolicy{ 241 &EventPolicy{ 242 Event: "", 243 Policy: PolicyRead, 244 }, 245 &EventPolicy{ 246 Event: "foo", 247 Policy: PolicyWrite, 248 }, 249 &EventPolicy{ 250 Event: "bar", 251 Policy: PolicyDeny, 252 }, 253 }, 254 Keys: []*KeyPolicy{ 255 &KeyPolicy{ 256 Prefix: "foo/", 257 Policy: PolicyWrite, 258 }, 259 &KeyPolicy{ 260 Prefix: "foo/priv/", 261 Policy: PolicyDeny, 262 }, 263 &KeyPolicy{ 264 Prefix: "bar/", 265 Policy: PolicyDeny, 266 }, 267 &KeyPolicy{ 268 Prefix: "zip/", 269 Policy: PolicyRead, 270 }, 271 &KeyPolicy{ 272 Prefix: "zap/", 273 Policy: PolicyList, 274 }, 275 }, 276 PreparedQueries: []*PreparedQueryPolicy{ 277 &PreparedQueryPolicy{ 278 Prefix: "", 279 Policy: PolicyRead, 280 }, 281 &PreparedQueryPolicy{ 282 Prefix: "foo", 283 Policy: PolicyWrite, 284 }, 285 &PreparedQueryPolicy{ 286 Prefix: "bar", 287 Policy: PolicyDeny, 288 }, 289 &PreparedQueryPolicy{ 290 Prefix: "zoo", 291 Policy: PolicyWrite, 292 }, 293 }, 294 Services: []*ServicePolicy{ 295 &ServicePolicy{ 296 Name: "", 297 Policy: PolicyWrite, 298 }, 299 &ServicePolicy{ 300 Name: "foo", 301 Policy: PolicyRead, 302 }, 303 &ServicePolicy{ 304 Name: "bar", 305 Policy: PolicyDeny, 306 }, 307 &ServicePolicy{ 308 Name: "barfoo", 309 Policy: PolicyWrite, 310 }, 311 }, 312 } 313 acl, err := New(all, policy, nil) 314 if err != nil { 315 t.Fatalf("err: %v", err) 316 } 317 318 type keycase struct { 319 inp string 320 read bool 321 write bool 322 writePrefix bool 323 list bool 324 } 325 cases := []keycase{ 326 {"other", true, true, true, true}, 327 {"foo/test", true, true, true, true}, 328 {"foo/priv/test", false, false, false, false}, 329 {"bar/any", false, false, false, false}, 330 {"zip/test", true, false, false, false}, 331 {"foo/", true, true, false, true}, 332 {"", true, true, false, true}, 333 {"zap/test", true, false, false, true}, 334 } 335 for _, c := range cases { 336 if c.read != acl.KeyRead(c.inp) { 337 t.Fatalf("Read fail: %#v", c) 338 } 339 if c.write != acl.KeyWrite(c.inp, nil) { 340 t.Fatalf("Write fail: %#v", c) 341 } 342 if c.writePrefix != acl.KeyWritePrefix(c.inp) { 343 t.Fatalf("Write prefix fail: %#v", c) 344 } 345 } 346 347 // Test the services 348 type servicecase struct { 349 inp string 350 read bool 351 write bool 352 } 353 scases := []servicecase{ 354 {"other", true, true}, 355 {"foo", true, false}, 356 {"bar", false, false}, 357 {"foobar", true, false}, 358 {"barfo", false, false}, 359 {"barfoo", true, true}, 360 {"barfoo2", true, true}, 361 } 362 for _, c := range scases { 363 if c.read != acl.ServiceRead(c.inp) { 364 t.Fatalf("Read fail: %#v", c) 365 } 366 if c.write != acl.ServiceWrite(c.inp, nil) { 367 t.Fatalf("Write fail: %#v", c) 368 } 369 } 370 371 // Test the events 372 type eventcase struct { 373 inp string 374 read bool 375 write bool 376 } 377 eventcases := []eventcase{ 378 {"foo", true, true}, 379 {"foobar", true, true}, 380 {"bar", false, false}, 381 {"barbaz", false, false}, 382 {"baz", true, false}, 383 } 384 for _, c := range eventcases { 385 if c.read != acl.EventRead(c.inp) { 386 t.Fatalf("Event fail: %#v", c) 387 } 388 if c.write != acl.EventWrite(c.inp) { 389 t.Fatalf("Event fail: %#v", c) 390 } 391 } 392 393 // Test prepared queries 394 type querycase struct { 395 inp string 396 read bool 397 write bool 398 } 399 querycases := []querycase{ 400 {"foo", true, true}, 401 {"foobar", true, true}, 402 {"bar", false, false}, 403 {"barbaz", false, false}, 404 {"baz", true, false}, 405 {"nope", true, false}, 406 {"zoo", true, true}, 407 {"zookeeper", true, true}, 408 } 409 for _, c := range querycases { 410 if c.read != acl.PreparedQueryRead(c.inp) { 411 t.Fatalf("Prepared query fail: %#v", c) 412 } 413 if c.write != acl.PreparedQueryWrite(c.inp) { 414 t.Fatalf("Prepared query fail: %#v", c) 415 } 416 } 417} 418 419func TestPolicyACL_Parent(t *testing.T) { 420 deny := DenyAll() 421 policyRoot := &Policy{ 422 Keys: []*KeyPolicy{ 423 &KeyPolicy{ 424 Prefix: "foo/", 425 Policy: PolicyWrite, 426 }, 427 &KeyPolicy{ 428 Prefix: "bar/", 429 Policy: PolicyRead, 430 }, 431 }, 432 PreparedQueries: []*PreparedQueryPolicy{ 433 &PreparedQueryPolicy{ 434 Prefix: "other", 435 Policy: PolicyWrite, 436 }, 437 &PreparedQueryPolicy{ 438 Prefix: "foo", 439 Policy: PolicyRead, 440 }, 441 }, 442 Services: []*ServicePolicy{ 443 &ServicePolicy{ 444 Name: "other", 445 Policy: PolicyWrite, 446 }, 447 &ServicePolicy{ 448 Name: "foo", 449 Policy: PolicyRead, 450 }, 451 }, 452 } 453 root, err := New(deny, policyRoot, nil) 454 if err != nil { 455 t.Fatalf("err: %v", err) 456 } 457 458 policy := &Policy{ 459 Keys: []*KeyPolicy{ 460 &KeyPolicy{ 461 Prefix: "foo/priv/", 462 Policy: PolicyRead, 463 }, 464 &KeyPolicy{ 465 Prefix: "bar/", 466 Policy: PolicyDeny, 467 }, 468 &KeyPolicy{ 469 Prefix: "zip/", 470 Policy: PolicyRead, 471 }, 472 }, 473 PreparedQueries: []*PreparedQueryPolicy{ 474 &PreparedQueryPolicy{ 475 Prefix: "bar", 476 Policy: PolicyDeny, 477 }, 478 }, 479 Services: []*ServicePolicy{ 480 &ServicePolicy{ 481 Name: "bar", 482 Policy: PolicyDeny, 483 }, 484 }, 485 } 486 acl, err := New(root, policy, nil) 487 if err != nil { 488 t.Fatalf("err: %v", err) 489 } 490 491 type keycase struct { 492 inp string 493 read bool 494 write bool 495 writePrefix bool 496 } 497 cases := []keycase{ 498 {"other", false, false, false}, 499 {"foo/test", true, true, true}, 500 {"foo/priv/test", true, false, false}, 501 {"bar/any", false, false, false}, 502 {"zip/test", true, false, false}, 503 } 504 for _, c := range cases { 505 if c.read != acl.KeyRead(c.inp) { 506 t.Fatalf("Read fail: %#v", c) 507 } 508 if c.write != acl.KeyWrite(c.inp, nil) { 509 t.Fatalf("Write fail: %#v", c) 510 } 511 if c.writePrefix != acl.KeyWritePrefix(c.inp) { 512 t.Fatalf("Write prefix fail: %#v", c) 513 } 514 } 515 516 // Test the services 517 type servicecase struct { 518 inp string 519 read bool 520 write bool 521 } 522 scases := []servicecase{ 523 {"fail", false, false}, 524 {"other", true, true}, 525 {"foo", true, false}, 526 {"bar", false, false}, 527 } 528 for _, c := range scases { 529 if c.read != acl.ServiceRead(c.inp) { 530 t.Fatalf("Read fail: %#v", c) 531 } 532 if c.write != acl.ServiceWrite(c.inp, nil) { 533 t.Fatalf("Write fail: %#v", c) 534 } 535 } 536 537 // Test prepared queries 538 type querycase struct { 539 inp string 540 read bool 541 write bool 542 } 543 querycases := []querycase{ 544 {"foo", true, false}, 545 {"foobar", true, false}, 546 {"bar", false, false}, 547 {"barbaz", false, false}, 548 {"baz", false, false}, 549 {"nope", false, false}, 550 } 551 for _, c := range querycases { 552 if c.read != acl.PreparedQueryRead(c.inp) { 553 t.Fatalf("Prepared query fail: %#v", c) 554 } 555 if c.write != acl.PreparedQueryWrite(c.inp) { 556 t.Fatalf("Prepared query fail: %#v", c) 557 } 558 } 559 560 // Check some management functions that chain up 561 if acl.ACLList() { 562 t.Fatalf("should not allow") 563 } 564 if acl.ACLModify() { 565 t.Fatalf("should not allow") 566 } 567 if acl.Snapshot() { 568 t.Fatalf("should not allow") 569 } 570} 571 572func TestPolicyACL_Agent(t *testing.T) { 573 deny := DenyAll() 574 policyRoot := &Policy{ 575 Agents: []*AgentPolicy{ 576 &AgentPolicy{ 577 Node: "root-nope", 578 Policy: PolicyDeny, 579 }, 580 &AgentPolicy{ 581 Node: "root-ro", 582 Policy: PolicyRead, 583 }, 584 &AgentPolicy{ 585 Node: "root-rw", 586 Policy: PolicyWrite, 587 }, 588 &AgentPolicy{ 589 Node: "override", 590 Policy: PolicyDeny, 591 }, 592 }, 593 } 594 root, err := New(deny, policyRoot, nil) 595 if err != nil { 596 t.Fatalf("err: %v", err) 597 } 598 599 policy := &Policy{ 600 Agents: []*AgentPolicy{ 601 &AgentPolicy{ 602 Node: "child-nope", 603 Policy: PolicyDeny, 604 }, 605 &AgentPolicy{ 606 Node: "child-ro", 607 Policy: PolicyRead, 608 }, 609 &AgentPolicy{ 610 Node: "child-rw", 611 Policy: PolicyWrite, 612 }, 613 &AgentPolicy{ 614 Node: "override", 615 Policy: PolicyWrite, 616 }, 617 }, 618 } 619 acl, err := New(root, policy, nil) 620 if err != nil { 621 t.Fatalf("err: %v", err) 622 } 623 624 type agentcase struct { 625 inp string 626 read bool 627 write bool 628 } 629 cases := []agentcase{ 630 {"nope", false, false}, 631 {"root-nope", false, false}, 632 {"root-ro", true, false}, 633 {"root-rw", true, true}, 634 {"root-nope-prefix", false, false}, 635 {"root-ro-prefix", true, false}, 636 {"root-rw-prefix", true, true}, 637 {"child-nope", false, false}, 638 {"child-ro", true, false}, 639 {"child-rw", true, true}, 640 {"child-nope-prefix", false, false}, 641 {"child-ro-prefix", true, false}, 642 {"child-rw-prefix", true, true}, 643 {"override", true, true}, 644 } 645 for _, c := range cases { 646 if c.read != acl.AgentRead(c.inp) { 647 t.Fatalf("Read fail: %#v", c) 648 } 649 if c.write != acl.AgentWrite(c.inp) { 650 t.Fatalf("Write fail: %#v", c) 651 } 652 } 653} 654 655func TestPolicyACL_Keyring(t *testing.T) { 656 type keyringcase struct { 657 inp string 658 read bool 659 write bool 660 } 661 cases := []keyringcase{ 662 {"", false, false}, 663 {PolicyRead, true, false}, 664 {PolicyWrite, true, true}, 665 {PolicyDeny, false, false}, 666 } 667 for _, c := range cases { 668 acl, err := New(DenyAll(), &Policy{Keyring: c.inp}, nil) 669 if err != nil { 670 t.Fatalf("bad: %s", err) 671 } 672 if acl.KeyringRead() != c.read { 673 t.Fatalf("bad: %#v", c) 674 } 675 if acl.KeyringWrite() != c.write { 676 t.Fatalf("bad: %#v", c) 677 } 678 } 679} 680 681func TestPolicyACL_Operator(t *testing.T) { 682 type operatorcase struct { 683 inp string 684 read bool 685 write bool 686 } 687 cases := []operatorcase{ 688 {"", false, false}, 689 {PolicyRead, true, false}, 690 {PolicyWrite, true, true}, 691 {PolicyDeny, false, false}, 692 } 693 for _, c := range cases { 694 acl, err := New(DenyAll(), &Policy{Operator: c.inp}, nil) 695 if err != nil { 696 t.Fatalf("bad: %s", err) 697 } 698 if acl.OperatorRead() != c.read { 699 t.Fatalf("bad: %#v", c) 700 } 701 if acl.OperatorWrite() != c.write { 702 t.Fatalf("bad: %#v", c) 703 } 704 } 705} 706 707func TestPolicyACL_Node(t *testing.T) { 708 deny := DenyAll() 709 policyRoot := &Policy{ 710 Nodes: []*NodePolicy{ 711 &NodePolicy{ 712 Name: "root-nope", 713 Policy: PolicyDeny, 714 }, 715 &NodePolicy{ 716 Name: "root-ro", 717 Policy: PolicyRead, 718 }, 719 &NodePolicy{ 720 Name: "root-rw", 721 Policy: PolicyWrite, 722 }, 723 &NodePolicy{ 724 Name: "override", 725 Policy: PolicyDeny, 726 }, 727 }, 728 } 729 root, err := New(deny, policyRoot, nil) 730 if err != nil { 731 t.Fatalf("err: %v", err) 732 } 733 734 policy := &Policy{ 735 Nodes: []*NodePolicy{ 736 &NodePolicy{ 737 Name: "child-nope", 738 Policy: PolicyDeny, 739 }, 740 &NodePolicy{ 741 Name: "child-ro", 742 Policy: PolicyRead, 743 }, 744 &NodePolicy{ 745 Name: "child-rw", 746 Policy: PolicyWrite, 747 }, 748 &NodePolicy{ 749 Name: "override", 750 Policy: PolicyWrite, 751 }, 752 }, 753 } 754 acl, err := New(root, policy, nil) 755 if err != nil { 756 t.Fatalf("err: %v", err) 757 } 758 759 type nodecase struct { 760 inp string 761 read bool 762 write bool 763 } 764 cases := []nodecase{ 765 {"nope", false, false}, 766 {"root-nope", false, false}, 767 {"root-ro", true, false}, 768 {"root-rw", true, true}, 769 {"root-nope-prefix", false, false}, 770 {"root-ro-prefix", true, false}, 771 {"root-rw-prefix", true, true}, 772 {"child-nope", false, false}, 773 {"child-ro", true, false}, 774 {"child-rw", true, true}, 775 {"child-nope-prefix", false, false}, 776 {"child-ro-prefix", true, false}, 777 {"child-rw-prefix", true, true}, 778 {"override", true, true}, 779 } 780 for _, c := range cases { 781 if c.read != acl.NodeRead(c.inp) { 782 t.Fatalf("Read fail: %#v", c) 783 } 784 if c.write != acl.NodeWrite(c.inp, nil) { 785 t.Fatalf("Write fail: %#v", c) 786 } 787 } 788} 789 790func TestPolicyACL_Session(t *testing.T) { 791 deny := DenyAll() 792 policyRoot := &Policy{ 793 Sessions: []*SessionPolicy{ 794 &SessionPolicy{ 795 Node: "root-nope", 796 Policy: PolicyDeny, 797 }, 798 &SessionPolicy{ 799 Node: "root-ro", 800 Policy: PolicyRead, 801 }, 802 &SessionPolicy{ 803 Node: "root-rw", 804 Policy: PolicyWrite, 805 }, 806 &SessionPolicy{ 807 Node: "override", 808 Policy: PolicyDeny, 809 }, 810 }, 811 } 812 root, err := New(deny, policyRoot, nil) 813 if err != nil { 814 t.Fatalf("err: %v", err) 815 } 816 817 policy := &Policy{ 818 Sessions: []*SessionPolicy{ 819 &SessionPolicy{ 820 Node: "child-nope", 821 Policy: PolicyDeny, 822 }, 823 &SessionPolicy{ 824 Node: "child-ro", 825 Policy: PolicyRead, 826 }, 827 &SessionPolicy{ 828 Node: "child-rw", 829 Policy: PolicyWrite, 830 }, 831 &SessionPolicy{ 832 Node: "override", 833 Policy: PolicyWrite, 834 }, 835 }, 836 } 837 acl, err := New(root, policy, nil) 838 if err != nil { 839 t.Fatalf("err: %v", err) 840 } 841 842 type sessioncase struct { 843 inp string 844 read bool 845 write bool 846 } 847 cases := []sessioncase{ 848 {"nope", false, false}, 849 {"root-nope", false, false}, 850 {"root-ro", true, false}, 851 {"root-rw", true, true}, 852 {"root-nope-prefix", false, false}, 853 {"root-ro-prefix", true, false}, 854 {"root-rw-prefix", true, true}, 855 {"child-nope", false, false}, 856 {"child-ro", true, false}, 857 {"child-rw", true, true}, 858 {"child-nope-prefix", false, false}, 859 {"child-ro-prefix", true, false}, 860 {"child-rw-prefix", true, true}, 861 {"override", true, true}, 862 } 863 for _, c := range cases { 864 if c.read != acl.SessionRead(c.inp) { 865 t.Fatalf("Read fail: %#v", c) 866 } 867 if c.write != acl.SessionWrite(c.inp) { 868 t.Fatalf("Write fail: %#v", c) 869 } 870 } 871} 872