A Kubernetes operator that bridges Hardware Security Module (HSM) data storage with Kubernetes Secrets, providing true secret portability th
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

consolidate writesecret and writesecretwithmetadata

+715 -1078
+101 -200
api/proto/hsm/v1/hsm.pb.go
··· 399 399 state protoimpl.MessageState `protogen:"open.v1"` 400 400 Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` 401 401 SecretData *SecretData `protobuf:"bytes,2,opt,name=secret_data,json=secretData,proto3" json:"secret_data,omitempty"` 402 + Metadata *SecretMetadata `protobuf:"bytes,3,opt,name=metadata,proto3" json:"metadata,omitempty"` 402 403 unknownFields protoimpl.UnknownFields 403 404 sizeCache protoimpl.SizeCache 404 405 } ··· 447 448 return nil 448 449 } 449 450 451 + func (x *WriteSecretRequest) GetMetadata() *SecretMetadata { 452 + if x != nil { 453 + return x.Metadata 454 + } 455 + return nil 456 + } 457 + 450 458 type WriteSecretResponse struct { 451 459 state protoimpl.MessageState `protogen:"open.v1"` 452 460 unknownFields protoimpl.UnknownFields ··· 483 491 return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{8} 484 492 } 485 493 486 - type WriteSecretWithMetadataRequest struct { 487 - state protoimpl.MessageState `protogen:"open.v1"` 488 - Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` 489 - SecretData *SecretData `protobuf:"bytes,2,opt,name=secret_data,json=secretData,proto3" json:"secret_data,omitempty"` 490 - Metadata *SecretMetadata `protobuf:"bytes,3,opt,name=metadata,proto3" json:"metadata,omitempty"` 491 - unknownFields protoimpl.UnknownFields 492 - sizeCache protoimpl.SizeCache 493 - } 494 - 495 - func (x *WriteSecretWithMetadataRequest) Reset() { 496 - *x = WriteSecretWithMetadataRequest{} 497 - mi := &file_hsm_v1_hsm_proto_msgTypes[9] 498 - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 499 - ms.StoreMessageInfo(mi) 500 - } 501 - 502 - func (x *WriteSecretWithMetadataRequest) String() string { 503 - return protoimpl.X.MessageStringOf(x) 504 - } 505 - 506 - func (*WriteSecretWithMetadataRequest) ProtoMessage() {} 507 - 508 - func (x *WriteSecretWithMetadataRequest) ProtoReflect() protoreflect.Message { 509 - mi := &file_hsm_v1_hsm_proto_msgTypes[9] 510 - if x != nil { 511 - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 512 - if ms.LoadMessageInfo() == nil { 513 - ms.StoreMessageInfo(mi) 514 - } 515 - return ms 516 - } 517 - return mi.MessageOf(x) 518 - } 519 - 520 - // Deprecated: Use WriteSecretWithMetadataRequest.ProtoReflect.Descriptor instead. 521 - func (*WriteSecretWithMetadataRequest) Descriptor() ([]byte, []int) { 522 - return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{9} 523 - } 524 - 525 - func (x *WriteSecretWithMetadataRequest) GetPath() string { 526 - if x != nil { 527 - return x.Path 528 - } 529 - return "" 530 - } 531 - 532 - func (x *WriteSecretWithMetadataRequest) GetSecretData() *SecretData { 533 - if x != nil { 534 - return x.SecretData 535 - } 536 - return nil 537 - } 538 - 539 - func (x *WriteSecretWithMetadataRequest) GetMetadata() *SecretMetadata { 540 - if x != nil { 541 - return x.Metadata 542 - } 543 - return nil 544 - } 545 - 546 - type WriteSecretWithMetadataResponse struct { 547 - state protoimpl.MessageState `protogen:"open.v1"` 548 - unknownFields protoimpl.UnknownFields 549 - sizeCache protoimpl.SizeCache 550 - } 551 - 552 - func (x *WriteSecretWithMetadataResponse) Reset() { 553 - *x = WriteSecretWithMetadataResponse{} 554 - mi := &file_hsm_v1_hsm_proto_msgTypes[10] 555 - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 556 - ms.StoreMessageInfo(mi) 557 - } 558 - 559 - func (x *WriteSecretWithMetadataResponse) String() string { 560 - return protoimpl.X.MessageStringOf(x) 561 - } 562 - 563 - func (*WriteSecretWithMetadataResponse) ProtoMessage() {} 564 - 565 - func (x *WriteSecretWithMetadataResponse) ProtoReflect() protoreflect.Message { 566 - mi := &file_hsm_v1_hsm_proto_msgTypes[10] 567 - if x != nil { 568 - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 569 - if ms.LoadMessageInfo() == nil { 570 - ms.StoreMessageInfo(mi) 571 - } 572 - return ms 573 - } 574 - return mi.MessageOf(x) 575 - } 576 - 577 - // Deprecated: Use WriteSecretWithMetadataResponse.ProtoReflect.Descriptor instead. 578 - func (*WriteSecretWithMetadataResponse) Descriptor() ([]byte, []int) { 579 - return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{10} 580 - } 581 - 582 494 type ReadMetadataRequest struct { 583 495 state protoimpl.MessageState `protogen:"open.v1"` 584 496 Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` ··· 588 500 589 501 func (x *ReadMetadataRequest) Reset() { 590 502 *x = ReadMetadataRequest{} 591 - mi := &file_hsm_v1_hsm_proto_msgTypes[11] 503 + mi := &file_hsm_v1_hsm_proto_msgTypes[9] 592 504 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 593 505 ms.StoreMessageInfo(mi) 594 506 } ··· 600 512 func (*ReadMetadataRequest) ProtoMessage() {} 601 513 602 514 func (x *ReadMetadataRequest) ProtoReflect() protoreflect.Message { 603 - mi := &file_hsm_v1_hsm_proto_msgTypes[11] 515 + mi := &file_hsm_v1_hsm_proto_msgTypes[9] 604 516 if x != nil { 605 517 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 606 518 if ms.LoadMessageInfo() == nil { ··· 613 525 614 526 // Deprecated: Use ReadMetadataRequest.ProtoReflect.Descriptor instead. 615 527 func (*ReadMetadataRequest) Descriptor() ([]byte, []int) { 616 - return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{11} 528 + return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{9} 617 529 } 618 530 619 531 func (x *ReadMetadataRequest) GetPath() string { ··· 632 544 633 545 func (x *ReadMetadataResponse) Reset() { 634 546 *x = ReadMetadataResponse{} 635 - mi := &file_hsm_v1_hsm_proto_msgTypes[12] 547 + mi := &file_hsm_v1_hsm_proto_msgTypes[10] 636 548 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 637 549 ms.StoreMessageInfo(mi) 638 550 } ··· 644 556 func (*ReadMetadataResponse) ProtoMessage() {} 645 557 646 558 func (x *ReadMetadataResponse) ProtoReflect() protoreflect.Message { 647 - mi := &file_hsm_v1_hsm_proto_msgTypes[12] 559 + mi := &file_hsm_v1_hsm_proto_msgTypes[10] 648 560 if x != nil { 649 561 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 650 562 if ms.LoadMessageInfo() == nil { ··· 657 569 658 570 // Deprecated: Use ReadMetadataResponse.ProtoReflect.Descriptor instead. 659 571 func (*ReadMetadataResponse) Descriptor() ([]byte, []int) { 660 - return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{12} 572 + return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{10} 661 573 } 662 574 663 575 func (x *ReadMetadataResponse) GetMetadata() *SecretMetadata { ··· 676 588 677 589 func (x *DeleteSecretRequest) Reset() { 678 590 *x = DeleteSecretRequest{} 679 - mi := &file_hsm_v1_hsm_proto_msgTypes[13] 591 + mi := &file_hsm_v1_hsm_proto_msgTypes[11] 680 592 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 681 593 ms.StoreMessageInfo(mi) 682 594 } ··· 688 600 func (*DeleteSecretRequest) ProtoMessage() {} 689 601 690 602 func (x *DeleteSecretRequest) ProtoReflect() protoreflect.Message { 691 - mi := &file_hsm_v1_hsm_proto_msgTypes[13] 603 + mi := &file_hsm_v1_hsm_proto_msgTypes[11] 692 604 if x != nil { 693 605 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 694 606 if ms.LoadMessageInfo() == nil { ··· 701 613 702 614 // Deprecated: Use DeleteSecretRequest.ProtoReflect.Descriptor instead. 703 615 func (*DeleteSecretRequest) Descriptor() ([]byte, []int) { 704 - return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{13} 616 + return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{11} 705 617 } 706 618 707 619 func (x *DeleteSecretRequest) GetPath() string { ··· 719 631 720 632 func (x *DeleteSecretResponse) Reset() { 721 633 *x = DeleteSecretResponse{} 722 - mi := &file_hsm_v1_hsm_proto_msgTypes[14] 634 + mi := &file_hsm_v1_hsm_proto_msgTypes[12] 723 635 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 724 636 ms.StoreMessageInfo(mi) 725 637 } ··· 731 643 func (*DeleteSecretResponse) ProtoMessage() {} 732 644 733 645 func (x *DeleteSecretResponse) ProtoReflect() protoreflect.Message { 734 - mi := &file_hsm_v1_hsm_proto_msgTypes[14] 646 + mi := &file_hsm_v1_hsm_proto_msgTypes[12] 735 647 if x != nil { 736 648 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 737 649 if ms.LoadMessageInfo() == nil { ··· 744 656 745 657 // Deprecated: Use DeleteSecretResponse.ProtoReflect.Descriptor instead. 746 658 func (*DeleteSecretResponse) Descriptor() ([]byte, []int) { 747 - return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{14} 659 + return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{12} 748 660 } 749 661 750 662 type ListSecretsRequest struct { ··· 756 668 757 669 func (x *ListSecretsRequest) Reset() { 758 670 *x = ListSecretsRequest{} 759 - mi := &file_hsm_v1_hsm_proto_msgTypes[15] 671 + mi := &file_hsm_v1_hsm_proto_msgTypes[13] 760 672 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 761 673 ms.StoreMessageInfo(mi) 762 674 } ··· 768 680 func (*ListSecretsRequest) ProtoMessage() {} 769 681 770 682 func (x *ListSecretsRequest) ProtoReflect() protoreflect.Message { 771 - mi := &file_hsm_v1_hsm_proto_msgTypes[15] 683 + mi := &file_hsm_v1_hsm_proto_msgTypes[13] 772 684 if x != nil { 773 685 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 774 686 if ms.LoadMessageInfo() == nil { ··· 781 693 782 694 // Deprecated: Use ListSecretsRequest.ProtoReflect.Descriptor instead. 783 695 func (*ListSecretsRequest) Descriptor() ([]byte, []int) { 784 - return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{15} 696 + return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{13} 785 697 } 786 698 787 699 func (x *ListSecretsRequest) GetPrefix() string { ··· 800 712 801 713 func (x *ListSecretsResponse) Reset() { 802 714 *x = ListSecretsResponse{} 803 - mi := &file_hsm_v1_hsm_proto_msgTypes[16] 715 + mi := &file_hsm_v1_hsm_proto_msgTypes[14] 804 716 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 805 717 ms.StoreMessageInfo(mi) 806 718 } ··· 812 724 func (*ListSecretsResponse) ProtoMessage() {} 813 725 814 726 func (x *ListSecretsResponse) ProtoReflect() protoreflect.Message { 815 - mi := &file_hsm_v1_hsm_proto_msgTypes[16] 727 + mi := &file_hsm_v1_hsm_proto_msgTypes[14] 816 728 if x != nil { 817 729 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 818 730 if ms.LoadMessageInfo() == nil { ··· 825 737 826 738 // Deprecated: Use ListSecretsResponse.ProtoReflect.Descriptor instead. 827 739 func (*ListSecretsResponse) Descriptor() ([]byte, []int) { 828 - return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{16} 740 + return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{14} 829 741 } 830 742 831 743 func (x *ListSecretsResponse) GetPaths() []string { ··· 844 756 845 757 func (x *GetChecksumRequest) Reset() { 846 758 *x = GetChecksumRequest{} 847 - mi := &file_hsm_v1_hsm_proto_msgTypes[17] 759 + mi := &file_hsm_v1_hsm_proto_msgTypes[15] 848 760 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 849 761 ms.StoreMessageInfo(mi) 850 762 } ··· 856 768 func (*GetChecksumRequest) ProtoMessage() {} 857 769 858 770 func (x *GetChecksumRequest) ProtoReflect() protoreflect.Message { 859 - mi := &file_hsm_v1_hsm_proto_msgTypes[17] 771 + mi := &file_hsm_v1_hsm_proto_msgTypes[15] 860 772 if x != nil { 861 773 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 862 774 if ms.LoadMessageInfo() == nil { ··· 869 781 870 782 // Deprecated: Use GetChecksumRequest.ProtoReflect.Descriptor instead. 871 783 func (*GetChecksumRequest) Descriptor() ([]byte, []int) { 872 - return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{17} 784 + return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{15} 873 785 } 874 786 875 787 func (x *GetChecksumRequest) GetPath() string { ··· 888 800 889 801 func (x *GetChecksumResponse) Reset() { 890 802 *x = GetChecksumResponse{} 891 - mi := &file_hsm_v1_hsm_proto_msgTypes[18] 803 + mi := &file_hsm_v1_hsm_proto_msgTypes[16] 892 804 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 893 805 ms.StoreMessageInfo(mi) 894 806 } ··· 900 812 func (*GetChecksumResponse) ProtoMessage() {} 901 813 902 814 func (x *GetChecksumResponse) ProtoReflect() protoreflect.Message { 903 - mi := &file_hsm_v1_hsm_proto_msgTypes[18] 815 + mi := &file_hsm_v1_hsm_proto_msgTypes[16] 904 816 if x != nil { 905 817 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 906 818 if ms.LoadMessageInfo() == nil { ··· 913 825 914 826 // Deprecated: Use GetChecksumResponse.ProtoReflect.Descriptor instead. 915 827 func (*GetChecksumResponse) Descriptor() ([]byte, []int) { 916 - return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{18} 828 + return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{16} 917 829 } 918 830 919 831 func (x *GetChecksumResponse) GetChecksum() string { ··· 931 843 932 844 func (x *IsConnectedRequest) Reset() { 933 845 *x = IsConnectedRequest{} 934 - mi := &file_hsm_v1_hsm_proto_msgTypes[19] 846 + mi := &file_hsm_v1_hsm_proto_msgTypes[17] 935 847 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 936 848 ms.StoreMessageInfo(mi) 937 849 } ··· 943 855 func (*IsConnectedRequest) ProtoMessage() {} 944 856 945 857 func (x *IsConnectedRequest) ProtoReflect() protoreflect.Message { 946 - mi := &file_hsm_v1_hsm_proto_msgTypes[19] 858 + mi := &file_hsm_v1_hsm_proto_msgTypes[17] 947 859 if x != nil { 948 860 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 949 861 if ms.LoadMessageInfo() == nil { ··· 956 868 957 869 // Deprecated: Use IsConnectedRequest.ProtoReflect.Descriptor instead. 958 870 func (*IsConnectedRequest) Descriptor() ([]byte, []int) { 959 - return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{19} 871 + return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{17} 960 872 } 961 873 962 874 type IsConnectedResponse struct { ··· 968 880 969 881 func (x *IsConnectedResponse) Reset() { 970 882 *x = IsConnectedResponse{} 971 - mi := &file_hsm_v1_hsm_proto_msgTypes[20] 883 + mi := &file_hsm_v1_hsm_proto_msgTypes[18] 972 884 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 973 885 ms.StoreMessageInfo(mi) 974 886 } ··· 980 892 func (*IsConnectedResponse) ProtoMessage() {} 981 893 982 894 func (x *IsConnectedResponse) ProtoReflect() protoreflect.Message { 983 - mi := &file_hsm_v1_hsm_proto_msgTypes[20] 895 + mi := &file_hsm_v1_hsm_proto_msgTypes[18] 984 896 if x != nil { 985 897 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 986 898 if ms.LoadMessageInfo() == nil { ··· 993 905 994 906 // Deprecated: Use IsConnectedResponse.ProtoReflect.Descriptor instead. 995 907 func (*IsConnectedResponse) Descriptor() ([]byte, []int) { 996 - return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{20} 908 + return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{18} 997 909 } 998 910 999 911 func (x *IsConnectedResponse) GetConnected() bool { ··· 1011 923 1012 924 func (x *HealthRequest) Reset() { 1013 925 *x = HealthRequest{} 1014 - mi := &file_hsm_v1_hsm_proto_msgTypes[21] 926 + mi := &file_hsm_v1_hsm_proto_msgTypes[19] 1015 927 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 1016 928 ms.StoreMessageInfo(mi) 1017 929 } ··· 1023 935 func (*HealthRequest) ProtoMessage() {} 1024 936 1025 937 func (x *HealthRequest) ProtoReflect() protoreflect.Message { 1026 - mi := &file_hsm_v1_hsm_proto_msgTypes[21] 938 + mi := &file_hsm_v1_hsm_proto_msgTypes[19] 1027 939 if x != nil { 1028 940 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 1029 941 if ms.LoadMessageInfo() == nil { ··· 1036 948 1037 949 // Deprecated: Use HealthRequest.ProtoReflect.Descriptor instead. 1038 950 func (*HealthRequest) Descriptor() ([]byte, []int) { 1039 - return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{21} 951 + return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{19} 1040 952 } 1041 953 1042 954 type HealthResponse struct { ··· 1049 961 1050 962 func (x *HealthResponse) Reset() { 1051 963 *x = HealthResponse{} 1052 - mi := &file_hsm_v1_hsm_proto_msgTypes[22] 964 + mi := &file_hsm_v1_hsm_proto_msgTypes[20] 1053 965 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 1054 966 ms.StoreMessageInfo(mi) 1055 967 } ··· 1061 973 func (*HealthResponse) ProtoMessage() {} 1062 974 1063 975 func (x *HealthResponse) ProtoReflect() protoreflect.Message { 1064 - mi := &file_hsm_v1_hsm_proto_msgTypes[22] 976 + mi := &file_hsm_v1_hsm_proto_msgTypes[20] 1065 977 if x != nil { 1066 978 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 1067 979 if ms.LoadMessageInfo() == nil { ··· 1074 986 1075 987 // Deprecated: Use HealthResponse.ProtoReflect.Descriptor instead. 1076 988 func (*HealthResponse) Descriptor() ([]byte, []int) { 1077 - return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{22} 989 + return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{20} 1078 990 } 1079 991 1080 992 func (x *HealthResponse) GetStatus() string { ··· 1126 1038 "\x04path\x18\x01 \x01(\tR\x04path\"I\n" + 1127 1039 "\x12ReadSecretResponse\x123\n" + 1128 1040 "\vsecret_data\x18\x01 \x01(\v2\x12.hsm.v1.SecretDataR\n" + 1129 - "secretData\"]\n" + 1041 + "secretData\"\x91\x01\n" + 1130 1042 "\x12WriteSecretRequest\x12\x12\n" + 1131 1043 "\x04path\x18\x01 \x01(\tR\x04path\x123\n" + 1132 1044 "\vsecret_data\x18\x02 \x01(\v2\x12.hsm.v1.SecretDataR\n" + 1133 - "secretData\"\x15\n" + 1134 - "\x13WriteSecretResponse\"\x9d\x01\n" + 1135 - "\x1eWriteSecretWithMetadataRequest\x12\x12\n" + 1136 - "\x04path\x18\x01 \x01(\tR\x04path\x123\n" + 1137 - "\vsecret_data\x18\x02 \x01(\v2\x12.hsm.v1.SecretDataR\n" + 1138 1045 "secretData\x122\n" + 1139 - "\bmetadata\x18\x03 \x01(\v2\x16.hsm.v1.SecretMetadataR\bmetadata\"!\n" + 1140 - "\x1fWriteSecretWithMetadataResponse\")\n" + 1046 + "\bmetadata\x18\x03 \x01(\v2\x16.hsm.v1.SecretMetadataR\bmetadata\"\x15\n" + 1047 + "\x13WriteSecretResponse\")\n" + 1141 1048 "\x13ReadMetadataRequest\x12\x12\n" + 1142 1049 "\x04path\x18\x01 \x01(\tR\x04path\"J\n" + 1143 1050 "\x14ReadMetadataResponse\x122\n" + ··· 1159 1066 "\rHealthRequest\"B\n" + 1160 1067 "\x0eHealthResponse\x12\x16\n" + 1161 1068 "\x06status\x18\x01 \x01(\tR\x06status\x12\x18\n" + 1162 - "\amessage\x18\x02 \x01(\tR\amessage2\xe6\x05\n" + 1069 + "\amessage\x18\x02 \x01(\tR\amessage2\xfa\x04\n" + 1163 1070 "\bHSMAgent\x12:\n" + 1164 1071 "\aGetInfo\x12\x16.hsm.v1.GetInfoRequest\x1a\x17.hsm.v1.GetInfoResponse\x12C\n" + 1165 1072 "\n" + 1166 1073 "ReadSecret\x12\x19.hsm.v1.ReadSecretRequest\x1a\x1a.hsm.v1.ReadSecretResponse\x12F\n" + 1167 - "\vWriteSecret\x12\x1a.hsm.v1.WriteSecretRequest\x1a\x1b.hsm.v1.WriteSecretResponse\x12j\n" + 1168 - "\x17WriteSecretWithMetadata\x12&.hsm.v1.WriteSecretWithMetadataRequest\x1a'.hsm.v1.WriteSecretWithMetadataResponse\x12I\n" + 1074 + "\vWriteSecret\x12\x1a.hsm.v1.WriteSecretRequest\x1a\x1b.hsm.v1.WriteSecretResponse\x12I\n" + 1169 1075 "\fReadMetadata\x12\x1b.hsm.v1.ReadMetadataRequest\x1a\x1c.hsm.v1.ReadMetadataResponse\x12I\n" + 1170 1076 "\fDeleteSecret\x12\x1b.hsm.v1.DeleteSecretRequest\x1a\x1c.hsm.v1.DeleteSecretResponse\x12F\n" + 1171 1077 "\vListSecrets\x12\x1a.hsm.v1.ListSecretsRequest\x1a\x1b.hsm.v1.ListSecretsResponse\x12F\n" + ··· 1185 1091 return file_hsm_v1_hsm_proto_rawDescData 1186 1092 } 1187 1093 1188 - var file_hsm_v1_hsm_proto_msgTypes = make([]protoimpl.MessageInfo, 25) 1094 + var file_hsm_v1_hsm_proto_msgTypes = make([]protoimpl.MessageInfo, 23) 1189 1095 var file_hsm_v1_hsm_proto_goTypes = []any{ 1190 - (*HSMInfo)(nil), // 0: hsm.v1.HSMInfo 1191 - (*SecretData)(nil), // 1: hsm.v1.SecretData 1192 - (*SecretMetadata)(nil), // 2: hsm.v1.SecretMetadata 1193 - (*GetInfoRequest)(nil), // 3: hsm.v1.GetInfoRequest 1194 - (*GetInfoResponse)(nil), // 4: hsm.v1.GetInfoResponse 1195 - (*ReadSecretRequest)(nil), // 5: hsm.v1.ReadSecretRequest 1196 - (*ReadSecretResponse)(nil), // 6: hsm.v1.ReadSecretResponse 1197 - (*WriteSecretRequest)(nil), // 7: hsm.v1.WriteSecretRequest 1198 - (*WriteSecretResponse)(nil), // 8: hsm.v1.WriteSecretResponse 1199 - (*WriteSecretWithMetadataRequest)(nil), // 9: hsm.v1.WriteSecretWithMetadataRequest 1200 - (*WriteSecretWithMetadataResponse)(nil), // 10: hsm.v1.WriteSecretWithMetadataResponse 1201 - (*ReadMetadataRequest)(nil), // 11: hsm.v1.ReadMetadataRequest 1202 - (*ReadMetadataResponse)(nil), // 12: hsm.v1.ReadMetadataResponse 1203 - (*DeleteSecretRequest)(nil), // 13: hsm.v1.DeleteSecretRequest 1204 - (*DeleteSecretResponse)(nil), // 14: hsm.v1.DeleteSecretResponse 1205 - (*ListSecretsRequest)(nil), // 15: hsm.v1.ListSecretsRequest 1206 - (*ListSecretsResponse)(nil), // 16: hsm.v1.ListSecretsResponse 1207 - (*GetChecksumRequest)(nil), // 17: hsm.v1.GetChecksumRequest 1208 - (*GetChecksumResponse)(nil), // 18: hsm.v1.GetChecksumResponse 1209 - (*IsConnectedRequest)(nil), // 19: hsm.v1.IsConnectedRequest 1210 - (*IsConnectedResponse)(nil), // 20: hsm.v1.IsConnectedResponse 1211 - (*HealthRequest)(nil), // 21: hsm.v1.HealthRequest 1212 - (*HealthResponse)(nil), // 22: hsm.v1.HealthResponse 1213 - nil, // 23: hsm.v1.SecretData.DataEntry 1214 - nil, // 24: hsm.v1.SecretMetadata.LabelsEntry 1096 + (*HSMInfo)(nil), // 0: hsm.v1.HSMInfo 1097 + (*SecretData)(nil), // 1: hsm.v1.SecretData 1098 + (*SecretMetadata)(nil), // 2: hsm.v1.SecretMetadata 1099 + (*GetInfoRequest)(nil), // 3: hsm.v1.GetInfoRequest 1100 + (*GetInfoResponse)(nil), // 4: hsm.v1.GetInfoResponse 1101 + (*ReadSecretRequest)(nil), // 5: hsm.v1.ReadSecretRequest 1102 + (*ReadSecretResponse)(nil), // 6: hsm.v1.ReadSecretResponse 1103 + (*WriteSecretRequest)(nil), // 7: hsm.v1.WriteSecretRequest 1104 + (*WriteSecretResponse)(nil), // 8: hsm.v1.WriteSecretResponse 1105 + (*ReadMetadataRequest)(nil), // 9: hsm.v1.ReadMetadataRequest 1106 + (*ReadMetadataResponse)(nil), // 10: hsm.v1.ReadMetadataResponse 1107 + (*DeleteSecretRequest)(nil), // 11: hsm.v1.DeleteSecretRequest 1108 + (*DeleteSecretResponse)(nil), // 12: hsm.v1.DeleteSecretResponse 1109 + (*ListSecretsRequest)(nil), // 13: hsm.v1.ListSecretsRequest 1110 + (*ListSecretsResponse)(nil), // 14: hsm.v1.ListSecretsResponse 1111 + (*GetChecksumRequest)(nil), // 15: hsm.v1.GetChecksumRequest 1112 + (*GetChecksumResponse)(nil), // 16: hsm.v1.GetChecksumResponse 1113 + (*IsConnectedRequest)(nil), // 17: hsm.v1.IsConnectedRequest 1114 + (*IsConnectedResponse)(nil), // 18: hsm.v1.IsConnectedResponse 1115 + (*HealthRequest)(nil), // 19: hsm.v1.HealthRequest 1116 + (*HealthResponse)(nil), // 20: hsm.v1.HealthResponse 1117 + nil, // 21: hsm.v1.SecretData.DataEntry 1118 + nil, // 22: hsm.v1.SecretMetadata.LabelsEntry 1215 1119 } 1216 1120 var file_hsm_v1_hsm_proto_depIdxs = []int32{ 1217 - 23, // 0: hsm.v1.SecretData.data:type_name -> hsm.v1.SecretData.DataEntry 1218 - 24, // 1: hsm.v1.SecretMetadata.labels:type_name -> hsm.v1.SecretMetadata.LabelsEntry 1121 + 21, // 0: hsm.v1.SecretData.data:type_name -> hsm.v1.SecretData.DataEntry 1122 + 22, // 1: hsm.v1.SecretMetadata.labels:type_name -> hsm.v1.SecretMetadata.LabelsEntry 1219 1123 0, // 2: hsm.v1.GetInfoResponse.hsm_info:type_name -> hsm.v1.HSMInfo 1220 1124 1, // 3: hsm.v1.ReadSecretResponse.secret_data:type_name -> hsm.v1.SecretData 1221 1125 1, // 4: hsm.v1.WriteSecretRequest.secret_data:type_name -> hsm.v1.SecretData 1222 - 1, // 5: hsm.v1.WriteSecretWithMetadataRequest.secret_data:type_name -> hsm.v1.SecretData 1223 - 2, // 6: hsm.v1.WriteSecretWithMetadataRequest.metadata:type_name -> hsm.v1.SecretMetadata 1224 - 2, // 7: hsm.v1.ReadMetadataResponse.metadata:type_name -> hsm.v1.SecretMetadata 1225 - 3, // 8: hsm.v1.HSMAgent.GetInfo:input_type -> hsm.v1.GetInfoRequest 1226 - 5, // 9: hsm.v1.HSMAgent.ReadSecret:input_type -> hsm.v1.ReadSecretRequest 1227 - 7, // 10: hsm.v1.HSMAgent.WriteSecret:input_type -> hsm.v1.WriteSecretRequest 1228 - 9, // 11: hsm.v1.HSMAgent.WriteSecretWithMetadata:input_type -> hsm.v1.WriteSecretWithMetadataRequest 1229 - 11, // 12: hsm.v1.HSMAgent.ReadMetadata:input_type -> hsm.v1.ReadMetadataRequest 1230 - 13, // 13: hsm.v1.HSMAgent.DeleteSecret:input_type -> hsm.v1.DeleteSecretRequest 1231 - 15, // 14: hsm.v1.HSMAgent.ListSecrets:input_type -> hsm.v1.ListSecretsRequest 1232 - 17, // 15: hsm.v1.HSMAgent.GetChecksum:input_type -> hsm.v1.GetChecksumRequest 1233 - 19, // 16: hsm.v1.HSMAgent.IsConnected:input_type -> hsm.v1.IsConnectedRequest 1234 - 21, // 17: hsm.v1.HSMAgent.Health:input_type -> hsm.v1.HealthRequest 1235 - 4, // 18: hsm.v1.HSMAgent.GetInfo:output_type -> hsm.v1.GetInfoResponse 1236 - 6, // 19: hsm.v1.HSMAgent.ReadSecret:output_type -> hsm.v1.ReadSecretResponse 1237 - 8, // 20: hsm.v1.HSMAgent.WriteSecret:output_type -> hsm.v1.WriteSecretResponse 1238 - 10, // 21: hsm.v1.HSMAgent.WriteSecretWithMetadata:output_type -> hsm.v1.WriteSecretWithMetadataResponse 1239 - 12, // 22: hsm.v1.HSMAgent.ReadMetadata:output_type -> hsm.v1.ReadMetadataResponse 1240 - 14, // 23: hsm.v1.HSMAgent.DeleteSecret:output_type -> hsm.v1.DeleteSecretResponse 1241 - 16, // 24: hsm.v1.HSMAgent.ListSecrets:output_type -> hsm.v1.ListSecretsResponse 1242 - 18, // 25: hsm.v1.HSMAgent.GetChecksum:output_type -> hsm.v1.GetChecksumResponse 1243 - 20, // 26: hsm.v1.HSMAgent.IsConnected:output_type -> hsm.v1.IsConnectedResponse 1244 - 22, // 27: hsm.v1.HSMAgent.Health:output_type -> hsm.v1.HealthResponse 1245 - 18, // [18:28] is the sub-list for method output_type 1246 - 8, // [8:18] is the sub-list for method input_type 1247 - 8, // [8:8] is the sub-list for extension type_name 1248 - 8, // [8:8] is the sub-list for extension extendee 1249 - 0, // [0:8] is the sub-list for field type_name 1126 + 2, // 5: hsm.v1.WriteSecretRequest.metadata:type_name -> hsm.v1.SecretMetadata 1127 + 2, // 6: hsm.v1.ReadMetadataResponse.metadata:type_name -> hsm.v1.SecretMetadata 1128 + 3, // 7: hsm.v1.HSMAgent.GetInfo:input_type -> hsm.v1.GetInfoRequest 1129 + 5, // 8: hsm.v1.HSMAgent.ReadSecret:input_type -> hsm.v1.ReadSecretRequest 1130 + 7, // 9: hsm.v1.HSMAgent.WriteSecret:input_type -> hsm.v1.WriteSecretRequest 1131 + 9, // 10: hsm.v1.HSMAgent.ReadMetadata:input_type -> hsm.v1.ReadMetadataRequest 1132 + 11, // 11: hsm.v1.HSMAgent.DeleteSecret:input_type -> hsm.v1.DeleteSecretRequest 1133 + 13, // 12: hsm.v1.HSMAgent.ListSecrets:input_type -> hsm.v1.ListSecretsRequest 1134 + 15, // 13: hsm.v1.HSMAgent.GetChecksum:input_type -> hsm.v1.GetChecksumRequest 1135 + 17, // 14: hsm.v1.HSMAgent.IsConnected:input_type -> hsm.v1.IsConnectedRequest 1136 + 19, // 15: hsm.v1.HSMAgent.Health:input_type -> hsm.v1.HealthRequest 1137 + 4, // 16: hsm.v1.HSMAgent.GetInfo:output_type -> hsm.v1.GetInfoResponse 1138 + 6, // 17: hsm.v1.HSMAgent.ReadSecret:output_type -> hsm.v1.ReadSecretResponse 1139 + 8, // 18: hsm.v1.HSMAgent.WriteSecret:output_type -> hsm.v1.WriteSecretResponse 1140 + 10, // 19: hsm.v1.HSMAgent.ReadMetadata:output_type -> hsm.v1.ReadMetadataResponse 1141 + 12, // 20: hsm.v1.HSMAgent.DeleteSecret:output_type -> hsm.v1.DeleteSecretResponse 1142 + 14, // 21: hsm.v1.HSMAgent.ListSecrets:output_type -> hsm.v1.ListSecretsResponse 1143 + 16, // 22: hsm.v1.HSMAgent.GetChecksum:output_type -> hsm.v1.GetChecksumResponse 1144 + 18, // 23: hsm.v1.HSMAgent.IsConnected:output_type -> hsm.v1.IsConnectedResponse 1145 + 20, // 24: hsm.v1.HSMAgent.Health:output_type -> hsm.v1.HealthResponse 1146 + 16, // [16:25] is the sub-list for method output_type 1147 + 7, // [7:16] is the sub-list for method input_type 1148 + 7, // [7:7] is the sub-list for extension type_name 1149 + 7, // [7:7] is the sub-list for extension extendee 1150 + 0, // [0:7] is the sub-list for field type_name 1250 1151 } 1251 1152 1252 1153 func init() { file_hsm_v1_hsm_proto_init() } ··· 1260 1161 GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 1261 1162 RawDescriptor: unsafe.Slice(unsafe.StringData(file_hsm_v1_hsm_proto_rawDesc), len(file_hsm_v1_hsm_proto_rawDesc)), 1262 1163 NumEnums: 0, 1263 - NumMessages: 25, 1164 + NumMessages: 23, 1264 1165 NumExtensions: 0, 1265 1166 NumServices: 1, 1266 1167 },
+2 -12
api/proto/hsm/v1/hsm.proto
··· 12 12 // ReadSecret reads secret data from the specified HSM path 13 13 rpc ReadSecret(ReadSecretRequest) returns (ReadSecretResponse); 14 14 15 - // WriteSecret writes secret data to the specified HSM path 15 + // WriteSecret writes secret data and metadata to the specified HSM path 16 16 rpc WriteSecret(WriteSecretRequest) returns (WriteSecretResponse); 17 - 18 - // WriteSecretWithMetadata writes secret data and metadata to the specified HSM path 19 - rpc WriteSecretWithMetadata(WriteSecretWithMetadataRequest) returns (WriteSecretWithMetadataResponse); 20 17 21 18 // ReadMetadata reads metadata for a secret at the given path 22 19 rpc ReadMetadata(ReadMetadataRequest) returns (ReadMetadataResponse); ··· 77 74 message WriteSecretRequest { 78 75 string path = 1; 79 76 SecretData secret_data = 2; 77 + SecretMetadata metadata = 3; 80 78 } 81 79 82 80 message WriteSecretResponse {} 83 - 84 - message WriteSecretWithMetadataRequest { 85 - string path = 1; 86 - SecretData secret_data = 2; 87 - SecretMetadata metadata = 3; 88 - } 89 - 90 - message WriteSecretWithMetadataResponse {} 91 81 92 82 message ReadMetadataRequest { 93 83 string path = 1;
+11 -51
api/proto/hsm/v1/hsm_grpc.pb.go
··· 19 19 const _ = grpc.SupportPackageIsVersion9 20 20 21 21 const ( 22 - HSMAgent_GetInfo_FullMethodName = "/hsm.v1.HSMAgent/GetInfo" 23 - HSMAgent_ReadSecret_FullMethodName = "/hsm.v1.HSMAgent/ReadSecret" 24 - HSMAgent_WriteSecret_FullMethodName = "/hsm.v1.HSMAgent/WriteSecret" 25 - HSMAgent_WriteSecretWithMetadata_FullMethodName = "/hsm.v1.HSMAgent/WriteSecretWithMetadata" 26 - HSMAgent_ReadMetadata_FullMethodName = "/hsm.v1.HSMAgent/ReadMetadata" 27 - HSMAgent_DeleteSecret_FullMethodName = "/hsm.v1.HSMAgent/DeleteSecret" 28 - HSMAgent_ListSecrets_FullMethodName = "/hsm.v1.HSMAgent/ListSecrets" 29 - HSMAgent_GetChecksum_FullMethodName = "/hsm.v1.HSMAgent/GetChecksum" 30 - HSMAgent_IsConnected_FullMethodName = "/hsm.v1.HSMAgent/IsConnected" 31 - HSMAgent_Health_FullMethodName = "/hsm.v1.HSMAgent/Health" 22 + HSMAgent_GetInfo_FullMethodName = "/hsm.v1.HSMAgent/GetInfo" 23 + HSMAgent_ReadSecret_FullMethodName = "/hsm.v1.HSMAgent/ReadSecret" 24 + HSMAgent_WriteSecret_FullMethodName = "/hsm.v1.HSMAgent/WriteSecret" 25 + HSMAgent_ReadMetadata_FullMethodName = "/hsm.v1.HSMAgent/ReadMetadata" 26 + HSMAgent_DeleteSecret_FullMethodName = "/hsm.v1.HSMAgent/DeleteSecret" 27 + HSMAgent_ListSecrets_FullMethodName = "/hsm.v1.HSMAgent/ListSecrets" 28 + HSMAgent_GetChecksum_FullMethodName = "/hsm.v1.HSMAgent/GetChecksum" 29 + HSMAgent_IsConnected_FullMethodName = "/hsm.v1.HSMAgent/IsConnected" 30 + HSMAgent_Health_FullMethodName = "/hsm.v1.HSMAgent/Health" 32 31 ) 33 32 34 33 // HSMAgentClient is the client API for HSMAgent service. ··· 41 40 GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*GetInfoResponse, error) 42 41 // ReadSecret reads secret data from the specified HSM path 43 42 ReadSecret(ctx context.Context, in *ReadSecretRequest, opts ...grpc.CallOption) (*ReadSecretResponse, error) 44 - // WriteSecret writes secret data to the specified HSM path 43 + // WriteSecret writes secret data and metadata to the specified HSM path 45 44 WriteSecret(ctx context.Context, in *WriteSecretRequest, opts ...grpc.CallOption) (*WriteSecretResponse, error) 46 - // WriteSecretWithMetadata writes secret data and metadata to the specified HSM path 47 - WriteSecretWithMetadata(ctx context.Context, in *WriteSecretWithMetadataRequest, opts ...grpc.CallOption) (*WriteSecretWithMetadataResponse, error) 48 45 // ReadMetadata reads metadata for a secret at the given path 49 46 ReadMetadata(ctx context.Context, in *ReadMetadataRequest, opts ...grpc.CallOption) (*ReadMetadataResponse, error) 50 47 // DeleteSecret removes secret data from the specified HSM path ··· 97 94 return out, nil 98 95 } 99 96 100 - func (c *hSMAgentClient) WriteSecretWithMetadata(ctx context.Context, in *WriteSecretWithMetadataRequest, opts ...grpc.CallOption) (*WriteSecretWithMetadataResponse, error) { 101 - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) 102 - out := new(WriteSecretWithMetadataResponse) 103 - err := c.cc.Invoke(ctx, HSMAgent_WriteSecretWithMetadata_FullMethodName, in, out, cOpts...) 104 - if err != nil { 105 - return nil, err 106 - } 107 - return out, nil 108 - } 109 - 110 97 func (c *hSMAgentClient) ReadMetadata(ctx context.Context, in *ReadMetadataRequest, opts ...grpc.CallOption) (*ReadMetadataResponse, error) { 111 98 cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) 112 99 out := new(ReadMetadataResponse) ··· 177 164 GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error) 178 165 // ReadSecret reads secret data from the specified HSM path 179 166 ReadSecret(context.Context, *ReadSecretRequest) (*ReadSecretResponse, error) 180 - // WriteSecret writes secret data to the specified HSM path 167 + // WriteSecret writes secret data and metadata to the specified HSM path 181 168 WriteSecret(context.Context, *WriteSecretRequest) (*WriteSecretResponse, error) 182 - // WriteSecretWithMetadata writes secret data and metadata to the specified HSM path 183 - WriteSecretWithMetadata(context.Context, *WriteSecretWithMetadataRequest) (*WriteSecretWithMetadataResponse, error) 184 169 // ReadMetadata reads metadata for a secret at the given path 185 170 ReadMetadata(context.Context, *ReadMetadataRequest) (*ReadMetadataResponse, error) 186 171 // DeleteSecret removes secret data from the specified HSM path ··· 212 197 func (UnimplementedHSMAgentServer) WriteSecret(context.Context, *WriteSecretRequest) (*WriteSecretResponse, error) { 213 198 return nil, status.Errorf(codes.Unimplemented, "method WriteSecret not implemented") 214 199 } 215 - func (UnimplementedHSMAgentServer) WriteSecretWithMetadata(context.Context, *WriteSecretWithMetadataRequest) (*WriteSecretWithMetadataResponse, error) { 216 - return nil, status.Errorf(codes.Unimplemented, "method WriteSecretWithMetadata not implemented") 217 - } 218 200 func (UnimplementedHSMAgentServer) ReadMetadata(context.Context, *ReadMetadataRequest) (*ReadMetadataResponse, error) { 219 201 return nil, status.Errorf(codes.Unimplemented, "method ReadMetadata not implemented") 220 202 } ··· 308 290 return interceptor(ctx, in, info, handler) 309 291 } 310 292 311 - func _HSMAgent_WriteSecretWithMetadata_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 312 - in := new(WriteSecretWithMetadataRequest) 313 - if err := dec(in); err != nil { 314 - return nil, err 315 - } 316 - if interceptor == nil { 317 - return srv.(HSMAgentServer).WriteSecretWithMetadata(ctx, in) 318 - } 319 - info := &grpc.UnaryServerInfo{ 320 - Server: srv, 321 - FullMethod: HSMAgent_WriteSecretWithMetadata_FullMethodName, 322 - } 323 - handler := func(ctx context.Context, req interface{}) (interface{}, error) { 324 - return srv.(HSMAgentServer).WriteSecretWithMetadata(ctx, req.(*WriteSecretWithMetadataRequest)) 325 - } 326 - return interceptor(ctx, in, info, handler) 327 - } 328 - 329 293 func _HSMAgent_ReadMetadata_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 330 294 in := new(ReadMetadataRequest) 331 295 if err := dec(in); err != nil { ··· 452 416 { 453 417 MethodName: "WriteSecret", 454 418 Handler: _HSMAgent_WriteSecret_Handler, 455 - }, 456 - { 457 - MethodName: "WriteSecretWithMetadata", 458 - Handler: _HSMAgent_WriteSecretWithMetadata_Handler, 459 419 }, 460 420 { 461 421 MethodName: "ReadMetadata",
-258
cmd/test-hsm/main.go
··· 1 - /* 2 - Copyright 2025. 3 - 4 - Licensed under the Apache License, Version 2.0 (the "License"); 5 - you may not use this file except in compliance with the License. 6 - You may obtain a copy of the License at 7 - 8 - http://www.apache.org/licenses/LICENSE-2.0 9 - 10 - Unless required by applicable law or agreed to in writing, software 11 - distributed under the License is distributed on an "AS IS" BASIS, 12 - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 - See the License for the specific language governing permissions and 14 - limitations under the License. 15 - */ 16 - 17 - package main 18 - 19 - import ( 20 - "context" 21 - "flag" 22 - "fmt" 23 - "log" 24 - "os" 25 - "time" 26 - 27 - "github.com/evanjarrett/hsm-secrets-operator/internal/hsm" 28 - ) 29 - 30 - // nolint:gocyclo 31 - func main() { 32 - var ( 33 - libraryPath = flag.String("library", "", "Path to PKCS#11 library (required)") 34 - slotID = flag.Uint("slot", 0, "PKCS#11 slot ID") 35 - useSlotID = flag.Bool("use-slot-id", false, "Use specific slot ID instead of auto-discovery") 36 - pin = flag.String("pin", "", "HSM PIN (required)") 37 - tokenLabel = flag.String("token", "", "Token label for auto-discovery") 38 - testPath = flag.String("path", "test/hsm-operator", "Secret path to test") 39 - operation = flag.String("op", "test", "Operation: test, list, info, write, read, delete") 40 - ) 41 - flag.Parse() 42 - 43 - if *libraryPath == "" { 44 - fmt.Println("ERROR: -library is required") 45 - fmt.Println("\nCommon PKCS#11 libraries:") 46 - fmt.Println(" OpenSC: /usr/lib/pkcs11/opensc-pkcs11.so") 47 - fmt.Println(" SoftHSM: /usr/lib/softhsm/libsofthsm2.so") 48 - fmt.Println(" Pico HSM: /usr/local/lib/libsc-hsm-pkcs11.so") 49 - os.Exit(1) 50 - } 51 - 52 - if *pin == "" { 53 - fmt.Println("ERROR: -pin is required") 54 - os.Exit(1) 55 - } 56 - 57 - // Create PKCS#11 client 58 - client := hsm.NewPKCS11Client() 59 - defer func() { 60 - if err := client.Close(); err != nil { 61 - log.Printf("Failed to close HSM client: %v", err) 62 - } 63 - }() 64 - 65 - // Configure HSM 66 - config := hsm.Config{ 67 - PKCS11LibraryPath: *libraryPath, 68 - SlotID: *slotID, 69 - UseSlotID: *useSlotID, 70 - PIN: *pin, 71 - TokenLabel: *tokenLabel, 72 - ConnectionTimeout: 30 * time.Second, 73 - RetryAttempts: 3, 74 - RetryDelay: 2 * time.Second, 75 - } 76 - 77 - ctx := context.Background() 78 - 79 - fmt.Printf("🔐 Testing HSM with library: %s\n", *libraryPath) 80 - if *useSlotID { 81 - fmt.Printf("📍 Using slot ID: %d\n", *slotID) 82 - } else if *tokenLabel != "" { 83 - fmt.Printf("🏷️ Looking for token: %s\n", *tokenLabel) 84 - } else { 85 - fmt.Printf("🔍 Auto-discovering first available slot\n") 86 - } 87 - 88 - // Initialize connection 89 - fmt.Print("🔌 Connecting to HSM... ") 90 - if err := client.Initialize(ctx, config); err != nil { 91 - fmt.Printf("❌ FAILED\n") 92 - log.Fatalf("Failed to initialize HSM: %v", err) 93 - } 94 - fmt.Printf("✅ SUCCESS\n") 95 - 96 - // Get HSM info 97 - fmt.Print("ℹ️ Getting HSM info... ") 98 - info, err := client.GetInfo(ctx) 99 - if err != nil { 100 - fmt.Printf("❌ FAILED: %v\n", err) 101 - } else { 102 - fmt.Printf("✅ SUCCESS\n") 103 - fmt.Printf(" Label: %s\n", info.Label) 104 - fmt.Printf(" Manufacturer: %s\n", info.Manufacturer) 105 - fmt.Printf(" Model: %s\n", info.Model) 106 - fmt.Printf(" Serial: %s\n", info.SerialNumber) 107 - fmt.Printf(" Firmware: %s\n", info.FirmwareVersion) 108 - } 109 - 110 - switch *operation { 111 - case "info": 112 - // Already displayed above 113 - return 114 - 115 - case "list": 116 - fmt.Print("📋 Listing secrets... ") 117 - paths, err := client.ListSecrets(ctx, "") 118 - if err != nil { 119 - fmt.Printf("❌ FAILED: %v\n", err) 120 - return 121 - } 122 - fmt.Printf("✅ Found %d secrets\n", len(paths)) 123 - for i, path := range paths { 124 - fmt.Printf(" %d. %s\n", i+1, path) 125 - } 126 - 127 - case "write": 128 - testData := hsm.SecretData{ 129 - "username": []byte("test-user"), 130 - "password": []byte("test-password-123"), 131 - "api-key": []byte("sk-test123456789"), 132 - } 133 - 134 - fmt.Printf("✍️ Writing test secret to '%s'... ", *testPath) 135 - if err := client.WriteSecret(ctx, *testPath, testData); err != nil { 136 - fmt.Printf("❌ FAILED: %v\n", err) 137 - return 138 - } 139 - fmt.Printf("✅ SUCCESS\n") 140 - fmt.Printf(" Written %d keys: username, password, api-key\n", len(testData)) 141 - 142 - case "read": 143 - fmt.Printf("📖 Reading secret from '%s'... ", *testPath) 144 - data, err := client.ReadSecret(ctx, *testPath) 145 - if err != nil { 146 - fmt.Printf("❌ FAILED: %v\n", err) 147 - return 148 - } 149 - fmt.Printf("✅ SUCCESS\n") 150 - fmt.Printf(" Found %d keys:\n", len(data)) 151 - for key, value := range data { 152 - fmt.Printf(" %s: %s\n", key, string(value)) 153 - } 154 - 155 - case "delete": 156 - fmt.Printf("🗑️ Deleting secret '%s'... ", *testPath) 157 - if err := client.DeleteSecret(ctx, *testPath); err != nil { 158 - fmt.Printf("❌ FAILED: %v\n", err) 159 - return 160 - } 161 - fmt.Printf("✅ SUCCESS\n") 162 - 163 - case "test": 164 - // Full test cycle 165 - testData := hsm.SecretData{ 166 - "test-key": []byte("test-value-" + time.Now().Format("15:04:05")), 167 - } 168 - 169 - fmt.Printf("\n🧪 Running full test cycle with path: %s\n", *testPath) 170 - 171 - // Test write 172 - fmt.Print("1️⃣ Writing test data... ") 173 - if err := client.WriteSecret(ctx, *testPath, testData); err != nil { 174 - fmt.Printf("❌ FAILED: %v\n", err) 175 - return 176 - } 177 - fmt.Printf("✅ SUCCESS\n") 178 - 179 - // Test read 180 - fmt.Print("2️⃣ Reading test data... ") 181 - readData, err := client.ReadSecret(ctx, *testPath) 182 - if err != nil { 183 - fmt.Printf("❌ FAILED: %v\n", err) 184 - return 185 - } 186 - fmt.Printf("✅ SUCCESS\n") 187 - 188 - // Verify data 189 - fmt.Print("3️⃣ Verifying data integrity... ") 190 - if len(readData) != len(testData) { 191 - fmt.Printf("❌ FAILED: key count mismatch\n") 192 - return 193 - } 194 - for key, expectedValue := range testData { 195 - if actualValue, exists := readData[key]; !exists { 196 - fmt.Printf("❌ FAILED: missing key '%s'\n", key) 197 - return 198 - } else if string(actualValue) != string(expectedValue) { 199 - fmt.Printf("❌ FAILED: value mismatch for key '%s'\n", key) 200 - return 201 - } 202 - } 203 - fmt.Printf("✅ SUCCESS\n") 204 - 205 - // Test checksum 206 - fmt.Print("4️⃣ Testing checksum... ") 207 - checksum, err := client.GetChecksum(ctx, *testPath) 208 - if err != nil { 209 - fmt.Printf("❌ FAILED: %v\n", err) 210 - return 211 - } 212 - fmt.Printf("✅ SUCCESS: %s\n", checksum[:16]+"...") 213 - 214 - // Test list 215 - fmt.Print("5️⃣ Testing list operation... ") 216 - paths, err := client.ListSecrets(ctx, "test") 217 - if err != nil { 218 - fmt.Printf("❌ FAILED: %v\n", err) 219 - return 220 - } 221 - found := false 222 - for _, path := range paths { 223 - if path == *testPath { 224 - found = true 225 - break 226 - } 227 - } 228 - if !found { 229 - fmt.Printf("❌ FAILED: test path not found in list\n") 230 - return 231 - } 232 - fmt.Printf("✅ SUCCESS\n") 233 - 234 - // Test delete 235 - fmt.Print("6️⃣ Cleaning up (delete)... ") 236 - if err := client.DeleteSecret(ctx, *testPath); err != nil { 237 - fmt.Printf("❌ FAILED: %v\n", err) 238 - return 239 - } 240 - fmt.Printf("✅ SUCCESS\n") 241 - 242 - // Verify deletion 243 - fmt.Print("7️⃣ Verifying deletion... ") 244 - _, err = client.ReadSecret(ctx, *testPath) 245 - if err == nil { 246 - fmt.Printf("❌ FAILED: secret still exists after deletion\n") 247 - return 248 - } 249 - fmt.Printf("✅ SUCCESS\n") 250 - 251 - fmt.Printf("\n🎉 All tests passed! HSM PKCS#11 implementation is working correctly.\n") 252 - 253 - default: 254 - fmt.Printf("❌ Unknown operation: %s\n", *operation) 255 - fmt.Println("Available operations: info, list, write, read, delete, test") 256 - os.Exit(1) 257 - } 258 - }
+46 -135
hsm/v1/hsm.pb.go
··· 7 7 package hsmv1 8 8 9 9 import ( 10 - protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 - protoimpl "google.golang.org/protobuf/runtime/protoimpl" 12 10 reflect "reflect" 13 11 sync "sync" 14 12 unsafe "unsafe" 13 + 14 + protoreflect "google.golang.org/protobuf/reflect/protoreflect" 15 + protoimpl "google.golang.org/protobuf/runtime/protoimpl" 15 16 ) 16 17 17 18 const ( ··· 407 408 state protoimpl.MessageState `protogen:"open.v1"` 408 409 Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` 409 410 SecretData *SecretData `protobuf:"bytes,2,opt,name=secret_data,json=secretData,proto3" json:"secret_data,omitempty"` 411 + Metadata *SecretMetadata `protobuf:"bytes,3,opt,name=metadata,proto3" json:"metadata,omitempty"` 410 412 unknownFields protoimpl.UnknownFields 411 413 sizeCache protoimpl.SizeCache 412 414 } 413 415 414 416 func (x *WriteSecretRequest) Reset() { 415 417 *x = WriteSecretRequest{} 416 - mi := &file_hsm_v1_hsm_proto_msgTypes[7] 418 + mi := &file_hsm_v1_hsm_proto_msgTypes[9] 417 419 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 418 420 ms.StoreMessageInfo(mi) 419 421 } ··· 425 427 func (*WriteSecretRequest) ProtoMessage() {} 426 428 427 429 func (x *WriteSecretRequest) ProtoReflect() protoreflect.Message { 428 - mi := &file_hsm_v1_hsm_proto_msgTypes[7] 430 + mi := &file_hsm_v1_hsm_proto_msgTypes[9] 429 431 if x != nil { 430 432 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 431 433 if ms.LoadMessageInfo() == nil { ··· 438 440 439 441 // Deprecated: Use WriteSecretRequest.ProtoReflect.Descriptor instead. 440 442 func (*WriteSecretRequest) Descriptor() ([]byte, []int) { 441 - return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{7} 443 + return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{9} 442 444 } 443 445 444 446 func (x *WriteSecretRequest) GetPath() string { ··· 455 457 return nil 456 458 } 457 459 460 + func (x *WriteSecretRequest) GetMetadata() *SecretMetadata { 461 + if x != nil { 462 + return x.Metadata 463 + } 464 + return nil 465 + } 466 + 458 467 type WriteSecretResponse struct { 459 468 state protoimpl.MessageState `protogen:"open.v1"` 460 469 unknownFields protoimpl.UnknownFields ··· 463 472 464 473 func (x *WriteSecretResponse) Reset() { 465 474 *x = WriteSecretResponse{} 466 - mi := &file_hsm_v1_hsm_proto_msgTypes[8] 475 + mi := &file_hsm_v1_hsm_proto_msgTypes[10] 467 476 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 468 477 ms.StoreMessageInfo(mi) 469 478 } ··· 475 484 func (*WriteSecretResponse) ProtoMessage() {} 476 485 477 486 func (x *WriteSecretResponse) ProtoReflect() protoreflect.Message { 478 - mi := &file_hsm_v1_hsm_proto_msgTypes[8] 487 + mi := &file_hsm_v1_hsm_proto_msgTypes[10] 479 488 if x != nil { 480 489 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 481 490 if ms.LoadMessageInfo() == nil { ··· 488 497 489 498 // Deprecated: Use WriteSecretResponse.ProtoReflect.Descriptor instead. 490 499 func (*WriteSecretResponse) Descriptor() ([]byte, []int) { 491 - return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{8} 492 - } 493 - 494 - type WriteSecretWithMetadataRequest struct { 495 - state protoimpl.MessageState `protogen:"open.v1"` 496 - Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` 497 - SecretData *SecretData `protobuf:"bytes,2,opt,name=secret_data,json=secretData,proto3" json:"secret_data,omitempty"` 498 - Metadata *SecretMetadata `protobuf:"bytes,3,opt,name=metadata,proto3" json:"metadata,omitempty"` 499 - unknownFields protoimpl.UnknownFields 500 - sizeCache protoimpl.SizeCache 501 - } 502 - 503 - func (x *WriteSecretWithMetadataRequest) Reset() { 504 - *x = WriteSecretWithMetadataRequest{} 505 - mi := &file_hsm_v1_hsm_proto_msgTypes[9] 506 - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 507 - ms.StoreMessageInfo(mi) 508 - } 509 - 510 - func (x *WriteSecretWithMetadataRequest) String() string { 511 - return protoimpl.X.MessageStringOf(x) 512 - } 513 - 514 - func (*WriteSecretWithMetadataRequest) ProtoMessage() {} 515 - 516 - func (x *WriteSecretWithMetadataRequest) ProtoReflect() protoreflect.Message { 517 - mi := &file_hsm_v1_hsm_proto_msgTypes[9] 518 - if x != nil { 519 - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 520 - if ms.LoadMessageInfo() == nil { 521 - ms.StoreMessageInfo(mi) 522 - } 523 - return ms 524 - } 525 - return mi.MessageOf(x) 526 - } 527 - 528 - // Deprecated: Use WriteSecretWithMetadataRequest.ProtoReflect.Descriptor instead. 529 - func (*WriteSecretWithMetadataRequest) Descriptor() ([]byte, []int) { 530 - return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{9} 531 - } 532 - 533 - func (x *WriteSecretWithMetadataRequest) GetPath() string { 534 - if x != nil { 535 - return x.Path 536 - } 537 - return "" 538 - } 539 - 540 - func (x *WriteSecretWithMetadataRequest) GetSecretData() *SecretData { 541 - if x != nil { 542 - return x.SecretData 543 - } 544 - return nil 545 - } 546 - 547 - func (x *WriteSecretWithMetadataRequest) GetMetadata() *SecretMetadata { 548 - if x != nil { 549 - return x.Metadata 550 - } 551 - return nil 552 - } 553 - 554 - type WriteSecretWithMetadataResponse struct { 555 - state protoimpl.MessageState `protogen:"open.v1"` 556 - unknownFields protoimpl.UnknownFields 557 - sizeCache protoimpl.SizeCache 558 - } 559 - 560 - func (x *WriteSecretWithMetadataResponse) Reset() { 561 - *x = WriteSecretWithMetadataResponse{} 562 - mi := &file_hsm_v1_hsm_proto_msgTypes[10] 563 - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 564 - ms.StoreMessageInfo(mi) 565 - } 566 - 567 - func (x *WriteSecretWithMetadataResponse) String() string { 568 - return protoimpl.X.MessageStringOf(x) 569 - } 570 - 571 - func (*WriteSecretWithMetadataResponse) ProtoMessage() {} 572 - 573 - func (x *WriteSecretWithMetadataResponse) ProtoReflect() protoreflect.Message { 574 - mi := &file_hsm_v1_hsm_proto_msgTypes[10] 575 - if x != nil { 576 - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 577 - if ms.LoadMessageInfo() == nil { 578 - ms.StoreMessageInfo(mi) 579 - } 580 - return ms 581 - } 582 - return mi.MessageOf(x) 583 - } 584 - 585 - // Deprecated: Use WriteSecretWithMetadataResponse.ProtoReflect.Descriptor instead. 586 - func (*WriteSecretWithMetadataResponse) Descriptor() ([]byte, []int) { 587 500 return file_hsm_v1_hsm_proto_rawDescGZIP(), []int{10} 588 501 } 589 502 ··· 1141 1054 "\vsecret_data\x18\x02 \x01(\v2\x12.hsm.v1.SecretDataR\n" + 1142 1055 "secretData\"\x15\n" + 1143 1056 "\x13WriteSecretResponse\"\x9d\x01\n" + 1144 - "\x1eWriteSecretWithMetadataRequest\x12\x12\n" + 1057 + "\x1eWriteSecretRequest\x12\x12\n" + 1145 1058 "\x04path\x18\x01 \x01(\tR\x04path\x123\n" + 1146 1059 "\vsecret_data\x18\x02 \x01(\v2\x12.hsm.v1.SecretDataR\n" + 1147 1060 "secretData\x122\n" + 1148 1061 "\bmetadata\x18\x03 \x01(\v2\x16.hsm.v1.SecretMetadataR\bmetadata\"!\n" + 1149 - "\x1fWriteSecretWithMetadataResponse\")\n" + 1062 + "\x1fWriteSecretResponse\")\n" + 1150 1063 "\x13ReadMetadataRequest\x12\x12\n" + 1151 1064 "\x04path\x18\x01 \x01(\tR\x04path\"J\n" + 1152 1065 "\x14ReadMetadataResponse\x122\n" + ··· 1174 1087 "\n" + 1175 1088 "ReadSecret\x12\x19.hsm.v1.ReadSecretRequest\x1a\x1a.hsm.v1.ReadSecretResponse\x12F\n" + 1176 1089 "\vWriteSecret\x12\x1a.hsm.v1.WriteSecretRequest\x1a\x1b.hsm.v1.WriteSecretResponse\x12j\n" + 1177 - "\x17WriteSecretWithMetadata\x12&.hsm.v1.WriteSecretWithMetadataRequest\x1a'.hsm.v1.WriteSecretWithMetadataResponse\x12I\n" + 1090 + "\x17WriteSecret\x12&.hsm.v1.WriteSecretRequest\x1a'.hsm.v1.WriteSecretResponse\x12I\n" + 1178 1091 "\fReadMetadata\x12\x1b.hsm.v1.ReadMetadataRequest\x1a\x1c.hsm.v1.ReadMetadataResponse\x12I\n" + 1179 1092 "\fDeleteSecret\x12\x1b.hsm.v1.DeleteSecretRequest\x1a\x1c.hsm.v1.DeleteSecretResponse\x12F\n" + 1180 1093 "\vListSecrets\x12\x1a.hsm.v1.ListSecretsRequest\x1a\x1b.hsm.v1.ListSecretsResponse\x12F\n" + ··· 1198 1111 1199 1112 var file_hsm_v1_hsm_proto_msgTypes = make([]protoimpl.MessageInfo, 25) 1200 1113 var file_hsm_v1_hsm_proto_goTypes = []any{ 1201 - (*HSMInfo)(nil), // 0: hsm.v1.HSMInfo 1202 - (*SecretData)(nil), // 1: hsm.v1.SecretData 1203 - (*SecretMetadata)(nil), // 2: hsm.v1.SecretMetadata 1204 - (*GetInfoRequest)(nil), // 3: hsm.v1.GetInfoRequest 1205 - (*GetInfoResponse)(nil), // 4: hsm.v1.GetInfoResponse 1206 - (*ReadSecretRequest)(nil), // 5: hsm.v1.ReadSecretRequest 1207 - (*ReadSecretResponse)(nil), // 6: hsm.v1.ReadSecretResponse 1208 - (*WriteSecretRequest)(nil), // 7: hsm.v1.WriteSecretRequest 1209 - (*WriteSecretResponse)(nil), // 8: hsm.v1.WriteSecretResponse 1210 - (*WriteSecretWithMetadataRequest)(nil), // 9: hsm.v1.WriteSecretWithMetadataRequest 1211 - (*WriteSecretWithMetadataResponse)(nil), // 10: hsm.v1.WriteSecretWithMetadataResponse 1212 - (*ReadMetadataRequest)(nil), // 11: hsm.v1.ReadMetadataRequest 1213 - (*ReadMetadataResponse)(nil), // 12: hsm.v1.ReadMetadataResponse 1214 - (*DeleteSecretRequest)(nil), // 13: hsm.v1.DeleteSecretRequest 1215 - (*DeleteSecretResponse)(nil), // 14: hsm.v1.DeleteSecretResponse 1216 - (*ListSecretsRequest)(nil), // 15: hsm.v1.ListSecretsRequest 1217 - (*ListSecretsResponse)(nil), // 16: hsm.v1.ListSecretsResponse 1218 - (*GetChecksumRequest)(nil), // 17: hsm.v1.GetChecksumRequest 1219 - (*GetChecksumResponse)(nil), // 18: hsm.v1.GetChecksumResponse 1220 - (*IsConnectedRequest)(nil), // 19: hsm.v1.IsConnectedRequest 1221 - (*IsConnectedResponse)(nil), // 20: hsm.v1.IsConnectedResponse 1222 - (*HealthRequest)(nil), // 21: hsm.v1.HealthRequest 1223 - (*HealthResponse)(nil), // 22: hsm.v1.HealthResponse 1224 - nil, // 23: hsm.v1.SecretData.DataEntry 1225 - nil, // 24: hsm.v1.SecretMetadata.TagsEntry 1114 + (*HSMInfo)(nil), // 0: hsm.v1.HSMInfo 1115 + (*SecretData)(nil), // 1: hsm.v1.SecretData 1116 + (*SecretMetadata)(nil), // 2: hsm.v1.SecretMetadata 1117 + (*GetInfoRequest)(nil), // 3: hsm.v1.GetInfoRequest 1118 + (*GetInfoResponse)(nil), // 4: hsm.v1.GetInfoResponse 1119 + (*ReadSecretRequest)(nil), // 5: hsm.v1.ReadSecretRequest 1120 + (*ReadSecretResponse)(nil), // 6: hsm.v1.ReadSecretResponse 1121 + (*WriteSecretRequest)(nil), // 7: hsm.v1.WriteSecretRequest 1122 + (*WriteSecretResponse)(nil), // 10: hsm.v1.WriteSecretResponse 1123 + (*ReadMetadataRequest)(nil), // 11: hsm.v1.ReadMetadataRequest 1124 + (*ReadMetadataResponse)(nil), // 12: hsm.v1.ReadMetadataResponse 1125 + (*DeleteSecretRequest)(nil), // 13: hsm.v1.DeleteSecretRequest 1126 + (*DeleteSecretResponse)(nil), // 14: hsm.v1.DeleteSecretResponse 1127 + (*ListSecretsRequest)(nil), // 15: hsm.v1.ListSecretsRequest 1128 + (*ListSecretsResponse)(nil), // 16: hsm.v1.ListSecretsResponse 1129 + (*GetChecksumRequest)(nil), // 17: hsm.v1.GetChecksumRequest 1130 + (*GetChecksumResponse)(nil), // 18: hsm.v1.GetChecksumResponse 1131 + (*IsConnectedRequest)(nil), // 19: hsm.v1.IsConnectedRequest 1132 + (*IsConnectedResponse)(nil), // 20: hsm.v1.IsConnectedResponse 1133 + (*HealthRequest)(nil), // 21: hsm.v1.HealthRequest 1134 + (*HealthResponse)(nil), // 22: hsm.v1.HealthResponse 1135 + nil, // 23: hsm.v1.SecretData.DataEntry 1136 + nil, // 24: hsm.v1.SecretMetadata.TagsEntry 1226 1137 } 1227 1138 var file_hsm_v1_hsm_proto_depIdxs = []int32{ 1228 1139 23, // 0: hsm.v1.SecretData.data:type_name -> hsm.v1.SecretData.DataEntry ··· 1230 1141 0, // 2: hsm.v1.GetInfoResponse.hsm_info:type_name -> hsm.v1.HSMInfo 1231 1142 1, // 3: hsm.v1.ReadSecretResponse.secret_data:type_name -> hsm.v1.SecretData 1232 1143 1, // 4: hsm.v1.WriteSecretRequest.secret_data:type_name -> hsm.v1.SecretData 1233 - 1, // 5: hsm.v1.WriteSecretWithMetadataRequest.secret_data:type_name -> hsm.v1.SecretData 1234 - 2, // 6: hsm.v1.WriteSecretWithMetadataRequest.metadata:type_name -> hsm.v1.SecretMetadata 1144 + 1, // 5: hsm.v1.WriteSecretRequest.secret_data:type_name -> hsm.v1.SecretData 1145 + 2, // 6: hsm.v1.WriteSecretRequest.metadata:type_name -> hsm.v1.SecretMetadata 1235 1146 2, // 7: hsm.v1.ReadMetadataResponse.metadata:type_name -> hsm.v1.SecretMetadata 1236 1147 3, // 8: hsm.v1.HSMAgent.GetInfo:input_type -> hsm.v1.GetInfoRequest 1237 1148 5, // 9: hsm.v1.HSMAgent.ReadSecret:input_type -> hsm.v1.ReadSecretRequest 1238 1149 7, // 10: hsm.v1.HSMAgent.WriteSecret:input_type -> hsm.v1.WriteSecretRequest 1239 - 9, // 11: hsm.v1.HSMAgent.WriteSecretWithMetadata:input_type -> hsm.v1.WriteSecretWithMetadataRequest 1150 + 9, // 11: hsm.v1.HSMAgent.WriteSecret:input_type -> hsm.v1.WriteSecretRequest 1240 1151 11, // 12: hsm.v1.HSMAgent.ReadMetadata:input_type -> hsm.v1.ReadMetadataRequest 1241 1152 13, // 13: hsm.v1.HSMAgent.DeleteSecret:input_type -> hsm.v1.DeleteSecretRequest 1242 1153 15, // 14: hsm.v1.HSMAgent.ListSecrets:input_type -> hsm.v1.ListSecretsRequest ··· 1246 1157 4, // 18: hsm.v1.HSMAgent.GetInfo:output_type -> hsm.v1.GetInfoResponse 1247 1158 6, // 19: hsm.v1.HSMAgent.ReadSecret:output_type -> hsm.v1.ReadSecretResponse 1248 1159 8, // 20: hsm.v1.HSMAgent.WriteSecret:output_type -> hsm.v1.WriteSecretResponse 1249 - 10, // 21: hsm.v1.HSMAgent.WriteSecretWithMetadata:output_type -> hsm.v1.WriteSecretWithMetadataResponse 1160 + 10, // 21: hsm.v1.HSMAgent.WriteSecret:output_type -> hsm.v1.WriteSecretResponse 1250 1161 12, // 22: hsm.v1.HSMAgent.ReadMetadata:output_type -> hsm.v1.ReadMetadataResponse 1251 1162 14, // 23: hsm.v1.HSMAgent.DeleteSecret:output_type -> hsm.v1.DeleteSecretResponse 1252 1163 16, // 24: hsm.v1.HSMAgent.ListSecrets:output_type -> hsm.v1.ListSecretsResponse
+12 -51
hsm/v1/hsm_grpc.pb.go
··· 8 8 9 9 import ( 10 10 context "context" 11 + 11 12 grpc "google.golang.org/grpc" 12 13 codes "google.golang.org/grpc/codes" 13 14 status "google.golang.org/grpc/status" ··· 19 20 const _ = grpc.SupportPackageIsVersion9 20 21 21 22 const ( 22 - HSMAgent_GetInfo_FullMethodName = "/hsm.v1.HSMAgent/GetInfo" 23 - HSMAgent_ReadSecret_FullMethodName = "/hsm.v1.HSMAgent/ReadSecret" 24 - HSMAgent_WriteSecret_FullMethodName = "/hsm.v1.HSMAgent/WriteSecret" 25 - HSMAgent_WriteSecretWithMetadata_FullMethodName = "/hsm.v1.HSMAgent/WriteSecretWithMetadata" 26 - HSMAgent_ReadMetadata_FullMethodName = "/hsm.v1.HSMAgent/ReadMetadata" 27 - HSMAgent_DeleteSecret_FullMethodName = "/hsm.v1.HSMAgent/DeleteSecret" 28 - HSMAgent_ListSecrets_FullMethodName = "/hsm.v1.HSMAgent/ListSecrets" 29 - HSMAgent_GetChecksum_FullMethodName = "/hsm.v1.HSMAgent/GetChecksum" 30 - HSMAgent_IsConnected_FullMethodName = "/hsm.v1.HSMAgent/IsConnected" 31 - HSMAgent_Health_FullMethodName = "/hsm.v1.HSMAgent/Health" 23 + HSMAgent_GetInfo_FullMethodName = "/hsm.v1.HSMAgent/GetInfo" 24 + HSMAgent_ReadSecret_FullMethodName = "/hsm.v1.HSMAgent/ReadSecret" 25 + HSMAgent_WriteSecret_FullMethodName = "/hsm.v1.HSMAgent/WriteSecret" 26 + HSMAgent_ReadMetadata_FullMethodName = "/hsm.v1.HSMAgent/ReadMetadata" 27 + HSMAgent_DeleteSecret_FullMethodName = "/hsm.v1.HSMAgent/DeleteSecret" 28 + HSMAgent_ListSecrets_FullMethodName = "/hsm.v1.HSMAgent/ListSecrets" 29 + HSMAgent_GetChecksum_FullMethodName = "/hsm.v1.HSMAgent/GetChecksum" 30 + HSMAgent_IsConnected_FullMethodName = "/hsm.v1.HSMAgent/IsConnected" 31 + HSMAgent_Health_FullMethodName = "/hsm.v1.HSMAgent/Health" 32 32 ) 33 33 34 34 // HSMAgentClient is the client API for HSMAgent service. ··· 41 41 GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*GetInfoResponse, error) 42 42 // ReadSecret reads secret data from the specified HSM path 43 43 ReadSecret(ctx context.Context, in *ReadSecretRequest, opts ...grpc.CallOption) (*ReadSecretResponse, error) 44 - // WriteSecret writes secret data to the specified HSM path 44 + // WriteSecret writes secret data and metadata to the specified HSM path 45 45 WriteSecret(ctx context.Context, in *WriteSecretRequest, opts ...grpc.CallOption) (*WriteSecretResponse, error) 46 - // WriteSecretWithMetadata writes secret data and metadata to the specified HSM path 47 - WriteSecretWithMetadata(ctx context.Context, in *WriteSecretWithMetadataRequest, opts ...grpc.CallOption) (*WriteSecretWithMetadataResponse, error) 48 46 // ReadMetadata reads metadata for a secret at the given path 49 47 ReadMetadata(ctx context.Context, in *ReadMetadataRequest, opts ...grpc.CallOption) (*ReadMetadataResponse, error) 50 48 // DeleteSecret removes secret data from the specified HSM path ··· 97 95 return out, nil 98 96 } 99 97 100 - func (c *hSMAgentClient) WriteSecretWithMetadata(ctx context.Context, in *WriteSecretWithMetadataRequest, opts ...grpc.CallOption) (*WriteSecretWithMetadataResponse, error) { 101 - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) 102 - out := new(WriteSecretWithMetadataResponse) 103 - err := c.cc.Invoke(ctx, HSMAgent_WriteSecretWithMetadata_FullMethodName, in, out, cOpts...) 104 - if err != nil { 105 - return nil, err 106 - } 107 - return out, nil 108 - } 109 - 110 98 func (c *hSMAgentClient) ReadMetadata(ctx context.Context, in *ReadMetadataRequest, opts ...grpc.CallOption) (*ReadMetadataResponse, error) { 111 99 cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) 112 100 out := new(ReadMetadataResponse) ··· 177 165 GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error) 178 166 // ReadSecret reads secret data from the specified HSM path 179 167 ReadSecret(context.Context, *ReadSecretRequest) (*ReadSecretResponse, error) 180 - // WriteSecret writes secret data to the specified HSM path 168 + // WriteSecret writes secret data and metadata to the specified HSM path 181 169 WriteSecret(context.Context, *WriteSecretRequest) (*WriteSecretResponse, error) 182 - // WriteSecretWithMetadata writes secret data and metadata to the specified HSM path 183 - WriteSecretWithMetadata(context.Context, *WriteSecretWithMetadataRequest) (*WriteSecretWithMetadataResponse, error) 184 170 // ReadMetadata reads metadata for a secret at the given path 185 171 ReadMetadata(context.Context, *ReadMetadataRequest) (*ReadMetadataResponse, error) 186 172 // DeleteSecret removes secret data from the specified HSM path ··· 212 198 func (UnimplementedHSMAgentServer) WriteSecret(context.Context, *WriteSecretRequest) (*WriteSecretResponse, error) { 213 199 return nil, status.Errorf(codes.Unimplemented, "method WriteSecret not implemented") 214 200 } 215 - func (UnimplementedHSMAgentServer) WriteSecretWithMetadata(context.Context, *WriteSecretWithMetadataRequest) (*WriteSecretWithMetadataResponse, error) { 216 - return nil, status.Errorf(codes.Unimplemented, "method WriteSecretWithMetadata not implemented") 217 - } 218 201 func (UnimplementedHSMAgentServer) ReadMetadata(context.Context, *ReadMetadataRequest) (*ReadMetadataResponse, error) { 219 202 return nil, status.Errorf(codes.Unimplemented, "method ReadMetadata not implemented") 220 203 } ··· 308 291 return interceptor(ctx, in, info, handler) 309 292 } 310 293 311 - func _HSMAgent_WriteSecretWithMetadata_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 312 - in := new(WriteSecretWithMetadataRequest) 313 - if err := dec(in); err != nil { 314 - return nil, err 315 - } 316 - if interceptor == nil { 317 - return srv.(HSMAgentServer).WriteSecretWithMetadata(ctx, in) 318 - } 319 - info := &grpc.UnaryServerInfo{ 320 - Server: srv, 321 - FullMethod: HSMAgent_WriteSecretWithMetadata_FullMethodName, 322 - } 323 - handler := func(ctx context.Context, req interface{}) (interface{}, error) { 324 - return srv.(HSMAgentServer).WriteSecretWithMetadata(ctx, req.(*WriteSecretWithMetadataRequest)) 325 - } 326 - return interceptor(ctx, in, info, handler) 327 - } 328 - 329 294 func _HSMAgent_ReadMetadata_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 330 295 in := new(ReadMetadataRequest) 331 296 if err := dec(in); err != nil { ··· 452 417 { 453 418 MethodName: "WriteSecret", 454 419 Handler: _HSMAgent_WriteSecret_Handler, 455 - }, 456 - { 457 - MethodName: "WriteSecretWithMetadata", 458 - Handler: _HSMAgent_WriteSecretWithMetadata_Handler, 459 420 }, 460 421 { 461 422 MethodName: "ReadMetadata",
+2 -6
internal/agent/connection_pool.go
··· 40 40 return cw.client.Initialize(ctx, config) 41 41 } 42 42 43 - func (cw *ClientWrapper) WriteSecret(ctx context.Context, path string, data hsm.SecretData) error { 44 - return cw.client.WriteSecret(ctx, path, data) 45 - } 46 - 47 43 func (cw *ClientWrapper) ReadSecret(ctx context.Context, path string) (hsm.SecretData, error) { 48 44 return cw.client.ReadSecret(ctx, path) 49 45 } ··· 56 52 return cw.client.ListSecrets(ctx, prefix) 57 53 } 58 54 59 - func (cw *ClientWrapper) WriteSecretWithMetadata(ctx context.Context, path string, data hsm.SecretData, metadata *hsm.SecretMetadata) error { 60 - return cw.client.WriteSecretWithMetadata(ctx, path, data, metadata) 55 + func (cw *ClientWrapper) WriteSecret(ctx context.Context, path string, data hsm.SecretData, metadata *hsm.SecretMetadata) error { 56 + return cw.client.WriteSecret(ctx, path, data, metadata) 61 57 } 62 58 63 59 func (cw *ClientWrapper) ReadMetadata(ctx context.Context, path string) (*hsm.SecretMetadata, error) {
+4 -28
internal/agent/grpc_client.go
··· 137 137 return secretData, nil 138 138 } 139 139 140 - // WriteSecret writes secret data to the specified HSM path 141 - func (c *GRPCClient) WriteSecret(ctx context.Context, path string, data hsm.SecretData) error { 142 - ctx, cancel := context.WithTimeout(ctx, c.timeout) 143 - defer cancel() 144 - 145 - // Convert hsm.SecretData to protobuf format 146 - pbData := make(map[string][]byte) 147 - for key, value := range data { 148 - pbData[key] = value 149 - } 150 - 151 - _, err := c.client.WriteSecret(ctx, &hsmv1.WriteSecretRequest{ 152 - Path: path, 153 - SecretData: &hsmv1.SecretData{ 154 - Data: pbData, 155 - }, 156 - }) 157 - if err != nil { 158 - return fmt.Errorf("failed to write secret: %w", err) 159 - } 160 - 161 - return nil 162 - } 163 - 164 - // WriteSecretWithMetadata writes secret data and metadata to the specified HSM path 165 - func (c *GRPCClient) WriteSecretWithMetadata(ctx context.Context, path string, data hsm.SecretData, metadata *hsm.SecretMetadata) error { 140 + // WriteSecret writes secret data and metadata to the specified HSM path 141 + func (c *GRPCClient) WriteSecret(ctx context.Context, path string, data hsm.SecretData, metadata *hsm.SecretMetadata) error { 166 142 ctx, cancel := context.WithTimeout(ctx, c.timeout) 167 143 defer cancel() 168 144 ··· 172 148 pbData[key] = value 173 149 } 174 150 175 - req := &hsmv1.WriteSecretWithMetadataRequest{ 151 + req := &hsmv1.WriteSecretRequest{ 176 152 Path: path, 177 153 SecretData: &hsmv1.SecretData{ 178 154 Data: pbData, ··· 191 167 } 192 168 } 193 169 194 - _, err := c.client.WriteSecretWithMetadata(ctx, req) 170 + _, err := c.client.WriteSecret(ctx, req) 195 171 if err != nil { 196 172 return fmt.Errorf("failed to write secret with metadata: %w", err) 197 173 }
+34 -36
internal/agent/grpc_client_test.go
··· 113 113 } 114 114 115 115 m.secrets[req.Path] = req.SecretData.Data 116 - return &hsmv1.WriteSecretResponse{}, nil 117 - } 118 - 119 - func (m *mockHSMAgentServer) WriteSecretWithMetadata(ctx context.Context, req *hsmv1.WriteSecretWithMetadataRequest) (*hsmv1.WriteSecretWithMetadataResponse, error) { 120 - if m.returnError { 121 - return nil, status.Error(m.errorCode, m.errorMessage) 122 - } 123 - 124 - if req.Path == "" { 125 - return nil, status.Error(codes.InvalidArgument, "path is required") 126 - } 127 - 128 - if req.SecretData == nil { 129 - return nil, status.Error(codes.InvalidArgument, "secret data is required") 130 - } 131 - 132 - m.secrets[req.Path] = req.SecretData.Data 133 116 if req.Metadata != nil { 134 117 m.metadata[req.Path] = req.Metadata 135 118 } 136 119 137 - return &hsmv1.WriteSecretWithMetadataResponse{}, nil 120 + return &hsmv1.WriteSecretResponse{}, nil 138 121 } 139 122 140 123 func (m *mockHSMAgentServer) ReadMetadata(ctx context.Context, req *hsmv1.ReadMetadataRequest) (*hsmv1.ReadMetadataResponse, error) { ··· 307 290 assert.Equal(t, "1.0.0", info.FirmwareVersion) 308 291 }) 309 292 310 - t.Run("WriteSecret", func(t *testing.T) { 311 - secretData := hsm.SecretData{ 312 - "username": []byte("testuser"), 313 - "password": []byte("testpass"), 314 - } 315 - 316 - err := client.WriteSecret(ctx, "test-secret", secretData) 317 - assert.NoError(t, err) 318 - }) 319 - 320 293 t.Run("ReadSecret", func(t *testing.T) { 321 294 // First write a secret 322 295 secretData := hsm.SecretData{ 323 296 "api_key": []byte("secret-key"), 324 297 "token": []byte("secret-token"), 325 298 } 326 - err := client.WriteSecret(ctx, "read-test", secretData) 299 + metadata := &hsm.SecretMetadata{ 300 + Description: "A test secret", 301 + Labels: map[string]string{"category": "test", "env": "demo"}, 302 + Format: "raw", 303 + DataType: "plaintext", 304 + CreatedAt: "2025-01-01T00:00:00Z", 305 + Source: "test", 306 + } 307 + 308 + err := client.WriteSecret(ctx, "read-test", secretData, metadata) 327 309 require.NoError(t, err) 328 310 329 311 // Then read it back ··· 332 314 assert.Equal(t, secretData, readData) 333 315 }) 334 316 335 - t.Run("WriteSecretWithMetadata", func(t *testing.T) { 317 + t.Run("WriteSecret", func(t *testing.T) { 336 318 secretData := hsm.SecretData{ 337 319 "data": []byte("test-data"), 338 320 } ··· 345 327 Source: "test", 346 328 } 347 329 348 - err := client.WriteSecretWithMetadata(ctx, "metadata-test", secretData, metadata) 330 + err := client.WriteSecret(ctx, "metadata-test", secretData, metadata) 349 331 assert.NoError(t, err) 350 332 }) 351 333 ··· 360 342 CreatedAt: "2025-01-01T12:00:00Z", 361 343 Source: "unit-test", 362 344 } 363 - err := client.WriteSecretWithMetadata(ctx, "metadata-read-test", secretData, metadata) 345 + err := client.WriteSecret(ctx, "metadata-read-test", secretData, metadata) 364 346 require.NoError(t, err) 365 347 366 348 // Then read metadata ··· 376 358 t.Run("DeleteSecret", func(t *testing.T) { 377 359 // First write a secret 378 360 secretData := hsm.SecretData{"temp": []byte("data")} 379 - err := client.WriteSecret(ctx, "delete-test", secretData) 361 + metadata := &hsm.SecretMetadata{ 362 + Description: "A test secret", 363 + Labels: map[string]string{"category": "test", "env": "demo"}, 364 + Format: "raw", 365 + DataType: "plaintext", 366 + CreatedAt: "2025-01-01T00:00:00Z", 367 + Source: "test", 368 + } 369 + err := client.WriteSecret(ctx, "delete-test", secretData, metadata) 380 370 require.NoError(t, err) 381 371 382 372 // Verify it exists ··· 399 389 "list/secret2": {"data": []byte("data2")}, 400 390 "other/secret": {"data": []byte("data3")}, 401 391 } 392 + metadata := &hsm.SecretMetadata{ 393 + Description: "A test secret", 394 + Labels: map[string]string{"category": "test", "env": "demo"}, 395 + Format: "raw", 396 + DataType: "plaintext", 397 + CreatedAt: "2025-01-01T00:00:00Z", 398 + Source: "test", 399 + } 402 400 403 401 for path, data := range secrets { 404 - err := client.WriteSecret(ctx, path, data) 402 + err := client.WriteSecret(ctx, path, data, metadata) 405 403 require.NoError(t, err) 406 404 } 407 405 ··· 491 489 assert.Error(t, err) 492 490 assert.Contains(t, err.Error(), "failed to read secret") 493 491 494 - err = client.WriteSecret(ctx, "test", hsm.SecretData{"key": []byte("value")}) 492 + err = client.WriteSecret(ctx, "test", hsm.SecretData{"key": []byte("value")}, nil) 495 493 assert.Error(t, err) 496 494 assert.Contains(t, err.Error(), "failed to write secret") 497 495 }) ··· 513 511 _, err := client.ReadSecret(ctx, "") 514 512 assert.Error(t, err) 515 513 516 - err = client.WriteSecret(ctx, "", hsm.SecretData{"key": []byte("value")}) 514 + err = client.WriteSecret(ctx, "", hsm.SecretData{"key": []byte("value")}, nil) 517 515 assert.Error(t, err) 518 516 519 517 err = client.DeleteSecret(ctx, "")
+6 -6
internal/agent/grpc_integration_test.go
··· 54 54 } 55 55 56 56 for path, data := range testData { 57 - err := mockHSMClient.WriteSecret(ctx, path, data) 57 + err := mockHSMClient.WriteSecret(ctx, path, data, nil) 58 58 require.NoError(t, err) 59 59 } 60 60 ··· 123 123 "db_password": []byte("secret123"), 124 124 } 125 125 126 - err := client.WriteSecret(ctx, "new-secret", newSecret) 126 + err := client.WriteSecret(ctx, "new-secret", newSecret, nil) 127 127 require.NoError(t, err) 128 128 129 129 // Read it back ··· 132 132 assert.Equal(t, newSecret, readData) 133 133 }) 134 134 135 - t.Run("WriteSecretWithMetadata", func(t *testing.T) { 135 + t.Run("WriteSecret", func(t *testing.T) { 136 136 secretData := hsm.SecretData{ 137 137 "certificate": []byte("-----BEGIN CERTIFICATE-----"), 138 138 } ··· 145 145 Source: "integration-test", 146 146 } 147 147 148 - err := client.WriteSecretWithMetadata(ctx, "ssl-cert", secretData, metadata) 148 + err := client.WriteSecret(ctx, "ssl-cert", secretData, metadata) 149 149 require.NoError(t, err) 150 150 151 151 // Verify the data ··· 194 194 t.Run("DeleteSecret", func(t *testing.T) { 195 195 // First create a secret to delete 196 196 tempData := hsm.SecretData{"temp": []byte("data")} 197 - err := client.WriteSecret(ctx, "temp-secret", tempData) 197 + err := client.WriteSecret(ctx, "temp-secret", tempData, nil) 198 198 require.NoError(t, err) 199 199 200 200 // Verify it exists ··· 222 222 assert.Error(t, err) 223 223 assert.Contains(t, err.Error(), "path is required") 224 224 225 - err = client.WriteSecret(ctx, "", hsm.SecretData{"key": []byte("value")}) 225 + err = client.WriteSecret(ctx, "", hsm.SecretData{"key": []byte("value")}, nil) 226 226 assert.Error(t, err) 227 227 assert.Contains(t, err.Error(), "path is required") 228 228 })
+3 -37
internal/agent/grpc_server.go
··· 175 175 }, nil 176 176 } 177 177 178 - // WriteSecret writes secret data to the specified HSM path 178 + // WriteSecret writes secret data and metadata to the specified HSM path 179 179 func (s *GRPCServer) WriteSecret(ctx context.Context, req *hsmv1.WriteSecretRequest) (*hsmv1.WriteSecretResponse, error) { 180 180 if req.Path == "" { 181 181 return nil, status.Error(codes.InvalidArgument, "path is required") ··· 195 195 hsmData[key] = value 196 196 } 197 197 198 - if err := s.hsmClient.WriteSecret(ctx, req.Path, hsmData); err != nil { 199 - s.logger.Error(err, "Failed to write secret", "path", req.Path) 200 - return nil, status.Errorf(codes.Internal, "failed to write secret: %v", err) 201 - } 202 - 203 - s.logger.V(1).Info("Successfully wrote secret", "path", req.Path, "keys_count", len(hsmData)) 204 - return &hsmv1.WriteSecretResponse{}, nil 205 - } 206 - 207 - // WriteSecretWithMetadata writes secret data and metadata to the specified HSM path 208 - func (s *GRPCServer) WriteSecretWithMetadata(ctx context.Context, req *hsmv1.WriteSecretWithMetadataRequest) (*hsmv1.WriteSecretWithMetadataResponse, error) { 209 - if req.Path == "" { 210 - return nil, status.Error(codes.InvalidArgument, "path is required") 211 - } 212 - 213 - if req.SecretData == nil { 214 - return nil, status.Error(codes.InvalidArgument, "secret data is required") 215 - } 216 - 217 - if s.hsmClient == nil || !s.hsmClient.IsConnected() { 218 - return nil, status.Error(codes.Unavailable, "HSM client not connected") 219 - } 220 - 221 - // Convert protobuf format to hsm.SecretData 222 - hsmData := make(hsm.SecretData) 223 - for key, value := range req.SecretData.Data { 224 - hsmData[key] = value 225 - } 226 - 227 198 // Convert protobuf metadata to hsm.SecretMetadata 228 199 var metadata *hsm.SecretMetadata 229 200 if req.Metadata != nil { ··· 237 208 } 238 209 } 239 210 240 - if err := s.hsmClient.WriteSecretWithMetadata(ctx, req.Path, hsmData, metadata); err != nil { 211 + if err := s.hsmClient.WriteSecret(ctx, req.Path, hsmData, metadata); err != nil { 241 212 s.logger.Error(err, "Failed to write secret with metadata", "path", req.Path) 242 213 return nil, status.Errorf(codes.Internal, "failed to write secret with metadata: %v", err) 243 214 } 244 215 245 216 s.logger.V(1).Info("Successfully wrote secret with metadata", "path", req.Path, "keys_count", len(hsmData)) 246 - return &hsmv1.WriteSecretWithMetadataResponse{}, nil 217 + return &hsmv1.WriteSecretResponse{}, nil 247 218 } 248 219 249 220 // ReadMetadata reads metadata for a secret at the given path ··· 413 384 case *hsmv1.ReadSecretRequest: 414 385 logFields = append(logFields, "path", r.Path) 415 386 case *hsmv1.WriteSecretRequest: 416 - logFields = append(logFields, "path", r.Path) 417 - if r.SecretData != nil { 418 - logFields = append(logFields, "keys_count", len(r.SecretData.Data)) 419 - } 420 - case *hsmv1.WriteSecretWithMetadataRequest: 421 387 logFields = append(logFields, "path", r.Path) 422 388 if r.SecretData != nil { 423 389 logFields = append(logFields, "keys_count", len(r.SecretData.Data))
+14 -87
internal/agent/grpc_server_test.go
··· 87 87 "username": []byte("testuser"), 88 88 "password": []byte("testpass"), 89 89 } 90 - err = mockClient.WriteSecret(ctx, "test-secret", testData) 90 + err = mockClient.WriteSecret(ctx, "test-secret", testData, nil) 91 91 require.NoError(t, err) 92 92 93 93 t.Run("success", func(t *testing.T) { ··· 126 126 err := mockClient.Initialize(ctx, hsm.Config{}) 127 127 require.NoError(t, err) 128 128 129 - t.Run("success", func(t *testing.T) { 130 - req := &hsmv1.WriteSecretRequest{ 131 - Path: "new-secret", 132 - SecretData: &hsmv1.SecretData{ 133 - Data: map[string][]byte{ 134 - "api_key": []byte("secret-key"), 135 - "token": []byte("secret-token"), 136 - }, 137 - }, 138 - } 139 - 140 - resp, err := server.WriteSecret(ctx, req) 141 - require.NoError(t, err) 142 - assert.NotNil(t, resp) 143 - 144 - // Verify it was written 145 - data, err := mockClient.ReadSecret(ctx, "new-secret") 146 - require.NoError(t, err) 147 - assert.Equal(t, []byte("secret-key"), data["api_key"]) 148 - assert.Equal(t, []byte("secret-token"), data["token"]) 149 - }) 150 - 151 - t.Run("empty path", func(t *testing.T) { 129 + t.Run("success with metadata", func(t *testing.T) { 152 130 req := &hsmv1.WriteSecretRequest{ 153 - Path: "", 154 - SecretData: &hsmv1.SecretData{ 155 - Data: map[string][]byte{"key": []byte("value")}, 156 - }, 157 - } 158 - 159 - resp, err := server.WriteSecret(ctx, req) 160 - assert.Error(t, err) 161 - assert.Nil(t, resp) 162 - assert.Contains(t, err.Error(), "path is required") 163 - }) 164 - 165 - t.Run("nil secret data", func(t *testing.T) { 166 - req := &hsmv1.WriteSecretRequest{ 167 - Path: "test-path", 168 - SecretData: nil, 169 - } 170 - 171 - resp, err := server.WriteSecret(ctx, req) 172 - assert.Error(t, err) 173 - assert.Nil(t, resp) 174 - assert.Contains(t, err.Error(), "secret data is required") 175 - }) 176 - 177 - t.Run("client not connected", func(t *testing.T) { 178 - server := NewGRPCServer(nil, 9090, 8080, logger) 179 - req := &hsmv1.WriteSecretRequest{ 180 - Path: "test-secret", 181 - SecretData: &hsmv1.SecretData{ 182 - Data: map[string][]byte{"key": []byte("value")}, 183 - }, 184 - } 185 - 186 - resp, err := server.WriteSecret(ctx, req) 187 - assert.Error(t, err) 188 - assert.Nil(t, resp) 189 - assert.Contains(t, err.Error(), "HSM client not connected") 190 - }) 191 - } 192 - 193 - func TestGRPCServerWriteSecretWithMetadata(t *testing.T) { 194 - mockClient := hsm.NewMockClient() 195 - logger := logr.Discard() 196 - server := NewGRPCServer(mockClient, 9090, 8080, logger) 197 - 198 - ctx := context.Background() 199 - err := mockClient.Initialize(ctx, hsm.Config{}) 200 - require.NoError(t, err) 201 - 202 - t.Run("success with metadata", func(t *testing.T) { 203 - req := &hsmv1.WriteSecretWithMetadataRequest{ 204 131 Path: "secret-with-metadata", 205 132 SecretData: &hsmv1.SecretData{ 206 133 Data: map[string][]byte{ ··· 217 144 }, 218 145 } 219 146 220 - resp, err := server.WriteSecretWithMetadata(ctx, req) 147 + resp, err := server.WriteSecret(ctx, req) 221 148 require.NoError(t, err) 222 149 assert.NotNil(t, resp) 223 150 ··· 236 163 }) 237 164 238 165 t.Run("success without metadata", func(t *testing.T) { 239 - req := &hsmv1.WriteSecretWithMetadataRequest{ 166 + req := &hsmv1.WriteSecretRequest{ 240 167 Path: "secret-no-metadata", 241 168 SecretData: &hsmv1.SecretData{ 242 169 Data: map[string][]byte{"data": []byte("test")}, ··· 244 171 Metadata: nil, 245 172 } 246 173 247 - resp, err := server.WriteSecretWithMetadata(ctx, req) 174 + resp, err := server.WriteSecret(ctx, req) 248 175 require.NoError(t, err) 249 176 assert.NotNil(t, resp) 250 177 }) 251 178 252 179 t.Run("validation errors", func(t *testing.T) { 253 180 // Empty path 254 - req := &hsmv1.WriteSecretWithMetadataRequest{ 181 + req := &hsmv1.WriteSecretRequest{ 255 182 Path: "", 256 183 SecretData: &hsmv1.SecretData{ 257 184 Data: map[string][]byte{"key": []byte("value")}, 258 185 }, 259 186 } 260 187 261 - resp, err := server.WriteSecretWithMetadata(ctx, req) 188 + resp, err := server.WriteSecret(ctx, req) 262 189 assert.Error(t, err) 263 190 assert.Nil(t, resp) 264 191 assert.Contains(t, err.Error(), "path is required") 265 192 266 193 // Nil secret data 267 - req = &hsmv1.WriteSecretWithMetadataRequest{ 194 + req = &hsmv1.WriteSecretRequest{ 268 195 Path: "test", 269 196 SecretData: nil, 270 197 } 271 198 272 - resp, err = server.WriteSecretWithMetadata(ctx, req) 199 + resp, err = server.WriteSecret(ctx, req) 273 200 assert.Error(t, err) 274 201 assert.Nil(t, resp) 275 202 assert.Contains(t, err.Error(), "secret data is required") ··· 295 222 CreatedAt: "2025-01-01T12:00:00Z", 296 223 Source: "unit-test", 297 224 } 298 - err = mockClient.WriteSecretWithMetadata(ctx, "metadata-test", testData, metadata) 225 + err = mockClient.WriteSecret(ctx, "metadata-test", testData, metadata) 299 226 require.NoError(t, err) 300 227 301 228 t.Run("success", func(t *testing.T) { ··· 311 238 312 239 t.Run("no metadata", func(t *testing.T) { 313 240 // Write secret without metadata 314 - err := mockClient.WriteSecret(ctx, "no-metadata", hsm.SecretData{"key": []byte("value")}) 241 + err := mockClient.WriteSecret(ctx, "no-metadata", hsm.SecretData{"key": []byte("value")}, nil) 315 242 require.NoError(t, err) 316 243 317 244 req := &hsmv1.ReadMetadataRequest{Path: "no-metadata"} ··· 342 269 343 270 // Write test secret 344 271 testData := hsm.SecretData{"temp": []byte("data")} 345 - err = mockClient.WriteSecret(ctx, "delete-test", testData) 272 + err = mockClient.WriteSecret(ctx, "delete-test", testData, nil) 346 273 require.NoError(t, err) 347 274 348 275 t.Run("success", func(t *testing.T) { ··· 388 315 } 389 316 390 317 for path, data := range secrets { 391 - err := mockClient.WriteSecret(ctx, path, data) 318 + err := mockClient.WriteSecret(ctx, path, data, nil) 392 319 require.NoError(t, err) 393 320 } 394 321 ··· 425 352 426 353 t.Run("success", func(t *testing.T) { 427 354 // Write a secret first since checksum needs data to exist 428 - err := mockClient.WriteSecret(ctx, "checksum-test", hsm.SecretData{"data": []byte("test")}) 355 + err := mockClient.WriteSecret(ctx, "checksum-test", hsm.SecretData{"data": []byte("test")}, nil) 429 356 require.NoError(t, err) 430 357 431 358 req := &hsmv1.GetChecksumRequest{Path: "checksum-test"}
+1 -6
internal/api/mock_test.go
··· 74 74 return args.Get(0).(hsm.SecretData), args.Error(1) 75 75 } 76 76 77 - func (m *MockHSMClient) WriteSecret(ctx context.Context, path string, data hsm.SecretData) error { 78 - args := m.Called(ctx, path, data) 79 - return args.Error(0) 80 - } 81 - 82 - func (m *MockHSMClient) WriteSecretWithMetadata(ctx context.Context, path string, data hsm.SecretData, metadata *hsm.SecretMetadata) error { 77 + func (m *MockHSMClient) WriteSecret(ctx context.Context, path string, data hsm.SecretData, metadata *hsm.SecretMetadata) error { 83 78 args := m.Called(ctx, path, data, metadata) 84 79 return args.Error(0) 85 80 }
+2 -4
internal/api/proxy_client.go
··· 790 790 791 791 var err error 792 792 if metadata != nil { 793 - err = client.WriteSecretWithMetadata(ctx, path, data, metadata) 794 - } else { 795 - err = client.WriteSecret(ctx, path, data) 793 + err = client.WriteSecret(ctx, path, data, metadata) 796 794 } 797 795 798 796 resultsMutex.Lock() ··· 840 838 841 839 // Write tombstone metadata (empty data with deletion markers) 842 840 emptyData := make(hsm.SecretData) 843 - err = client.WriteSecretWithMetadata(ctx, path, emptyData, tombstoneMetadata) 841 + err = client.WriteSecret(ctx, path, emptyData, tombstoneMetadata) 844 842 845 843 resultsMutex.Lock() 846 844 results[deviceName] = WriteResult{
+69 -3
internal/controller/hsmsecret_controller.go
··· 133 133 // Add finalizer if not present 134 134 if !controllerutil.ContainsFinalizer(&hsmSecret, HSMSecretFinalizer) { 135 135 controllerutil.AddFinalizer(&hsmSecret, HSMSecretFinalizer) 136 - if err := r.Update(ctx, &hsmSecret); err != nil { 136 + if err := r.updateWithRetry(ctx, &hsmSecret); err != nil { 137 137 logger.Error(err, "Failed to add finalizer") 138 138 return ctrl.Result{}, err 139 139 } ··· 282 282 UID: k8sSecret.UID, 283 283 } 284 284 285 - if err := r.Status().Update(ctx, hsmSecret); err != nil { 285 + if err := r.updateStatusWithRetry(ctx, hsmSecret); err != nil { 286 286 logger.Error(err, "Failed to update HSMSecret status") 287 287 return ctrl.Result{}, err 288 288 } ··· 324 324 325 325 // Remove finalizer 326 326 controllerutil.RemoveFinalizer(hsmSecret, HSMSecretFinalizer) 327 - if err := r.Update(ctx, hsmSecret); err != nil { 327 + if err := r.updateWithRetry(ctx, hsmSecret); err != nil { 328 328 logger.Error(err, "Failed to remove finalizer") 329 329 return err 330 330 } ··· 485 485 result := make(hsm.SecretData) 486 486 maps.Copy(result, secretData) 487 487 return result 488 + } 489 + 490 + // updateWithRetry updates the HSMSecret with conflict retry logic 491 + func (r *HSMSecretReconciler) updateWithRetry(ctx context.Context, hsmSecret *hsmv1alpha1.HSMSecret) error { 492 + logger := log.FromContext(ctx) 493 + maxRetries := 3 494 + 495 + for attempt := range maxRetries { 496 + if err := r.Update(ctx, hsmSecret); err != nil { 497 + if errors.IsConflict(err) && attempt < maxRetries-1 { 498 + logger.V(1).Info("Update conflict, retrying", "attempt", attempt+1) 499 + // Fresh read of the HSMSecret to get latest resourceVersion 500 + var freshSecret hsmv1alpha1.HSMSecret 501 + if err := r.Get(ctx, types.NamespacedName{Name: hsmSecret.Name, Namespace: hsmSecret.Namespace}, &freshSecret); err != nil { 502 + if errors.IsNotFound(err) { 503 + return nil // Resource was deleted 504 + } 505 + return fmt.Errorf("failed to get fresh HSMSecret: %w", err) 506 + } 507 + // Copy our changes to the fresh copy 508 + freshSecret.Finalizers = hsmSecret.Finalizers 509 + *hsmSecret = freshSecret 510 + continue 511 + } 512 + return err 513 + } 514 + 515 + // Success 516 + return nil 517 + } 518 + 519 + return fmt.Errorf("failed to update after %d attempts", maxRetries) 520 + } 521 + 522 + // updateStatusWithRetry updates the HSMSecret status with conflict retry logic 523 + func (r *HSMSecretReconciler) updateStatusWithRetry(ctx context.Context, hsmSecret *hsmv1alpha1.HSMSecret) error { 524 + logger := log.FromContext(ctx) 525 + maxRetries := 3 526 + 527 + for attempt := range maxRetries { 528 + // Fresh read of the HSMSecret to get latest resourceVersion 529 + var freshSecret hsmv1alpha1.HSMSecret 530 + if err := r.Get(ctx, types.NamespacedName{Name: hsmSecret.Name, Namespace: hsmSecret.Namespace}, &freshSecret); err != nil { 531 + if errors.IsNotFound(err) { 532 + return nil // Resource was deleted, no need to update status 533 + } 534 + return fmt.Errorf("failed to get fresh HSMSecret: %w", err) 535 + } 536 + 537 + // Copy the status from our working copy to the fresh copy 538 + freshSecret.Status = hsmSecret.Status 539 + 540 + // Attempt to update status 541 + if err := r.Status().Update(ctx, &freshSecret); err != nil { 542 + if errors.IsConflict(err) && attempt < maxRetries-1 { 543 + logger.V(1).Info("Status update conflict, retrying", "attempt", attempt+1) 544 + continue 545 + } 546 + return err 547 + } 548 + 549 + // Success 550 + return nil 551 + } 552 + 553 + return fmt.Errorf("failed to update status after %d attempts", maxRetries) 488 554 } 489 555 490 556 // updateStatus updates the HSMSecret status
+1 -1
internal/controller/hsmsecret_grpc_test.go
··· 78 78 "password": []byte("testpass123"), 79 79 "api_key": []byte("secret-api-key"), 80 80 } 81 - err = mockHSMClient.WriteSecret(context.Background(), "existing-secret", testData) 81 + err = mockHSMClient.WriteSecret(context.Background(), "existing-secret", testData, nil) 82 82 require.NoError(t, err) 83 83 84 84 // Start gRPC server
+2 -5
internal/hsm/client.go
··· 59 59 // ReadSecret reads secret data from the specified HSM path 60 60 ReadSecret(ctx context.Context, path string) (SecretData, error) 61 61 62 - // WriteSecret writes secret data to the specified HSM path 63 - WriteSecret(ctx context.Context, path string, data SecretData) error 64 - 65 - // WriteSecretWithMetadata writes secret data and metadata to the specified HSM path 66 - WriteSecretWithMetadata(ctx context.Context, path string, data SecretData, metadata *SecretMetadata) error 62 + // WriteSecret writes secret data and metadata to the specified HSM path 63 + WriteSecret(ctx context.Context, path string, data SecretData, metadata *SecretMetadata) error 67 64 68 65 // ReadMetadata reads metadata for a secret at the given path 69 66 ReadMetadata(ctx context.Context, path string) (*SecretMetadata, error)
+6 -7
internal/hsm/client_test.go
··· 693 693 }{ 694 694 {"GetInfo", func() error { _, err := client.GetInfo(ctx); return err }}, 695 695 {"ReadSecret", func() error { _, err := client.ReadSecret(ctx, "test"); return err }}, 696 - {"WriteSecret", func() error { return client.WriteSecret(ctx, "test", SecretData{}) }}, 697 - {"WriteSecretWithMetadata", func() error { return client.WriteSecretWithMetadata(ctx, "test", SecretData{}, nil) }}, 696 + {"WriteSecret", func() error { return client.WriteSecret(ctx, "test", SecretData{}, nil) }}, 698 697 {"ReadMetadata", func() error { _, err := client.ReadMetadata(ctx, "test"); return err }}, 699 698 {"DeleteSecret", func() error { return client.DeleteSecret(ctx, "test") }}, 700 699 {"ListSecrets", func() error { _, err := client.ListSecrets(ctx, ""); return err }}, ··· 720 719 longPath := strings.Repeat("very-long-path-segment/", 50) + "final-secret" 721 720 data := SecretData{"key": []byte("value")} 722 721 723 - err = client.WriteSecret(ctx, longPath, data) 722 + err = client.WriteSecret(ctx, longPath, data, nil) 724 723 assert.NoError(t, err, "should handle long paths") 725 724 726 725 readData, err := client.ReadSecret(ctx, longPath) ··· 746 745 t.Run(fmt.Sprintf("path: %s", path), func(t *testing.T) { 747 746 data := SecretData{"key": []byte("test-value")} 748 747 749 - err := client.WriteSecret(ctx, path, data) 748 + err := client.WriteSecret(ctx, path, data, nil) 750 749 assert.NoError(t, err, "should handle special character paths") 751 750 752 751 readData, err := client.ReadSecret(ctx, path) ··· 772 771 "password": []byte("original-pass"), 773 772 } 774 773 775 - err = client.WriteSecret(ctx, "test/memory-safety", originalData) 774 + err = client.WriteSecret(ctx, "test/memory-safety", originalData, nil) 776 775 require.NoError(t, err) 777 776 778 777 // Read and modify the returned data ··· 801 800 require.NoError(t, err) 802 801 803 802 // Test writing nil data 804 - err = client.WriteSecret(ctx, "test/nil-data", nil) 803 + err = client.WriteSecret(ctx, "test/nil-data", nil, nil) 805 804 assert.NoError(t, err) 806 805 807 806 readData, err := client.ReadSecret(ctx, "test/nil-data") ··· 816 815 "real": []byte("value"), 817 816 } 818 817 819 - err = client.WriteSecret(ctx, "test/with-nils", dataWithNils) 818 + err = client.WriteSecret(ctx, "test/with-nils", dataWithNils, nil) 820 819 assert.NoError(t, err) 821 820 822 821 readData, err = client.ReadSecret(ctx, "test/with-nils")
+2 -12
internal/hsm/mock_client.go
··· 123 123 return result, nil 124 124 } 125 125 126 - // WriteSecret writes secret data to mock storage 127 - func (m *MockClient) WriteSecret(ctx context.Context, path string, data SecretData) error { 126 + // WriteSecret writes secret data and metadata to the specified HSM path 127 + func (m *MockClient) WriteSecret(ctx context.Context, path string, data SecretData, metadata *SecretMetadata) error { 128 128 m.mutex.Lock() 129 129 defer m.mutex.Unlock() 130 130 ··· 141 141 m.secrets[path] = stored 142 142 m.logger.Info("Wrote secret to mock HSM", 143 143 "path", path, "keys", len(data)) 144 - return nil 145 - } 146 - 147 - // WriteSecretWithMetadata writes secret data and metadata to the specified HSM path 148 - func (m *MockClient) WriteSecretWithMetadata(ctx context.Context, path string, data SecretData, metadata *SecretMetadata) error { 149 - if err := m.WriteSecret(ctx, path, data); err != nil { 150 - return err 151 - } 152 144 153 145 if metadata != nil { 154 - m.mutex.Lock() 155 - defer m.mutex.Unlock() 156 146 m.metadata[path] = metadata 157 147 m.logger.V(1).Info("Wrote metadata to mock HSM", "path", path) 158 148 }
+10 -58
internal/hsm/mock_client_test.go
··· 131 131 client := NewMockClient() 132 132 ctx := context.Background() 133 133 134 - t.Run("not connected", func(t *testing.T) { 135 - data := SecretData{"key": []byte("value")} 136 - err := client.WriteSecret(ctx, "test/path", data) 137 - assert.Error(t, err) 138 - assert.Contains(t, err.Error(), "HSM not connected") 139 - }) 140 - 141 - t.Run("write new secret", func(t *testing.T) { 142 - err := client.Initialize(ctx, DefaultConfig()) 143 - require.NoError(t, err) 144 - 145 - data := SecretData{ 146 - "api_key": []byte("secret-key"), 147 - "token": []byte("secret-token"), 148 - } 149 - 150 - err = client.WriteSecret(ctx, "test/new-secret", data) 151 - require.NoError(t, err) 152 - 153 - // Verify it was written 154 - readData, err := client.ReadSecret(ctx, "test/new-secret") 155 - require.NoError(t, err) 156 - assert.Equal(t, data, readData) 157 - }) 158 - 159 - t.Run("overwrite existing secret", func(t *testing.T) { 160 - err := client.Initialize(ctx, DefaultConfig()) 161 - require.NoError(t, err) 162 - 163 - originalData := SecretData{"key": []byte("original")} 164 - newData := SecretData{"key": []byte("updated")} 165 - 166 - err = client.WriteSecret(ctx, "test/overwrite", originalData) 167 - require.NoError(t, err) 168 - 169 - err = client.WriteSecret(ctx, "test/overwrite", newData) 170 - require.NoError(t, err) 171 - 172 - readData, err := client.ReadSecret(ctx, "test/overwrite") 173 - require.NoError(t, err) 174 - assert.Equal(t, newData, readData) 175 - }) 176 - } 177 - 178 - func TestMockClientWriteSecretWithMetadata(t *testing.T) { 179 - client := NewMockClient() 180 - ctx := context.Background() 181 - 182 134 err := client.Initialize(ctx, DefaultConfig()) 183 135 require.NoError(t, err) 184 136 ··· 192 144 Source: "test", 193 145 } 194 146 195 - err = client.WriteSecretWithMetadata(ctx, "test/with-metadata", data, metadata) 147 + err = client.WriteSecret(ctx, "test/with-metadata", data, metadata) 196 148 require.NoError(t, err) 197 149 198 150 // Verify data was written ··· 237 189 Description: "Test metadata", 238 190 Labels: map[string]string{"type": "test"}, 239 191 } 240 - err = client.WriteSecretWithMetadata(ctx, "test/metadata", data, metadata) 192 + err = client.WriteSecret(ctx, "test/metadata", data, metadata) 241 193 require.NoError(t, err) 242 194 243 195 // Read back metadata ··· 271 223 require.NoError(t, err) 272 224 273 225 data := SecretData{"key": []byte("value")} 274 - err = client.WriteSecret(ctx, "test/delete", data) 226 + err = client.WriteSecret(ctx, "test/delete", data, nil) 275 227 require.NoError(t, err) 276 228 277 229 // Verify it exists ··· 293 245 294 246 data := SecretData{"key": []byte("value")} 295 247 metadata := &SecretMetadata{Description: "To be deleted"} 296 - err = client.WriteSecretWithMetadata(ctx, "test/delete-with-meta", data, metadata) 248 + err = client.WriteSecret(ctx, "test/delete-with-meta", data, metadata) 297 249 require.NoError(t, err) 298 250 299 251 // Delete the secret ··· 335 287 require.NoError(t, err) 336 288 337 289 // Add some test secrets 338 - err = client.WriteSecret(ctx, "app/config", SecretData{"key": []byte("value")}) 290 + err = client.WriteSecret(ctx, "app/config", SecretData{"key": []byte("value")}, nil) 339 291 require.NoError(t, err) 340 - err = client.WriteSecret(ctx, "app/database", SecretData{"key": []byte("value")}) 292 + err = client.WriteSecret(ctx, "app/database", SecretData{"key": []byte("value")}, nil) 341 293 require.NoError(t, err) 342 - err = client.WriteSecret(ctx, "other/secret", SecretData{"key": []byte("value")}) 294 + err = client.WriteSecret(ctx, "other/secret", SecretData{"key": []byte("value")}, nil) 343 295 require.NoError(t, err) 344 296 345 297 // List with "app/" prefix ··· 379 331 "username": []byte("testuser"), 380 332 "password": []byte("testpass"), 381 333 } 382 - err = client.WriteSecret(ctx, "test/checksum", data) 334 + err = client.WriteSecret(ctx, "test/checksum", data, nil) 383 335 require.NoError(t, err) 384 336 385 337 checksum, err := client.GetChecksum(ctx, "test/checksum") ··· 441 393 442 394 // Add a test secret 443 395 data := SecretData{"key": []byte("value")} 444 - err = client.WriteSecret(ctx, "test/all-secrets", data) 396 + err = client.WriteSecret(ctx, "test/all-secrets", data, nil) 445 397 require.NoError(t, err) 446 398 447 399 allSecrets := client.GetAllSecrets() ··· 469 421 go func() { 470 422 for i := 0; i < 100; i++ { 471 423 data := SecretData{"key": []byte("value")} 472 - _ = client.WriteSecret(ctx, "concurrent/test", data) 424 + _ = client.WriteSecret(ctx, "concurrent/test", data, nil) 473 425 } 474 426 done <- true 475 427 }()
+7 -15
internal/hsm/pkcs11_client.go
··· 367 367 return data, nil 368 368 } 369 369 370 - // WriteSecretWithMetadata writes secret data and metadata to the specified HSM path 371 - func (c *PKCS11Client) WriteSecretWithMetadata(ctx context.Context, path string, data SecretData, metadata *SecretMetadata) error { 372 - if err := c.WriteSecret(ctx, path, data); err != nil { 373 - return err 374 - } 375 - 376 - if metadata != nil { 377 - return c.writeMetadata(path, metadata) 378 - } 379 - 380 - return nil 381 - } 382 - 383 - // WriteSecret writes secret data to the specified HSM path 384 - func (c *PKCS11Client) WriteSecret(ctx context.Context, path string, data SecretData) error { 370 + // WriteSecret writes secret data and metadata to the specified HSM path 371 + func (c *PKCS11Client) WriteSecret(ctx context.Context, path string, data SecretData, metadata *SecretMetadata) error { 385 372 c.mutex.Lock() 386 373 defer c.mutex.Unlock() 387 374 ··· 450 437 } 451 438 452 439 c.logger.Info("Successfully wrote secret to HSM", "path", path) 440 + 441 + if metadata != nil { 442 + return c.writeMetadata(path, metadata) 443 + } 444 + 453 445 return nil 454 446 } 455 447
+1 -1
internal/hsm/pkcs11_client_nocgo.go
··· 60 60 } 61 61 62 62 // WriteSecret returns an error indicating PKCS#11 is not available 63 - func (c *PKCS11Client) WriteSecret(ctx context.Context, path string, data SecretData) error { 63 + func (c *PKCS11Client) WriteSecret(ctx context.Context, path string, data SecretData, metadata *SecretMetadata) error { 64 64 return fmt.Errorf("PKCS#11 support not available: binary was built without CGO") 65 65 } 66 66
+21 -57
internal/mirror/manager.go
··· 21 21 "crypto/sha256" 22 22 "fmt" 23 23 "sort" 24 - "strings" 25 24 "time" 26 25 27 26 "github.com/go-logr/logr" ··· 151 150 152 151 // Check if device is connected 153 152 if !grpcClient.IsConnected() { 154 - logger.Info("Device not connected", "device", deviceId) 153 + logger.V(1).Info("Device not connected", "device", deviceId) 155 154 for secretPath := range inventory { 156 155 inventory[secretPath].DeviceStates[deviceId] = &SecretState{ 157 156 Present: false, ··· 180 179 data, err := grpcClient.ReadSecret(ctx, secretPath) 181 180 if err != nil { 182 181 // Secret doesn't exist on this device - leave state.Error = nil so device gets added to devicesNeedingSecret 183 - logger.Info("Secret not found on device", "device", deviceId, "secret", secretPath) 182 + logger.V(1).Info("Secret not found on device", "device", deviceId, "secret", secretPath) 184 183 } else { 185 184 // Secret exists, calculate checksum 186 185 state.Present = true 187 186 state.Checksum = mm.calculateChecksum(data) 188 - logger.Info("Secret found on device", "device", deviceId, "secret", secretPath, "checksum", state.Checksum[:8]) 187 + logger.V(1).Info("Secret found on device", "device", deviceId, "secret", secretPath, "checksum", state.Checksum[:8]) 189 188 190 189 // Try to read metadata 191 190 metadata, metaErr := grpcClient.ReadMetadata(ctx, secretPath) ··· 202 201 state.Timestamp = timestamp 203 202 } 204 203 } 205 - logger.Info("Metadata found", "device", deviceId, "secret", secretPath, 204 + logger.V(1).Info("Metadata found", "device", deviceId, "secret", secretPath, 206 205 "version", state.Version, "timestamp", state.Timestamp.Format(time.RFC3339)) 207 206 } else { 208 - logger.Info("No metadata found", "device", deviceId, "secret", secretPath) 207 + logger.V(1).Info("No metadata found", "device", deviceId, "secret", secretPath) 209 208 } 210 209 } 211 210 ··· 243 242 // Analyze all device states for this secret 244 243 for deviceName, state := range inventory.DeviceStates { 245 244 if state.Error != nil { 246 - logger.Info("Device has error, skipping", "device", deviceName, "secret", secretPath, "error", state.Error) 245 + logger.V(1).Info("Device has error, skipping", "device", deviceName, "secret", secretPath, "error", state.Error) 247 246 continue 248 247 } 249 248 ··· 279 278 // Determine sync operation type 280 279 if len(devicesWithSecret) == 0 { 281 280 // No devices have this secret - nothing to sync 282 - logger.Info("Secret not found on any device", "secret", secretPath) 281 + logger.V(1).Info("Secret not found on any device", "secret", secretPath) 283 282 return nil 284 283 } 285 284 ··· 296 295 } 297 296 298 297 if allInSync { 299 - logger.Info("Secret already in sync across all devices", "secret", secretPath) 298 + logger.V(1).Info("Secret already in sync across all devices", "secret", secretPath) 300 299 return &SecretMirrorPlan{ 301 300 SecretPath: secretPath, 302 301 SourceDevice: sourceDevice, ··· 445 444 // Skip if no sync needed 446 445 if plan.MirrorType == MirrorTypeSkip { 447 446 result.Success = true 448 - logger.Info("Skipping sync - already in sync", "secret", plan.SecretPath) 447 + logger.V(1).Info("Skipping sync - already in sync", "secret", plan.SecretPath) 449 448 return result 450 449 } 451 450 ··· 485 484 if plan.MirrorType == MirrorTypeRestoreMetadata { 486 485 if sourceMetadata == nil || sourceMetadata.Labels == nil || sourceMetadata.Labels["sync.version"] == "" { 487 486 logger.Info("Restoring metadata on source device", "device", plan.SourceDevice, "secret", plan.SecretPath) 488 - if err := mm.writeSecretWithMetadata(ctx, sourceDevice, plan.SecretPath, sourceData, syncMetadata, logger); err != nil { 487 + if err := mm.WriteSecret(ctx, sourceDevice, plan.SecretPath, sourceData, syncMetadata, logger); err != nil { 489 488 result.Error = fmt.Errorf("failed to restore metadata on source: %w", err) 490 489 return result 491 490 } ··· 506 505 var syncErr error 507 506 switch plan.MirrorType { 508 507 case MirrorTypeCreate, MirrorTypeUpdate: 509 - syncErr = mm.writeSecretWithMetadata(ctx, targetDevice, plan.SecretPath, sourceData, syncMetadata, logger) 508 + syncErr = mm.WriteSecret(ctx, targetDevice, plan.SecretPath, sourceData, syncMetadata, logger) 510 509 case MirrorTypeRestoreMetadata: 511 510 // For metadata restoration, we just update the metadata without changing the data 512 511 syncErr = mm.writeMetadataOnly(ctx, targetDevice, plan.SecretPath, syncMetadata, logger) ··· 554 553 // Read metadata (may not exist) 555 554 metadata, err := grpcClient.ReadMetadata(ctx, secretPath) 556 555 if err != nil { 557 - logger.Info("No metadata found for secret", "secret", secretPath, "device", device.SerialNumber) 556 + logger.V(1).Info("No metadata found for secret", "secret", secretPath, "device", device.SerialNumber) 558 557 metadata = nil // Not an error - metadata may not exist 559 558 } 560 559 561 560 return data, metadata, nil 562 561 } 563 562 564 - // writeSecretWithMetadata writes both secret data and metadata to a device 565 - func (mm *MirrorManager) writeSecretWithMetadata(ctx context.Context, device hsmv1alpha1.DiscoveredDevice, secretPath string, data hsm.SecretData, metadata *hsm.SecretMetadata, logger logr.Logger) error { 563 + // WriteSecret writes both secret data and metadata to a device 564 + func (mm *MirrorManager) WriteSecret(ctx context.Context, device hsmv1alpha1.DiscoveredDevice, secretPath string, data hsm.SecretData, metadata *hsm.SecretMetadata, logger logr.Logger) error { 566 565 grpcClient, err := mm.agentManager.CreateGRPCClient(ctx, device, logger) 567 566 if err != nil { 568 567 return fmt.Errorf("failed to create gRPC client: %w", err) ··· 572 571 } 573 572 574 573 // Write secret with metadata 575 - if err := grpcClient.WriteSecretWithMetadata(ctx, secretPath, data, metadata); err != nil { 574 + if err := grpcClient.WriteSecret(ctx, secretPath, data, metadata); err != nil { 576 575 return fmt.Errorf("failed to write secret with metadata: %w", err) 577 576 } 578 577 ··· 597 596 } 598 597 599 598 // Write secret with updated metadata 600 - if err := grpcClient.WriteSecretWithMetadata(ctx, secretPath, existingData, metadata); err != nil { 599 + if err := grpcClient.WriteSecret(ctx, secretPath, existingData, metadata); err != nil { 601 600 return fmt.Errorf("failed to write secret with metadata: %w", err) 602 601 } 603 602 ··· 730 729 // Ensure client is closed after use 731 730 defer func() { 732 731 if closeErr := hsmClient.Close(); closeErr != nil { 733 - logger.Info("Error closing HSM client", "error", closeErr) 732 + logger.V(1).Info("Error closing HSM client", "error", closeErr) 734 733 } 735 734 }() 736 735 ··· 743 742 lastErr = fmt.Errorf("failed to list secrets: %w", err) 744 743 attemptLogger.Info("Failed to list secrets on device", "error", err) 745 744 746 - // Check for specific connection-related errors that might benefit from retry 747 - if isConnectionError(err) && attempt < maxRetries { 748 - backoffDuration := time.Duration(attempt) * time.Second 749 - attemptLogger.Info("Connection error detected, retrying after backoff", 750 - "backoff", backoffDuration.String(), "error", err) 751 - time.Sleep(backoffDuration) 752 - continue 753 - } 754 - 755 745 if attempt < maxRetries { 756 746 backoffDuration := time.Duration(attempt) * time.Second 757 747 attemptLogger.Info("Retrying list secrets after backoff", "backoff", backoffDuration.String()) ··· 769 759 return nil, fmt.Errorf("failed to discover secrets after %d attempts: %w", maxRetries, lastErr) 770 760 } 771 761 772 - // isConnectionError checks if an error is related to connection issues that might benefit from retry 773 - func isConnectionError(err error) bool { 774 - if err == nil { 775 - return false 776 - } 777 - 778 - errStr := err.Error() 779 - connectionErrors := []string{ 780 - "grpc: the client connection is closing", 781 - "connection refused", 782 - "connection reset", 783 - "connection timeout", 784 - "context deadline exceeded", 785 - "rpc error: code = Canceled", 786 - "rpc error: code = Unavailable", 787 - } 788 - 789 - for _, connErr := range connectionErrors { 790 - if strings.Contains(errStr, connErr) { 791 - return true 792 - } 793 - } 794 - 795 - return false 796 - } 797 - 798 762 // calculateChecksum calculates SHA256 checksum of secret data 799 763 func (mm *MirrorManager) calculateChecksum(data hsm.SecretData) string { 800 764 if data == nil { ··· 839 803 case <-ticker.C: 840 804 devices, err := mm.agentManager.GetAvailableDevices(ctx, mm.operatorNamespace) 841 805 if err != nil { 842 - logger.Info("Failed to check available devices", "error", err) 806 + logger.V(1).Info("Failed to check available devices", "error", err) 843 807 continue 844 808 } 845 809 ··· 848 812 for _, device := range devices { 849 813 grpcClient, err := mm.agentManager.CreateGRPCClient(ctx, device, logger) 850 814 if err != nil { 851 - logger.Info("Agent not ready yet", "device", device.SerialNumber, "error", err) 815 + logger.V(1).Info("Agent not ready yet", "device", device.SerialNumber, "error", err) 852 816 continue 853 817 } 854 818 855 819 // Test connection 856 820 if grpcClient.IsConnected() { 857 821 if closeErr := grpcClient.Close(); closeErr != nil { 858 - logger.Info("Failed to close gRPC client", "error", closeErr) 822 + logger.V(1).Info("Failed to close gRPC client", "error", closeErr) 859 823 } 860 824 logger.Info("HSM agents are ready", "readyDevices", len(devices)) 861 825 return true, nil ··· 863 827 } 864 828 } 865 829 866 - logger.Info("Still waiting for agents", "availableDevices", len(devices)) 830 + logger.V(1).Info("Still waiting for agents", "availableDevices", len(devices)) 867 831 } 868 832 } 869 833 }
+2 -2
internal/mirror/manager_test.go
··· 640 640 641 641 // Add test secrets to client1 642 642 testData := hsm.SecretData{"username": []byte("user1"), "password": []byte("pass1")} 643 - err = mockClient1.WriteSecret(ctx, "test-secret", testData) 643 + err = mockClient1.WriteSecret(ctx, "test-secret", testData, nil) 644 644 require.NoError(t, err) 645 645 646 646 // Add different secret to client2 647 647 testData2 := hsm.SecretData{"api_key": []byte("key123")} 648 - err = mockClient2.WriteSecret(ctx, "test-secret", testData2) 648 + err = mockClient2.WriteSecret(ctx, "test-secret", testData2, nil) 649 649 require.NoError(t, err) 650 650 651 651 mockAgentManager.SetClient("device1", mockClient1)
+356
internal/modes/discovery/discovery_test.go
··· 19 19 import ( 20 20 "encoding/json" 21 21 "flag" 22 + "fmt" 22 23 "os" 23 24 "testing" 24 25 "time" ··· 683 684 deviceReport["devices"] != nil 684 685 } 685 686 } 687 + 688 + // TestMaxDevicesLimiting tests the maxDevices limiting logic 689 + func TestMaxDevicesLimiting(t *testing.T) { 690 + tests := []struct { 691 + name string 692 + maxDevices int32 693 + discoveredCount int 694 + expectedCount int 695 + shouldLimit bool 696 + }{ 697 + { 698 + name: "no limit - zero maxDevices", 699 + maxDevices: 0, 700 + discoveredCount: 5, 701 + expectedCount: 5, 702 + shouldLimit: false, 703 + }, 704 + { 705 + name: "no limit - negative maxDevices", 706 + maxDevices: -1, 707 + discoveredCount: 3, 708 + expectedCount: 3, 709 + shouldLimit: false, 710 + }, 711 + { 712 + name: "limit higher than discovered count", 713 + maxDevices: 10, 714 + discoveredCount: 3, 715 + expectedCount: 3, 716 + shouldLimit: false, 717 + }, 718 + { 719 + name: "limit equal to discovered count", 720 + maxDevices: 5, 721 + discoveredCount: 5, 722 + expectedCount: 5, 723 + shouldLimit: false, 724 + }, 725 + { 726 + name: "limit lower than discovered count", 727 + maxDevices: 3, 728 + discoveredCount: 7, 729 + expectedCount: 3, 730 + shouldLimit: true, 731 + }, 732 + { 733 + name: "limit to one device", 734 + maxDevices: 1, 735 + discoveredCount: 5, 736 + expectedCount: 1, 737 + shouldLimit: true, 738 + }, 739 + { 740 + name: "very large limit", 741 + maxDevices: 1000, 742 + discoveredCount: 2, 743 + expectedCount: 2, 744 + shouldLimit: false, 745 + }, 746 + } 747 + 748 + for _, tt := range tests { 749 + t.Run(tt.name, func(t *testing.T) { 750 + // Create mock discovered devices 751 + devices := make([]hsmv1alpha1.DiscoveredDevice, tt.discoveredCount) 752 + for i := 0; i < tt.discoveredCount; i++ { 753 + devices[i] = hsmv1alpha1.DiscoveredDevice{ 754 + DevicePath: fmt.Sprintf("/dev/ttyUSB%d", i), 755 + SerialNumber: fmt.Sprintf("SERIAL%d", i), 756 + NodeName: "test-node", 757 + LastSeen: metav1.Now(), 758 + Available: true, 759 + DeviceInfo: map[string]string{ 760 + "vendor-id": "20a0", 761 + "product-id": "4230", 762 + }, 763 + } 764 + } 765 + 766 + // Apply maxDevices limiting logic (extracted from discoverDevicesForSpec) 767 + originalCount := len(devices) 768 + if tt.maxDevices > 0 && int32(len(devices)) > tt.maxDevices { 769 + devices = devices[:tt.maxDevices] 770 + } 771 + 772 + // Verify results 773 + assert.Equal(t, tt.expectedCount, len(devices)) 774 + 775 + if tt.shouldLimit { 776 + assert.Less(t, len(devices), originalCount, "Should have limited the device count") 777 + assert.Equal(t, int(tt.maxDevices), len(devices), "Should match maxDevices limit") 778 + } else { 779 + assert.Equal(t, originalCount, len(devices), "Should not have limited the device count") 780 + } 781 + 782 + // Verify that the first N devices are preserved (slice truncation from beginning) 783 + for i := 0; i < len(devices); i++ { 784 + expectedPath := fmt.Sprintf("/dev/ttyUSB%d", i) 785 + assert.Equal(t, expectedPath, devices[i].DevicePath) 786 + expectedSerial := fmt.Sprintf("SERIAL%d", i) 787 + assert.Equal(t, expectedSerial, devices[i].SerialNumber) 788 + } 789 + }) 790 + } 791 + } 792 + 793 + // TestDiscoverDevicesForSpec_MaxDevices tests the maxDevices logic within discoverDevicesForSpec 794 + func TestDiscoverDevicesForSpec_MaxDevices(t *testing.T) { 795 + tests := []struct { 796 + name string 797 + maxDevices int32 798 + deviceCount int 799 + expectedCount int 800 + expectLimiting bool 801 + }{ 802 + { 803 + name: "no limiting when under maxDevices", 804 + maxDevices: 5, 805 + deviceCount: 3, 806 + expectedCount: 3, 807 + expectLimiting: false, 808 + }, 809 + { 810 + name: "limiting when over maxDevices", 811 + maxDevices: 2, 812 + deviceCount: 5, 813 + expectedCount: 2, 814 + expectLimiting: true, 815 + }, 816 + { 817 + name: "no limiting when maxDevices is zero", 818 + maxDevices: 0, 819 + deviceCount: 10, 820 + expectedCount: 10, 821 + expectLimiting: false, 822 + }, 823 + } 824 + 825 + for _, tt := range tests { 826 + t.Run(tt.name, func(t *testing.T) { 827 + // Create a mock HSMDevice 828 + hsmDevice := &hsmv1alpha1.HSMDevice{ 829 + ObjectMeta: metav1.ObjectMeta{ 830 + Name: "test-device", 831 + }, 832 + Spec: hsmv1alpha1.HSMDeviceSpec{ 833 + DeviceType: "PicoHSM", 834 + MaxDevices: tt.maxDevices, 835 + Discovery: &hsmv1alpha1.DiscoverySpec{ 836 + AutoDiscovery: true, 837 + }, 838 + }, 839 + } 840 + 841 + // Note: We don't need to use the discovery agent directly in this test 842 + // as we're testing the maxDevices logic in isolation 843 + 844 + // Create expected devices that would be returned from discovery 845 + expectedDevices := make([]hsmv1alpha1.DiscoveredDevice, tt.deviceCount) 846 + for i := 0; i < tt.deviceCount; i++ { 847 + expectedDevices[i] = hsmv1alpha1.DiscoveredDevice{ 848 + DevicePath: fmt.Sprintf("/dev/ttyUSB%d", i), 849 + SerialNumber: fmt.Sprintf("TEST%03d", i), 850 + NodeName: "test-node", 851 + LastSeen: metav1.Now(), 852 + Available: true, 853 + DeviceInfo: map[string]string{ 854 + "vendor-id": "20a0", 855 + "product-id": "4230", 856 + "discovery-type": "auto", 857 + }, 858 + } 859 + } 860 + 861 + // Simulate the maxDevices limiting logic from discoverDevicesForSpec 862 + devices := expectedDevices 863 + originalCount := len(devices) 864 + if hsmDevice.Spec.MaxDevices > 0 && int32(len(devices)) > hsmDevice.Spec.MaxDevices { 865 + devices = devices[:hsmDevice.Spec.MaxDevices] 866 + } 867 + 868 + // Verify the results 869 + assert.Equal(t, tt.expectedCount, len(devices)) 870 + 871 + if tt.expectLimiting { 872 + assert.Less(t, len(devices), originalCount) 873 + assert.Equal(t, int(tt.maxDevices), len(devices)) 874 + } else { 875 + assert.Equal(t, originalCount, len(devices)) 876 + } 877 + 878 + // Verify that devices maintain correct properties after limiting 879 + for i, device := range devices { 880 + assert.Equal(t, fmt.Sprintf("/dev/ttyUSB%d", i), device.DevicePath) 881 + assert.Equal(t, fmt.Sprintf("TEST%03d", i), device.SerialNumber) 882 + assert.Equal(t, "test-node", device.NodeName) 883 + assert.True(t, device.Available) 884 + assert.Equal(t, "20a0", device.DeviceInfo["vendor-id"]) 885 + assert.Equal(t, "4230", device.DeviceInfo["product-id"]) 886 + assert.Equal(t, "auto", device.DeviceInfo["discovery-type"]) 887 + } 888 + }) 889 + } 890 + } 891 + 892 + // TestMaxDevicesWithDifferentDiscoveryTypes tests maxDevices with different discovery methods 893 + func TestMaxDevicesWithDifferentDiscoveryTypes(t *testing.T) { 894 + tests := []struct { 895 + name string 896 + discoverySpec *hsmv1alpha1.DiscoverySpec 897 + maxDevices int32 898 + description string 899 + }{ 900 + { 901 + name: "maxDevices with USB discovery", 902 + discoverySpec: &hsmv1alpha1.DiscoverySpec{ 903 + USB: &hsmv1alpha1.USBDeviceSpec{ 904 + VendorID: "20a0", 905 + ProductID: "4230", 906 + }, 907 + }, 908 + maxDevices: 2, 909 + description: "USB discovery with maxDevices limit", 910 + }, 911 + { 912 + name: "maxDevices with path discovery", 913 + discoverySpec: &hsmv1alpha1.DiscoverySpec{ 914 + DevicePath: &hsmv1alpha1.DevicePathSpec{ 915 + Path: "/dev/ttyUSB*", 916 + }, 917 + }, 918 + maxDevices: 3, 919 + description: "Path discovery with maxDevices limit", 920 + }, 921 + { 922 + name: "maxDevices with auto discovery", 923 + discoverySpec: &hsmv1alpha1.DiscoverySpec{ 924 + AutoDiscovery: true, 925 + }, 926 + maxDevices: 1, 927 + description: "Auto discovery with maxDevices limit", 928 + }, 929 + } 930 + 931 + for _, tt := range tests { 932 + t.Run(tt.name, func(t *testing.T) { 933 + hsmDevice := &hsmv1alpha1.HSMDevice{ 934 + ObjectMeta: metav1.ObjectMeta{ 935 + Name: "test-device", 936 + }, 937 + Spec: hsmv1alpha1.HSMDeviceSpec{ 938 + DeviceType: "PicoHSM", 939 + MaxDevices: tt.maxDevices, 940 + Discovery: tt.discoverySpec, 941 + }, 942 + } 943 + 944 + // Test that maxDevices configuration is properly set 945 + assert.Equal(t, tt.maxDevices, hsmDevice.Spec.MaxDevices) 946 + assert.NotNil(t, hsmDevice.Spec.Discovery) 947 + 948 + // Verify discovery spec is correctly configured 949 + if tt.discoverySpec.USB != nil { 950 + assert.NotNil(t, hsmDevice.Spec.Discovery.USB) 951 + assert.Equal(t, "20a0", hsmDevice.Spec.Discovery.USB.VendorID) 952 + assert.Equal(t, "4230", hsmDevice.Spec.Discovery.USB.ProductID) 953 + } 954 + 955 + if tt.discoverySpec.DevicePath != nil { 956 + assert.NotNil(t, hsmDevice.Spec.Discovery.DevicePath) 957 + assert.Equal(t, "/dev/ttyUSB*", hsmDevice.Spec.Discovery.DevicePath.Path) 958 + } 959 + 960 + if tt.discoverySpec.AutoDiscovery { 961 + assert.True(t, hsmDevice.Spec.Discovery.AutoDiscovery) 962 + } 963 + }) 964 + } 965 + } 966 + 967 + // TestMaxDevicesEdgeCases tests edge cases for maxDevices functionality 968 + func TestMaxDevicesEdgeCases(t *testing.T) { 969 + tests := []struct { 970 + name string 971 + maxDevices int32 972 + discoveredCount int 973 + expectedCount int 974 + description string 975 + }{ 976 + { 977 + name: "maxDevices boundary - exactly at limit", 978 + maxDevices: 5, 979 + discoveredCount: 5, 980 + expectedCount: 5, 981 + description: "When discovered count equals maxDevices exactly", 982 + }, 983 + { 984 + name: "maxDevices boundary - one over limit", 985 + maxDevices: 5, 986 + discoveredCount: 6, 987 + expectedCount: 5, 988 + description: "When discovered count is one more than maxDevices", 989 + }, 990 + { 991 + name: "maxDevices with single device", 992 + maxDevices: 1, 993 + discoveredCount: 1, 994 + expectedCount: 1, 995 + description: "Single device with maxDevices of 1", 996 + }, 997 + { 998 + name: "maxDevices with no devices discovered", 999 + maxDevices: 5, 1000 + discoveredCount: 0, 1001 + expectedCount: 0, 1002 + description: "No devices discovered, maxDevices should not matter", 1003 + }, 1004 + { 1005 + name: "large maxDevices with few devices", 1006 + maxDevices: 1000, 1007 + discoveredCount: 3, 1008 + expectedCount: 3, 1009 + description: "Very large maxDevices limit with few actual devices", 1010 + }, 1011 + } 1012 + 1013 + for _, tt := range tests { 1014 + t.Run(tt.name, func(t *testing.T) { 1015 + // Create mock devices 1016 + devices := make([]hsmv1alpha1.DiscoveredDevice, tt.discoveredCount) 1017 + for i := 0; i < tt.discoveredCount; i++ { 1018 + devices[i] = hsmv1alpha1.DiscoveredDevice{ 1019 + DevicePath: fmt.Sprintf("/dev/test%d", i), 1020 + SerialNumber: fmt.Sprintf("EDGE%d", i), 1021 + NodeName: "test-node", 1022 + LastSeen: metav1.Now(), 1023 + Available: true, 1024 + } 1025 + } 1026 + 1027 + // Apply maxDevices limiting (the core logic being tested) 1028 + if tt.maxDevices > 0 && int32(len(devices)) > tt.maxDevices { 1029 + devices = devices[:tt.maxDevices] 1030 + } 1031 + 1032 + assert.Equal(t, tt.expectedCount, len(devices), tt.description) 1033 + 1034 + // Verify device ordering is preserved (first N devices kept) 1035 + for i, device := range devices { 1036 + assert.Equal(t, fmt.Sprintf("/dev/test%d", i), device.DevicePath) 1037 + assert.Equal(t, fmt.Sprintf("EDGE%d", i), device.SerialNumber) 1038 + } 1039 + }) 1040 + } 1041 + }