From 10bed950124c9b32fdb1ef705bff8044291a374d Mon Sep 17 00:00:00 2001 From: jmpfar Date: Tue, 20 Oct 2020 08:46:34 +0000 Subject: [PATCH 1/5] Adding the iSCSI APIs 1. Currently has a very limited support for Multipath 2. Does not contain node level APIs to set reverse CHAP or node IQN 3. Tests format a disk, so special flag is used to enable the iSCSI tests. Take care to use disposable clean VMs for tests. Change-Id: I0fe8431fed00715883ad0431671ec6a060718e0e --- .github/workflows/windows.yml | 1 + README.md | 6 + client/api/iscsi/v1alpha1/api.pb.go | 1079 +++++++++++++++++ client/api/iscsi/v1alpha1/api.proto | 149 +++ .../groups/iscsi/v1alpha1/client_generated.go | 80 ++ cmd/csi-proxy/main.go | 8 + go.mod | 2 + integrationtests/iscsi_ps_scripts.go | 184 +++ integrationtests/iscsi_test.go | 365 ++++++ integrationtests/utils.go | 8 + internal/os/iscsi/api.go | 192 +++ internal/os/iscsi/types.go | 9 + internal/server/iscsi/api_group_generated.go | 27 + .../server/iscsi/internal/conversion_test.go | 61 + internal/server/iscsi/internal/types.go | 109 ++ .../server/iscsi/internal/types_generated.go | 25 + .../iscsi/internal/v1alpha1/conversion.go | 24 + .../internal/v1alpha1/conversion_generated.go | 427 +++++++ .../internal/v1alpha1/server_generated.go | 161 +++ internal/server/iscsi/server.go | 170 +++ .../client/api/iscsi/v1alpha1/api.pb.go | 1079 +++++++++++++++++ .../client/api/iscsi/v1alpha1/api.proto | 149 +++ .../groups/iscsi/v1alpha1/client_generated.go | 80 ++ vendor/modules.txt | 14 + 24 files changed, 4409 insertions(+) create mode 100644 client/api/iscsi/v1alpha1/api.pb.go create mode 100644 client/api/iscsi/v1alpha1/api.proto create mode 100644 client/groups/iscsi/v1alpha1/client_generated.go create mode 100644 integrationtests/iscsi_ps_scripts.go create mode 100644 integrationtests/iscsi_test.go create mode 100644 internal/os/iscsi/api.go create mode 100644 internal/os/iscsi/types.go create mode 100644 internal/server/iscsi/api_group_generated.go create mode 100644 internal/server/iscsi/internal/conversion_test.go create mode 100644 internal/server/iscsi/internal/types.go create mode 100644 internal/server/iscsi/internal/types_generated.go create mode 100644 internal/server/iscsi/internal/v1alpha1/conversion.go create mode 100644 internal/server/iscsi/internal/v1alpha1/conversion_generated.go create mode 100644 internal/server/iscsi/internal/v1alpha1/server_generated.go create mode 100644 internal/server/iscsi/server.go create mode 100644 vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.pb.go create mode 100644 vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.proto create mode 100644 vendor/github.com/kubernetes-csi/csi-proxy/client/groups/iscsi/v1alpha1/client_generated.go diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index e020a8fe..4a185077 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -31,6 +31,7 @@ jobs: Write-Output "getting named pipes" [System.IO.Directory]::GetFiles("\\.\\pipe\\") $env:CSI_PROXY_GH_ACTIONS="TRUE" + $env:ENABLE_ISCSI_TESTS="TRUE" go test -v -race ./integrationtests/... unit_tests: strategy: diff --git a/README.md b/README.md index d92f57de..fedf89b4 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,8 @@ spec: mountPath: \\.\pipe\csi-proxy-volume-v1alpha1 - name: csi-proxy-filesystem-pipe mountPath: \\.\pipe\csi-proxy-filesystem-v1alpha1 + - name: csi-proxy-iscsi-pipe + mountPath: \\.\pipe\csi-proxy-iscsi-v1alpha1 volumes: - name: csi-proxy-disk-pipe hostPath: @@ -112,6 +114,10 @@ spec: hostPath: path: \\.\pipe\csi-proxy-filesystem-v1alpha1 type: "" + - name: csi-proxy-iscsi-pipe + hostPath: + path: \\.\pipe\csi-proxy-iscsi-v1alpha1 + type: "" - name: registration-dir hostPath: path: C:\var\lib\kubelet\plugins_registry\ diff --git a/client/api/iscsi/v1alpha1/api.pb.go b/client/api/iscsi/v1alpha1/api.pb.go new file mode 100644 index 00000000..c1d35cac --- /dev/null +++ b/client/api/iscsi/v1alpha1/api.pb.go @@ -0,0 +1,1079 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.proto + +package v1alpha1 + +import ( + context "context" + fmt "fmt" + proto "github.com/golang/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type AuthenticationType int32 + +const ( + AuthenticationType_NONE AuthenticationType = 0 + AuthenticationType_ONE_WAY_CHAP AuthenticationType = 1 + AuthenticationType_MUTUAL_CHAP AuthenticationType = 2 +) + +var AuthenticationType_name = map[int32]string{ + 0: "NONE", + 1: "ONE_WAY_CHAP", + 2: "MUTUAL_CHAP", +} + +var AuthenticationType_value = map[string]int32{ + "NONE": 0, + "ONE_WAY_CHAP": 1, + "MUTUAL_CHAP": 2, +} + +func (x AuthenticationType) String() string { + return proto.EnumName(AuthenticationType_name, int32(x)) +} + +func (AuthenticationType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{0} +} + +// TargetPortal is an address and port pair for a specific iSCSI storage +// target. +type TargetPortal struct { + // iSCSI Target (server) address + TargetAddress string `protobuf:"bytes,1,opt,name=target_address,json=targetAddress,proto3" json:"target_address,omitempty"` + // iSCSI Target port (default iSCSI port is 3260) + TargetPort uint32 `protobuf:"varint,2,opt,name=target_port,json=targetPort,proto3" json:"target_port,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TargetPortal) Reset() { *m = TargetPortal{} } +func (m *TargetPortal) String() string { return proto.CompactTextString(m) } +func (*TargetPortal) ProtoMessage() {} +func (*TargetPortal) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{0} +} + +func (m *TargetPortal) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TargetPortal.Unmarshal(m, b) +} +func (m *TargetPortal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TargetPortal.Marshal(b, m, deterministic) +} +func (m *TargetPortal) XXX_Merge(src proto.Message) { + xxx_messageInfo_TargetPortal.Merge(m, src) +} +func (m *TargetPortal) XXX_Size() int { + return xxx_messageInfo_TargetPortal.Size(m) +} +func (m *TargetPortal) XXX_DiscardUnknown() { + xxx_messageInfo_TargetPortal.DiscardUnknown(m) +} + +var xxx_messageInfo_TargetPortal proto.InternalMessageInfo + +func (m *TargetPortal) GetTargetAddress() string { + if m != nil { + return m.TargetAddress + } + return "" +} + +func (m *TargetPortal) GetTargetPort() uint32 { + if m != nil { + return m.TargetPort + } + return 0 +} + +type AddTargetPortalRequest struct { + // iSCSI Target Portal to register in the initiator + TargetPortal *TargetPortal `protobuf:"bytes,1,opt,name=target_portal,json=targetPortal,proto3" json:"target_portal,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AddTargetPortalRequest) Reset() { *m = AddTargetPortalRequest{} } +func (m *AddTargetPortalRequest) String() string { return proto.CompactTextString(m) } +func (*AddTargetPortalRequest) ProtoMessage() {} +func (*AddTargetPortalRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{1} +} + +func (m *AddTargetPortalRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AddTargetPortalRequest.Unmarshal(m, b) +} +func (m *AddTargetPortalRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AddTargetPortalRequest.Marshal(b, m, deterministic) +} +func (m *AddTargetPortalRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_AddTargetPortalRequest.Merge(m, src) +} +func (m *AddTargetPortalRequest) XXX_Size() int { + return xxx_messageInfo_AddTargetPortalRequest.Size(m) +} +func (m *AddTargetPortalRequest) XXX_DiscardUnknown() { + xxx_messageInfo_AddTargetPortalRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_AddTargetPortalRequest proto.InternalMessageInfo + +func (m *AddTargetPortalRequest) GetTargetPortal() *TargetPortal { + if m != nil { + return m.TargetPortal + } + return nil +} + +type AddTargetPortalResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AddTargetPortalResponse) Reset() { *m = AddTargetPortalResponse{} } +func (m *AddTargetPortalResponse) String() string { return proto.CompactTextString(m) } +func (*AddTargetPortalResponse) ProtoMessage() {} +func (*AddTargetPortalResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{2} +} + +func (m *AddTargetPortalResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AddTargetPortalResponse.Unmarshal(m, b) +} +func (m *AddTargetPortalResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AddTargetPortalResponse.Marshal(b, m, deterministic) +} +func (m *AddTargetPortalResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_AddTargetPortalResponse.Merge(m, src) +} +func (m *AddTargetPortalResponse) XXX_Size() int { + return xxx_messageInfo_AddTargetPortalResponse.Size(m) +} +func (m *AddTargetPortalResponse) XXX_DiscardUnknown() { + xxx_messageInfo_AddTargetPortalResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_AddTargetPortalResponse proto.InternalMessageInfo + +type DiscoverTargetPortalRequest struct { + // iSCSI Target Portal on which to initiate discovery + TargetPortal *TargetPortal `protobuf:"bytes,1,opt,name=target_portal,json=targetPortal,proto3" json:"target_portal,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DiscoverTargetPortalRequest) Reset() { *m = DiscoverTargetPortalRequest{} } +func (m *DiscoverTargetPortalRequest) String() string { return proto.CompactTextString(m) } +func (*DiscoverTargetPortalRequest) ProtoMessage() {} +func (*DiscoverTargetPortalRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{3} +} + +func (m *DiscoverTargetPortalRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DiscoverTargetPortalRequest.Unmarshal(m, b) +} +func (m *DiscoverTargetPortalRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DiscoverTargetPortalRequest.Marshal(b, m, deterministic) +} +func (m *DiscoverTargetPortalRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DiscoverTargetPortalRequest.Merge(m, src) +} +func (m *DiscoverTargetPortalRequest) XXX_Size() int { + return xxx_messageInfo_DiscoverTargetPortalRequest.Size(m) +} +func (m *DiscoverTargetPortalRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DiscoverTargetPortalRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_DiscoverTargetPortalRequest proto.InternalMessageInfo + +func (m *DiscoverTargetPortalRequest) GetTargetPortal() *TargetPortal { + if m != nil { + return m.TargetPortal + } + return nil +} + +type DiscoverTargetPortalResponse struct { + // List of discovered IQN addresses + // follows IQN format: iqn.yyyy-mm.naming-authority:unique-name + Iqns []string `protobuf:"bytes,1,rep,name=iqns,proto3" json:"iqns,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DiscoverTargetPortalResponse) Reset() { *m = DiscoverTargetPortalResponse{} } +func (m *DiscoverTargetPortalResponse) String() string { return proto.CompactTextString(m) } +func (*DiscoverTargetPortalResponse) ProtoMessage() {} +func (*DiscoverTargetPortalResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{4} +} + +func (m *DiscoverTargetPortalResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DiscoverTargetPortalResponse.Unmarshal(m, b) +} +func (m *DiscoverTargetPortalResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DiscoverTargetPortalResponse.Marshal(b, m, deterministic) +} +func (m *DiscoverTargetPortalResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_DiscoverTargetPortalResponse.Merge(m, src) +} +func (m *DiscoverTargetPortalResponse) XXX_Size() int { + return xxx_messageInfo_DiscoverTargetPortalResponse.Size(m) +} +func (m *DiscoverTargetPortalResponse) XXX_DiscardUnknown() { + xxx_messageInfo_DiscoverTargetPortalResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_DiscoverTargetPortalResponse proto.InternalMessageInfo + +func (m *DiscoverTargetPortalResponse) GetIqns() []string { + if m != nil { + return m.Iqns + } + return nil +} + +type RemoveTargetPortalRequest struct { + // iSCSI Target Portal + TargetPortal *TargetPortal `protobuf:"bytes,1,opt,name=target_portal,json=targetPortal,proto3" json:"target_portal,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RemoveTargetPortalRequest) Reset() { *m = RemoveTargetPortalRequest{} } +func (m *RemoveTargetPortalRequest) String() string { return proto.CompactTextString(m) } +func (*RemoveTargetPortalRequest) ProtoMessage() {} +func (*RemoveTargetPortalRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{5} +} + +func (m *RemoveTargetPortalRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RemoveTargetPortalRequest.Unmarshal(m, b) +} +func (m *RemoveTargetPortalRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RemoveTargetPortalRequest.Marshal(b, m, deterministic) +} +func (m *RemoveTargetPortalRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveTargetPortalRequest.Merge(m, src) +} +func (m *RemoveTargetPortalRequest) XXX_Size() int { + return xxx_messageInfo_RemoveTargetPortalRequest.Size(m) +} +func (m *RemoveTargetPortalRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveTargetPortalRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveTargetPortalRequest proto.InternalMessageInfo + +func (m *RemoveTargetPortalRequest) GetTargetPortal() *TargetPortal { + if m != nil { + return m.TargetPortal + } + return nil +} + +type RemoveTargetPortalResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RemoveTargetPortalResponse) Reset() { *m = RemoveTargetPortalResponse{} } +func (m *RemoveTargetPortalResponse) String() string { return proto.CompactTextString(m) } +func (*RemoveTargetPortalResponse) ProtoMessage() {} +func (*RemoveTargetPortalResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{6} +} + +func (m *RemoveTargetPortalResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RemoveTargetPortalResponse.Unmarshal(m, b) +} +func (m *RemoveTargetPortalResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RemoveTargetPortalResponse.Marshal(b, m, deterministic) +} +func (m *RemoveTargetPortalResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveTargetPortalResponse.Merge(m, src) +} +func (m *RemoveTargetPortalResponse) XXX_Size() int { + return xxx_messageInfo_RemoveTargetPortalResponse.Size(m) +} +func (m *RemoveTargetPortalResponse) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveTargetPortalResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveTargetPortalResponse proto.InternalMessageInfo + +type ListTargetPortalsRequest struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListTargetPortalsRequest) Reset() { *m = ListTargetPortalsRequest{} } +func (m *ListTargetPortalsRequest) String() string { return proto.CompactTextString(m) } +func (*ListTargetPortalsRequest) ProtoMessage() {} +func (*ListTargetPortalsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{7} +} + +func (m *ListTargetPortalsRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ListTargetPortalsRequest.Unmarshal(m, b) +} +func (m *ListTargetPortalsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ListTargetPortalsRequest.Marshal(b, m, deterministic) +} +func (m *ListTargetPortalsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListTargetPortalsRequest.Merge(m, src) +} +func (m *ListTargetPortalsRequest) XXX_Size() int { + return xxx_messageInfo_ListTargetPortalsRequest.Size(m) +} +func (m *ListTargetPortalsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListTargetPortalsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ListTargetPortalsRequest proto.InternalMessageInfo + +type ListTargetPortalsResponse struct { + // A list of Target Portals currently registered in the initiator + TargetPortals []*TargetPortal `protobuf:"bytes,1,rep,name=target_portals,json=targetPortals,proto3" json:"target_portals,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListTargetPortalsResponse) Reset() { *m = ListTargetPortalsResponse{} } +func (m *ListTargetPortalsResponse) String() string { return proto.CompactTextString(m) } +func (*ListTargetPortalsResponse) ProtoMessage() {} +func (*ListTargetPortalsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{8} +} + +func (m *ListTargetPortalsResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ListTargetPortalsResponse.Unmarshal(m, b) +} +func (m *ListTargetPortalsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ListTargetPortalsResponse.Marshal(b, m, deterministic) +} +func (m *ListTargetPortalsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListTargetPortalsResponse.Merge(m, src) +} +func (m *ListTargetPortalsResponse) XXX_Size() int { + return xxx_messageInfo_ListTargetPortalsResponse.Size(m) +} +func (m *ListTargetPortalsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListTargetPortalsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ListTargetPortalsResponse proto.InternalMessageInfo + +func (m *ListTargetPortalsResponse) GetTargetPortals() []*TargetPortal { + if m != nil { + return m.TargetPortals + } + return nil +} + +type ConnectTargetRequest struct { + // Target portal to which the initiator will connect. + TargetPortal *TargetPortal `protobuf:"bytes,1,opt,name=target_portal,json=targetPortal,proto3" json:"target_portal,omitempty"` + // IQN of the iSCSI Target + Iqn string `protobuf:"bytes,2,opt,name=iqn,proto3" json:"iqn,omitempty"` + // Connection authentication type, None by default + // + // One Way Chap uses the chap_username and chap_secret + // fields mentioned below to authenticate the initiator. + // + // Mutual Chap uses both the user/secret mentioned below + // and the Initiator Chap Secret to authenticate the target and initiator. + AuthType AuthenticationType `protobuf:"varint,3,opt,name=auth_type,json=authType,proto3,enum=v1alpha1.AuthenticationType" json:"auth_type,omitempty"` + // CHAP Username used to authenticate the initiator + ChapUsername string `protobuf:"bytes,4,opt,name=chap_username,json=chapUsername,proto3" json:"chap_username,omitempty"` + // CHAP password used to authenticate the initiator + ChapSecret string `protobuf:"bytes,5,opt,name=chap_secret,json=chapSecret,proto3" json:"chap_secret,omitempty"` + // Should enable multipath on the connection + // In order for multipath to work on Windows, the Multipath feature + // needs to be installed as well as MPIO should be correctly configured + IsMultipath bool `protobuf:"varint,6,opt,name=is_multipath,json=isMultipath,proto3" json:"is_multipath,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ConnectTargetRequest) Reset() { *m = ConnectTargetRequest{} } +func (m *ConnectTargetRequest) String() string { return proto.CompactTextString(m) } +func (*ConnectTargetRequest) ProtoMessage() {} +func (*ConnectTargetRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{9} +} + +func (m *ConnectTargetRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ConnectTargetRequest.Unmarshal(m, b) +} +func (m *ConnectTargetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ConnectTargetRequest.Marshal(b, m, deterministic) +} +func (m *ConnectTargetRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConnectTargetRequest.Merge(m, src) +} +func (m *ConnectTargetRequest) XXX_Size() int { + return xxx_messageInfo_ConnectTargetRequest.Size(m) +} +func (m *ConnectTargetRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ConnectTargetRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ConnectTargetRequest proto.InternalMessageInfo + +func (m *ConnectTargetRequest) GetTargetPortal() *TargetPortal { + if m != nil { + return m.TargetPortal + } + return nil +} + +func (m *ConnectTargetRequest) GetIqn() string { + if m != nil { + return m.Iqn + } + return "" +} + +func (m *ConnectTargetRequest) GetAuthType() AuthenticationType { + if m != nil { + return m.AuthType + } + return AuthenticationType_NONE +} + +func (m *ConnectTargetRequest) GetChapUsername() string { + if m != nil { + return m.ChapUsername + } + return "" +} + +func (m *ConnectTargetRequest) GetChapSecret() string { + if m != nil { + return m.ChapSecret + } + return "" +} + +func (m *ConnectTargetRequest) GetIsMultipath() bool { + if m != nil { + return m.IsMultipath + } + return false +} + +type ConnectTargetResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ConnectTargetResponse) Reset() { *m = ConnectTargetResponse{} } +func (m *ConnectTargetResponse) String() string { return proto.CompactTextString(m) } +func (*ConnectTargetResponse) ProtoMessage() {} +func (*ConnectTargetResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{10} +} + +func (m *ConnectTargetResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ConnectTargetResponse.Unmarshal(m, b) +} +func (m *ConnectTargetResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ConnectTargetResponse.Marshal(b, m, deterministic) +} +func (m *ConnectTargetResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConnectTargetResponse.Merge(m, src) +} +func (m *ConnectTargetResponse) XXX_Size() int { + return xxx_messageInfo_ConnectTargetResponse.Size(m) +} +func (m *ConnectTargetResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ConnectTargetResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ConnectTargetResponse proto.InternalMessageInfo + +type GetTargetDisksRequest struct { + // Target portal to which the initiator will connect. + TargetPortal *TargetPortal `protobuf:"bytes,1,opt,name=target_portal,json=targetPortal,proto3" json:"target_portal,omitempty"` + // IQN of the iSCSI Target + Iqn string `protobuf:"bytes,2,opt,name=iqn,proto3" json:"iqn,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetTargetDisksRequest) Reset() { *m = GetTargetDisksRequest{} } +func (m *GetTargetDisksRequest) String() string { return proto.CompactTextString(m) } +func (*GetTargetDisksRequest) ProtoMessage() {} +func (*GetTargetDisksRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{11} +} + +func (m *GetTargetDisksRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetTargetDisksRequest.Unmarshal(m, b) +} +func (m *GetTargetDisksRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetTargetDisksRequest.Marshal(b, m, deterministic) +} +func (m *GetTargetDisksRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTargetDisksRequest.Merge(m, src) +} +func (m *GetTargetDisksRequest) XXX_Size() int { + return xxx_messageInfo_GetTargetDisksRequest.Size(m) +} +func (m *GetTargetDisksRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetTargetDisksRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetTargetDisksRequest proto.InternalMessageInfo + +func (m *GetTargetDisksRequest) GetTargetPortal() *TargetPortal { + if m != nil { + return m.TargetPortal + } + return nil +} + +func (m *GetTargetDisksRequest) GetIqn() string { + if m != nil { + return m.Iqn + } + return "" +} + +type GetTargetDisksResponse struct { + // List composed of disk ids (numbers) that are associated with the + // iSCSI target + DiskIds []string `protobuf:"bytes,1,rep,name=diskIds,proto3" json:"diskIds,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetTargetDisksResponse) Reset() { *m = GetTargetDisksResponse{} } +func (m *GetTargetDisksResponse) String() string { return proto.CompactTextString(m) } +func (*GetTargetDisksResponse) ProtoMessage() {} +func (*GetTargetDisksResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{12} +} + +func (m *GetTargetDisksResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetTargetDisksResponse.Unmarshal(m, b) +} +func (m *GetTargetDisksResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetTargetDisksResponse.Marshal(b, m, deterministic) +} +func (m *GetTargetDisksResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTargetDisksResponse.Merge(m, src) +} +func (m *GetTargetDisksResponse) XXX_Size() int { + return xxx_messageInfo_GetTargetDisksResponse.Size(m) +} +func (m *GetTargetDisksResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetTargetDisksResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetTargetDisksResponse proto.InternalMessageInfo + +func (m *GetTargetDisksResponse) GetDiskIds() []string { + if m != nil { + return m.DiskIds + } + return nil +} + +type DisconnectTargetRequest struct { + // Target portal to which the initiator will connect. + TargetPortal *TargetPortal `protobuf:"bytes,1,opt,name=target_portal,json=targetPortal,proto3" json:"target_portal,omitempty"` + // IQN of the iSCSI Target + Iqn string `protobuf:"bytes,2,opt,name=iqn,proto3" json:"iqn,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DisconnectTargetRequest) Reset() { *m = DisconnectTargetRequest{} } +func (m *DisconnectTargetRequest) String() string { return proto.CompactTextString(m) } +func (*DisconnectTargetRequest) ProtoMessage() {} +func (*DisconnectTargetRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{13} +} + +func (m *DisconnectTargetRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DisconnectTargetRequest.Unmarshal(m, b) +} +func (m *DisconnectTargetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DisconnectTargetRequest.Marshal(b, m, deterministic) +} +func (m *DisconnectTargetRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DisconnectTargetRequest.Merge(m, src) +} +func (m *DisconnectTargetRequest) XXX_Size() int { + return xxx_messageInfo_DisconnectTargetRequest.Size(m) +} +func (m *DisconnectTargetRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DisconnectTargetRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_DisconnectTargetRequest proto.InternalMessageInfo + +func (m *DisconnectTargetRequest) GetTargetPortal() *TargetPortal { + if m != nil { + return m.TargetPortal + } + return nil +} + +func (m *DisconnectTargetRequest) GetIqn() string { + if m != nil { + return m.Iqn + } + return "" +} + +type DisconnectTargetResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DisconnectTargetResponse) Reset() { *m = DisconnectTargetResponse{} } +func (m *DisconnectTargetResponse) String() string { return proto.CompactTextString(m) } +func (*DisconnectTargetResponse) ProtoMessage() {} +func (*DisconnectTargetResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{14} +} + +func (m *DisconnectTargetResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DisconnectTargetResponse.Unmarshal(m, b) +} +func (m *DisconnectTargetResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DisconnectTargetResponse.Marshal(b, m, deterministic) +} +func (m *DisconnectTargetResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_DisconnectTargetResponse.Merge(m, src) +} +func (m *DisconnectTargetResponse) XXX_Size() int { + return xxx_messageInfo_DisconnectTargetResponse.Size(m) +} +func (m *DisconnectTargetResponse) XXX_DiscardUnknown() { + xxx_messageInfo_DisconnectTargetResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_DisconnectTargetResponse proto.InternalMessageInfo + +func init() { + proto.RegisterEnum("v1alpha1.AuthenticationType", AuthenticationType_name, AuthenticationType_value) + proto.RegisterType((*TargetPortal)(nil), "v1alpha1.TargetPortal") + proto.RegisterType((*AddTargetPortalRequest)(nil), "v1alpha1.AddTargetPortalRequest") + proto.RegisterType((*AddTargetPortalResponse)(nil), "v1alpha1.AddTargetPortalResponse") + proto.RegisterType((*DiscoverTargetPortalRequest)(nil), "v1alpha1.DiscoverTargetPortalRequest") + proto.RegisterType((*DiscoverTargetPortalResponse)(nil), "v1alpha1.DiscoverTargetPortalResponse") + proto.RegisterType((*RemoveTargetPortalRequest)(nil), "v1alpha1.RemoveTargetPortalRequest") + proto.RegisterType((*RemoveTargetPortalResponse)(nil), "v1alpha1.RemoveTargetPortalResponse") + proto.RegisterType((*ListTargetPortalsRequest)(nil), "v1alpha1.ListTargetPortalsRequest") + proto.RegisterType((*ListTargetPortalsResponse)(nil), "v1alpha1.ListTargetPortalsResponse") + proto.RegisterType((*ConnectTargetRequest)(nil), "v1alpha1.ConnectTargetRequest") + proto.RegisterType((*ConnectTargetResponse)(nil), "v1alpha1.ConnectTargetResponse") + proto.RegisterType((*GetTargetDisksRequest)(nil), "v1alpha1.GetTargetDisksRequest") + proto.RegisterType((*GetTargetDisksResponse)(nil), "v1alpha1.GetTargetDisksResponse") + proto.RegisterType((*DisconnectTargetRequest)(nil), "v1alpha1.DisconnectTargetRequest") + proto.RegisterType((*DisconnectTargetResponse)(nil), "v1alpha1.DisconnectTargetResponse") +} + +func init() { + proto.RegisterFile("github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.proto", fileDescriptor_0438d9bfe30f1df4) +} + +var fileDescriptor_0438d9bfe30f1df4 = []byte{ + // 671 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xdb, 0x4e, 0xdb, 0x40, + 0x10, 0x25, 0xdc, 0x9a, 0x4c, 0x12, 0x48, 0x47, 0x5c, 0x8c, 0x8b, 0x4a, 0x58, 0x4a, 0x15, 0x55, + 0x22, 0x11, 0xe9, 0x53, 0x55, 0xa1, 0xca, 0x05, 0x44, 0x91, 0xb8, 0xc9, 0x25, 0x2d, 0xa5, 0x52, + 0xa3, 0xc5, 0xd9, 0xe2, 0x15, 0x89, 0x6d, 0xbc, 0x6b, 0x54, 0x3e, 0xa1, 0x1f, 0xd0, 0xff, 0xad, + 0x7c, 0x8d, 0x49, 0x9c, 0xf4, 0xa1, 0xf0, 0xb6, 0x7b, 0xf6, 0xcc, 0x99, 0xd9, 0x99, 0xd9, 0x59, + 0x38, 0xb8, 0xe6, 0xd2, 0xf4, 0xae, 0xea, 0x86, 0xdd, 0x6b, 0xdc, 0x78, 0x57, 0xcc, 0xb5, 0x98, + 0x64, 0x62, 0xcb, 0x10, 0xbc, 0x61, 0x08, 0xbe, 0xe5, 0xb8, 0xf6, 0xaf, 0xfb, 0x86, 0xd1, 0xe5, + 0xcc, 0x92, 0x0d, 0xea, 0xf0, 0x06, 0x17, 0xfe, 0xd1, 0xdd, 0x36, 0xed, 0x3a, 0x26, 0xdd, 0xf6, + 0xa1, 0xba, 0xe3, 0xda, 0xd2, 0xc6, 0x7c, 0x8c, 0x91, 0x2f, 0x50, 0x3a, 0xa7, 0xee, 0x35, 0x93, + 0x67, 0xb6, 0x2b, 0x69, 0x17, 0x37, 0x61, 0x4e, 0x06, 0xfb, 0x36, 0xed, 0x74, 0x5c, 0x26, 0x84, + 0x92, 0xab, 0xe6, 0x6a, 0x05, 0xbd, 0x1c, 0xa2, 0x5a, 0x08, 0xe2, 0x1a, 0x14, 0x23, 0x9a, 0x63, + 0xbb, 0x52, 0x99, 0xac, 0xe6, 0x6a, 0x65, 0x1d, 0x64, 0xa2, 0x44, 0x5a, 0xb0, 0xa4, 0x75, 0x3a, + 0x69, 0x69, 0x9d, 0xdd, 0x7a, 0x4c, 0x48, 0x7c, 0x0f, 0xe5, 0x94, 0x29, 0xed, 0x06, 0x0e, 0x8a, + 0xcd, 0xa5, 0x7a, 0x1c, 0x53, 0xfd, 0x81, 0x55, 0x49, 0xa6, 0x76, 0x64, 0x05, 0x96, 0x87, 0x64, + 0x85, 0x63, 0x5b, 0x82, 0x91, 0x4b, 0x78, 0xb1, 0xc7, 0x85, 0x61, 0xdf, 0x31, 0xf7, 0xd1, 0xdd, + 0x36, 0x61, 0x35, 0x5b, 0x3b, 0xf4, 0x8d, 0x08, 0xd3, 0xfc, 0xd6, 0xf2, 0x73, 0x35, 0x55, 0x2b, + 0xe8, 0xc1, 0x9a, 0x5c, 0xc0, 0x8a, 0xce, 0x7a, 0xf6, 0x1d, 0x7b, 0xf4, 0x68, 0x56, 0x41, 0xcd, + 0x52, 0x8e, 0xf2, 0xa0, 0x82, 0x72, 0xc4, 0x85, 0x4c, 0x9f, 0x89, 0xc8, 0x2d, 0xb9, 0x84, 0x95, + 0x8c, 0xb3, 0xe8, 0x12, 0x3b, 0x49, 0xe9, 0xc3, 0x98, 0xc2, 0xeb, 0x8c, 0x0e, 0xaa, 0x9c, 0x0e, + 0x4a, 0x90, 0xdf, 0x93, 0xb0, 0xb0, 0x6b, 0x5b, 0x16, 0x33, 0x22, 0xfd, 0xc7, 0xb8, 0x2b, 0x56, + 0x60, 0x8a, 0xdf, 0x5a, 0x41, 0x83, 0x15, 0x74, 0x7f, 0x89, 0xef, 0xa0, 0x40, 0x3d, 0x69, 0xb6, + 0xe5, 0xbd, 0xc3, 0x94, 0xa9, 0x6a, 0xae, 0x36, 0xd7, 0x5c, 0xed, 0x4b, 0x69, 0x9e, 0x34, 0x99, + 0x25, 0xb9, 0x41, 0x25, 0xb7, 0xad, 0xf3, 0x7b, 0x87, 0xe9, 0x79, 0x9f, 0xee, 0xaf, 0x70, 0x03, + 0xca, 0x86, 0x49, 0x9d, 0xb6, 0x27, 0x98, 0x6b, 0xd1, 0x1e, 0x53, 0xa6, 0x03, 0xd9, 0x92, 0x0f, + 0xb6, 0x22, 0xcc, 0x6f, 0xed, 0x80, 0x24, 0x98, 0xe1, 0x32, 0xa9, 0xcc, 0x04, 0x14, 0xf0, 0xa1, + 0xcf, 0x01, 0x82, 0xeb, 0x50, 0xe2, 0xa2, 0xdd, 0xf3, 0xba, 0x92, 0x3b, 0x54, 0x9a, 0xca, 0x6c, + 0x35, 0x57, 0xcb, 0xeb, 0x45, 0x2e, 0x8e, 0x63, 0x88, 0x2c, 0xc3, 0xe2, 0x40, 0x2a, 0xa2, 0xe2, + 0xfc, 0x84, 0xc5, 0x03, 0x16, 0x81, 0x7b, 0x5c, 0xdc, 0x88, 0xa7, 0x49, 0x12, 0x69, 0xc2, 0xd2, + 0xa0, 0x9f, 0xa8, 0xca, 0x0a, 0x3c, 0xeb, 0x70, 0x71, 0x73, 0xd8, 0x89, 0xbb, 0x35, 0xde, 0x12, + 0x13, 0x96, 0x83, 0x26, 0x7f, 0xf2, 0x12, 0xfa, 0x2d, 0x3a, 0xec, 0x29, 0x8c, 0xef, 0x8d, 0x06, + 0x38, 0x5c, 0x43, 0xcc, 0xc3, 0xf4, 0xc9, 0xe9, 0xc9, 0x7e, 0x65, 0x02, 0x2b, 0x50, 0x3a, 0x3d, + 0xd9, 0x6f, 0x7f, 0xd5, 0xbe, 0xb5, 0x77, 0x3f, 0x69, 0x67, 0x95, 0x1c, 0xce, 0x43, 0xf1, 0xb8, + 0x75, 0xde, 0xd2, 0x8e, 0x42, 0x60, 0xb2, 0xf9, 0x67, 0x06, 0x66, 0x0e, 0xfd, 0xd1, 0x87, 0x17, + 0x30, 0x3f, 0x30, 0x2e, 0xb0, 0x9a, 0xea, 0x95, 0xcc, 0x01, 0xa5, 0xae, 0x8f, 0x61, 0x44, 0x65, + 0x9c, 0xc0, 0x6b, 0x58, 0xc8, 0x9a, 0x08, 0xb8, 0xd9, 0x37, 0x1e, 0x33, 0x8d, 0xd4, 0xd7, 0xff, + 0xa2, 0x25, 0x8e, 0x28, 0xe0, 0xf0, 0x63, 0xc7, 0x8d, 0xbe, 0xfd, 0xc8, 0x21, 0xa3, 0xbe, 0x1a, + 0x4f, 0x4a, 0x5c, 0xfc, 0x80, 0xe7, 0x43, 0x53, 0x01, 0x49, 0xdf, 0x78, 0xd4, 0x38, 0x51, 0x37, + 0xc6, 0x72, 0x12, 0x7d, 0x1d, 0xca, 0x0f, 0x5e, 0x03, 0xbe, 0xec, 0xdb, 0x65, 0x4d, 0x0c, 0x75, + 0x6d, 0xe4, 0x79, 0xa2, 0xf9, 0x1d, 0x2a, 0x83, 0x2d, 0x84, 0xeb, 0x03, 0x49, 0xcd, 0x50, 0x26, + 0xe3, 0x28, 0x89, 0x78, 0x0b, 0xe6, 0x1e, 0xbe, 0x1e, 0x4c, 0x45, 0x94, 0xf9, 0x7e, 0xd5, 0xea, + 0x68, 0x42, 0x2c, 0xfb, 0xf1, 0xc3, 0xe5, 0xce, 0x7f, 0x7d, 0xe0, 0x57, 0xb3, 0xc1, 0xef, 0xfd, + 0xf6, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0x6d, 0xe8, 0x73, 0x08, 0x08, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConnInterface + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion6 + +// IscsiClient is the client API for Iscsi service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type IscsiClient interface { + // AddTargetPortal registers an iSCSI target network address for later + // discovery. + AddTargetPortal(ctx context.Context, in *AddTargetPortalRequest, opts ...grpc.CallOption) (*AddTargetPortalResponse, error) + // DiscoverTargetPortal initiates discovery on an iSCSI target network address + // and returns discovered IQNs. + DiscoverTargetPortal(ctx context.Context, in *DiscoverTargetPortalRequest, opts ...grpc.CallOption) (*DiscoverTargetPortalResponse, error) + // RemoveTargetPortal removes an iSCSI target network address registration. + RemoveTargetPortal(ctx context.Context, in *RemoveTargetPortalRequest, opts ...grpc.CallOption) (*RemoveTargetPortalResponse, error) + // ListTargetPortal lists all currently registered iSCSI target network + // addresses. + ListTargetPortals(ctx context.Context, in *ListTargetPortalsRequest, opts ...grpc.CallOption) (*ListTargetPortalsResponse, error) + // ConnectTarget connects to an iSCSI Target + ConnectTarget(ctx context.Context, in *ConnectTargetRequest, opts ...grpc.CallOption) (*ConnectTargetResponse, error) + // DisconnectTarget disconnects from an iSCSI Target + DisconnectTarget(ctx context.Context, in *DisconnectTargetRequest, opts ...grpc.CallOption) (*DisconnectTargetResponse, error) + // GetTargetDisks returns the disk addresses that correspond to an iSCSI + // target + GetTargetDisks(ctx context.Context, in *GetTargetDisksRequest, opts ...grpc.CallOption) (*GetTargetDisksResponse, error) +} + +type iscsiClient struct { + cc grpc.ClientConnInterface +} + +func NewIscsiClient(cc grpc.ClientConnInterface) IscsiClient { + return &iscsiClient{cc} +} + +func (c *iscsiClient) AddTargetPortal(ctx context.Context, in *AddTargetPortalRequest, opts ...grpc.CallOption) (*AddTargetPortalResponse, error) { + out := new(AddTargetPortalResponse) + err := c.cc.Invoke(ctx, "/v1alpha1.Iscsi/AddTargetPortal", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *iscsiClient) DiscoverTargetPortal(ctx context.Context, in *DiscoverTargetPortalRequest, opts ...grpc.CallOption) (*DiscoverTargetPortalResponse, error) { + out := new(DiscoverTargetPortalResponse) + err := c.cc.Invoke(ctx, "/v1alpha1.Iscsi/DiscoverTargetPortal", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *iscsiClient) RemoveTargetPortal(ctx context.Context, in *RemoveTargetPortalRequest, opts ...grpc.CallOption) (*RemoveTargetPortalResponse, error) { + out := new(RemoveTargetPortalResponse) + err := c.cc.Invoke(ctx, "/v1alpha1.Iscsi/RemoveTargetPortal", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *iscsiClient) ListTargetPortals(ctx context.Context, in *ListTargetPortalsRequest, opts ...grpc.CallOption) (*ListTargetPortalsResponse, error) { + out := new(ListTargetPortalsResponse) + err := c.cc.Invoke(ctx, "/v1alpha1.Iscsi/ListTargetPortals", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *iscsiClient) ConnectTarget(ctx context.Context, in *ConnectTargetRequest, opts ...grpc.CallOption) (*ConnectTargetResponse, error) { + out := new(ConnectTargetResponse) + err := c.cc.Invoke(ctx, "/v1alpha1.Iscsi/ConnectTarget", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *iscsiClient) DisconnectTarget(ctx context.Context, in *DisconnectTargetRequest, opts ...grpc.CallOption) (*DisconnectTargetResponse, error) { + out := new(DisconnectTargetResponse) + err := c.cc.Invoke(ctx, "/v1alpha1.Iscsi/DisconnectTarget", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *iscsiClient) GetTargetDisks(ctx context.Context, in *GetTargetDisksRequest, opts ...grpc.CallOption) (*GetTargetDisksResponse, error) { + out := new(GetTargetDisksResponse) + err := c.cc.Invoke(ctx, "/v1alpha1.Iscsi/GetTargetDisks", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// IscsiServer is the server API for Iscsi service. +type IscsiServer interface { + // AddTargetPortal registers an iSCSI target network address for later + // discovery. + AddTargetPortal(context.Context, *AddTargetPortalRequest) (*AddTargetPortalResponse, error) + // DiscoverTargetPortal initiates discovery on an iSCSI target network address + // and returns discovered IQNs. + DiscoverTargetPortal(context.Context, *DiscoverTargetPortalRequest) (*DiscoverTargetPortalResponse, error) + // RemoveTargetPortal removes an iSCSI target network address registration. + RemoveTargetPortal(context.Context, *RemoveTargetPortalRequest) (*RemoveTargetPortalResponse, error) + // ListTargetPortal lists all currently registered iSCSI target network + // addresses. + ListTargetPortals(context.Context, *ListTargetPortalsRequest) (*ListTargetPortalsResponse, error) + // ConnectTarget connects to an iSCSI Target + ConnectTarget(context.Context, *ConnectTargetRequest) (*ConnectTargetResponse, error) + // DisconnectTarget disconnects from an iSCSI Target + DisconnectTarget(context.Context, *DisconnectTargetRequest) (*DisconnectTargetResponse, error) + // GetTargetDisks returns the disk addresses that correspond to an iSCSI + // target + GetTargetDisks(context.Context, *GetTargetDisksRequest) (*GetTargetDisksResponse, error) +} + +// UnimplementedIscsiServer can be embedded to have forward compatible implementations. +type UnimplementedIscsiServer struct { +} + +func (*UnimplementedIscsiServer) AddTargetPortal(ctx context.Context, req *AddTargetPortalRequest) (*AddTargetPortalResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddTargetPortal not implemented") +} +func (*UnimplementedIscsiServer) DiscoverTargetPortal(ctx context.Context, req *DiscoverTargetPortalRequest) (*DiscoverTargetPortalResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DiscoverTargetPortal not implemented") +} +func (*UnimplementedIscsiServer) RemoveTargetPortal(ctx context.Context, req *RemoveTargetPortalRequest) (*RemoveTargetPortalResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveTargetPortal not implemented") +} +func (*UnimplementedIscsiServer) ListTargetPortals(ctx context.Context, req *ListTargetPortalsRequest) (*ListTargetPortalsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListTargetPortals not implemented") +} +func (*UnimplementedIscsiServer) ConnectTarget(ctx context.Context, req *ConnectTargetRequest) (*ConnectTargetResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConnectTarget not implemented") +} +func (*UnimplementedIscsiServer) DisconnectTarget(ctx context.Context, req *DisconnectTargetRequest) (*DisconnectTargetResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DisconnectTarget not implemented") +} +func (*UnimplementedIscsiServer) GetTargetDisks(ctx context.Context, req *GetTargetDisksRequest) (*GetTargetDisksResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetTargetDisks not implemented") +} + +func RegisterIscsiServer(s *grpc.Server, srv IscsiServer) { + s.RegisterService(&_Iscsi_serviceDesc, srv) +} + +func _Iscsi_AddTargetPortal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddTargetPortalRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(IscsiServer).AddTargetPortal(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1alpha1.Iscsi/AddTargetPortal", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(IscsiServer).AddTargetPortal(ctx, req.(*AddTargetPortalRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Iscsi_DiscoverTargetPortal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DiscoverTargetPortalRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(IscsiServer).DiscoverTargetPortal(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1alpha1.Iscsi/DiscoverTargetPortal", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(IscsiServer).DiscoverTargetPortal(ctx, req.(*DiscoverTargetPortalRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Iscsi_RemoveTargetPortal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveTargetPortalRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(IscsiServer).RemoveTargetPortal(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1alpha1.Iscsi/RemoveTargetPortal", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(IscsiServer).RemoveTargetPortal(ctx, req.(*RemoveTargetPortalRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Iscsi_ListTargetPortals_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListTargetPortalsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(IscsiServer).ListTargetPortals(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1alpha1.Iscsi/ListTargetPortals", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(IscsiServer).ListTargetPortals(ctx, req.(*ListTargetPortalsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Iscsi_ConnectTarget_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ConnectTargetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(IscsiServer).ConnectTarget(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1alpha1.Iscsi/ConnectTarget", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(IscsiServer).ConnectTarget(ctx, req.(*ConnectTargetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Iscsi_DisconnectTarget_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DisconnectTargetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(IscsiServer).DisconnectTarget(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1alpha1.Iscsi/DisconnectTarget", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(IscsiServer).DisconnectTarget(ctx, req.(*DisconnectTargetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Iscsi_GetTargetDisks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetTargetDisksRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(IscsiServer).GetTargetDisks(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1alpha1.Iscsi/GetTargetDisks", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(IscsiServer).GetTargetDisks(ctx, req.(*GetTargetDisksRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Iscsi_serviceDesc = grpc.ServiceDesc{ + ServiceName: "v1alpha1.Iscsi", + HandlerType: (*IscsiServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "AddTargetPortal", + Handler: _Iscsi_AddTargetPortal_Handler, + }, + { + MethodName: "DiscoverTargetPortal", + Handler: _Iscsi_DiscoverTargetPortal_Handler, + }, + { + MethodName: "RemoveTargetPortal", + Handler: _Iscsi_RemoveTargetPortal_Handler, + }, + { + MethodName: "ListTargetPortals", + Handler: _Iscsi_ListTargetPortals_Handler, + }, + { + MethodName: "ConnectTarget", + Handler: _Iscsi_ConnectTarget_Handler, + }, + { + MethodName: "DisconnectTarget", + Handler: _Iscsi_DisconnectTarget_Handler, + }, + { + MethodName: "GetTargetDisks", + Handler: _Iscsi_GetTargetDisks_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.proto", +} diff --git a/client/api/iscsi/v1alpha1/api.proto b/client/api/iscsi/v1alpha1/api.proto new file mode 100644 index 00000000..66de2c19 --- /dev/null +++ b/client/api/iscsi/v1alpha1/api.proto @@ -0,0 +1,149 @@ +syntax = "proto3"; + +package v1alpha1; + +option go_package = "github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1"; + +service Iscsi { + // AddTargetPortal registers an iSCSI target network address for later + // discovery. + rpc AddTargetPortal(AddTargetPortalRequest) + returns (AddTargetPortalResponse) {} + + // DiscoverTargetPortal initiates discovery on an iSCSI target network address + // and returns discovered IQNs. + rpc DiscoverTargetPortal(DiscoverTargetPortalRequest) + returns (DiscoverTargetPortalResponse) {} + + // RemoveTargetPortal removes an iSCSI target network address registration. + rpc RemoveTargetPortal(RemoveTargetPortalRequest) + returns (RemoveTargetPortalResponse) {} + + // ListTargetPortal lists all currently registered iSCSI target network + // addresses. + rpc ListTargetPortals(ListTargetPortalsRequest) + returns (ListTargetPortalsResponse) {} + + // ConnectTarget connects to an iSCSI Target + rpc ConnectTarget(ConnectTargetRequest) returns (ConnectTargetResponse) {} + + // DisconnectTarget disconnects from an iSCSI Target + rpc DisconnectTarget(DisconnectTargetRequest) + returns (DisconnectTargetResponse) {} + + // GetTargetDisks returns the disk addresses that correspond to an iSCSI + // target + rpc GetTargetDisks(GetTargetDisksRequest) returns (GetTargetDisksResponse) {} +} + +// TargetPortal is an address and port pair for a specific iSCSI storage +// target. +message TargetPortal { + // iSCSI Target (server) address + string target_address = 1; + + // iSCSI Target port (default iSCSI port is 3260) + uint32 target_port = 2; +} + +message AddTargetPortalRequest { + // iSCSI Target Portal to register in the initiator + TargetPortal target_portal = 1; +} + +message AddTargetPortalResponse { + // Intentionally empty +} + +message DiscoverTargetPortalRequest { + // iSCSI Target Portal on which to initiate discovery + TargetPortal target_portal = 1; +} + +message DiscoverTargetPortalResponse { + // List of discovered IQN addresses + // follows IQN format: iqn.yyyy-mm.naming-authority:unique-name + repeated string iqns = 1; +} + +message RemoveTargetPortalRequest { + // iSCSI Target Portal + TargetPortal target_portal = 1; +} + +message RemoveTargetPortalResponse { + // Intentionally empty +} + +message ListTargetPortalsRequest { + // Intentionally empty +} + +message ListTargetPortalsResponse { + // A list of Target Portals currently registered in the initiator + repeated TargetPortal target_portals = 1; +} + +enum AuthenticationType { + NONE = 0; + ONE_WAY_CHAP = 1; + MUTUAL_CHAP = 2; +} + +message ConnectTargetRequest { + // Target portal to which the initiator will connect. + TargetPortal target_portal = 1; + + // IQN of the iSCSI Target + string iqn = 2; + + // Connection authentication type, None by default + // + // One Way Chap uses the chap_username and chap_secret + // fields mentioned below to authenticate the initiator. + // + // Mutual Chap uses both the user/secret mentioned below + // and the Initiator Chap Secret to authenticate the target and initiator. + AuthenticationType auth_type = 3; + + // CHAP Username used to authenticate the initiator + string chap_username = 4; + + // CHAP password used to authenticate the initiator + string chap_secret = 5; + + // Should enable multipath on the connection + // In order for multipath to work on Windows, the Multipath feature + // needs to be installed as well as MPIO should be correctly configured + bool is_multipath = 6; +} + +message ConnectTargetResponse { + // Intentionally empty +} + +message GetTargetDisksRequest { + // Target portal to which the initiator will connect. + TargetPortal target_portal = 1; + + // IQN of the iSCSI Target + string iqn = 2; +} + +message GetTargetDisksResponse { + // List composed of disk ids (numbers) that are associated with the + // iSCSI target + repeated string diskIds = 1; +} + +message DisconnectTargetRequest { + // Target portal to which the initiator will connect. + TargetPortal target_portal = 1; + + // IQN of the iSCSI Target + string iqn = 2; +} + +message DisconnectTargetResponse { + // Intentionally empty +} diff --git a/client/groups/iscsi/v1alpha1/client_generated.go b/client/groups/iscsi/v1alpha1/client_generated.go new file mode 100644 index 00000000..d4e75b61 --- /dev/null +++ b/client/groups/iscsi/v1alpha1/client_generated.go @@ -0,0 +1,80 @@ +// Code generated by csi-proxy-api-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "net" + + "github.com/Microsoft/go-winio" + "github.com/kubernetes-csi/csi-proxy/client" + "github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1" + "github.com/kubernetes-csi/csi-proxy/client/apiversion" + "google.golang.org/grpc" +) + +const groupName = "iscsi" + +var version = apiversion.NewVersionOrPanic("v1alpha1") + +type Client struct { + client v1alpha1.IscsiClient + connection *grpc.ClientConn +} + +// NewClient returns a client to make calls to the iscsi API group version v1alpha1. +// It's the caller's responsibility to Close the client when done. +func NewClient() (*Client, error) { + pipePath := client.PipePath(groupName, version) + + connection, err := grpc.Dial(pipePath, + grpc.WithContextDialer(func(context context.Context, s string) (net.Conn, error) { + return winio.DialPipeContext(context, s) + }), + grpc.WithInsecure()) + if err != nil { + return nil, err + } + + client := v1alpha1.NewIscsiClient(connection) + return &Client{ + client: client, + connection: connection, + }, nil +} + +// Close closes the client. It must be called before the client gets GC-ed. +func (w *Client) Close() error { + return w.connection.Close() +} + +// ensures we implement all the required methods +var _ v1alpha1.IscsiClient = &Client{} + +func (w *Client) AddTargetPortal(context context.Context, request *v1alpha1.AddTargetPortalRequest, opts ...grpc.CallOption) (*v1alpha1.AddTargetPortalResponse, error) { + return w.client.AddTargetPortal(context, request, opts...) +} + +func (w *Client) ConnectTarget(context context.Context, request *v1alpha1.ConnectTargetRequest, opts ...grpc.CallOption) (*v1alpha1.ConnectTargetResponse, error) { + return w.client.ConnectTarget(context, request, opts...) +} + +func (w *Client) DisconnectTarget(context context.Context, request *v1alpha1.DisconnectTargetRequest, opts ...grpc.CallOption) (*v1alpha1.DisconnectTargetResponse, error) { + return w.client.DisconnectTarget(context, request, opts...) +} + +func (w *Client) DiscoverTargetPortal(context context.Context, request *v1alpha1.DiscoverTargetPortalRequest, opts ...grpc.CallOption) (*v1alpha1.DiscoverTargetPortalResponse, error) { + return w.client.DiscoverTargetPortal(context, request, opts...) +} + +func (w *Client) GetTargetDisks(context context.Context, request *v1alpha1.GetTargetDisksRequest, opts ...grpc.CallOption) (*v1alpha1.GetTargetDisksResponse, error) { + return w.client.GetTargetDisks(context, request, opts...) +} + +func (w *Client) ListTargetPortals(context context.Context, request *v1alpha1.ListTargetPortalsRequest, opts ...grpc.CallOption) (*v1alpha1.ListTargetPortalsResponse, error) { + return w.client.ListTargetPortals(context, request, opts...) +} + +func (w *Client) RemoveTargetPortal(context context.Context, request *v1alpha1.RemoveTargetPortalRequest, opts ...grpc.CallOption) (*v1alpha1.RemoveTargetPortalResponse, error) { + return w.client.RemoveTargetPortal(context, request, opts...) +} diff --git a/cmd/csi-proxy/main.go b/cmd/csi-proxy/main.go index ff004a9f..a5b95311 100644 --- a/cmd/csi-proxy/main.go +++ b/cmd/csi-proxy/main.go @@ -5,12 +5,14 @@ import ( diskapi "github.com/kubernetes-csi/csi-proxy/internal/os/disk" filesystemapi "github.com/kubernetes-csi/csi-proxy/internal/os/filesystem" + iscsiapi "github.com/kubernetes-csi/csi-proxy/internal/os/iscsi" smbapi "github.com/kubernetes-csi/csi-proxy/internal/os/smb" sysapi "github.com/kubernetes-csi/csi-proxy/internal/os/system" volumeapi "github.com/kubernetes-csi/csi-proxy/internal/os/volume" "github.com/kubernetes-csi/csi-proxy/internal/server" disksrv "github.com/kubernetes-csi/csi-proxy/internal/server/disk" filesystemsrv "github.com/kubernetes-csi/csi-proxy/internal/server/filesystem" + iscsisrv "github.com/kubernetes-csi/csi-proxy/internal/server/iscsi" smbsrv "github.com/kubernetes-csi/csi-proxy/internal/server/smb" syssrv "github.com/kubernetes-csi/csi-proxy/internal/server/system" srvtypes "github.com/kubernetes-csi/csi-proxy/internal/server/types" @@ -84,12 +86,18 @@ func apiGroups() ([]srvtypes.APIGroup, error) { return []srvtypes.APIGroup{}, err } + iscsisrv, err := iscsisrv.NewServer(iscsiapi.New()) + if err != nil { + return []srvtypes.APIGroup{}, err + } + return []srvtypes.APIGroup{ fssrv, disksrv, volumesrv, smbsrv, syssrv, + iscsisrv, }, nil } diff --git a/go.mod b/go.mod index d0f2bf72..5556822d 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ replace ( require ( github.com/Microsoft/go-winio v0.4.14 github.com/golang/protobuf v1.4.1 + github.com/google/go-cmp v0.5.0 github.com/iancoleman/strcase v0.0.0-20190422225806-e506e3ef7365 github.com/kubernetes-csi/csi-proxy/client v0.0.0-00010101000000-000000000000 github.com/pkg/errors v0.8.1 @@ -25,6 +26,7 @@ require ( golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 golang.org/x/text v0.3.2 // indirect google.golang.org/grpc v1.27.1 + google.golang.org/protobuf v1.25.0 k8s.io/gengo v0.0.0-00010101000000-000000000000 k8s.io/klog v1.0.0 ) diff --git a/integrationtests/iscsi_ps_scripts.go b/integrationtests/iscsi_ps_scripts.go new file mode 100644 index 00000000..dbaa7c39 --- /dev/null +++ b/integrationtests/iscsi_ps_scripts.go @@ -0,0 +1,184 @@ +package integrationtests + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "os/exec" + "testing" +) + +func installIscsiTarget() error { + _, err := runPowershellScript(IscsiTargetInstallScript) + if err != nil { + return fmt.Errorf("failed installing iSCSI target. err=%v", err) + } + + return nil +} + +const IscsiTargetInstallScript = ` +$ErrorActionPreference = "Stop" +$ProgressPreference = "SilentlyContinue" + +# Install iSCSI Target +Install-WindowsFeature FS-iSCSITarget-Server + +# Setup for loopback usage +Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\iSCSI Target" -Name AllowLoopBack -Value 1 +Restart-Service WinTarget +` + +type IscsiSetupConfig struct { + Iqn string `json:"iqn"` + Ip string `json:"ip"` +} + +const IscsiEnvironmentSetupScript = ` +$ErrorActionPreference = "Stop" +$ProgressPreference = "SilentlyContinue" + +$targetName = "%s" + +# Get local IPv4 (e.g. 10.30.1.15, not 127.0.0.1) +$address = $(Get-NetIPAddress | Where-Object { $_.InterfaceAlias -eq "Ethernet" -and $_.AddressFamily -eq "IPv4" }).IPAddress + +# Create virtual disk in RAM +New-IscsiVirtualDisk -Path "ramdisk:scratch-${targetName}.vhdx" -Size 100MB | Out-Null + +# Create a target that allows all initiator IQNs and map a disk to the new target +$target = New-IscsiServerTarget -TargetName $targetName -InitiatorIds @("Iqn:*") +Add-IscsiVirtualDiskTargetMapping -TargetName $targetName -DevicePath "ramdisk:scratch-${targetName}.vhdx" | Out-Null + +$output = @{ + "iqn" = "$($target.TargetIqn)" + "ip" = $address +} + +$output | ConvertTo-Json | Write-Output +` + +const IscsiSetChapScript = ` +$ErrorActionPreference = "Stop" +$ProgressPreference = "SilentlyContinue" + +$targetName = "%s" +$username = "%s" +$password = "%s" +$securestring = ConvertTo-SecureString -String $password -AsPlainText -Force +$chap = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($username, $securestring) +Set-IscsiServerTarget -TargetName $targetName -EnableChap $true -Chap $chap +` + +func setChap(targetName string, username string, password string) error { + script := fmt.Sprintf(IscsiSetChapScript, targetName, username, password) + _, err := runPowershellScript(script) + if err != nil { + return fmt.Errorf("failed setting CHAP on iSCSI target=%v. err=%v", targetName, err) + } + + return nil +} + +const IscsiSetReverseChapScript = ` +$ErrorActionPreference = "Stop" +$ProgressPreference = "SilentlyContinue" + +$targetName = "%s" +$password = "%s" +$username = "doesnt-matter" +$securestring = ConvertTo-SecureString -String $password -AsPlainText -Force + +# Windows initiator does not uses the username for mutual authentication +$chap = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($username, $securestring) +Set-IscsiServerTarget -TargetName $targetName -EnableReverseChap $true -ReverseChap $chap +` + +func setReverseChap(targetName string, password string) error { + script := fmt.Sprintf(IscsiSetReverseChapScript, targetName, password) + _, err := runPowershellScript(script) + if err != nil { + return fmt.Errorf("failed setting reverse CHAP on iSCSI target=%v. err=%v", targetName, err) + } + + return nil +} + +func cleanup(t *testing.T) error { + _, err := runPowershellScript(IscsiCleanupScript) + if err != nil { + msg := fmt.Sprintf("failed cleaning up environment. err=%v", err) + t.Fatal(msg) + // exits function + } + + return nil +} + +const IscsiCleanupScript = ` +$ErrorActionPreference = "Stop" +$ProgressPreference = "SilentlyContinue" + +# Clean initiator +Get-Disk | Where-Object {$_.Bustype -eq "iSCSI"} | Set-Disk -IsOffline:$true +Get-IscsiTarget | Disconnect-IscsiTarget -Confirm:$false +Get-IscsiTargetPortal | Remove-IscsiTargetPortal -confirm:$false + +# Clean target +Get-IscsiServerTarget | Remove-IscsiServerTarget +Get-IscsiVirtualDisk | Remove-IscsiVirtualDisk + +# Stop iSCSI initiator +Get-Service "MsiSCSI" | Stop-Service +` + +func writeTempFile(text string, extension string) (string, error) { + pattern := fmt.Sprintf("*.%s", extension) + tempfile, err := ioutil.TempFile(os.TempDir(), pattern) + if err != nil { + return "", fmt.Errorf("failed creating temp file pattern=%v: %w", pattern, err) + } + + defer tempfile.Close() + + _, err = tempfile.WriteString(text) + if err != nil { + return "", fmt.Errorf("failed writing to temp file name=%v: %w", tempfile.Name(), err) + } + + return tempfile.Name(), nil +} + +func runPowershellScript(script string) (string, error) { + path, err := writeTempFile(script, "ps1") + if err != nil { + return "", err + } + + defer os.Remove(path) + + cmd := exec.Command("powershell", "-File", path) + out, err := cmd.CombinedOutput() + if err != nil { + return "", fmt.Errorf("error running powershell script. path %s, output: %s, err: %w", path, string(out), err) + } + + return string(out), nil +} + +func setupEnv(targetName string) (*IscsiSetupConfig, error) { + script := fmt.Sprintf(IscsiEnvironmentSetupScript, targetName) + out, err := runPowershellScript(script) + if err != nil { + return nil, fmt.Errorf("failed setting up environment. err=%v", err) + } + + config := IscsiSetupConfig{} + err = json.Unmarshal([]byte(out), &config) + if err != nil { + return nil, err + } + + return &config, nil +} diff --git a/integrationtests/iscsi_test.go b/integrationtests/iscsi_test.go new file mode 100644 index 00000000..c1bb88a0 --- /dev/null +++ b/integrationtests/iscsi_test.go @@ -0,0 +1,365 @@ +package integrationtests + +// Needs to run the test in a VM with a local iSCSI target. +// See iscsi_test_setup.ps1 script + +import ( + "context" + "fmt" + "testing" + + disk_api "github.com/kubernetes-csi/csi-proxy/client/api/disk/v1beta2" + iscsi_api "github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1" + system_api "github.com/kubernetes-csi/csi-proxy/client/api/system/v1alpha1" + disk_client "github.com/kubernetes-csi/csi-proxy/client/groups/disk/v1beta2" + iscsi_client "github.com/kubernetes-csi/csi-proxy/client/groups/iscsi/v1alpha1" + system_client "github.com/kubernetes-csi/csi-proxy/client/groups/system/v1alpha1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const defaultIscsiPort = 3260 +const defaultProtoPort = 0 // default value when port is not set + +func TestIscsiAPIGroup(t *testing.T) { + skipTestOnCondition(t, !shouldRunIscsiTests()) + + err := installIscsiTarget() + require.NoError(t, err, "Failed installing iSCSI target") + + t.Run("List/Add/Remove TargetPortal (Port=3260)", func(t *testing.T) { + targetPortalTest(t, defaultIscsiPort) + }) + + t.Run("List/Add/Remove TargetPortal (Port not mentioned, effectively 3260)", func(t *testing.T) { + targetPortalTest(t, defaultProtoPort) + }) + + t.Run("Discover Target and Connect/Disconnect (No CHAP)", func(t *testing.T) { + targetTest(t) + }) + + t.Run("Discover Target and Connect/Disconnect (CHAP)", func(t *testing.T) { + targetChapTest(t) + }) + + t.Run("Discover Target and Connect/Disconnect (Mutual CHAP)", func(t *testing.T) { + targetMutualChapTest(t) + }) + + t.Run("Full flow", func(t *testing.T) { + e2e_test(t) + }) + +} + +func e2e_test(t *testing.T) { + config, err := setupEnv("e2e") + assert.NoError(t, err) + + defer cleanup(t) + + iscsi, err := iscsi_client.NewClient() + require.Nil(t, err) + + defer func() { assert.NoError(t, iscsi.Close()) }() + + disk, err := disk_client.NewClient() + require.Nil(t, err) + + defer func() { assert.NoError(t, disk.Close()) }() + + system, err := system_client.NewClient() + require.Nil(t, err) + + defer func() { assert.NoError(t, system.Close()) }() + + startReq := &system_api.StartServiceRequest{Name: "MSiSCSI"} + _, err = system.StartService(context.TODO(), startReq) + require.NoError(t, err) + + tp := &iscsi_api.TargetPortal{ + TargetAddress: config.Ip, + TargetPort: defaultIscsiPort, + } + + addTpReq := &iscsi_api.AddTargetPortalRequest{ + TargetPortal: tp, + } + _, err = iscsi.AddTargetPortal(context.Background(), addTpReq) + assert.Nil(t, err) + + discReq := &iscsi_api.DiscoverTargetPortalRequest{TargetPortal: tp} + discResp, err := iscsi.DiscoverTargetPortal(context.TODO(), discReq) + if assert.Nil(t, err) { + assert.Contains(t, discResp.Iqns, config.Iqn) + } + + connectReq := &iscsi_api.ConnectTargetRequest{TargetPortal: tp, Iqn: config.Iqn} + _, err = iscsi.ConnectTarget(context.TODO(), connectReq) + assert.Nil(t, err) + + tgtDisksReq := &iscsi_api.GetTargetDisksRequest{TargetPortal: tp, Iqn: config.Iqn} + tgtDisksResp, err := iscsi.GetTargetDisks(context.TODO(), tgtDisksReq) + require.Nil(t, err) + require.Len(t, tgtDisksResp.DiskIds, 1) + + diskId := tgtDisksResp.DiskIds[0] + + attachReq := &disk_api.SetAttachStateRequest{DiskID: diskId, IsOnline: true} + _, err = disk.SetAttachState(context.TODO(), attachReq) + require.Nil(t, err) + + partReq := &disk_api.PartitionDiskRequest{DiskID: diskId} + _, err = disk.PartitionDisk(context.TODO(), partReq) + assert.Nil(t, err) + + detachReq := &disk_api.SetAttachStateRequest{DiskID: diskId, IsOnline: false} + _, err = disk.SetAttachState(context.TODO(), detachReq) + assert.Nil(t, err) +} + +func targetTest(t *testing.T) { + config, err := setupEnv("target") + assert.NoError(t, err) + + defer cleanup(t) + + client, err := iscsi_client.NewClient() + require.Nil(t, err) + + defer func() { assert.NoError(t, client.Close()) }() + + system, err := system_client.NewClient() + require.Nil(t, err) + + defer func() { assert.NoError(t, system.Close()) }() + + startReq := &system_api.StartServiceRequest{Name: "MSiSCSI"} + _, err = system.StartService(context.TODO(), startReq) + require.NoError(t, err) + + tp := &iscsi_api.TargetPortal{ + TargetAddress: config.Ip, + TargetPort: defaultIscsiPort, + } + + addTpReq := &iscsi_api.AddTargetPortalRequest{ + TargetPortal: tp, + } + _, err = client.AddTargetPortal(context.Background(), addTpReq) + assert.Nil(t, err) + + discReq := &iscsi_api.DiscoverTargetPortalRequest{TargetPortal: tp} + discResp, err := client.DiscoverTargetPortal(context.TODO(), discReq) + if assert.Nil(t, err) { + assert.Contains(t, discResp.Iqns, config.Iqn) + } + + connectReq := &iscsi_api.ConnectTargetRequest{TargetPortal: tp, Iqn: config.Iqn} + _, err = client.ConnectTarget(context.TODO(), connectReq) + assert.Nil(t, err) + + disconReq := &iscsi_api.DisconnectTargetRequest{TargetPortal: tp, Iqn: config.Iqn} + _, err = client.DisconnectTarget(context.TODO(), disconReq) + assert.Nil(t, err) +} + +func targetChapTest(t *testing.T) { + const targetName = "chapTarget" + const username = "someuser" + const password = "verysecretpass" + + config, err := setupEnv(targetName) + require.NoError(t, err) + + defer cleanup(t) + + err = setChap(targetName, username, password) + require.NoError(t, err) + + client, err := iscsi_client.NewClient() + require.Nil(t, err) + + defer func() { assert.NoError(t, client.Close()) }() + + system, err := system_client.NewClient() + require.Nil(t, err) + + defer func() { assert.NoError(t, system.Close()) }() + + startReq := &system_api.StartServiceRequest{Name: "MSiSCSI"} + _, err = system.StartService(context.TODO(), startReq) + require.NoError(t, err) + + tp := &iscsi_api.TargetPortal{ + TargetAddress: config.Ip, + TargetPort: defaultIscsiPort, + } + + addTpReq := &iscsi_api.AddTargetPortalRequest{ + TargetPortal: tp, + } + _, err = client.AddTargetPortal(context.Background(), addTpReq) + assert.Nil(t, err) + + discReq := &iscsi_api.DiscoverTargetPortalRequest{TargetPortal: tp} + discResp, err := client.DiscoverTargetPortal(context.TODO(), discReq) + if assert.Nil(t, err) { + assert.Contains(t, discResp.Iqns, config.Iqn) + } + + connectReq := &iscsi_api.ConnectTargetRequest{ + TargetPortal: tp, + Iqn: config.Iqn, + ChapUsername: username, + ChapSecret: password, + AuthType: iscsi_api.AuthenticationType_ONE_WAY_CHAP, + } + _, err = client.ConnectTarget(context.TODO(), connectReq) + assert.Nil(t, err) + + disconReq := &iscsi_api.DisconnectTargetRequest{TargetPortal: tp, Iqn: config.Iqn} + _, err = client.DisconnectTarget(context.TODO(), disconReq) + assert.Nil(t, err) +} + +func targetMutualChapTest(t *testing.T) { + const targetName = "mutualChapTarget" + const username = "anotheruser" + const password = "broccoli-man" + const reverse_password = "reversssssssse" + + config, err := setupEnv(targetName) + require.NoError(t, err) + + defer cleanup(t) + + err = setChap(targetName, username, password) + require.NoError(t, err) + + err = setReverseChap(targetName, reverse_password) + require.NoError(t, err) + + client, err := iscsi_client.NewClient() + require.Nil(t, err) + + defer func() { assert.NoError(t, client.Close()) }() + + system, err := system_client.NewClient() + require.Nil(t, err) + + defer func() { assert.NoError(t, system.Close()) }() + + startReq := &system_api.StartServiceRequest{Name: "MSiSCSI"} + _, err = system.StartService(context.TODO(), startReq) + require.NoError(t, err) + + tp := &iscsi_api.TargetPortal{ + TargetAddress: config.Ip, + TargetPort: defaultIscsiPort, + } + + addTpReq := &iscsi_api.AddTargetPortalRequest{ + TargetPortal: tp, + } + _, err = client.AddTargetPortal(context.Background(), addTpReq) + assert.Nil(t, err) + + discReq := &iscsi_api.DiscoverTargetPortalRequest{TargetPortal: tp} + discResp, err := client.DiscoverTargetPortal(context.TODO(), discReq) + if assert.Nil(t, err) { + assert.Contains(t, discResp.Iqns, config.Iqn) + } + + connectReq := &iscsi_api.ConnectTargetRequest{ + TargetPortal: tp, + Iqn: config.Iqn, + ChapUsername: username, + ChapSecret: password, + AuthType: iscsi_api.AuthenticationType_MUTUAL_CHAP, + } + + // TODO: Replace this with the corresponding csi-proxy API when add it + // Try using a wrong initiator password and expect error + out, err := runPowershellCmd(`Set-IscsiChapSecret -ChapSecret "made-up-pass"`) + require.NoErrorf(t, err, "Cannot set initiator chap out=%v", out) + + _, err = client.ConnectTarget(context.TODO(), connectReq) + assert.NotNil(t, err) + + // TODO: Replace this with the corresponding csi-proxy API when add it + out, err = runPowershellCmd(fmt.Sprintf(`Set-IscsiChapSecret -ChapSecret "%s"`, + reverse_password)) + require.NoErrorf(t, err, "Cannot set initiator chap out=%v", out) + + _, err = client.ConnectTarget(context.TODO(), connectReq) + assert.Nil(t, err) + + disconReq := &iscsi_api.DisconnectTargetRequest{TargetPortal: tp, Iqn: config.Iqn} + _, err = client.DisconnectTarget(context.TODO(), disconReq) + assert.Nil(t, err) +} + +func targetPortalTest(t *testing.T, port uint32) { + config, err := setupEnv(fmt.Sprintf("targetportal-%d", port)) + assert.NoError(t, err) + + defer cleanup(t) + + client, err := iscsi_client.NewClient() + require.Nil(t, err) + + defer func() { assert.NoError(t, client.Close()) }() + + system, err := system_client.NewClient() + require.Nil(t, err) + + defer func() { assert.NoError(t, system.Close()) }() + + startReq := &system_api.StartServiceRequest{Name: "MSiSCSI"} + _, err = system.StartService(context.TODO(), startReq) + require.NoError(t, err) + + tp := &iscsi_api.TargetPortal{ + TargetAddress: config.Ip, + TargetPort: port, + } + + listReq := &iscsi_api.ListTargetPortalsRequest{} + + listResp, err := client.ListTargetPortals(context.Background(), listReq) + if assert.Nil(t, err) { + assert.Len(t, listResp.TargetPortals, 0, + "Expect no registered target portals") + } + + addTpReq := &iscsi_api.AddTargetPortalRequest{TargetPortal: tp} + _, err = client.AddTargetPortal(context.Background(), addTpReq) + assert.Nil(t, err) + + // Port 0 (unset) is handled as the default iSCSI port + expectedPort := port + if expectedPort == 0 { + expectedPort = defaultIscsiPort + } + + gotListResp, err := client.ListTargetPortals(context.Background(), listReq) + if assert.Nil(t, err) { + assert.Len(t, gotListResp.TargetPortals, 1) + assert.Equal(t, gotListResp.TargetPortals[0].TargetPort, expectedPort) + assert.Equal(t, gotListResp.TargetPortals[0].TargetAddress, tp.TargetAddress) + } + + remReq := &iscsi_api.RemoveTargetPortalRequest{ + TargetPortal: tp, + } + _, err = client.RemoveTargetPortal(context.Background(), remReq) + assert.Nil(t, err) + + listResp, err = client.ListTargetPortals(context.Background(), listReq) + if assert.Nil(t, err) { + assert.Len(t, listResp.TargetPortals, 0, + "Expect no registered target portals after delete") + } +} diff --git a/integrationtests/utils.go b/integrationtests/utils.go index 3d9d064b..5c739974 100644 --- a/integrationtests/utils.go +++ b/integrationtests/utils.go @@ -144,3 +144,11 @@ func skipTestOnCondition(t *testing.T, condition bool) { t.Skip("Skipping test") } } + +// returns true if ENABLE_ISCSI_TESTS is set to "TRUE" +// used to skip iSCSI tests as they affect the test machine +// e.g. install an iSCSI target, format a disk, etc. +// Take care to use disposable clean VMs for tests +func shouldRunIscsiTests() bool { + return os.Getenv("ENABLE_ISCSI_TESTS") == "TRUE" +} diff --git a/internal/os/iscsi/api.go b/internal/os/iscsi/api.go new file mode 100644 index 00000000..13433044 --- /dev/null +++ b/internal/os/iscsi/api.go @@ -0,0 +1,192 @@ +package iscsi + +import ( + "encoding/json" + "fmt" + "os" + "os/exec" +) + +// Implements the iSCSI OS API calls. All code here should be very simple +// pass-through to the OS APIs. Any logic around the APIs should go in +// internal/server/iscsi/server.go so that logic can be easily unit-tested +// without requiring specific OS environments. + +type APIImplementor struct{} + +func New() APIImplementor { + return APIImplementor{} +} + +func (APIImplementor) AddTargetPortal(portal *TargetPortal) error { + cmdLine := fmt.Sprintf( + `New-IscsiTargetPortal -TargetPortalAddress ${Env:iscsi_tp_address} ` + + `-TargetPortalPortNumber ${Env:iscsi_tp_port}`) + cmd := exec.Command("powershell.exe", "/c", cmdLine) + cmd.Env = append(os.Environ(), + fmt.Sprintf("iscsi_tp_address=%s", portal.Address), + fmt.Sprintf("iscsi_tp_port=%d", portal.Port)) + + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("error adding target portal. cmd %s, output: %s, err: %v", cmdLine, string(out), err) + } + + return nil +} + +func (APIImplementor) DiscoverTargetPortal(portal *TargetPortal) ([]string, error) { + // ConvertTo-Json is not part of the pipeline because powershell converts an + // array with one element to a single element + cmdLine := fmt.Sprintf( + `ConvertTo-Json -InputObject @(Get-IscsiTargetPortal -TargetPortalAddress ` + + `${Env:iscsi_tp_address} -TargetPortalPortNumber ${Env:iscsi_tp_port} | ` + + `Get-IscsiTarget | Select-Object -ExpandProperty NodeAddress)`) + cmd := exec.Command("powershell.exe", "/c", cmdLine) + cmd.Env = append(os.Environ(), + fmt.Sprintf("iscsi_tp_address=%s", portal.Address), + fmt.Sprintf("iscsi_tp_port=%d", portal.Port)) + + out, err := cmd.CombinedOutput() + if err != nil { + return nil, fmt.Errorf("error discovering target portal. cmd: %s, output: %s, err: %w", cmdLine, string(out), err) + } + + var iqns []string + err = json.Unmarshal(out, &iqns) + if err != nil { + return nil, fmt.Errorf("failed parsing iqn list. cmd: %s output: %s, err: %w", cmdLine, string(out), err) + } + + return iqns, nil +} + +func (APIImplementor) ListTargetPortals() ([]TargetPortal, error) { + cmdLine := fmt.Sprintf( + `ConvertTo-Json -InputObject @(Get-IscsiTargetPortal | ` + + `Select-Object TargetPortalAddress, TargetPortalPortNumber)`) + + cmd := exec.Command("powershell.exe", "/c", cmdLine) + out, err := cmd.CombinedOutput() + if err != nil { + return nil, fmt.Errorf("error listing target portals. cmd %s, output: %s, err: %w", cmdLine, string(out), err) + } + + var portals []TargetPortal + err = json.Unmarshal(out, &portals) + if err != nil { + return nil, fmt.Errorf("failed parsing target portal list. cmd: %s output: %s, err: %w", cmdLine, string(out), err) + } + + return portals, nil +} + +func (APIImplementor) RemoveTargetPortal(portal *TargetPortal) error { + cmdLine := fmt.Sprintf( + `Get-IscsiTargetPortal -TargetPortalAddress ${Env:iscsi_tp_address} ` + + `-TargetPortalPortNumber ${Env:iscsi_tp_port} | Remove-IscsiTargetPortal ` + + `-Confirm:$false`) + + cmd := exec.Command("powershell.exe", "/c", cmdLine) + cmd.Env = append(os.Environ(), + fmt.Sprintf("iscsi_tp_address=%s", portal.Address), + fmt.Sprintf("iscsi_tp_port=%d", portal.Port)) + + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("error removing target portal. cmd %s, output: %s, err: %w", cmdLine, string(out), err) + } + + return nil +} + +func (APIImplementor) ConnectTarget(portal *TargetPortal, iqn string, isMultipath bool, + authType string, chapUser string, chapSecret string) error { + // Not using InputObject as Connect-IscsiTarget's InputObject does not work. + // This is due to being a static WMI method together with a bug in the + // powershell version of the API. + cmdLine := fmt.Sprintf( + `Connect-IscsiTarget -TargetPortalAddress ${Env:iscsi_tp_address}` + + ` -TargetPortalPortNumber ${Env:iscsi_tp_port} -NodeAddress ${Env:iscsi_target_iqn}` + + ` -AuthenticationType ${Env:iscsi_auth_type}` + + ` -IsMultipathEnabled $([System.Convert]::ToBoolean(${Env:iscsi_is_multipath}))`) + + if chapUser != "" { + cmdLine += fmt.Sprintf(` -ChapUsername ${Env:iscsi_chap_user}`) + } + + if chapSecret != "" { + cmdLine += fmt.Sprintf(` -ChapSecret ${Env:iscsi_chap_secret}`) + } + + cmd := exec.Command("powershell.exe", "/c", cmdLine) + cmd.Env = append(os.Environ(), + fmt.Sprintf("iscsi_tp_address=%s", portal.Address), + fmt.Sprintf("iscsi_tp_port=%d", portal.Port), + fmt.Sprintf("iscsi_target_iqn=%s", iqn), + fmt.Sprintf("iscsi_auth_type=%s", authType), + fmt.Sprintf("iscsi_chap_user=%s", chapUser), + fmt.Sprintf("iscsi_chap_secret=%s", chapSecret), + fmt.Sprintf("iscsi_is_multipath=%t", isMultipath), + ) + + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("error connecting to target portal. cmd %s, output: %s, err: %w", cmdLine, string(out), err) + } + + return nil +} + +func (APIImplementor) DisconnectTarget(portal *TargetPortal, iqn string) error { + // Using InputObject instead of pipe to verify input is not empty + cmdLine := fmt.Sprintf( + `Disconnect-IscsiTarget -InputObject (Get-IscsiTargetPortal ` + + `-TargetPortalAddress ${Env:iscsi_tp_address} -TargetPortalPortNumber ${Env:iscsi_tp_port} ` + + ` | Get-IscsiTarget | Where-Object { $_.NodeAddress -eq ${Env:iscsi_target_iqn} }) ` + + `-Confirm:$false`) + + cmd := exec.Command("powershell.exe", "/c", cmdLine) + cmd.Env = append(os.Environ(), + fmt.Sprintf("iscsi_tp_address=%s", portal.Address), + fmt.Sprintf("iscsi_tp_port=%d", portal.Port), + fmt.Sprintf("iscsi_target_iqn=%s", iqn)) + + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("error disconnecting from target portal. cmd %s, output: %s, err: %w", cmdLine, string(out), err) + } + + return nil +} + +func (APIImplementor) GetTargetDisks(portal *TargetPortal, iqn string) ([]string, error) { + // Converting DiskNumber to string for compatibility with disk api group + // Not using pipeline in order to validate that items are non-empty + cmdLine := fmt.Sprintf( + `$ErrorActionPreference = "Stop"; ` + + `$tp = Get-IscsiTargetPortal -TargetPortalAddress ${Env:iscsi_tp_address} -TargetPortalPortNumber ${Env:iscsi_tp_port}; ` + + `$t = $tp | Get-IscsiTarget | Where-Object { $_.NodeAddress -eq ${Env:iscsi_target_iqn} }; ` + + `$c = Get-IscsiConnection -IscsiTarget $t; ` + + `$ids = $c | Get-Disk | Select -ExpandProperty Number | Out-String -Stream; ` + + `ConvertTo-Json -InputObject @($ids)`) + + cmd := exec.Command("powershell.exe", "/c", cmdLine) + cmd.Env = append(os.Environ(), + fmt.Sprintf("iscsi_tp_address=%s", portal.Address), + fmt.Sprintf("iscsi_tp_port=%d", portal.Port), + fmt.Sprintf("iscsi_target_iqn=%s", iqn)) + + out, err := cmd.CombinedOutput() + if err != nil { + return nil, fmt.Errorf("error getting target disks. cmd %s, output: %s, err: %w", cmdLine, string(out), err) + } + + var ids []string + err = json.Unmarshal(out, &ids) + if err != nil { + return nil, fmt.Errorf("error parsing iqn target disks. cmd: %s output: %s, err: %w", cmdLine, string(out), err) + } + + return ids, nil +} diff --git a/internal/os/iscsi/types.go b/internal/os/iscsi/types.go new file mode 100644 index 00000000..c8a7dc98 --- /dev/null +++ b/internal/os/iscsi/types.go @@ -0,0 +1,9 @@ +package iscsi + +// TargetPortal is an address and port pair for a specific iSCSI storage +// target. +// JSON field names are the WMI MSFT_iSCSITargetPortal field names. +type TargetPortal struct { + Address string `json:"TargetPortalAddress"` + Port uint32 `json:"TargetPortalPortNumber"` +} diff --git a/internal/server/iscsi/api_group_generated.go b/internal/server/iscsi/api_group_generated.go new file mode 100644 index 00000000..a950065e --- /dev/null +++ b/internal/server/iscsi/api_group_generated.go @@ -0,0 +1,27 @@ +// Code generated by csi-proxy-api-gen. DO NOT EDIT. + +package iscsi + +import ( + "github.com/kubernetes-csi/csi-proxy/client/apiversion" + "github.com/kubernetes-csi/csi-proxy/internal/server/iscsi/internal" + "github.com/kubernetes-csi/csi-proxy/internal/server/iscsi/internal/v1alpha1" + srvtypes "github.com/kubernetes-csi/csi-proxy/internal/server/types" +) + +const name = "iscsi" + +// ensure the server defines all the required methods +var _ internal.ServerInterface = &Server{} + +func (s *Server) VersionedAPIs() []*srvtypes.VersionedAPI { + v1alpha1Server := v1alpha1.NewVersionedServer(s) + + return []*srvtypes.VersionedAPI{ + { + Group: name, + Version: apiversion.NewVersionOrPanic("v1alpha1"), + Registrant: v1alpha1Server.Register, + }, + } +} diff --git a/internal/server/iscsi/internal/conversion_test.go b/internal/server/iscsi/internal/conversion_test.go new file mode 100644 index 00000000..699511da --- /dev/null +++ b/internal/server/iscsi/internal/conversion_test.go @@ -0,0 +1,61 @@ +package internal_test + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1" + "github.com/kubernetes-csi/csi-proxy/internal/server/iscsi/internal" + v1alpha1_internal "github.com/kubernetes-csi/csi-proxy/internal/server/iscsi/internal/v1alpha1" + "google.golang.org/protobuf/testing/protocmp" +) + +func TestListTargetPortals_Conversion(t *testing.T) { + testCases := []struct { + in *internal.ListTargetPortalsResponse + wantOut *v1alpha1.ListTargetPortalsResponse + wantErr bool + }{ + { + in: &internal.ListTargetPortalsResponse{ + TargetPortals: []*internal.TargetPortal{{TargetPort: 3260, TargetAddress: "test.iqn"}}, + }, + wantOut: &v1alpha1.ListTargetPortalsResponse{ + TargetPortals: []*v1alpha1.TargetPortal{{TargetPort: 3260, TargetAddress: "test.iqn"}}, + }, + wantErr: false, + }, + { + in: &internal.ListTargetPortalsResponse{ + TargetPortals: []*internal.TargetPortal{{TargetPort: 3260, TargetAddress: "test.iqn"}, + {TargetPort: 1000, TargetAddress: "test.iqn.2"}, + }, + }, + wantOut: &v1alpha1.ListTargetPortalsResponse{ + TargetPortals: []*v1alpha1.TargetPortal{{TargetPort: 3260, TargetAddress: "test.iqn"}, + {TargetPort: 1000, TargetAddress: "test.iqn.2"}, + }, + }, + wantErr: false, + }, + { + in: &internal.ListTargetPortalsResponse{}, + wantOut: &v1alpha1.ListTargetPortalsResponse{}, + wantErr: false, + }, + } + + for _, tc := range testCases { + got := v1alpha1.ListTargetPortalsResponse{} + err := v1alpha1_internal.Convert_internal_ListTargetPortalsResponse_To_v1alpha1_ListTargetPortalsResponse(tc.in, &got) + if tc.wantErr && err == nil { + t.Errorf("Expected error but returned a nil error") + } + if !tc.wantErr && err != nil { + t.Errorf("Expected no errors but returned error: %s", err) + } + if diff := cmp.Diff(tc.wantOut, got, protocmp.Transform()); diff != "" { + t.Errorf("Returned unexpected difference between conversion (-want +got):\n%s", diff) + } + } +} diff --git a/internal/server/iscsi/internal/types.go b/internal/server/iscsi/internal/types.go new file mode 100644 index 00000000..e22768a4 --- /dev/null +++ b/internal/server/iscsi/internal/types.go @@ -0,0 +1,109 @@ +package internal + +type AddTargetPortalRequest struct { + // iSCSI Target Portal to register in the initiator + TargetPortal *TargetPortal +} + +type AddTargetPortalResponse struct { + // Intentionally empty +} + +type AuthenticationType uint32 + +const ( + NONE = 0 + ONE_WAY_CHAP = 1 + MUTUAL_CHAP = 2 +) + +type ConnectTargetRequest struct { + // Target portal to which the initiator will connect. + TargetPortal *TargetPortal + + // IQN of the iSCSI Target + Iqn string + + // Connection authentication type, None by default + // + // One Way Chap uses the chap_username and chap_secret + // fields mentioned below to authenticate the initiator. + // + // Mutual Chap uses both the user/secret mentioned below + // and the Initiator Chap Secret to authenticate the target and initiator. + AuthType AuthenticationType + + // CHAP Username used to authenticate the initiator + ChapUsername string + + // CHAP password used to authenticate the initiator + ChapSecret string + + // Should enable multipath on the connection + // In order for multipath to work on Windows, the Multipath feature + // needs to be installed as well as MPIO should be correctly configured + IsMultipath bool +} + +type ConnectTargetResponse struct { + // Intentionally empty +} + +type DisconnectTargetRequest struct { + // Target portal to which the initiator will connect. + TargetPortal *TargetPortal + // IQN of the iSCSI Target + Iqn string +} + +type DisconnectTargetResponse struct { + // Intentionally empty +} + +type DiscoverTargetPortalRequest struct { + // iSCSI Target Portal on which to initiate discovery + TargetPortal *TargetPortal +} + +type DiscoverTargetPortalResponse struct { + // List of discovered IQN addresses + // follows IQN format: iqn.yyyy-mm.naming-authority:unique-name + Iqns []string +} + +type GetTargetDisksRequest struct { + // Target portal to which the initiator will connect. + TargetPortal *TargetPortal + // IQN of the iSCSI Target + Iqn string +} + +type GetTargetDisksResponse struct { + // List composed of disk ids (numbers) that are associated with the + // iSCSI target + DiskIds []string +} + +type ListTargetPortalsRequest struct { +} + +type ListTargetPortalsResponse struct { + // A list of Target Portals currently registered in the initiator + TargetPortals []*TargetPortal +} + +type RemoveTargetPortalRequest struct { + // iSCSI Target Portal + TargetPortal *TargetPortal +} + +type RemoveTargetPortalResponse struct { + // Intentionally empty +} + +type TargetPortal struct { + // iSCSI Target (server) address + TargetAddress string + // iSCSI Target port (default iSCSI port is 3260) + TargetPort uint32 +} diff --git a/internal/server/iscsi/internal/types_generated.go b/internal/server/iscsi/internal/types_generated.go new file mode 100644 index 00000000..6c2be21d --- /dev/null +++ b/internal/server/iscsi/internal/types_generated.go @@ -0,0 +1,25 @@ +// Code generated by csi-proxy-api-gen. DO NOT EDIT. + +package internal + +import ( + "context" + + "github.com/kubernetes-csi/csi-proxy/client/apiversion" + "google.golang.org/grpc" +) + +type VersionedAPI interface { + Register(grpcServer *grpc.Server) +} + +// All the functions this group's server needs to define. +type ServerInterface interface { + AddTargetPortal(context.Context, *AddTargetPortalRequest, apiversion.Version) (*AddTargetPortalResponse, error) + ConnectTarget(context.Context, *ConnectTargetRequest, apiversion.Version) (*ConnectTargetResponse, error) + DisconnectTarget(context.Context, *DisconnectTargetRequest, apiversion.Version) (*DisconnectTargetResponse, error) + DiscoverTargetPortal(context.Context, *DiscoverTargetPortalRequest, apiversion.Version) (*DiscoverTargetPortalResponse, error) + GetTargetDisks(context.Context, *GetTargetDisksRequest, apiversion.Version) (*GetTargetDisksResponse, error) + ListTargetPortals(context.Context, *ListTargetPortalsRequest, apiversion.Version) (*ListTargetPortalsResponse, error) + RemoveTargetPortal(context.Context, *RemoveTargetPortalRequest, apiversion.Version) (*RemoveTargetPortalResponse, error) +} diff --git a/internal/server/iscsi/internal/v1alpha1/conversion.go b/internal/server/iscsi/internal/v1alpha1/conversion.go new file mode 100644 index 00000000..5d163b29 --- /dev/null +++ b/internal/server/iscsi/internal/v1alpha1/conversion.go @@ -0,0 +1,24 @@ +package v1alpha1 + +import ( + "github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1" + "github.com/kubernetes-csi/csi-proxy/internal/server/iscsi/internal" +) + +// Add manual conversion functions here to override automatic conversion functions + +func Convert_internal_ListTargetPortalsResponse_To_v1alpha1_ListTargetPortalsResponse(in *internal.ListTargetPortalsResponse, out *v1alpha1.ListTargetPortalsResponse) error { + if in.TargetPortals != nil { + in, out := &in.TargetPortals, &out.TargetPortals + *out = make([]*v1alpha1.TargetPortal, len(*in)) + for i := range *in { + (*out)[i] = new(v1alpha1.TargetPortal) + if err := Convert_internal_TargetPortal_To_v1alpha1_TargetPortal(*&(*in)[i], *&(*out)[i]); err != nil { + return err + } + } + } else { + out.TargetPortals = nil + } + return nil +} diff --git a/internal/server/iscsi/internal/v1alpha1/conversion_generated.go b/internal/server/iscsi/internal/v1alpha1/conversion_generated.go new file mode 100644 index 00000000..32f8435a --- /dev/null +++ b/internal/server/iscsi/internal/v1alpha1/conversion_generated.go @@ -0,0 +1,427 @@ +// Code generated by csi-proxy-api-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + unsafe "unsafe" + + v1alpha1 "github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1" + internal "github.com/kubernetes-csi/csi-proxy/internal/server/iscsi/internal" +) + +func autoConvert_v1alpha1_AddTargetPortalRequest_To_internal_AddTargetPortalRequest(in *v1alpha1.AddTargetPortalRequest, out *internal.AddTargetPortalRequest) error { + if in.TargetPortal != nil { + in, out := &in.TargetPortal, &out.TargetPortal + *out = new(internal.TargetPortal) + if err := Convert_v1alpha1_TargetPortal_To_internal_TargetPortal(*in, *out); err != nil { + return err + } + } else { + out.TargetPortal = nil + } + return nil +} + +// Convert_v1alpha1_AddTargetPortalRequest_To_internal_AddTargetPortalRequest is an autogenerated conversion function. +func Convert_v1alpha1_AddTargetPortalRequest_To_internal_AddTargetPortalRequest(in *v1alpha1.AddTargetPortalRequest, out *internal.AddTargetPortalRequest) error { + return autoConvert_v1alpha1_AddTargetPortalRequest_To_internal_AddTargetPortalRequest(in, out) +} + +func autoConvert_internal_AddTargetPortalRequest_To_v1alpha1_AddTargetPortalRequest(in *internal.AddTargetPortalRequest, out *v1alpha1.AddTargetPortalRequest) error { + if in.TargetPortal != nil { + in, out := &in.TargetPortal, &out.TargetPortal + *out = new(v1alpha1.TargetPortal) + if err := Convert_internal_TargetPortal_To_v1alpha1_TargetPortal(*in, *out); err != nil { + return err + } + } else { + out.TargetPortal = nil + } + return nil +} + +// Convert_internal_AddTargetPortalRequest_To_v1alpha1_AddTargetPortalRequest is an autogenerated conversion function. +func Convert_internal_AddTargetPortalRequest_To_v1alpha1_AddTargetPortalRequest(in *internal.AddTargetPortalRequest, out *v1alpha1.AddTargetPortalRequest) error { + return autoConvert_internal_AddTargetPortalRequest_To_v1alpha1_AddTargetPortalRequest(in, out) +} + +func autoConvert_v1alpha1_AddTargetPortalResponse_To_internal_AddTargetPortalResponse(in *v1alpha1.AddTargetPortalResponse, out *internal.AddTargetPortalResponse) error { + return nil +} + +// Convert_v1alpha1_AddTargetPortalResponse_To_internal_AddTargetPortalResponse is an autogenerated conversion function. +func Convert_v1alpha1_AddTargetPortalResponse_To_internal_AddTargetPortalResponse(in *v1alpha1.AddTargetPortalResponse, out *internal.AddTargetPortalResponse) error { + return autoConvert_v1alpha1_AddTargetPortalResponse_To_internal_AddTargetPortalResponse(in, out) +} + +func autoConvert_internal_AddTargetPortalResponse_To_v1alpha1_AddTargetPortalResponse(in *internal.AddTargetPortalResponse, out *v1alpha1.AddTargetPortalResponse) error { + return nil +} + +// Convert_internal_AddTargetPortalResponse_To_v1alpha1_AddTargetPortalResponse is an autogenerated conversion function. +func Convert_internal_AddTargetPortalResponse_To_v1alpha1_AddTargetPortalResponse(in *internal.AddTargetPortalResponse, out *v1alpha1.AddTargetPortalResponse) error { + return autoConvert_internal_AddTargetPortalResponse_To_v1alpha1_AddTargetPortalResponse(in, out) +} + +func autoConvert_v1alpha1_ConnectTargetRequest_To_internal_ConnectTargetRequest(in *v1alpha1.ConnectTargetRequest, out *internal.ConnectTargetRequest) error { + if in.TargetPortal != nil { + in, out := &in.TargetPortal, &out.TargetPortal + *out = new(internal.TargetPortal) + if err := Convert_v1alpha1_TargetPortal_To_internal_TargetPortal(*in, *out); err != nil { + return err + } + } else { + out.TargetPortal = nil + } + out.Iqn = in.Iqn + out.AuthType = internal.AuthenticationType(in.AuthType) + out.ChapUsername = in.ChapUsername + out.ChapSecret = in.ChapSecret + out.IsMultipath = in.IsMultipath + return nil +} + +// Convert_v1alpha1_ConnectTargetRequest_To_internal_ConnectTargetRequest is an autogenerated conversion function. +func Convert_v1alpha1_ConnectTargetRequest_To_internal_ConnectTargetRequest(in *v1alpha1.ConnectTargetRequest, out *internal.ConnectTargetRequest) error { + return autoConvert_v1alpha1_ConnectTargetRequest_To_internal_ConnectTargetRequest(in, out) +} + +func autoConvert_internal_ConnectTargetRequest_To_v1alpha1_ConnectTargetRequest(in *internal.ConnectTargetRequest, out *v1alpha1.ConnectTargetRequest) error { + if in.TargetPortal != nil { + in, out := &in.TargetPortal, &out.TargetPortal + *out = new(v1alpha1.TargetPortal) + if err := Convert_internal_TargetPortal_To_v1alpha1_TargetPortal(*in, *out); err != nil { + return err + } + } else { + out.TargetPortal = nil + } + out.Iqn = in.Iqn + out.AuthType = v1alpha1.AuthenticationType(in.AuthType) + out.ChapUsername = in.ChapUsername + out.ChapSecret = in.ChapSecret + out.IsMultipath = in.IsMultipath + return nil +} + +// Convert_internal_ConnectTargetRequest_To_v1alpha1_ConnectTargetRequest is an autogenerated conversion function. +func Convert_internal_ConnectTargetRequest_To_v1alpha1_ConnectTargetRequest(in *internal.ConnectTargetRequest, out *v1alpha1.ConnectTargetRequest) error { + return autoConvert_internal_ConnectTargetRequest_To_v1alpha1_ConnectTargetRequest(in, out) +} + +func autoConvert_v1alpha1_ConnectTargetResponse_To_internal_ConnectTargetResponse(in *v1alpha1.ConnectTargetResponse, out *internal.ConnectTargetResponse) error { + return nil +} + +// Convert_v1alpha1_ConnectTargetResponse_To_internal_ConnectTargetResponse is an autogenerated conversion function. +func Convert_v1alpha1_ConnectTargetResponse_To_internal_ConnectTargetResponse(in *v1alpha1.ConnectTargetResponse, out *internal.ConnectTargetResponse) error { + return autoConvert_v1alpha1_ConnectTargetResponse_To_internal_ConnectTargetResponse(in, out) +} + +func autoConvert_internal_ConnectTargetResponse_To_v1alpha1_ConnectTargetResponse(in *internal.ConnectTargetResponse, out *v1alpha1.ConnectTargetResponse) error { + return nil +} + +// Convert_internal_ConnectTargetResponse_To_v1alpha1_ConnectTargetResponse is an autogenerated conversion function. +func Convert_internal_ConnectTargetResponse_To_v1alpha1_ConnectTargetResponse(in *internal.ConnectTargetResponse, out *v1alpha1.ConnectTargetResponse) error { + return autoConvert_internal_ConnectTargetResponse_To_v1alpha1_ConnectTargetResponse(in, out) +} + +func autoConvert_v1alpha1_DisconnectTargetRequest_To_internal_DisconnectTargetRequest(in *v1alpha1.DisconnectTargetRequest, out *internal.DisconnectTargetRequest) error { + if in.TargetPortal != nil { + in, out := &in.TargetPortal, &out.TargetPortal + *out = new(internal.TargetPortal) + if err := Convert_v1alpha1_TargetPortal_To_internal_TargetPortal(*in, *out); err != nil { + return err + } + } else { + out.TargetPortal = nil + } + out.Iqn = in.Iqn + return nil +} + +// Convert_v1alpha1_DisconnectTargetRequest_To_internal_DisconnectTargetRequest is an autogenerated conversion function. +func Convert_v1alpha1_DisconnectTargetRequest_To_internal_DisconnectTargetRequest(in *v1alpha1.DisconnectTargetRequest, out *internal.DisconnectTargetRequest) error { + return autoConvert_v1alpha1_DisconnectTargetRequest_To_internal_DisconnectTargetRequest(in, out) +} + +func autoConvert_internal_DisconnectTargetRequest_To_v1alpha1_DisconnectTargetRequest(in *internal.DisconnectTargetRequest, out *v1alpha1.DisconnectTargetRequest) error { + if in.TargetPortal != nil { + in, out := &in.TargetPortal, &out.TargetPortal + *out = new(v1alpha1.TargetPortal) + if err := Convert_internal_TargetPortal_To_v1alpha1_TargetPortal(*in, *out); err != nil { + return err + } + } else { + out.TargetPortal = nil + } + out.Iqn = in.Iqn + return nil +} + +// Convert_internal_DisconnectTargetRequest_To_v1alpha1_DisconnectTargetRequest is an autogenerated conversion function. +func Convert_internal_DisconnectTargetRequest_To_v1alpha1_DisconnectTargetRequest(in *internal.DisconnectTargetRequest, out *v1alpha1.DisconnectTargetRequest) error { + return autoConvert_internal_DisconnectTargetRequest_To_v1alpha1_DisconnectTargetRequest(in, out) +} + +func autoConvert_v1alpha1_DisconnectTargetResponse_To_internal_DisconnectTargetResponse(in *v1alpha1.DisconnectTargetResponse, out *internal.DisconnectTargetResponse) error { + return nil +} + +// Convert_v1alpha1_DisconnectTargetResponse_To_internal_DisconnectTargetResponse is an autogenerated conversion function. +func Convert_v1alpha1_DisconnectTargetResponse_To_internal_DisconnectTargetResponse(in *v1alpha1.DisconnectTargetResponse, out *internal.DisconnectTargetResponse) error { + return autoConvert_v1alpha1_DisconnectTargetResponse_To_internal_DisconnectTargetResponse(in, out) +} + +func autoConvert_internal_DisconnectTargetResponse_To_v1alpha1_DisconnectTargetResponse(in *internal.DisconnectTargetResponse, out *v1alpha1.DisconnectTargetResponse) error { + return nil +} + +// Convert_internal_DisconnectTargetResponse_To_v1alpha1_DisconnectTargetResponse is an autogenerated conversion function. +func Convert_internal_DisconnectTargetResponse_To_v1alpha1_DisconnectTargetResponse(in *internal.DisconnectTargetResponse, out *v1alpha1.DisconnectTargetResponse) error { + return autoConvert_internal_DisconnectTargetResponse_To_v1alpha1_DisconnectTargetResponse(in, out) +} + +func autoConvert_v1alpha1_DiscoverTargetPortalRequest_To_internal_DiscoverTargetPortalRequest(in *v1alpha1.DiscoverTargetPortalRequest, out *internal.DiscoverTargetPortalRequest) error { + if in.TargetPortal != nil { + in, out := &in.TargetPortal, &out.TargetPortal + *out = new(internal.TargetPortal) + if err := Convert_v1alpha1_TargetPortal_To_internal_TargetPortal(*in, *out); err != nil { + return err + } + } else { + out.TargetPortal = nil + } + return nil +} + +// Convert_v1alpha1_DiscoverTargetPortalRequest_To_internal_DiscoverTargetPortalRequest is an autogenerated conversion function. +func Convert_v1alpha1_DiscoverTargetPortalRequest_To_internal_DiscoverTargetPortalRequest(in *v1alpha1.DiscoverTargetPortalRequest, out *internal.DiscoverTargetPortalRequest) error { + return autoConvert_v1alpha1_DiscoverTargetPortalRequest_To_internal_DiscoverTargetPortalRequest(in, out) +} + +func autoConvert_internal_DiscoverTargetPortalRequest_To_v1alpha1_DiscoverTargetPortalRequest(in *internal.DiscoverTargetPortalRequest, out *v1alpha1.DiscoverTargetPortalRequest) error { + if in.TargetPortal != nil { + in, out := &in.TargetPortal, &out.TargetPortal + *out = new(v1alpha1.TargetPortal) + if err := Convert_internal_TargetPortal_To_v1alpha1_TargetPortal(*in, *out); err != nil { + return err + } + } else { + out.TargetPortal = nil + } + return nil +} + +// Convert_internal_DiscoverTargetPortalRequest_To_v1alpha1_DiscoverTargetPortalRequest is an autogenerated conversion function. +func Convert_internal_DiscoverTargetPortalRequest_To_v1alpha1_DiscoverTargetPortalRequest(in *internal.DiscoverTargetPortalRequest, out *v1alpha1.DiscoverTargetPortalRequest) error { + return autoConvert_internal_DiscoverTargetPortalRequest_To_v1alpha1_DiscoverTargetPortalRequest(in, out) +} + +func autoConvert_v1alpha1_DiscoverTargetPortalResponse_To_internal_DiscoverTargetPortalResponse(in *v1alpha1.DiscoverTargetPortalResponse, out *internal.DiscoverTargetPortalResponse) error { + out.Iqns = *(*[]string)(unsafe.Pointer(&in.Iqns)) + return nil +} + +// Convert_v1alpha1_DiscoverTargetPortalResponse_To_internal_DiscoverTargetPortalResponse is an autogenerated conversion function. +func Convert_v1alpha1_DiscoverTargetPortalResponse_To_internal_DiscoverTargetPortalResponse(in *v1alpha1.DiscoverTargetPortalResponse, out *internal.DiscoverTargetPortalResponse) error { + return autoConvert_v1alpha1_DiscoverTargetPortalResponse_To_internal_DiscoverTargetPortalResponse(in, out) +} + +func autoConvert_internal_DiscoverTargetPortalResponse_To_v1alpha1_DiscoverTargetPortalResponse(in *internal.DiscoverTargetPortalResponse, out *v1alpha1.DiscoverTargetPortalResponse) error { + out.Iqns = *(*[]string)(unsafe.Pointer(&in.Iqns)) + return nil +} + +// Convert_internal_DiscoverTargetPortalResponse_To_v1alpha1_DiscoverTargetPortalResponse is an autogenerated conversion function. +func Convert_internal_DiscoverTargetPortalResponse_To_v1alpha1_DiscoverTargetPortalResponse(in *internal.DiscoverTargetPortalResponse, out *v1alpha1.DiscoverTargetPortalResponse) error { + return autoConvert_internal_DiscoverTargetPortalResponse_To_v1alpha1_DiscoverTargetPortalResponse(in, out) +} + +func autoConvert_v1alpha1_GetTargetDisksRequest_To_internal_GetTargetDisksRequest(in *v1alpha1.GetTargetDisksRequest, out *internal.GetTargetDisksRequest) error { + if in.TargetPortal != nil { + in, out := &in.TargetPortal, &out.TargetPortal + *out = new(internal.TargetPortal) + if err := Convert_v1alpha1_TargetPortal_To_internal_TargetPortal(*in, *out); err != nil { + return err + } + } else { + out.TargetPortal = nil + } + out.Iqn = in.Iqn + return nil +} + +// Convert_v1alpha1_GetTargetDisksRequest_To_internal_GetTargetDisksRequest is an autogenerated conversion function. +func Convert_v1alpha1_GetTargetDisksRequest_To_internal_GetTargetDisksRequest(in *v1alpha1.GetTargetDisksRequest, out *internal.GetTargetDisksRequest) error { + return autoConvert_v1alpha1_GetTargetDisksRequest_To_internal_GetTargetDisksRequest(in, out) +} + +func autoConvert_internal_GetTargetDisksRequest_To_v1alpha1_GetTargetDisksRequest(in *internal.GetTargetDisksRequest, out *v1alpha1.GetTargetDisksRequest) error { + if in.TargetPortal != nil { + in, out := &in.TargetPortal, &out.TargetPortal + *out = new(v1alpha1.TargetPortal) + if err := Convert_internal_TargetPortal_To_v1alpha1_TargetPortal(*in, *out); err != nil { + return err + } + } else { + out.TargetPortal = nil + } + out.Iqn = in.Iqn + return nil +} + +// Convert_internal_GetTargetDisksRequest_To_v1alpha1_GetTargetDisksRequest is an autogenerated conversion function. +func Convert_internal_GetTargetDisksRequest_To_v1alpha1_GetTargetDisksRequest(in *internal.GetTargetDisksRequest, out *v1alpha1.GetTargetDisksRequest) error { + return autoConvert_internal_GetTargetDisksRequest_To_v1alpha1_GetTargetDisksRequest(in, out) +} + +func autoConvert_v1alpha1_GetTargetDisksResponse_To_internal_GetTargetDisksResponse(in *v1alpha1.GetTargetDisksResponse, out *internal.GetTargetDisksResponse) error { + out.DiskIds = *(*[]string)(unsafe.Pointer(&in.DiskIds)) + return nil +} + +// Convert_v1alpha1_GetTargetDisksResponse_To_internal_GetTargetDisksResponse is an autogenerated conversion function. +func Convert_v1alpha1_GetTargetDisksResponse_To_internal_GetTargetDisksResponse(in *v1alpha1.GetTargetDisksResponse, out *internal.GetTargetDisksResponse) error { + return autoConvert_v1alpha1_GetTargetDisksResponse_To_internal_GetTargetDisksResponse(in, out) +} + +func autoConvert_internal_GetTargetDisksResponse_To_v1alpha1_GetTargetDisksResponse(in *internal.GetTargetDisksResponse, out *v1alpha1.GetTargetDisksResponse) error { + out.DiskIds = *(*[]string)(unsafe.Pointer(&in.DiskIds)) + return nil +} + +// Convert_internal_GetTargetDisksResponse_To_v1alpha1_GetTargetDisksResponse is an autogenerated conversion function. +func Convert_internal_GetTargetDisksResponse_To_v1alpha1_GetTargetDisksResponse(in *internal.GetTargetDisksResponse, out *v1alpha1.GetTargetDisksResponse) error { + return autoConvert_internal_GetTargetDisksResponse_To_v1alpha1_GetTargetDisksResponse(in, out) +} + +func autoConvert_v1alpha1_ListTargetPortalsRequest_To_internal_ListTargetPortalsRequest(in *v1alpha1.ListTargetPortalsRequest, out *internal.ListTargetPortalsRequest) error { + return nil +} + +// Convert_v1alpha1_ListTargetPortalsRequest_To_internal_ListTargetPortalsRequest is an autogenerated conversion function. +func Convert_v1alpha1_ListTargetPortalsRequest_To_internal_ListTargetPortalsRequest(in *v1alpha1.ListTargetPortalsRequest, out *internal.ListTargetPortalsRequest) error { + return autoConvert_v1alpha1_ListTargetPortalsRequest_To_internal_ListTargetPortalsRequest(in, out) +} + +func autoConvert_internal_ListTargetPortalsRequest_To_v1alpha1_ListTargetPortalsRequest(in *internal.ListTargetPortalsRequest, out *v1alpha1.ListTargetPortalsRequest) error { + return nil +} + +// Convert_internal_ListTargetPortalsRequest_To_v1alpha1_ListTargetPortalsRequest is an autogenerated conversion function. +func Convert_internal_ListTargetPortalsRequest_To_v1alpha1_ListTargetPortalsRequest(in *internal.ListTargetPortalsRequest, out *v1alpha1.ListTargetPortalsRequest) error { + return autoConvert_internal_ListTargetPortalsRequest_To_v1alpha1_ListTargetPortalsRequest(in, out) +} + +func autoConvert_v1alpha1_ListTargetPortalsResponse_To_internal_ListTargetPortalsResponse(in *v1alpha1.ListTargetPortalsResponse, out *internal.ListTargetPortalsResponse) error { + if in.TargetPortals != nil { + in, out := &in.TargetPortals, &out.TargetPortals + *out = make([]*internal.TargetPortal, len(*in)) + for i := range *in { + if err := Convert_v1alpha1_TargetPortal_To_internal_TargetPortal(*&(*in)[i], *&(*out)[i]); err != nil { + return err + } + } + } else { + out.TargetPortals = nil + } + return nil +} + +// Convert_v1alpha1_ListTargetPortalsResponse_To_internal_ListTargetPortalsResponse is an autogenerated conversion function. +func Convert_v1alpha1_ListTargetPortalsResponse_To_internal_ListTargetPortalsResponse(in *v1alpha1.ListTargetPortalsResponse, out *internal.ListTargetPortalsResponse) error { + return autoConvert_v1alpha1_ListTargetPortalsResponse_To_internal_ListTargetPortalsResponse(in, out) +} + +func autoConvert_internal_ListTargetPortalsResponse_To_v1alpha1_ListTargetPortalsResponse(in *internal.ListTargetPortalsResponse, out *v1alpha1.ListTargetPortalsResponse) error { + if in.TargetPortals != nil { + in, out := &in.TargetPortals, &out.TargetPortals + *out = make([]*v1alpha1.TargetPortal, len(*in)) + for i := range *in { + if err := Convert_internal_TargetPortal_To_v1alpha1_TargetPortal(*&(*in)[i], *&(*out)[i]); err != nil { + return err + } + } + } else { + out.TargetPortals = nil + } + return nil +} + +func autoConvert_v1alpha1_RemoveTargetPortalRequest_To_internal_RemoveTargetPortalRequest(in *v1alpha1.RemoveTargetPortalRequest, out *internal.RemoveTargetPortalRequest) error { + if in.TargetPortal != nil { + in, out := &in.TargetPortal, &out.TargetPortal + *out = new(internal.TargetPortal) + if err := Convert_v1alpha1_TargetPortal_To_internal_TargetPortal(*in, *out); err != nil { + return err + } + } else { + out.TargetPortal = nil + } + return nil +} + +// Convert_v1alpha1_RemoveTargetPortalRequest_To_internal_RemoveTargetPortalRequest is an autogenerated conversion function. +func Convert_v1alpha1_RemoveTargetPortalRequest_To_internal_RemoveTargetPortalRequest(in *v1alpha1.RemoveTargetPortalRequest, out *internal.RemoveTargetPortalRequest) error { + return autoConvert_v1alpha1_RemoveTargetPortalRequest_To_internal_RemoveTargetPortalRequest(in, out) +} + +func autoConvert_internal_RemoveTargetPortalRequest_To_v1alpha1_RemoveTargetPortalRequest(in *internal.RemoveTargetPortalRequest, out *v1alpha1.RemoveTargetPortalRequest) error { + if in.TargetPortal != nil { + in, out := &in.TargetPortal, &out.TargetPortal + *out = new(v1alpha1.TargetPortal) + if err := Convert_internal_TargetPortal_To_v1alpha1_TargetPortal(*in, *out); err != nil { + return err + } + } else { + out.TargetPortal = nil + } + return nil +} + +// Convert_internal_RemoveTargetPortalRequest_To_v1alpha1_RemoveTargetPortalRequest is an autogenerated conversion function. +func Convert_internal_RemoveTargetPortalRequest_To_v1alpha1_RemoveTargetPortalRequest(in *internal.RemoveTargetPortalRequest, out *v1alpha1.RemoveTargetPortalRequest) error { + return autoConvert_internal_RemoveTargetPortalRequest_To_v1alpha1_RemoveTargetPortalRequest(in, out) +} + +func autoConvert_v1alpha1_RemoveTargetPortalResponse_To_internal_RemoveTargetPortalResponse(in *v1alpha1.RemoveTargetPortalResponse, out *internal.RemoveTargetPortalResponse) error { + return nil +} + +// Convert_v1alpha1_RemoveTargetPortalResponse_To_internal_RemoveTargetPortalResponse is an autogenerated conversion function. +func Convert_v1alpha1_RemoveTargetPortalResponse_To_internal_RemoveTargetPortalResponse(in *v1alpha1.RemoveTargetPortalResponse, out *internal.RemoveTargetPortalResponse) error { + return autoConvert_v1alpha1_RemoveTargetPortalResponse_To_internal_RemoveTargetPortalResponse(in, out) +} + +func autoConvert_internal_RemoveTargetPortalResponse_To_v1alpha1_RemoveTargetPortalResponse(in *internal.RemoveTargetPortalResponse, out *v1alpha1.RemoveTargetPortalResponse) error { + return nil +} + +// Convert_internal_RemoveTargetPortalResponse_To_v1alpha1_RemoveTargetPortalResponse is an autogenerated conversion function. +func Convert_internal_RemoveTargetPortalResponse_To_v1alpha1_RemoveTargetPortalResponse(in *internal.RemoveTargetPortalResponse, out *v1alpha1.RemoveTargetPortalResponse) error { + return autoConvert_internal_RemoveTargetPortalResponse_To_v1alpha1_RemoveTargetPortalResponse(in, out) +} + +func autoConvert_v1alpha1_TargetPortal_To_internal_TargetPortal(in *v1alpha1.TargetPortal, out *internal.TargetPortal) error { + out.TargetAddress = in.TargetAddress + out.TargetPort = in.TargetPort + return nil +} + +// Convert_v1alpha1_TargetPortal_To_internal_TargetPortal is an autogenerated conversion function. +func Convert_v1alpha1_TargetPortal_To_internal_TargetPortal(in *v1alpha1.TargetPortal, out *internal.TargetPortal) error { + return autoConvert_v1alpha1_TargetPortal_To_internal_TargetPortal(in, out) +} + +func autoConvert_internal_TargetPortal_To_v1alpha1_TargetPortal(in *internal.TargetPortal, out *v1alpha1.TargetPortal) error { + out.TargetAddress = in.TargetAddress + out.TargetPort = in.TargetPort + return nil +} + +// Convert_internal_TargetPortal_To_v1alpha1_TargetPortal is an autogenerated conversion function. +func Convert_internal_TargetPortal_To_v1alpha1_TargetPortal(in *internal.TargetPortal, out *v1alpha1.TargetPortal) error { + return autoConvert_internal_TargetPortal_To_v1alpha1_TargetPortal(in, out) +} diff --git a/internal/server/iscsi/internal/v1alpha1/server_generated.go b/internal/server/iscsi/internal/v1alpha1/server_generated.go new file mode 100644 index 00000000..fe0cbaef --- /dev/null +++ b/internal/server/iscsi/internal/v1alpha1/server_generated.go @@ -0,0 +1,161 @@ +// Code generated by csi-proxy-api-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + + "github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1" + "github.com/kubernetes-csi/csi-proxy/client/apiversion" + "github.com/kubernetes-csi/csi-proxy/internal/server/iscsi/internal" + "google.golang.org/grpc" +) + +var version = apiversion.NewVersionOrPanic("v1alpha1") + +type versionedAPI struct { + apiGroupServer internal.ServerInterface +} + +func NewVersionedServer(apiGroupServer internal.ServerInterface) internal.VersionedAPI { + return &versionedAPI{ + apiGroupServer: apiGroupServer, + } +} + +func (s *versionedAPI) Register(grpcServer *grpc.Server) { + v1alpha1.RegisterIscsiServer(grpcServer, s) +} + +func (s *versionedAPI) AddTargetPortal(context context.Context, versionedRequest *v1alpha1.AddTargetPortalRequest) (*v1alpha1.AddTargetPortalResponse, error) { + request := &internal.AddTargetPortalRequest{} + if err := Convert_v1alpha1_AddTargetPortalRequest_To_internal_AddTargetPortalRequest(versionedRequest, request); err != nil { + return nil, err + } + + response, err := s.apiGroupServer.AddTargetPortal(context, request, version) + if err != nil { + return nil, err + } + + versionedResponse := &v1alpha1.AddTargetPortalResponse{} + if err := Convert_internal_AddTargetPortalResponse_To_v1alpha1_AddTargetPortalResponse(response, versionedResponse); err != nil { + return nil, err + } + + return versionedResponse, err +} + +func (s *versionedAPI) ConnectTarget(context context.Context, versionedRequest *v1alpha1.ConnectTargetRequest) (*v1alpha1.ConnectTargetResponse, error) { + request := &internal.ConnectTargetRequest{} + if err := Convert_v1alpha1_ConnectTargetRequest_To_internal_ConnectTargetRequest(versionedRequest, request); err != nil { + return nil, err + } + + response, err := s.apiGroupServer.ConnectTarget(context, request, version) + if err != nil { + return nil, err + } + + versionedResponse := &v1alpha1.ConnectTargetResponse{} + if err := Convert_internal_ConnectTargetResponse_To_v1alpha1_ConnectTargetResponse(response, versionedResponse); err != nil { + return nil, err + } + + return versionedResponse, err +} + +func (s *versionedAPI) DisconnectTarget(context context.Context, versionedRequest *v1alpha1.DisconnectTargetRequest) (*v1alpha1.DisconnectTargetResponse, error) { + request := &internal.DisconnectTargetRequest{} + if err := Convert_v1alpha1_DisconnectTargetRequest_To_internal_DisconnectTargetRequest(versionedRequest, request); err != nil { + return nil, err + } + + response, err := s.apiGroupServer.DisconnectTarget(context, request, version) + if err != nil { + return nil, err + } + + versionedResponse := &v1alpha1.DisconnectTargetResponse{} + if err := Convert_internal_DisconnectTargetResponse_To_v1alpha1_DisconnectTargetResponse(response, versionedResponse); err != nil { + return nil, err + } + + return versionedResponse, err +} + +func (s *versionedAPI) DiscoverTargetPortal(context context.Context, versionedRequest *v1alpha1.DiscoverTargetPortalRequest) (*v1alpha1.DiscoverTargetPortalResponse, error) { + request := &internal.DiscoverTargetPortalRequest{} + if err := Convert_v1alpha1_DiscoverTargetPortalRequest_To_internal_DiscoverTargetPortalRequest(versionedRequest, request); err != nil { + return nil, err + } + + response, err := s.apiGroupServer.DiscoverTargetPortal(context, request, version) + if err != nil { + return nil, err + } + + versionedResponse := &v1alpha1.DiscoverTargetPortalResponse{} + if err := Convert_internal_DiscoverTargetPortalResponse_To_v1alpha1_DiscoverTargetPortalResponse(response, versionedResponse); err != nil { + return nil, err + } + + return versionedResponse, err +} + +func (s *versionedAPI) GetTargetDisks(context context.Context, versionedRequest *v1alpha1.GetTargetDisksRequest) (*v1alpha1.GetTargetDisksResponse, error) { + request := &internal.GetTargetDisksRequest{} + if err := Convert_v1alpha1_GetTargetDisksRequest_To_internal_GetTargetDisksRequest(versionedRequest, request); err != nil { + return nil, err + } + + response, err := s.apiGroupServer.GetTargetDisks(context, request, version) + if err != nil { + return nil, err + } + + versionedResponse := &v1alpha1.GetTargetDisksResponse{} + if err := Convert_internal_GetTargetDisksResponse_To_v1alpha1_GetTargetDisksResponse(response, versionedResponse); err != nil { + return nil, err + } + + return versionedResponse, err +} + +func (s *versionedAPI) ListTargetPortals(context context.Context, versionedRequest *v1alpha1.ListTargetPortalsRequest) (*v1alpha1.ListTargetPortalsResponse, error) { + request := &internal.ListTargetPortalsRequest{} + if err := Convert_v1alpha1_ListTargetPortalsRequest_To_internal_ListTargetPortalsRequest(versionedRequest, request); err != nil { + return nil, err + } + + response, err := s.apiGroupServer.ListTargetPortals(context, request, version) + if err != nil { + return nil, err + } + + versionedResponse := &v1alpha1.ListTargetPortalsResponse{} + if err := Convert_internal_ListTargetPortalsResponse_To_v1alpha1_ListTargetPortalsResponse(response, versionedResponse); err != nil { + return nil, err + } + + return versionedResponse, err +} + +func (s *versionedAPI) RemoveTargetPortal(context context.Context, versionedRequest *v1alpha1.RemoveTargetPortalRequest) (*v1alpha1.RemoveTargetPortalResponse, error) { + request := &internal.RemoveTargetPortalRequest{} + if err := Convert_v1alpha1_RemoveTargetPortalRequest_To_internal_RemoveTargetPortalRequest(versionedRequest, request); err != nil { + return nil, err + } + + response, err := s.apiGroupServer.RemoveTargetPortal(context, request, version) + if err != nil { + return nil, err + } + + versionedResponse := &v1alpha1.RemoveTargetPortalResponse{} + if err := Convert_internal_RemoveTargetPortalResponse_To_v1alpha1_RemoveTargetPortalResponse(response, versionedResponse); err != nil { + return nil, err + } + + return versionedResponse, err +} diff --git a/internal/server/iscsi/server.go b/internal/server/iscsi/server.go new file mode 100644 index 00000000..fa285da3 --- /dev/null +++ b/internal/server/iscsi/server.go @@ -0,0 +1,170 @@ +package iscsi + +import ( + "context" + "fmt" + + "github.com/kubernetes-csi/csi-proxy/client/apiversion" + "github.com/kubernetes-csi/csi-proxy/internal/os/iscsi" + "github.com/kubernetes-csi/csi-proxy/internal/server/iscsi/internal" + "k8s.io/klog" +) + +const defaultIscsiPort = 3260 + +type Server struct { + hostAPI API +} + +type API interface { + AddTargetPortal(portal *iscsi.TargetPortal) error + DiscoverTargetPortal(portal *iscsi.TargetPortal) ([]string, error) + ListTargetPortals() ([]iscsi.TargetPortal, error) + RemoveTargetPortal(portal *iscsi.TargetPortal) error + ConnectTarget(portal *iscsi.TargetPortal, iqn string, isMultipath bool, + authType string, chapUser string, chapSecret string) error + DisconnectTarget(portal *iscsi.TargetPortal, iqn string) error + GetTargetDisks(portal *iscsi.TargetPortal, iqn string) ([]string, error) +} + +func NewServer(hostAPI API) (*Server, error) { + return &Server{ + hostAPI: hostAPI, + }, nil +} + +func (s *Server) requestTPtoAPITP(portal *internal.TargetPortal) *iscsi.TargetPortal { + port := portal.TargetPort + if port == 0 { + port = defaultIscsiPort + } + return &iscsi.TargetPortal{Address: portal.TargetAddress, Port: port} +} + +func (s *Server) AddTargetPortal(context context.Context, request *internal.AddTargetPortalRequest, version apiversion.Version) (*internal.AddTargetPortalResponse, error) { + klog.V(4).Infof("calling AddTargetPortal with portal %s:%d", request.TargetPortal.TargetAddress, request.TargetPortal.TargetPort) + response := &internal.AddTargetPortalResponse{} + err := s.hostAPI.AddTargetPortal(s.requestTPtoAPITP(request.TargetPortal)) + if err != nil { + klog.Errorf("failed AddTargetPortal %v", err) + return response, err + } + + return response, nil +} + +func AuthTypeToString(authType internal.AuthenticationType) (string, error) { + switch authType { + case internal.NONE: + return "NONE", nil + case internal.ONE_WAY_CHAP: + return "ONEWAYCHAP", nil + case internal.MUTUAL_CHAP: + return "MUTUALCHAP", nil + default: + return "", fmt.Errorf("invalid authentication type authType=%v", authType) + } +} + +func (s *Server) ConnectTarget(context context.Context, req *internal.ConnectTargetRequest, version apiversion.Version) (*internal.ConnectTargetResponse, error) { + klog.V(4).Infof("calling ConnectTarget with portal %s:%d and iqn %s"+ + " multipath=%v auth=%v chapuser=%v", req.TargetPortal.TargetAddress, + req.TargetPortal.TargetPort, req.Iqn, req.IsMultipath, req.AuthType, req.ChapUsername) + + response := &internal.ConnectTargetResponse{} + authType, err := AuthTypeToString(req.AuthType) + if err != nil { + klog.Errorf("Error parsing parameters: %v", err) + return response, err + } + + err = s.hostAPI.ConnectTarget(s.requestTPtoAPITP(req.TargetPortal), req.Iqn, + req.IsMultipath, authType, req.ChapUsername, req.ChapSecret) + if err != nil { + klog.Errorf("failed ConnectTarget %v", err) + return response, err + } + + return response, nil +} + +func (s *Server) DisconnectTarget(context context.Context, request *internal.DisconnectTargetRequest, version apiversion.Version) (*internal.DisconnectTargetResponse, error) { + klog.V(4).Infof("calling DisconnectTarget with portal %s:%d and iqn %s", + request.TargetPortal.TargetAddress, request.TargetPortal.TargetPort, request.Iqn) + + response := &internal.DisconnectTargetResponse{} + err := s.hostAPI.DisconnectTarget(s.requestTPtoAPITP(request.TargetPortal), request.Iqn) + if err != nil { + klog.Errorf("failed DisconnectTarget %v", err) + return response, err + } + + return response, nil +} + +func (s *Server) DiscoverTargetPortal(context context.Context, request *internal.DiscoverTargetPortalRequest, version apiversion.Version) (*internal.DiscoverTargetPortalResponse, error) { + klog.V(4).Infof("calling DiscoverTargetPortal with portal %s:%d", request.TargetPortal.TargetAddress, request.TargetPortal.TargetPort) + response := &internal.DiscoverTargetPortalResponse{} + iqns, err := s.hostAPI.DiscoverTargetPortal(s.requestTPtoAPITP(request.TargetPortal)) + if err != nil { + klog.Errorf("failed DiscoverTargetPortal %v", err) + return response, err + } + + response.Iqns = iqns + return response, nil +} + +func (s *Server) GetTargetDisks(context context.Context, request *internal.GetTargetDisksRequest, version apiversion.Version) (*internal.GetTargetDisksResponse, error) { + klog.V(4).Infof("calling GetTargetDisks with portal %s:%d and iqn %s", + request.TargetPortal.TargetAddress, request.TargetPortal.TargetPort, request.Iqn) + response := &internal.GetTargetDisksResponse{} + disks, err := s.hostAPI.GetTargetDisks(s.requestTPtoAPITP(request.TargetPortal), request.Iqn) + if err != nil { + klog.Errorf("failed GetTargetDisks %v", err) + return response, err + } + + result := make([]string, 0, len(disks)) + for _, d := range disks { + result = append(result, d) + } + + response.DiskIds = result + + return response, nil +} + +func (s *Server) ListTargetPortals(context context.Context, request *internal.ListTargetPortalsRequest, version apiversion.Version) (*internal.ListTargetPortalsResponse, error) { + klog.V(4).Infof("calling ListTargetPortals") + response := &internal.ListTargetPortalsResponse{} + portals, err := s.hostAPI.ListTargetPortals() + if err != nil { + klog.Errorf("failed ListTargetPortals %v", err) + return response, err + } + + result := make([]*internal.TargetPortal, 0, len(portals)) + for _, p := range portals { + result = append(result, &internal.TargetPortal{ + TargetAddress: p.Address, + TargetPort: p.Port, + }) + } + + response.TargetPortals = result + + return response, nil +} + +func (s *Server) RemoveTargetPortal(context context.Context, request *internal.RemoveTargetPortalRequest, version apiversion.Version) (*internal.RemoveTargetPortalResponse, error) { + klog.V(4).Infof("calling RemoveTargetPortal with portal %s:%d", request.TargetPortal.TargetAddress, request.TargetPortal.TargetPort) + response := &internal.RemoveTargetPortalResponse{} + err := s.hostAPI.RemoveTargetPortal(s.requestTPtoAPITP(request.TargetPortal)) + if err != nil { + klog.Errorf("failed RemoveTargetPortal %v", err) + return response, err + } + + return response, nil +} diff --git a/vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.pb.go b/vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.pb.go new file mode 100644 index 00000000..c1d35cac --- /dev/null +++ b/vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.pb.go @@ -0,0 +1,1079 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.proto + +package v1alpha1 + +import ( + context "context" + fmt "fmt" + proto "github.com/golang/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type AuthenticationType int32 + +const ( + AuthenticationType_NONE AuthenticationType = 0 + AuthenticationType_ONE_WAY_CHAP AuthenticationType = 1 + AuthenticationType_MUTUAL_CHAP AuthenticationType = 2 +) + +var AuthenticationType_name = map[int32]string{ + 0: "NONE", + 1: "ONE_WAY_CHAP", + 2: "MUTUAL_CHAP", +} + +var AuthenticationType_value = map[string]int32{ + "NONE": 0, + "ONE_WAY_CHAP": 1, + "MUTUAL_CHAP": 2, +} + +func (x AuthenticationType) String() string { + return proto.EnumName(AuthenticationType_name, int32(x)) +} + +func (AuthenticationType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{0} +} + +// TargetPortal is an address and port pair for a specific iSCSI storage +// target. +type TargetPortal struct { + // iSCSI Target (server) address + TargetAddress string `protobuf:"bytes,1,opt,name=target_address,json=targetAddress,proto3" json:"target_address,omitempty"` + // iSCSI Target port (default iSCSI port is 3260) + TargetPort uint32 `protobuf:"varint,2,opt,name=target_port,json=targetPort,proto3" json:"target_port,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TargetPortal) Reset() { *m = TargetPortal{} } +func (m *TargetPortal) String() string { return proto.CompactTextString(m) } +func (*TargetPortal) ProtoMessage() {} +func (*TargetPortal) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{0} +} + +func (m *TargetPortal) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TargetPortal.Unmarshal(m, b) +} +func (m *TargetPortal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TargetPortal.Marshal(b, m, deterministic) +} +func (m *TargetPortal) XXX_Merge(src proto.Message) { + xxx_messageInfo_TargetPortal.Merge(m, src) +} +func (m *TargetPortal) XXX_Size() int { + return xxx_messageInfo_TargetPortal.Size(m) +} +func (m *TargetPortal) XXX_DiscardUnknown() { + xxx_messageInfo_TargetPortal.DiscardUnknown(m) +} + +var xxx_messageInfo_TargetPortal proto.InternalMessageInfo + +func (m *TargetPortal) GetTargetAddress() string { + if m != nil { + return m.TargetAddress + } + return "" +} + +func (m *TargetPortal) GetTargetPort() uint32 { + if m != nil { + return m.TargetPort + } + return 0 +} + +type AddTargetPortalRequest struct { + // iSCSI Target Portal to register in the initiator + TargetPortal *TargetPortal `protobuf:"bytes,1,opt,name=target_portal,json=targetPortal,proto3" json:"target_portal,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AddTargetPortalRequest) Reset() { *m = AddTargetPortalRequest{} } +func (m *AddTargetPortalRequest) String() string { return proto.CompactTextString(m) } +func (*AddTargetPortalRequest) ProtoMessage() {} +func (*AddTargetPortalRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{1} +} + +func (m *AddTargetPortalRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AddTargetPortalRequest.Unmarshal(m, b) +} +func (m *AddTargetPortalRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AddTargetPortalRequest.Marshal(b, m, deterministic) +} +func (m *AddTargetPortalRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_AddTargetPortalRequest.Merge(m, src) +} +func (m *AddTargetPortalRequest) XXX_Size() int { + return xxx_messageInfo_AddTargetPortalRequest.Size(m) +} +func (m *AddTargetPortalRequest) XXX_DiscardUnknown() { + xxx_messageInfo_AddTargetPortalRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_AddTargetPortalRequest proto.InternalMessageInfo + +func (m *AddTargetPortalRequest) GetTargetPortal() *TargetPortal { + if m != nil { + return m.TargetPortal + } + return nil +} + +type AddTargetPortalResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AddTargetPortalResponse) Reset() { *m = AddTargetPortalResponse{} } +func (m *AddTargetPortalResponse) String() string { return proto.CompactTextString(m) } +func (*AddTargetPortalResponse) ProtoMessage() {} +func (*AddTargetPortalResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{2} +} + +func (m *AddTargetPortalResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AddTargetPortalResponse.Unmarshal(m, b) +} +func (m *AddTargetPortalResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AddTargetPortalResponse.Marshal(b, m, deterministic) +} +func (m *AddTargetPortalResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_AddTargetPortalResponse.Merge(m, src) +} +func (m *AddTargetPortalResponse) XXX_Size() int { + return xxx_messageInfo_AddTargetPortalResponse.Size(m) +} +func (m *AddTargetPortalResponse) XXX_DiscardUnknown() { + xxx_messageInfo_AddTargetPortalResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_AddTargetPortalResponse proto.InternalMessageInfo + +type DiscoverTargetPortalRequest struct { + // iSCSI Target Portal on which to initiate discovery + TargetPortal *TargetPortal `protobuf:"bytes,1,opt,name=target_portal,json=targetPortal,proto3" json:"target_portal,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DiscoverTargetPortalRequest) Reset() { *m = DiscoverTargetPortalRequest{} } +func (m *DiscoverTargetPortalRequest) String() string { return proto.CompactTextString(m) } +func (*DiscoverTargetPortalRequest) ProtoMessage() {} +func (*DiscoverTargetPortalRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{3} +} + +func (m *DiscoverTargetPortalRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DiscoverTargetPortalRequest.Unmarshal(m, b) +} +func (m *DiscoverTargetPortalRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DiscoverTargetPortalRequest.Marshal(b, m, deterministic) +} +func (m *DiscoverTargetPortalRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DiscoverTargetPortalRequest.Merge(m, src) +} +func (m *DiscoverTargetPortalRequest) XXX_Size() int { + return xxx_messageInfo_DiscoverTargetPortalRequest.Size(m) +} +func (m *DiscoverTargetPortalRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DiscoverTargetPortalRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_DiscoverTargetPortalRequest proto.InternalMessageInfo + +func (m *DiscoverTargetPortalRequest) GetTargetPortal() *TargetPortal { + if m != nil { + return m.TargetPortal + } + return nil +} + +type DiscoverTargetPortalResponse struct { + // List of discovered IQN addresses + // follows IQN format: iqn.yyyy-mm.naming-authority:unique-name + Iqns []string `protobuf:"bytes,1,rep,name=iqns,proto3" json:"iqns,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DiscoverTargetPortalResponse) Reset() { *m = DiscoverTargetPortalResponse{} } +func (m *DiscoverTargetPortalResponse) String() string { return proto.CompactTextString(m) } +func (*DiscoverTargetPortalResponse) ProtoMessage() {} +func (*DiscoverTargetPortalResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{4} +} + +func (m *DiscoverTargetPortalResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DiscoverTargetPortalResponse.Unmarshal(m, b) +} +func (m *DiscoverTargetPortalResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DiscoverTargetPortalResponse.Marshal(b, m, deterministic) +} +func (m *DiscoverTargetPortalResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_DiscoverTargetPortalResponse.Merge(m, src) +} +func (m *DiscoverTargetPortalResponse) XXX_Size() int { + return xxx_messageInfo_DiscoverTargetPortalResponse.Size(m) +} +func (m *DiscoverTargetPortalResponse) XXX_DiscardUnknown() { + xxx_messageInfo_DiscoverTargetPortalResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_DiscoverTargetPortalResponse proto.InternalMessageInfo + +func (m *DiscoverTargetPortalResponse) GetIqns() []string { + if m != nil { + return m.Iqns + } + return nil +} + +type RemoveTargetPortalRequest struct { + // iSCSI Target Portal + TargetPortal *TargetPortal `protobuf:"bytes,1,opt,name=target_portal,json=targetPortal,proto3" json:"target_portal,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RemoveTargetPortalRequest) Reset() { *m = RemoveTargetPortalRequest{} } +func (m *RemoveTargetPortalRequest) String() string { return proto.CompactTextString(m) } +func (*RemoveTargetPortalRequest) ProtoMessage() {} +func (*RemoveTargetPortalRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{5} +} + +func (m *RemoveTargetPortalRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RemoveTargetPortalRequest.Unmarshal(m, b) +} +func (m *RemoveTargetPortalRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RemoveTargetPortalRequest.Marshal(b, m, deterministic) +} +func (m *RemoveTargetPortalRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveTargetPortalRequest.Merge(m, src) +} +func (m *RemoveTargetPortalRequest) XXX_Size() int { + return xxx_messageInfo_RemoveTargetPortalRequest.Size(m) +} +func (m *RemoveTargetPortalRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveTargetPortalRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveTargetPortalRequest proto.InternalMessageInfo + +func (m *RemoveTargetPortalRequest) GetTargetPortal() *TargetPortal { + if m != nil { + return m.TargetPortal + } + return nil +} + +type RemoveTargetPortalResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RemoveTargetPortalResponse) Reset() { *m = RemoveTargetPortalResponse{} } +func (m *RemoveTargetPortalResponse) String() string { return proto.CompactTextString(m) } +func (*RemoveTargetPortalResponse) ProtoMessage() {} +func (*RemoveTargetPortalResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{6} +} + +func (m *RemoveTargetPortalResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RemoveTargetPortalResponse.Unmarshal(m, b) +} +func (m *RemoveTargetPortalResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RemoveTargetPortalResponse.Marshal(b, m, deterministic) +} +func (m *RemoveTargetPortalResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveTargetPortalResponse.Merge(m, src) +} +func (m *RemoveTargetPortalResponse) XXX_Size() int { + return xxx_messageInfo_RemoveTargetPortalResponse.Size(m) +} +func (m *RemoveTargetPortalResponse) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveTargetPortalResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveTargetPortalResponse proto.InternalMessageInfo + +type ListTargetPortalsRequest struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListTargetPortalsRequest) Reset() { *m = ListTargetPortalsRequest{} } +func (m *ListTargetPortalsRequest) String() string { return proto.CompactTextString(m) } +func (*ListTargetPortalsRequest) ProtoMessage() {} +func (*ListTargetPortalsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{7} +} + +func (m *ListTargetPortalsRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ListTargetPortalsRequest.Unmarshal(m, b) +} +func (m *ListTargetPortalsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ListTargetPortalsRequest.Marshal(b, m, deterministic) +} +func (m *ListTargetPortalsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListTargetPortalsRequest.Merge(m, src) +} +func (m *ListTargetPortalsRequest) XXX_Size() int { + return xxx_messageInfo_ListTargetPortalsRequest.Size(m) +} +func (m *ListTargetPortalsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListTargetPortalsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ListTargetPortalsRequest proto.InternalMessageInfo + +type ListTargetPortalsResponse struct { + // A list of Target Portals currently registered in the initiator + TargetPortals []*TargetPortal `protobuf:"bytes,1,rep,name=target_portals,json=targetPortals,proto3" json:"target_portals,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListTargetPortalsResponse) Reset() { *m = ListTargetPortalsResponse{} } +func (m *ListTargetPortalsResponse) String() string { return proto.CompactTextString(m) } +func (*ListTargetPortalsResponse) ProtoMessage() {} +func (*ListTargetPortalsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{8} +} + +func (m *ListTargetPortalsResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ListTargetPortalsResponse.Unmarshal(m, b) +} +func (m *ListTargetPortalsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ListTargetPortalsResponse.Marshal(b, m, deterministic) +} +func (m *ListTargetPortalsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListTargetPortalsResponse.Merge(m, src) +} +func (m *ListTargetPortalsResponse) XXX_Size() int { + return xxx_messageInfo_ListTargetPortalsResponse.Size(m) +} +func (m *ListTargetPortalsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListTargetPortalsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ListTargetPortalsResponse proto.InternalMessageInfo + +func (m *ListTargetPortalsResponse) GetTargetPortals() []*TargetPortal { + if m != nil { + return m.TargetPortals + } + return nil +} + +type ConnectTargetRequest struct { + // Target portal to which the initiator will connect. + TargetPortal *TargetPortal `protobuf:"bytes,1,opt,name=target_portal,json=targetPortal,proto3" json:"target_portal,omitempty"` + // IQN of the iSCSI Target + Iqn string `protobuf:"bytes,2,opt,name=iqn,proto3" json:"iqn,omitempty"` + // Connection authentication type, None by default + // + // One Way Chap uses the chap_username and chap_secret + // fields mentioned below to authenticate the initiator. + // + // Mutual Chap uses both the user/secret mentioned below + // and the Initiator Chap Secret to authenticate the target and initiator. + AuthType AuthenticationType `protobuf:"varint,3,opt,name=auth_type,json=authType,proto3,enum=v1alpha1.AuthenticationType" json:"auth_type,omitempty"` + // CHAP Username used to authenticate the initiator + ChapUsername string `protobuf:"bytes,4,opt,name=chap_username,json=chapUsername,proto3" json:"chap_username,omitempty"` + // CHAP password used to authenticate the initiator + ChapSecret string `protobuf:"bytes,5,opt,name=chap_secret,json=chapSecret,proto3" json:"chap_secret,omitempty"` + // Should enable multipath on the connection + // In order for multipath to work on Windows, the Multipath feature + // needs to be installed as well as MPIO should be correctly configured + IsMultipath bool `protobuf:"varint,6,opt,name=is_multipath,json=isMultipath,proto3" json:"is_multipath,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ConnectTargetRequest) Reset() { *m = ConnectTargetRequest{} } +func (m *ConnectTargetRequest) String() string { return proto.CompactTextString(m) } +func (*ConnectTargetRequest) ProtoMessage() {} +func (*ConnectTargetRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{9} +} + +func (m *ConnectTargetRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ConnectTargetRequest.Unmarshal(m, b) +} +func (m *ConnectTargetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ConnectTargetRequest.Marshal(b, m, deterministic) +} +func (m *ConnectTargetRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConnectTargetRequest.Merge(m, src) +} +func (m *ConnectTargetRequest) XXX_Size() int { + return xxx_messageInfo_ConnectTargetRequest.Size(m) +} +func (m *ConnectTargetRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ConnectTargetRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ConnectTargetRequest proto.InternalMessageInfo + +func (m *ConnectTargetRequest) GetTargetPortal() *TargetPortal { + if m != nil { + return m.TargetPortal + } + return nil +} + +func (m *ConnectTargetRequest) GetIqn() string { + if m != nil { + return m.Iqn + } + return "" +} + +func (m *ConnectTargetRequest) GetAuthType() AuthenticationType { + if m != nil { + return m.AuthType + } + return AuthenticationType_NONE +} + +func (m *ConnectTargetRequest) GetChapUsername() string { + if m != nil { + return m.ChapUsername + } + return "" +} + +func (m *ConnectTargetRequest) GetChapSecret() string { + if m != nil { + return m.ChapSecret + } + return "" +} + +func (m *ConnectTargetRequest) GetIsMultipath() bool { + if m != nil { + return m.IsMultipath + } + return false +} + +type ConnectTargetResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ConnectTargetResponse) Reset() { *m = ConnectTargetResponse{} } +func (m *ConnectTargetResponse) String() string { return proto.CompactTextString(m) } +func (*ConnectTargetResponse) ProtoMessage() {} +func (*ConnectTargetResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{10} +} + +func (m *ConnectTargetResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ConnectTargetResponse.Unmarshal(m, b) +} +func (m *ConnectTargetResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ConnectTargetResponse.Marshal(b, m, deterministic) +} +func (m *ConnectTargetResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConnectTargetResponse.Merge(m, src) +} +func (m *ConnectTargetResponse) XXX_Size() int { + return xxx_messageInfo_ConnectTargetResponse.Size(m) +} +func (m *ConnectTargetResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ConnectTargetResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ConnectTargetResponse proto.InternalMessageInfo + +type GetTargetDisksRequest struct { + // Target portal to which the initiator will connect. + TargetPortal *TargetPortal `protobuf:"bytes,1,opt,name=target_portal,json=targetPortal,proto3" json:"target_portal,omitempty"` + // IQN of the iSCSI Target + Iqn string `protobuf:"bytes,2,opt,name=iqn,proto3" json:"iqn,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetTargetDisksRequest) Reset() { *m = GetTargetDisksRequest{} } +func (m *GetTargetDisksRequest) String() string { return proto.CompactTextString(m) } +func (*GetTargetDisksRequest) ProtoMessage() {} +func (*GetTargetDisksRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{11} +} + +func (m *GetTargetDisksRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetTargetDisksRequest.Unmarshal(m, b) +} +func (m *GetTargetDisksRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetTargetDisksRequest.Marshal(b, m, deterministic) +} +func (m *GetTargetDisksRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTargetDisksRequest.Merge(m, src) +} +func (m *GetTargetDisksRequest) XXX_Size() int { + return xxx_messageInfo_GetTargetDisksRequest.Size(m) +} +func (m *GetTargetDisksRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetTargetDisksRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetTargetDisksRequest proto.InternalMessageInfo + +func (m *GetTargetDisksRequest) GetTargetPortal() *TargetPortal { + if m != nil { + return m.TargetPortal + } + return nil +} + +func (m *GetTargetDisksRequest) GetIqn() string { + if m != nil { + return m.Iqn + } + return "" +} + +type GetTargetDisksResponse struct { + // List composed of disk ids (numbers) that are associated with the + // iSCSI target + DiskIds []string `protobuf:"bytes,1,rep,name=diskIds,proto3" json:"diskIds,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetTargetDisksResponse) Reset() { *m = GetTargetDisksResponse{} } +func (m *GetTargetDisksResponse) String() string { return proto.CompactTextString(m) } +func (*GetTargetDisksResponse) ProtoMessage() {} +func (*GetTargetDisksResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{12} +} + +func (m *GetTargetDisksResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetTargetDisksResponse.Unmarshal(m, b) +} +func (m *GetTargetDisksResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetTargetDisksResponse.Marshal(b, m, deterministic) +} +func (m *GetTargetDisksResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTargetDisksResponse.Merge(m, src) +} +func (m *GetTargetDisksResponse) XXX_Size() int { + return xxx_messageInfo_GetTargetDisksResponse.Size(m) +} +func (m *GetTargetDisksResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetTargetDisksResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetTargetDisksResponse proto.InternalMessageInfo + +func (m *GetTargetDisksResponse) GetDiskIds() []string { + if m != nil { + return m.DiskIds + } + return nil +} + +type DisconnectTargetRequest struct { + // Target portal to which the initiator will connect. + TargetPortal *TargetPortal `protobuf:"bytes,1,opt,name=target_portal,json=targetPortal,proto3" json:"target_portal,omitempty"` + // IQN of the iSCSI Target + Iqn string `protobuf:"bytes,2,opt,name=iqn,proto3" json:"iqn,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DisconnectTargetRequest) Reset() { *m = DisconnectTargetRequest{} } +func (m *DisconnectTargetRequest) String() string { return proto.CompactTextString(m) } +func (*DisconnectTargetRequest) ProtoMessage() {} +func (*DisconnectTargetRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{13} +} + +func (m *DisconnectTargetRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DisconnectTargetRequest.Unmarshal(m, b) +} +func (m *DisconnectTargetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DisconnectTargetRequest.Marshal(b, m, deterministic) +} +func (m *DisconnectTargetRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DisconnectTargetRequest.Merge(m, src) +} +func (m *DisconnectTargetRequest) XXX_Size() int { + return xxx_messageInfo_DisconnectTargetRequest.Size(m) +} +func (m *DisconnectTargetRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DisconnectTargetRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_DisconnectTargetRequest proto.InternalMessageInfo + +func (m *DisconnectTargetRequest) GetTargetPortal() *TargetPortal { + if m != nil { + return m.TargetPortal + } + return nil +} + +func (m *DisconnectTargetRequest) GetIqn() string { + if m != nil { + return m.Iqn + } + return "" +} + +type DisconnectTargetResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DisconnectTargetResponse) Reset() { *m = DisconnectTargetResponse{} } +func (m *DisconnectTargetResponse) String() string { return proto.CompactTextString(m) } +func (*DisconnectTargetResponse) ProtoMessage() {} +func (*DisconnectTargetResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0438d9bfe30f1df4, []int{14} +} + +func (m *DisconnectTargetResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DisconnectTargetResponse.Unmarshal(m, b) +} +func (m *DisconnectTargetResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DisconnectTargetResponse.Marshal(b, m, deterministic) +} +func (m *DisconnectTargetResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_DisconnectTargetResponse.Merge(m, src) +} +func (m *DisconnectTargetResponse) XXX_Size() int { + return xxx_messageInfo_DisconnectTargetResponse.Size(m) +} +func (m *DisconnectTargetResponse) XXX_DiscardUnknown() { + xxx_messageInfo_DisconnectTargetResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_DisconnectTargetResponse proto.InternalMessageInfo + +func init() { + proto.RegisterEnum("v1alpha1.AuthenticationType", AuthenticationType_name, AuthenticationType_value) + proto.RegisterType((*TargetPortal)(nil), "v1alpha1.TargetPortal") + proto.RegisterType((*AddTargetPortalRequest)(nil), "v1alpha1.AddTargetPortalRequest") + proto.RegisterType((*AddTargetPortalResponse)(nil), "v1alpha1.AddTargetPortalResponse") + proto.RegisterType((*DiscoverTargetPortalRequest)(nil), "v1alpha1.DiscoverTargetPortalRequest") + proto.RegisterType((*DiscoverTargetPortalResponse)(nil), "v1alpha1.DiscoverTargetPortalResponse") + proto.RegisterType((*RemoveTargetPortalRequest)(nil), "v1alpha1.RemoveTargetPortalRequest") + proto.RegisterType((*RemoveTargetPortalResponse)(nil), "v1alpha1.RemoveTargetPortalResponse") + proto.RegisterType((*ListTargetPortalsRequest)(nil), "v1alpha1.ListTargetPortalsRequest") + proto.RegisterType((*ListTargetPortalsResponse)(nil), "v1alpha1.ListTargetPortalsResponse") + proto.RegisterType((*ConnectTargetRequest)(nil), "v1alpha1.ConnectTargetRequest") + proto.RegisterType((*ConnectTargetResponse)(nil), "v1alpha1.ConnectTargetResponse") + proto.RegisterType((*GetTargetDisksRequest)(nil), "v1alpha1.GetTargetDisksRequest") + proto.RegisterType((*GetTargetDisksResponse)(nil), "v1alpha1.GetTargetDisksResponse") + proto.RegisterType((*DisconnectTargetRequest)(nil), "v1alpha1.DisconnectTargetRequest") + proto.RegisterType((*DisconnectTargetResponse)(nil), "v1alpha1.DisconnectTargetResponse") +} + +func init() { + proto.RegisterFile("github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.proto", fileDescriptor_0438d9bfe30f1df4) +} + +var fileDescriptor_0438d9bfe30f1df4 = []byte{ + // 671 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xdb, 0x4e, 0xdb, 0x40, + 0x10, 0x25, 0xdc, 0x9a, 0x4c, 0x12, 0x48, 0x47, 0x5c, 0x8c, 0x8b, 0x4a, 0x58, 0x4a, 0x15, 0x55, + 0x22, 0x11, 0xe9, 0x53, 0x55, 0xa1, 0xca, 0x05, 0x44, 0x91, 0xb8, 0xc9, 0x25, 0x2d, 0xa5, 0x52, + 0xa3, 0xc5, 0xd9, 0xe2, 0x15, 0x89, 0x6d, 0xbc, 0x6b, 0x54, 0x3e, 0xa1, 0x1f, 0xd0, 0xff, 0xad, + 0x7c, 0x8d, 0x49, 0x9c, 0xf4, 0xa1, 0xf0, 0xb6, 0x7b, 0xf6, 0xcc, 0x99, 0xd9, 0x99, 0xd9, 0x59, + 0x38, 0xb8, 0xe6, 0xd2, 0xf4, 0xae, 0xea, 0x86, 0xdd, 0x6b, 0xdc, 0x78, 0x57, 0xcc, 0xb5, 0x98, + 0x64, 0x62, 0xcb, 0x10, 0xbc, 0x61, 0x08, 0xbe, 0xe5, 0xb8, 0xf6, 0xaf, 0xfb, 0x86, 0xd1, 0xe5, + 0xcc, 0x92, 0x0d, 0xea, 0xf0, 0x06, 0x17, 0xfe, 0xd1, 0xdd, 0x36, 0xed, 0x3a, 0x26, 0xdd, 0xf6, + 0xa1, 0xba, 0xe3, 0xda, 0xd2, 0xc6, 0x7c, 0x8c, 0x91, 0x2f, 0x50, 0x3a, 0xa7, 0xee, 0x35, 0x93, + 0x67, 0xb6, 0x2b, 0x69, 0x17, 0x37, 0x61, 0x4e, 0x06, 0xfb, 0x36, 0xed, 0x74, 0x5c, 0x26, 0x84, + 0x92, 0xab, 0xe6, 0x6a, 0x05, 0xbd, 0x1c, 0xa2, 0x5a, 0x08, 0xe2, 0x1a, 0x14, 0x23, 0x9a, 0x63, + 0xbb, 0x52, 0x99, 0xac, 0xe6, 0x6a, 0x65, 0x1d, 0x64, 0xa2, 0x44, 0x5a, 0xb0, 0xa4, 0x75, 0x3a, + 0x69, 0x69, 0x9d, 0xdd, 0x7a, 0x4c, 0x48, 0x7c, 0x0f, 0xe5, 0x94, 0x29, 0xed, 0x06, 0x0e, 0x8a, + 0xcd, 0xa5, 0x7a, 0x1c, 0x53, 0xfd, 0x81, 0x55, 0x49, 0xa6, 0x76, 0x64, 0x05, 0x96, 0x87, 0x64, + 0x85, 0x63, 0x5b, 0x82, 0x91, 0x4b, 0x78, 0xb1, 0xc7, 0x85, 0x61, 0xdf, 0x31, 0xf7, 0xd1, 0xdd, + 0x36, 0x61, 0x35, 0x5b, 0x3b, 0xf4, 0x8d, 0x08, 0xd3, 0xfc, 0xd6, 0xf2, 0x73, 0x35, 0x55, 0x2b, + 0xe8, 0xc1, 0x9a, 0x5c, 0xc0, 0x8a, 0xce, 0x7a, 0xf6, 0x1d, 0x7b, 0xf4, 0x68, 0x56, 0x41, 0xcd, + 0x52, 0x8e, 0xf2, 0xa0, 0x82, 0x72, 0xc4, 0x85, 0x4c, 0x9f, 0x89, 0xc8, 0x2d, 0xb9, 0x84, 0x95, + 0x8c, 0xb3, 0xe8, 0x12, 0x3b, 0x49, 0xe9, 0xc3, 0x98, 0xc2, 0xeb, 0x8c, 0x0e, 0xaa, 0x9c, 0x0e, + 0x4a, 0x90, 0xdf, 0x93, 0xb0, 0xb0, 0x6b, 0x5b, 0x16, 0x33, 0x22, 0xfd, 0xc7, 0xb8, 0x2b, 0x56, + 0x60, 0x8a, 0xdf, 0x5a, 0x41, 0x83, 0x15, 0x74, 0x7f, 0x89, 0xef, 0xa0, 0x40, 0x3d, 0x69, 0xb6, + 0xe5, 0xbd, 0xc3, 0x94, 0xa9, 0x6a, 0xae, 0x36, 0xd7, 0x5c, 0xed, 0x4b, 0x69, 0x9e, 0x34, 0x99, + 0x25, 0xb9, 0x41, 0x25, 0xb7, 0xad, 0xf3, 0x7b, 0x87, 0xe9, 0x79, 0x9f, 0xee, 0xaf, 0x70, 0x03, + 0xca, 0x86, 0x49, 0x9d, 0xb6, 0x27, 0x98, 0x6b, 0xd1, 0x1e, 0x53, 0xa6, 0x03, 0xd9, 0x92, 0x0f, + 0xb6, 0x22, 0xcc, 0x6f, 0xed, 0x80, 0x24, 0x98, 0xe1, 0x32, 0xa9, 0xcc, 0x04, 0x14, 0xf0, 0xa1, + 0xcf, 0x01, 0x82, 0xeb, 0x50, 0xe2, 0xa2, 0xdd, 0xf3, 0xba, 0x92, 0x3b, 0x54, 0x9a, 0xca, 0x6c, + 0x35, 0x57, 0xcb, 0xeb, 0x45, 0x2e, 0x8e, 0x63, 0x88, 0x2c, 0xc3, 0xe2, 0x40, 0x2a, 0xa2, 0xe2, + 0xfc, 0x84, 0xc5, 0x03, 0x16, 0x81, 0x7b, 0x5c, 0xdc, 0x88, 0xa7, 0x49, 0x12, 0x69, 0xc2, 0xd2, + 0xa0, 0x9f, 0xa8, 0xca, 0x0a, 0x3c, 0xeb, 0x70, 0x71, 0x73, 0xd8, 0x89, 0xbb, 0x35, 0xde, 0x12, + 0x13, 0x96, 0x83, 0x26, 0x7f, 0xf2, 0x12, 0xfa, 0x2d, 0x3a, 0xec, 0x29, 0x8c, 0xef, 0x8d, 0x06, + 0x38, 0x5c, 0x43, 0xcc, 0xc3, 0xf4, 0xc9, 0xe9, 0xc9, 0x7e, 0x65, 0x02, 0x2b, 0x50, 0x3a, 0x3d, + 0xd9, 0x6f, 0x7f, 0xd5, 0xbe, 0xb5, 0x77, 0x3f, 0x69, 0x67, 0x95, 0x1c, 0xce, 0x43, 0xf1, 0xb8, + 0x75, 0xde, 0xd2, 0x8e, 0x42, 0x60, 0xb2, 0xf9, 0x67, 0x06, 0x66, 0x0e, 0xfd, 0xd1, 0x87, 0x17, + 0x30, 0x3f, 0x30, 0x2e, 0xb0, 0x9a, 0xea, 0x95, 0xcc, 0x01, 0xa5, 0xae, 0x8f, 0x61, 0x44, 0x65, + 0x9c, 0xc0, 0x6b, 0x58, 0xc8, 0x9a, 0x08, 0xb8, 0xd9, 0x37, 0x1e, 0x33, 0x8d, 0xd4, 0xd7, 0xff, + 0xa2, 0x25, 0x8e, 0x28, 0xe0, 0xf0, 0x63, 0xc7, 0x8d, 0xbe, 0xfd, 0xc8, 0x21, 0xa3, 0xbe, 0x1a, + 0x4f, 0x4a, 0x5c, 0xfc, 0x80, 0xe7, 0x43, 0x53, 0x01, 0x49, 0xdf, 0x78, 0xd4, 0x38, 0x51, 0x37, + 0xc6, 0x72, 0x12, 0x7d, 0x1d, 0xca, 0x0f, 0x5e, 0x03, 0xbe, 0xec, 0xdb, 0x65, 0x4d, 0x0c, 0x75, + 0x6d, 0xe4, 0x79, 0xa2, 0xf9, 0x1d, 0x2a, 0x83, 0x2d, 0x84, 0xeb, 0x03, 0x49, 0xcd, 0x50, 0x26, + 0xe3, 0x28, 0x89, 0x78, 0x0b, 0xe6, 0x1e, 0xbe, 0x1e, 0x4c, 0x45, 0x94, 0xf9, 0x7e, 0xd5, 0xea, + 0x68, 0x42, 0x2c, 0xfb, 0xf1, 0xc3, 0xe5, 0xce, 0x7f, 0x7d, 0xe0, 0x57, 0xb3, 0xc1, 0xef, 0xfd, + 0xf6, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0x6d, 0xe8, 0x73, 0x08, 0x08, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConnInterface + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion6 + +// IscsiClient is the client API for Iscsi service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type IscsiClient interface { + // AddTargetPortal registers an iSCSI target network address for later + // discovery. + AddTargetPortal(ctx context.Context, in *AddTargetPortalRequest, opts ...grpc.CallOption) (*AddTargetPortalResponse, error) + // DiscoverTargetPortal initiates discovery on an iSCSI target network address + // and returns discovered IQNs. + DiscoverTargetPortal(ctx context.Context, in *DiscoverTargetPortalRequest, opts ...grpc.CallOption) (*DiscoverTargetPortalResponse, error) + // RemoveTargetPortal removes an iSCSI target network address registration. + RemoveTargetPortal(ctx context.Context, in *RemoveTargetPortalRequest, opts ...grpc.CallOption) (*RemoveTargetPortalResponse, error) + // ListTargetPortal lists all currently registered iSCSI target network + // addresses. + ListTargetPortals(ctx context.Context, in *ListTargetPortalsRequest, opts ...grpc.CallOption) (*ListTargetPortalsResponse, error) + // ConnectTarget connects to an iSCSI Target + ConnectTarget(ctx context.Context, in *ConnectTargetRequest, opts ...grpc.CallOption) (*ConnectTargetResponse, error) + // DisconnectTarget disconnects from an iSCSI Target + DisconnectTarget(ctx context.Context, in *DisconnectTargetRequest, opts ...grpc.CallOption) (*DisconnectTargetResponse, error) + // GetTargetDisks returns the disk addresses that correspond to an iSCSI + // target + GetTargetDisks(ctx context.Context, in *GetTargetDisksRequest, opts ...grpc.CallOption) (*GetTargetDisksResponse, error) +} + +type iscsiClient struct { + cc grpc.ClientConnInterface +} + +func NewIscsiClient(cc grpc.ClientConnInterface) IscsiClient { + return &iscsiClient{cc} +} + +func (c *iscsiClient) AddTargetPortal(ctx context.Context, in *AddTargetPortalRequest, opts ...grpc.CallOption) (*AddTargetPortalResponse, error) { + out := new(AddTargetPortalResponse) + err := c.cc.Invoke(ctx, "/v1alpha1.Iscsi/AddTargetPortal", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *iscsiClient) DiscoverTargetPortal(ctx context.Context, in *DiscoverTargetPortalRequest, opts ...grpc.CallOption) (*DiscoverTargetPortalResponse, error) { + out := new(DiscoverTargetPortalResponse) + err := c.cc.Invoke(ctx, "/v1alpha1.Iscsi/DiscoverTargetPortal", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *iscsiClient) RemoveTargetPortal(ctx context.Context, in *RemoveTargetPortalRequest, opts ...grpc.CallOption) (*RemoveTargetPortalResponse, error) { + out := new(RemoveTargetPortalResponse) + err := c.cc.Invoke(ctx, "/v1alpha1.Iscsi/RemoveTargetPortal", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *iscsiClient) ListTargetPortals(ctx context.Context, in *ListTargetPortalsRequest, opts ...grpc.CallOption) (*ListTargetPortalsResponse, error) { + out := new(ListTargetPortalsResponse) + err := c.cc.Invoke(ctx, "/v1alpha1.Iscsi/ListTargetPortals", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *iscsiClient) ConnectTarget(ctx context.Context, in *ConnectTargetRequest, opts ...grpc.CallOption) (*ConnectTargetResponse, error) { + out := new(ConnectTargetResponse) + err := c.cc.Invoke(ctx, "/v1alpha1.Iscsi/ConnectTarget", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *iscsiClient) DisconnectTarget(ctx context.Context, in *DisconnectTargetRequest, opts ...grpc.CallOption) (*DisconnectTargetResponse, error) { + out := new(DisconnectTargetResponse) + err := c.cc.Invoke(ctx, "/v1alpha1.Iscsi/DisconnectTarget", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *iscsiClient) GetTargetDisks(ctx context.Context, in *GetTargetDisksRequest, opts ...grpc.CallOption) (*GetTargetDisksResponse, error) { + out := new(GetTargetDisksResponse) + err := c.cc.Invoke(ctx, "/v1alpha1.Iscsi/GetTargetDisks", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// IscsiServer is the server API for Iscsi service. +type IscsiServer interface { + // AddTargetPortal registers an iSCSI target network address for later + // discovery. + AddTargetPortal(context.Context, *AddTargetPortalRequest) (*AddTargetPortalResponse, error) + // DiscoverTargetPortal initiates discovery on an iSCSI target network address + // and returns discovered IQNs. + DiscoverTargetPortal(context.Context, *DiscoverTargetPortalRequest) (*DiscoverTargetPortalResponse, error) + // RemoveTargetPortal removes an iSCSI target network address registration. + RemoveTargetPortal(context.Context, *RemoveTargetPortalRequest) (*RemoveTargetPortalResponse, error) + // ListTargetPortal lists all currently registered iSCSI target network + // addresses. + ListTargetPortals(context.Context, *ListTargetPortalsRequest) (*ListTargetPortalsResponse, error) + // ConnectTarget connects to an iSCSI Target + ConnectTarget(context.Context, *ConnectTargetRequest) (*ConnectTargetResponse, error) + // DisconnectTarget disconnects from an iSCSI Target + DisconnectTarget(context.Context, *DisconnectTargetRequest) (*DisconnectTargetResponse, error) + // GetTargetDisks returns the disk addresses that correspond to an iSCSI + // target + GetTargetDisks(context.Context, *GetTargetDisksRequest) (*GetTargetDisksResponse, error) +} + +// UnimplementedIscsiServer can be embedded to have forward compatible implementations. +type UnimplementedIscsiServer struct { +} + +func (*UnimplementedIscsiServer) AddTargetPortal(ctx context.Context, req *AddTargetPortalRequest) (*AddTargetPortalResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddTargetPortal not implemented") +} +func (*UnimplementedIscsiServer) DiscoverTargetPortal(ctx context.Context, req *DiscoverTargetPortalRequest) (*DiscoverTargetPortalResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DiscoverTargetPortal not implemented") +} +func (*UnimplementedIscsiServer) RemoveTargetPortal(ctx context.Context, req *RemoveTargetPortalRequest) (*RemoveTargetPortalResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveTargetPortal not implemented") +} +func (*UnimplementedIscsiServer) ListTargetPortals(ctx context.Context, req *ListTargetPortalsRequest) (*ListTargetPortalsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListTargetPortals not implemented") +} +func (*UnimplementedIscsiServer) ConnectTarget(ctx context.Context, req *ConnectTargetRequest) (*ConnectTargetResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConnectTarget not implemented") +} +func (*UnimplementedIscsiServer) DisconnectTarget(ctx context.Context, req *DisconnectTargetRequest) (*DisconnectTargetResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DisconnectTarget not implemented") +} +func (*UnimplementedIscsiServer) GetTargetDisks(ctx context.Context, req *GetTargetDisksRequest) (*GetTargetDisksResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetTargetDisks not implemented") +} + +func RegisterIscsiServer(s *grpc.Server, srv IscsiServer) { + s.RegisterService(&_Iscsi_serviceDesc, srv) +} + +func _Iscsi_AddTargetPortal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddTargetPortalRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(IscsiServer).AddTargetPortal(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1alpha1.Iscsi/AddTargetPortal", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(IscsiServer).AddTargetPortal(ctx, req.(*AddTargetPortalRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Iscsi_DiscoverTargetPortal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DiscoverTargetPortalRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(IscsiServer).DiscoverTargetPortal(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1alpha1.Iscsi/DiscoverTargetPortal", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(IscsiServer).DiscoverTargetPortal(ctx, req.(*DiscoverTargetPortalRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Iscsi_RemoveTargetPortal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveTargetPortalRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(IscsiServer).RemoveTargetPortal(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1alpha1.Iscsi/RemoveTargetPortal", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(IscsiServer).RemoveTargetPortal(ctx, req.(*RemoveTargetPortalRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Iscsi_ListTargetPortals_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListTargetPortalsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(IscsiServer).ListTargetPortals(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1alpha1.Iscsi/ListTargetPortals", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(IscsiServer).ListTargetPortals(ctx, req.(*ListTargetPortalsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Iscsi_ConnectTarget_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ConnectTargetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(IscsiServer).ConnectTarget(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1alpha1.Iscsi/ConnectTarget", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(IscsiServer).ConnectTarget(ctx, req.(*ConnectTargetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Iscsi_DisconnectTarget_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DisconnectTargetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(IscsiServer).DisconnectTarget(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1alpha1.Iscsi/DisconnectTarget", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(IscsiServer).DisconnectTarget(ctx, req.(*DisconnectTargetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Iscsi_GetTargetDisks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetTargetDisksRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(IscsiServer).GetTargetDisks(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1alpha1.Iscsi/GetTargetDisks", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(IscsiServer).GetTargetDisks(ctx, req.(*GetTargetDisksRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Iscsi_serviceDesc = grpc.ServiceDesc{ + ServiceName: "v1alpha1.Iscsi", + HandlerType: (*IscsiServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "AddTargetPortal", + Handler: _Iscsi_AddTargetPortal_Handler, + }, + { + MethodName: "DiscoverTargetPortal", + Handler: _Iscsi_DiscoverTargetPortal_Handler, + }, + { + MethodName: "RemoveTargetPortal", + Handler: _Iscsi_RemoveTargetPortal_Handler, + }, + { + MethodName: "ListTargetPortals", + Handler: _Iscsi_ListTargetPortals_Handler, + }, + { + MethodName: "ConnectTarget", + Handler: _Iscsi_ConnectTarget_Handler, + }, + { + MethodName: "DisconnectTarget", + Handler: _Iscsi_DisconnectTarget_Handler, + }, + { + MethodName: "GetTargetDisks", + Handler: _Iscsi_GetTargetDisks_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.proto", +} diff --git a/vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.proto b/vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.proto new file mode 100644 index 00000000..66de2c19 --- /dev/null +++ b/vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.proto @@ -0,0 +1,149 @@ +syntax = "proto3"; + +package v1alpha1; + +option go_package = "github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1"; + +service Iscsi { + // AddTargetPortal registers an iSCSI target network address for later + // discovery. + rpc AddTargetPortal(AddTargetPortalRequest) + returns (AddTargetPortalResponse) {} + + // DiscoverTargetPortal initiates discovery on an iSCSI target network address + // and returns discovered IQNs. + rpc DiscoverTargetPortal(DiscoverTargetPortalRequest) + returns (DiscoverTargetPortalResponse) {} + + // RemoveTargetPortal removes an iSCSI target network address registration. + rpc RemoveTargetPortal(RemoveTargetPortalRequest) + returns (RemoveTargetPortalResponse) {} + + // ListTargetPortal lists all currently registered iSCSI target network + // addresses. + rpc ListTargetPortals(ListTargetPortalsRequest) + returns (ListTargetPortalsResponse) {} + + // ConnectTarget connects to an iSCSI Target + rpc ConnectTarget(ConnectTargetRequest) returns (ConnectTargetResponse) {} + + // DisconnectTarget disconnects from an iSCSI Target + rpc DisconnectTarget(DisconnectTargetRequest) + returns (DisconnectTargetResponse) {} + + // GetTargetDisks returns the disk addresses that correspond to an iSCSI + // target + rpc GetTargetDisks(GetTargetDisksRequest) returns (GetTargetDisksResponse) {} +} + +// TargetPortal is an address and port pair for a specific iSCSI storage +// target. +message TargetPortal { + // iSCSI Target (server) address + string target_address = 1; + + // iSCSI Target port (default iSCSI port is 3260) + uint32 target_port = 2; +} + +message AddTargetPortalRequest { + // iSCSI Target Portal to register in the initiator + TargetPortal target_portal = 1; +} + +message AddTargetPortalResponse { + // Intentionally empty +} + +message DiscoverTargetPortalRequest { + // iSCSI Target Portal on which to initiate discovery + TargetPortal target_portal = 1; +} + +message DiscoverTargetPortalResponse { + // List of discovered IQN addresses + // follows IQN format: iqn.yyyy-mm.naming-authority:unique-name + repeated string iqns = 1; +} + +message RemoveTargetPortalRequest { + // iSCSI Target Portal + TargetPortal target_portal = 1; +} + +message RemoveTargetPortalResponse { + // Intentionally empty +} + +message ListTargetPortalsRequest { + // Intentionally empty +} + +message ListTargetPortalsResponse { + // A list of Target Portals currently registered in the initiator + repeated TargetPortal target_portals = 1; +} + +enum AuthenticationType { + NONE = 0; + ONE_WAY_CHAP = 1; + MUTUAL_CHAP = 2; +} + +message ConnectTargetRequest { + // Target portal to which the initiator will connect. + TargetPortal target_portal = 1; + + // IQN of the iSCSI Target + string iqn = 2; + + // Connection authentication type, None by default + // + // One Way Chap uses the chap_username and chap_secret + // fields mentioned below to authenticate the initiator. + // + // Mutual Chap uses both the user/secret mentioned below + // and the Initiator Chap Secret to authenticate the target and initiator. + AuthenticationType auth_type = 3; + + // CHAP Username used to authenticate the initiator + string chap_username = 4; + + // CHAP password used to authenticate the initiator + string chap_secret = 5; + + // Should enable multipath on the connection + // In order for multipath to work on Windows, the Multipath feature + // needs to be installed as well as MPIO should be correctly configured + bool is_multipath = 6; +} + +message ConnectTargetResponse { + // Intentionally empty +} + +message GetTargetDisksRequest { + // Target portal to which the initiator will connect. + TargetPortal target_portal = 1; + + // IQN of the iSCSI Target + string iqn = 2; +} + +message GetTargetDisksResponse { + // List composed of disk ids (numbers) that are associated with the + // iSCSI target + repeated string diskIds = 1; +} + +message DisconnectTargetRequest { + // Target portal to which the initiator will connect. + TargetPortal target_portal = 1; + + // IQN of the iSCSI Target + string iqn = 2; +} + +message DisconnectTargetResponse { + // Intentionally empty +} diff --git a/vendor/github.com/kubernetes-csi/csi-proxy/client/groups/iscsi/v1alpha1/client_generated.go b/vendor/github.com/kubernetes-csi/csi-proxy/client/groups/iscsi/v1alpha1/client_generated.go new file mode 100644 index 00000000..d4e75b61 --- /dev/null +++ b/vendor/github.com/kubernetes-csi/csi-proxy/client/groups/iscsi/v1alpha1/client_generated.go @@ -0,0 +1,80 @@ +// Code generated by csi-proxy-api-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "net" + + "github.com/Microsoft/go-winio" + "github.com/kubernetes-csi/csi-proxy/client" + "github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1" + "github.com/kubernetes-csi/csi-proxy/client/apiversion" + "google.golang.org/grpc" +) + +const groupName = "iscsi" + +var version = apiversion.NewVersionOrPanic("v1alpha1") + +type Client struct { + client v1alpha1.IscsiClient + connection *grpc.ClientConn +} + +// NewClient returns a client to make calls to the iscsi API group version v1alpha1. +// It's the caller's responsibility to Close the client when done. +func NewClient() (*Client, error) { + pipePath := client.PipePath(groupName, version) + + connection, err := grpc.Dial(pipePath, + grpc.WithContextDialer(func(context context.Context, s string) (net.Conn, error) { + return winio.DialPipeContext(context, s) + }), + grpc.WithInsecure()) + if err != nil { + return nil, err + } + + client := v1alpha1.NewIscsiClient(connection) + return &Client{ + client: client, + connection: connection, + }, nil +} + +// Close closes the client. It must be called before the client gets GC-ed. +func (w *Client) Close() error { + return w.connection.Close() +} + +// ensures we implement all the required methods +var _ v1alpha1.IscsiClient = &Client{} + +func (w *Client) AddTargetPortal(context context.Context, request *v1alpha1.AddTargetPortalRequest, opts ...grpc.CallOption) (*v1alpha1.AddTargetPortalResponse, error) { + return w.client.AddTargetPortal(context, request, opts...) +} + +func (w *Client) ConnectTarget(context context.Context, request *v1alpha1.ConnectTargetRequest, opts ...grpc.CallOption) (*v1alpha1.ConnectTargetResponse, error) { + return w.client.ConnectTarget(context, request, opts...) +} + +func (w *Client) DisconnectTarget(context context.Context, request *v1alpha1.DisconnectTargetRequest, opts ...grpc.CallOption) (*v1alpha1.DisconnectTargetResponse, error) { + return w.client.DisconnectTarget(context, request, opts...) +} + +func (w *Client) DiscoverTargetPortal(context context.Context, request *v1alpha1.DiscoverTargetPortalRequest, opts ...grpc.CallOption) (*v1alpha1.DiscoverTargetPortalResponse, error) { + return w.client.DiscoverTargetPortal(context, request, opts...) +} + +func (w *Client) GetTargetDisks(context context.Context, request *v1alpha1.GetTargetDisksRequest, opts ...grpc.CallOption) (*v1alpha1.GetTargetDisksResponse, error) { + return w.client.GetTargetDisks(context, request, opts...) +} + +func (w *Client) ListTargetPortals(context context.Context, request *v1alpha1.ListTargetPortalsRequest, opts ...grpc.CallOption) (*v1alpha1.ListTargetPortalsResponse, error) { + return w.client.ListTargetPortals(context, request, opts...) +} + +func (w *Client) RemoveTargetPortal(context context.Context, request *v1alpha1.RemoveTargetPortalRequest, opts ...grpc.CallOption) (*v1alpha1.RemoveTargetPortalResponse, error) { + return w.client.RemoveTargetPortal(context, request, opts...) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 50bb863c..4fcb4578 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -9,6 +9,13 @@ github.com/golang/protobuf/ptypes github.com/golang/protobuf/ptypes/any github.com/golang/protobuf/ptypes/duration github.com/golang/protobuf/ptypes/timestamp +# github.com/google/go-cmp v0.5.0 +github.com/google/go-cmp/cmp +github.com/google/go-cmp/cmp/cmpopts +github.com/google/go-cmp/cmp/internal/diff +github.com/google/go-cmp/cmp/internal/flags +github.com/google/go-cmp/cmp/internal/function +github.com/google/go-cmp/cmp/internal/value # github.com/iancoleman/strcase v0.0.0-20190422225806-e506e3ef7365 github.com/iancoleman/strcase # github.com/kubernetes-csi/csi-proxy/client v0.0.0-00010101000000-000000000000 => ./client @@ -18,6 +25,7 @@ github.com/kubernetes-csi/csi-proxy/client/api/disk/v1beta1 github.com/kubernetes-csi/csi-proxy/client/api/disk/v1beta2 github.com/kubernetes-csi/csi-proxy/client/api/filesystem/v1alpha1 github.com/kubernetes-csi/csi-proxy/client/api/filesystem/v1beta1 +github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1 github.com/kubernetes-csi/csi-proxy/client/api/smb/v1alpha1 github.com/kubernetes-csi/csi-proxy/client/api/smb/v1beta1 github.com/kubernetes-csi/csi-proxy/client/api/system/v1alpha1 @@ -28,6 +36,7 @@ github.com/kubernetes-csi/csi-proxy/client/apiversion github.com/kubernetes-csi/csi-proxy/client/groups/disk/v1beta1 github.com/kubernetes-csi/csi-proxy/client/groups/disk/v1beta2 github.com/kubernetes-csi/csi-proxy/client/groups/filesystem/v1beta1 +github.com/kubernetes-csi/csi-proxy/client/groups/iscsi/v1alpha1 github.com/kubernetes-csi/csi-proxy/client/groups/smb/v1beta1 github.com/kubernetes-csi/csi-proxy/client/groups/system/v1alpha1 github.com/kubernetes-csi/csi-proxy/client/groups/volume/v1alpha1 @@ -71,6 +80,9 @@ golang.org/x/tools/internal/gopathwalk golang.org/x/tools/internal/imports golang.org/x/tools/internal/module golang.org/x/tools/internal/semver +# golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 +golang.org/x/xerrors +golang.org/x/xerrors/internal # google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 google.golang.org/genproto/googleapis/rpc/status # google.golang.org/grpc v1.27.1 @@ -128,6 +140,7 @@ google.golang.org/protobuf/internal/flags google.golang.org/protobuf/internal/genid google.golang.org/protobuf/internal/impl google.golang.org/protobuf/internal/mapsort +google.golang.org/protobuf/internal/msgfmt google.golang.org/protobuf/internal/pragma google.golang.org/protobuf/internal/set google.golang.org/protobuf/internal/strs @@ -137,6 +150,7 @@ google.golang.org/protobuf/reflect/protoreflect google.golang.org/protobuf/reflect/protoregistry google.golang.org/protobuf/runtime/protoiface google.golang.org/protobuf/runtime/protoimpl +google.golang.org/protobuf/testing/protocmp google.golang.org/protobuf/types/known/anypb google.golang.org/protobuf/types/known/durationpb google.golang.org/protobuf/types/known/timestamppb From c1ce7560e8e67417c23c644858138f687040f3aa Mon Sep 17 00:00:00 2001 From: jmpfar Date: Tue, 20 Oct 2020 08:56:48 +0000 Subject: [PATCH 2/5] Adding newly added vendor files Change-Id: Ib669ed4aafad4a7ce6fa4821998e24264072c049 --- vendor/github.com/google/go-cmp/LICENSE | 27 + .../google/go-cmp/cmp/cmpopts/equate.go | 156 ++++ .../google/go-cmp/cmp/cmpopts/ignore.go | 206 ++++++ .../google/go-cmp/cmp/cmpopts/sort.go | 147 ++++ .../go-cmp/cmp/cmpopts/struct_filter.go | 187 +++++ .../google/go-cmp/cmp/cmpopts/xform.go | 35 + .../github.com/google/go-cmp/cmp/compare.go | 682 ++++++++++++++++++ .../google/go-cmp/cmp/export_panic.go | 15 + .../google/go-cmp/cmp/export_unsafe.go | 35 + .../go-cmp/cmp/internal/diff/debug_disable.go | 17 + .../go-cmp/cmp/internal/diff/debug_enable.go | 122 ++++ .../google/go-cmp/cmp/internal/diff/diff.go | 392 ++++++++++ .../google/go-cmp/cmp/internal/flags/flags.go | 9 + .../cmp/internal/flags/toolchain_legacy.go | 10 + .../cmp/internal/flags/toolchain_recent.go | 10 + .../go-cmp/cmp/internal/function/func.go | 99 +++ .../google/go-cmp/cmp/internal/value/name.go | 157 ++++ .../cmp/internal/value/pointer_purego.go | 33 + .../cmp/internal/value/pointer_unsafe.go | 36 + .../google/go-cmp/cmp/internal/value/sort.go | 106 +++ .../google/go-cmp/cmp/internal/value/zero.go | 48 ++ .../github.com/google/go-cmp/cmp/options.go | 549 ++++++++++++++ vendor/github.com/google/go-cmp/cmp/path.go | 378 ++++++++++ vendor/github.com/google/go-cmp/cmp/report.go | 54 ++ .../google/go-cmp/cmp/report_compare.go | 432 +++++++++++ .../google/go-cmp/cmp/report_references.go | 264 +++++++ .../google/go-cmp/cmp/report_reflect.go | 353 +++++++++ .../google/go-cmp/cmp/report_slices.go | 448 ++++++++++++ .../google/go-cmp/cmp/report_text.go | 431 +++++++++++ .../google/go-cmp/cmp/report_value.go | 121 ++++ vendor/golang.org/x/xerrors/LICENSE | 27 + vendor/golang.org/x/xerrors/PATENTS | 22 + vendor/golang.org/x/xerrors/README | 2 + vendor/golang.org/x/xerrors/adaptor.go | 193 +++++ vendor/golang.org/x/xerrors/codereview.cfg | 1 + vendor/golang.org/x/xerrors/doc.go | 22 + vendor/golang.org/x/xerrors/errors.go | 33 + vendor/golang.org/x/xerrors/fmt.go | 187 +++++ vendor/golang.org/x/xerrors/format.go | 34 + vendor/golang.org/x/xerrors/frame.go | 56 ++ vendor/golang.org/x/xerrors/go.mod | 3 + .../golang.org/x/xerrors/internal/internal.go | 8 + vendor/golang.org/x/xerrors/wrap.go | 106 +++ .../protobuf/internal/msgfmt/format.go | 275 +++++++ .../protobuf/testing/protocmp/reflect.go | 261 +++++++ .../protobuf/testing/protocmp/util.go | 679 +++++++++++++++++ .../protobuf/testing/protocmp/xform.go | 332 +++++++++ 47 files changed, 7800 insertions(+) create mode 100644 vendor/github.com/google/go-cmp/LICENSE create mode 100644 vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go create mode 100644 vendor/github.com/google/go-cmp/cmp/cmpopts/ignore.go create mode 100644 vendor/github.com/google/go-cmp/cmp/cmpopts/sort.go create mode 100644 vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go create mode 100644 vendor/github.com/google/go-cmp/cmp/cmpopts/xform.go create mode 100644 vendor/github.com/google/go-cmp/cmp/compare.go create mode 100644 vendor/github.com/google/go-cmp/cmp/export_panic.go create mode 100644 vendor/github.com/google/go-cmp/cmp/export_unsafe.go create mode 100644 vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go create mode 100644 vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go create mode 100644 vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go create mode 100644 vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go create mode 100644 vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_legacy.go create mode 100644 vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_recent.go create mode 100644 vendor/github.com/google/go-cmp/cmp/internal/function/func.go create mode 100644 vendor/github.com/google/go-cmp/cmp/internal/value/name.go create mode 100644 vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go create mode 100644 vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go create mode 100644 vendor/github.com/google/go-cmp/cmp/internal/value/sort.go create mode 100644 vendor/github.com/google/go-cmp/cmp/internal/value/zero.go create mode 100644 vendor/github.com/google/go-cmp/cmp/options.go create mode 100644 vendor/github.com/google/go-cmp/cmp/path.go create mode 100644 vendor/github.com/google/go-cmp/cmp/report.go create mode 100644 vendor/github.com/google/go-cmp/cmp/report_compare.go create mode 100644 vendor/github.com/google/go-cmp/cmp/report_references.go create mode 100644 vendor/github.com/google/go-cmp/cmp/report_reflect.go create mode 100644 vendor/github.com/google/go-cmp/cmp/report_slices.go create mode 100644 vendor/github.com/google/go-cmp/cmp/report_text.go create mode 100644 vendor/github.com/google/go-cmp/cmp/report_value.go create mode 100644 vendor/golang.org/x/xerrors/LICENSE create mode 100644 vendor/golang.org/x/xerrors/PATENTS create mode 100644 vendor/golang.org/x/xerrors/README create mode 100644 vendor/golang.org/x/xerrors/adaptor.go create mode 100644 vendor/golang.org/x/xerrors/codereview.cfg create mode 100644 vendor/golang.org/x/xerrors/doc.go create mode 100644 vendor/golang.org/x/xerrors/errors.go create mode 100644 vendor/golang.org/x/xerrors/fmt.go create mode 100644 vendor/golang.org/x/xerrors/format.go create mode 100644 vendor/golang.org/x/xerrors/frame.go create mode 100644 vendor/golang.org/x/xerrors/go.mod create mode 100644 vendor/golang.org/x/xerrors/internal/internal.go create mode 100644 vendor/golang.org/x/xerrors/wrap.go create mode 100644 vendor/google.golang.org/protobuf/internal/msgfmt/format.go create mode 100644 vendor/google.golang.org/protobuf/testing/protocmp/reflect.go create mode 100644 vendor/google.golang.org/protobuf/testing/protocmp/util.go create mode 100644 vendor/google.golang.org/protobuf/testing/protocmp/xform.go diff --git a/vendor/github.com/google/go-cmp/LICENSE b/vendor/github.com/google/go-cmp/LICENSE new file mode 100644 index 00000000..32017f8f --- /dev/null +++ b/vendor/github.com/google/go-cmp/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2017 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go new file mode 100644 index 00000000..8667908c --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go @@ -0,0 +1,156 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +// Package cmpopts provides common options for the cmp package. +package cmpopts + +import ( + "math" + "reflect" + "time" + + "github.com/google/go-cmp/cmp" + "golang.org/x/xerrors" +) + +func equateAlways(_, _ interface{}) bool { return true } + +// EquateEmpty returns a Comparer option that determines all maps and slices +// with a length of zero to be equal, regardless of whether they are nil. +// +// EquateEmpty can be used in conjunction with SortSlices and SortMaps. +func EquateEmpty() cmp.Option { + return cmp.FilterValues(isEmpty, cmp.Comparer(equateAlways)) +} + +func isEmpty(x, y interface{}) bool { + vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) + return (x != nil && y != nil && vx.Type() == vy.Type()) && + (vx.Kind() == reflect.Slice || vx.Kind() == reflect.Map) && + (vx.Len() == 0 && vy.Len() == 0) +} + +// EquateApprox returns a Comparer option that determines float32 or float64 +// values to be equal if they are within a relative fraction or absolute margin. +// This option is not used when either x or y is NaN or infinite. +// +// The fraction determines that the difference of two values must be within the +// smaller fraction of the two values, while the margin determines that the two +// values must be within some absolute margin. +// To express only a fraction or only a margin, use 0 for the other parameter. +// The fraction and margin must be non-negative. +// +// The mathematical expression used is equivalent to: +// |x-y| ≤ max(fraction*min(|x|, |y|), margin) +// +// EquateApprox can be used in conjunction with EquateNaNs. +func EquateApprox(fraction, margin float64) cmp.Option { + if margin < 0 || fraction < 0 || math.IsNaN(margin) || math.IsNaN(fraction) { + panic("margin or fraction must be a non-negative number") + } + a := approximator{fraction, margin} + return cmp.Options{ + cmp.FilterValues(areRealF64s, cmp.Comparer(a.compareF64)), + cmp.FilterValues(areRealF32s, cmp.Comparer(a.compareF32)), + } +} + +type approximator struct{ frac, marg float64 } + +func areRealF64s(x, y float64) bool { + return !math.IsNaN(x) && !math.IsNaN(y) && !math.IsInf(x, 0) && !math.IsInf(y, 0) +} +func areRealF32s(x, y float32) bool { + return areRealF64s(float64(x), float64(y)) +} +func (a approximator) compareF64(x, y float64) bool { + relMarg := a.frac * math.Min(math.Abs(x), math.Abs(y)) + return math.Abs(x-y) <= math.Max(a.marg, relMarg) +} +func (a approximator) compareF32(x, y float32) bool { + return a.compareF64(float64(x), float64(y)) +} + +// EquateNaNs returns a Comparer option that determines float32 and float64 +// NaN values to be equal. +// +// EquateNaNs can be used in conjunction with EquateApprox. +func EquateNaNs() cmp.Option { + return cmp.Options{ + cmp.FilterValues(areNaNsF64s, cmp.Comparer(equateAlways)), + cmp.FilterValues(areNaNsF32s, cmp.Comparer(equateAlways)), + } +} + +func areNaNsF64s(x, y float64) bool { + return math.IsNaN(x) && math.IsNaN(y) +} +func areNaNsF32s(x, y float32) bool { + return areNaNsF64s(float64(x), float64(y)) +} + +// EquateApproxTime returns a Comparer option that determines two non-zero +// time.Time values to be equal if they are within some margin of one another. +// If both times have a monotonic clock reading, then the monotonic time +// difference will be used. The margin must be non-negative. +func EquateApproxTime(margin time.Duration) cmp.Option { + if margin < 0 { + panic("margin must be a non-negative number") + } + a := timeApproximator{margin} + return cmp.FilterValues(areNonZeroTimes, cmp.Comparer(a.compare)) +} + +func areNonZeroTimes(x, y time.Time) bool { + return !x.IsZero() && !y.IsZero() +} + +type timeApproximator struct { + margin time.Duration +} + +func (a timeApproximator) compare(x, y time.Time) bool { + // Avoid subtracting times to avoid overflow when the + // difference is larger than the largest representible duration. + if x.After(y) { + // Ensure x is always before y + x, y = y, x + } + // We're within the margin if x+margin >= y. + // Note: time.Time doesn't have AfterOrEqual method hence the negation. + return !x.Add(a.margin).Before(y) +} + +// AnyError is an error that matches any non-nil error. +var AnyError anyError + +type anyError struct{} + +func (anyError) Error() string { return "any error" } +func (anyError) Is(err error) bool { return err != nil } + +// EquateErrors returns a Comparer option that determines errors to be equal +// if errors.Is reports them to match. The AnyError error can be used to +// match any non-nil error. +func EquateErrors() cmp.Option { + return cmp.FilterValues(areConcreteErrors, cmp.Comparer(compareErrors)) +} + +// areConcreteErrors reports whether x and y are types that implement error. +// The input types are deliberately of the interface{} type rather than the +// error type so that we can handle situations where the current type is an +// interface{}, but the underlying concrete types both happen to implement +// the error interface. +func areConcreteErrors(x, y interface{}) bool { + _, ok1 := x.(error) + _, ok2 := y.(error) + return ok1 && ok2 +} + +func compareErrors(x, y interface{}) bool { + xe := x.(error) + ye := y.(error) + // TODO(≥go1.13): Use standard definition of errors.Is. + return xerrors.Is(xe, ye) || xerrors.Is(ye, xe) +} diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/ignore.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/ignore.go new file mode 100644 index 00000000..afd36bee --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/ignore.go @@ -0,0 +1,206 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +package cmpopts + +import ( + "fmt" + "reflect" + "unicode" + "unicode/utf8" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/internal/function" +) + +// IgnoreFields returns an Option that ignores fields of the +// given names on a single struct type. It respects the names of exported fields +// that are forwarded due to struct embedding. +// The struct type is specified by passing in a value of that type. +// +// The name may be a dot-delimited string (e.g., "Foo.Bar") to ignore a +// specific sub-field that is embedded or nested within the parent struct. +func IgnoreFields(typ interface{}, names ...string) cmp.Option { + sf := newStructFilter(typ, names...) + return cmp.FilterPath(sf.filter, cmp.Ignore()) +} + +// IgnoreTypes returns an Option that ignores all values assignable to +// certain types, which are specified by passing in a value of each type. +func IgnoreTypes(typs ...interface{}) cmp.Option { + tf := newTypeFilter(typs...) + return cmp.FilterPath(tf.filter, cmp.Ignore()) +} + +type typeFilter []reflect.Type + +func newTypeFilter(typs ...interface{}) (tf typeFilter) { + for _, typ := range typs { + t := reflect.TypeOf(typ) + if t == nil { + // This occurs if someone tries to pass in sync.Locker(nil) + panic("cannot determine type; consider using IgnoreInterfaces") + } + tf = append(tf, t) + } + return tf +} +func (tf typeFilter) filter(p cmp.Path) bool { + if len(p) < 1 { + return false + } + t := p.Last().Type() + for _, ti := range tf { + if t.AssignableTo(ti) { + return true + } + } + return false +} + +// IgnoreInterfaces returns an Option that ignores all values or references of +// values assignable to certain interface types. These interfaces are specified +// by passing in an anonymous struct with the interface types embedded in it. +// For example, to ignore sync.Locker, pass in struct{sync.Locker}{}. +func IgnoreInterfaces(ifaces interface{}) cmp.Option { + tf := newIfaceFilter(ifaces) + return cmp.FilterPath(tf.filter, cmp.Ignore()) +} + +type ifaceFilter []reflect.Type + +func newIfaceFilter(ifaces interface{}) (tf ifaceFilter) { + t := reflect.TypeOf(ifaces) + if ifaces == nil || t.Name() != "" || t.Kind() != reflect.Struct { + panic("input must be an anonymous struct") + } + for i := 0; i < t.NumField(); i++ { + fi := t.Field(i) + switch { + case !fi.Anonymous: + panic("struct cannot have named fields") + case fi.Type.Kind() != reflect.Interface: + panic("embedded field must be an interface type") + case fi.Type.NumMethod() == 0: + // This matches everything; why would you ever want this? + panic("cannot ignore empty interface") + default: + tf = append(tf, fi.Type) + } + } + return tf +} +func (tf ifaceFilter) filter(p cmp.Path) bool { + if len(p) < 1 { + return false + } + t := p.Last().Type() + for _, ti := range tf { + if t.AssignableTo(ti) { + return true + } + if t.Kind() != reflect.Ptr && reflect.PtrTo(t).AssignableTo(ti) { + return true + } + } + return false +} + +// IgnoreUnexported returns an Option that only ignores the immediate unexported +// fields of a struct, including anonymous fields of unexported types. +// In particular, unexported fields within the struct's exported fields +// of struct types, including anonymous fields, will not be ignored unless the +// type of the field itself is also passed to IgnoreUnexported. +// +// Avoid ignoring unexported fields of a type which you do not control (i.e. a +// type from another repository), as changes to the implementation of such types +// may change how the comparison behaves. Prefer a custom Comparer instead. +func IgnoreUnexported(typs ...interface{}) cmp.Option { + ux := newUnexportedFilter(typs...) + return cmp.FilterPath(ux.filter, cmp.Ignore()) +} + +type unexportedFilter struct{ m map[reflect.Type]bool } + +func newUnexportedFilter(typs ...interface{}) unexportedFilter { + ux := unexportedFilter{m: make(map[reflect.Type]bool)} + for _, typ := range typs { + t := reflect.TypeOf(typ) + if t == nil || t.Kind() != reflect.Struct { + panic(fmt.Sprintf("invalid struct type: %T", typ)) + } + ux.m[t] = true + } + return ux +} +func (xf unexportedFilter) filter(p cmp.Path) bool { + sf, ok := p.Index(-1).(cmp.StructField) + if !ok { + return false + } + return xf.m[p.Index(-2).Type()] && !isExported(sf.Name()) +} + +// isExported reports whether the identifier is exported. +func isExported(id string) bool { + r, _ := utf8.DecodeRuneInString(id) + return unicode.IsUpper(r) +} + +// IgnoreSliceElements returns an Option that ignores elements of []V. +// The discard function must be of the form "func(T) bool" which is used to +// ignore slice elements of type V, where V is assignable to T. +// Elements are ignored if the function reports true. +func IgnoreSliceElements(discardFunc interface{}) cmp.Option { + vf := reflect.ValueOf(discardFunc) + if !function.IsType(vf.Type(), function.ValuePredicate) || vf.IsNil() { + panic(fmt.Sprintf("invalid discard function: %T", discardFunc)) + } + return cmp.FilterPath(func(p cmp.Path) bool { + si, ok := p.Index(-1).(cmp.SliceIndex) + if !ok { + return false + } + if !si.Type().AssignableTo(vf.Type().In(0)) { + return false + } + vx, vy := si.Values() + if vx.IsValid() && vf.Call([]reflect.Value{vx})[0].Bool() { + return true + } + if vy.IsValid() && vf.Call([]reflect.Value{vy})[0].Bool() { + return true + } + return false + }, cmp.Ignore()) +} + +// IgnoreMapEntries returns an Option that ignores entries of map[K]V. +// The discard function must be of the form "func(T, R) bool" which is used to +// ignore map entries of type K and V, where K and V are assignable to T and R. +// Entries are ignored if the function reports true. +func IgnoreMapEntries(discardFunc interface{}) cmp.Option { + vf := reflect.ValueOf(discardFunc) + if !function.IsType(vf.Type(), function.KeyValuePredicate) || vf.IsNil() { + panic(fmt.Sprintf("invalid discard function: %T", discardFunc)) + } + return cmp.FilterPath(func(p cmp.Path) bool { + mi, ok := p.Index(-1).(cmp.MapIndex) + if !ok { + return false + } + if !mi.Key().Type().AssignableTo(vf.Type().In(0)) || !mi.Type().AssignableTo(vf.Type().In(1)) { + return false + } + k := mi.Key() + vx, vy := mi.Values() + if vx.IsValid() && vf.Call([]reflect.Value{k, vx})[0].Bool() { + return true + } + if vy.IsValid() && vf.Call([]reflect.Value{k, vy})[0].Bool() { + return true + } + return false + }, cmp.Ignore()) +} diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/sort.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/sort.go new file mode 100644 index 00000000..3a480462 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/sort.go @@ -0,0 +1,147 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +package cmpopts + +import ( + "fmt" + "reflect" + "sort" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/internal/function" +) + +// SortSlices returns a Transformer option that sorts all []V. +// The less function must be of the form "func(T, T) bool" which is used to +// sort any slice with element type V that is assignable to T. +// +// The less function must be: +// • Deterministic: less(x, y) == less(x, y) +// • Irreflexive: !less(x, x) +// • Transitive: if !less(x, y) and !less(y, z), then !less(x, z) +// +// The less function does not have to be "total". That is, if !less(x, y) and +// !less(y, x) for two elements x and y, their relative order is maintained. +// +// SortSlices can be used in conjunction with EquateEmpty. +func SortSlices(lessFunc interface{}) cmp.Option { + vf := reflect.ValueOf(lessFunc) + if !function.IsType(vf.Type(), function.Less) || vf.IsNil() { + panic(fmt.Sprintf("invalid less function: %T", lessFunc)) + } + ss := sliceSorter{vf.Type().In(0), vf} + return cmp.FilterValues(ss.filter, cmp.Transformer("cmpopts.SortSlices", ss.sort)) +} + +type sliceSorter struct { + in reflect.Type // T + fnc reflect.Value // func(T, T) bool +} + +func (ss sliceSorter) filter(x, y interface{}) bool { + vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) + if !(x != nil && y != nil && vx.Type() == vy.Type()) || + !(vx.Kind() == reflect.Slice && vx.Type().Elem().AssignableTo(ss.in)) || + (vx.Len() <= 1 && vy.Len() <= 1) { + return false + } + // Check whether the slices are already sorted to avoid an infinite + // recursion cycle applying the same transform to itself. + ok1 := sort.SliceIsSorted(x, func(i, j int) bool { return ss.less(vx, i, j) }) + ok2 := sort.SliceIsSorted(y, func(i, j int) bool { return ss.less(vy, i, j) }) + return !ok1 || !ok2 +} +func (ss sliceSorter) sort(x interface{}) interface{} { + src := reflect.ValueOf(x) + dst := reflect.MakeSlice(src.Type(), src.Len(), src.Len()) + for i := 0; i < src.Len(); i++ { + dst.Index(i).Set(src.Index(i)) + } + sort.SliceStable(dst.Interface(), func(i, j int) bool { return ss.less(dst, i, j) }) + ss.checkSort(dst) + return dst.Interface() +} +func (ss sliceSorter) checkSort(v reflect.Value) { + start := -1 // Start of a sequence of equal elements. + for i := 1; i < v.Len(); i++ { + if ss.less(v, i-1, i) { + // Check that first and last elements in v[start:i] are equal. + if start >= 0 && (ss.less(v, start, i-1) || ss.less(v, i-1, start)) { + panic(fmt.Sprintf("incomparable values detected: want equal elements: %v", v.Slice(start, i))) + } + start = -1 + } else if start == -1 { + start = i + } + } +} +func (ss sliceSorter) less(v reflect.Value, i, j int) bool { + vx, vy := v.Index(i), v.Index(j) + return ss.fnc.Call([]reflect.Value{vx, vy})[0].Bool() +} + +// SortMaps returns a Transformer option that flattens map[K]V types to be a +// sorted []struct{K, V}. The less function must be of the form +// "func(T, T) bool" which is used to sort any map with key K that is +// assignable to T. +// +// Flattening the map into a slice has the property that cmp.Equal is able to +// use Comparers on K or the K.Equal method if it exists. +// +// The less function must be: +// • Deterministic: less(x, y) == less(x, y) +// • Irreflexive: !less(x, x) +// • Transitive: if !less(x, y) and !less(y, z), then !less(x, z) +// • Total: if x != y, then either less(x, y) or less(y, x) +// +// SortMaps can be used in conjunction with EquateEmpty. +func SortMaps(lessFunc interface{}) cmp.Option { + vf := reflect.ValueOf(lessFunc) + if !function.IsType(vf.Type(), function.Less) || vf.IsNil() { + panic(fmt.Sprintf("invalid less function: %T", lessFunc)) + } + ms := mapSorter{vf.Type().In(0), vf} + return cmp.FilterValues(ms.filter, cmp.Transformer("cmpopts.SortMaps", ms.sort)) +} + +type mapSorter struct { + in reflect.Type // T + fnc reflect.Value // func(T, T) bool +} + +func (ms mapSorter) filter(x, y interface{}) bool { + vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) + return (x != nil && y != nil && vx.Type() == vy.Type()) && + (vx.Kind() == reflect.Map && vx.Type().Key().AssignableTo(ms.in)) && + (vx.Len() != 0 || vy.Len() != 0) +} +func (ms mapSorter) sort(x interface{}) interface{} { + src := reflect.ValueOf(x) + outType := reflect.StructOf([]reflect.StructField{ + {Name: "K", Type: src.Type().Key()}, + {Name: "V", Type: src.Type().Elem()}, + }) + dst := reflect.MakeSlice(reflect.SliceOf(outType), src.Len(), src.Len()) + for i, k := range src.MapKeys() { + v := reflect.New(outType).Elem() + v.Field(0).Set(k) + v.Field(1).Set(src.MapIndex(k)) + dst.Index(i).Set(v) + } + sort.Slice(dst.Interface(), func(i, j int) bool { return ms.less(dst, i, j) }) + ms.checkSort(dst) + return dst.Interface() +} +func (ms mapSorter) checkSort(v reflect.Value) { + for i := 1; i < v.Len(); i++ { + if !ms.less(v, i-1, i) { + panic(fmt.Sprintf("partial order detected: want %v < %v", v.Index(i-1), v.Index(i))) + } + } +} +func (ms mapSorter) less(v reflect.Value, i, j int) bool { + vx, vy := v.Index(i).Field(0), v.Index(j).Field(0) + return ms.fnc.Call([]reflect.Value{vx, vy})[0].Bool() +} diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go new file mode 100644 index 00000000..dae7ced5 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go @@ -0,0 +1,187 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +package cmpopts + +import ( + "fmt" + "reflect" + "strings" + + "github.com/google/go-cmp/cmp" +) + +// filterField returns a new Option where opt is only evaluated on paths that +// include a specific exported field on a single struct type. +// The struct type is specified by passing in a value of that type. +// +// The name may be a dot-delimited string (e.g., "Foo.Bar") to select a +// specific sub-field that is embedded or nested within the parent struct. +func filterField(typ interface{}, name string, opt cmp.Option) cmp.Option { + // TODO: This is currently unexported over concerns of how helper filters + // can be composed together easily. + // TODO: Add tests for FilterField. + + sf := newStructFilter(typ, name) + return cmp.FilterPath(sf.filter, opt) +} + +type structFilter struct { + t reflect.Type // The root struct type to match on + ft fieldTree // Tree of fields to match on +} + +func newStructFilter(typ interface{}, names ...string) structFilter { + // TODO: Perhaps allow * as a special identifier to allow ignoring any + // number of path steps until the next field match? + // This could be useful when a concrete struct gets transformed into + // an anonymous struct where it is not possible to specify that by type, + // but the transformer happens to provide guarantees about the names of + // the transformed fields. + + t := reflect.TypeOf(typ) + if t == nil || t.Kind() != reflect.Struct { + panic(fmt.Sprintf("%T must be a struct", typ)) + } + var ft fieldTree + for _, name := range names { + cname, err := canonicalName(t, name) + if err != nil { + panic(fmt.Sprintf("%s: %v", strings.Join(cname, "."), err)) + } + ft.insert(cname) + } + return structFilter{t, ft} +} + +func (sf structFilter) filter(p cmp.Path) bool { + for i, ps := range p { + if ps.Type().AssignableTo(sf.t) && sf.ft.matchPrefix(p[i+1:]) { + return true + } + } + return false +} + +// fieldTree represents a set of dot-separated identifiers. +// +// For example, inserting the following selectors: +// Foo +// Foo.Bar.Baz +// Foo.Buzz +// Nuka.Cola.Quantum +// +// Results in a tree of the form: +// {sub: { +// "Foo": {ok: true, sub: { +// "Bar": {sub: { +// "Baz": {ok: true}, +// }}, +// "Buzz": {ok: true}, +// }}, +// "Nuka": {sub: { +// "Cola": {sub: { +// "Quantum": {ok: true}, +// }}, +// }}, +// }} +type fieldTree struct { + ok bool // Whether this is a specified node + sub map[string]fieldTree // The sub-tree of fields under this node +} + +// insert inserts a sequence of field accesses into the tree. +func (ft *fieldTree) insert(cname []string) { + if ft.sub == nil { + ft.sub = make(map[string]fieldTree) + } + if len(cname) == 0 { + ft.ok = true + return + } + sub := ft.sub[cname[0]] + sub.insert(cname[1:]) + ft.sub[cname[0]] = sub +} + +// matchPrefix reports whether any selector in the fieldTree matches +// the start of path p. +func (ft fieldTree) matchPrefix(p cmp.Path) bool { + for _, ps := range p { + switch ps := ps.(type) { + case cmp.StructField: + ft = ft.sub[ps.Name()] + if ft.ok { + return true + } + if len(ft.sub) == 0 { + return false + } + case cmp.Indirect: + default: + return false + } + } + return false +} + +// canonicalName returns a list of identifiers where any struct field access +// through an embedded field is expanded to include the names of the embedded +// types themselves. +// +// For example, suppose field "Foo" is not directly in the parent struct, +// but actually from an embedded struct of type "Bar". Then, the canonical name +// of "Foo" is actually "Bar.Foo". +// +// Suppose field "Foo" is not directly in the parent struct, but actually +// a field in two different embedded structs of types "Bar" and "Baz". +// Then the selector "Foo" causes a panic since it is ambiguous which one it +// refers to. The user must specify either "Bar.Foo" or "Baz.Foo". +func canonicalName(t reflect.Type, sel string) ([]string, error) { + var name string + sel = strings.TrimPrefix(sel, ".") + if sel == "" { + return nil, fmt.Errorf("name must not be empty") + } + if i := strings.IndexByte(sel, '.'); i < 0 { + name, sel = sel, "" + } else { + name, sel = sel[:i], sel[i:] + } + + // Type must be a struct or pointer to struct. + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + if t.Kind() != reflect.Struct { + return nil, fmt.Errorf("%v must be a struct", t) + } + + // Find the canonical name for this current field name. + // If the field exists in an embedded struct, then it will be expanded. + sf, _ := t.FieldByName(name) + if !isExported(name) { + // Avoid using reflect.Type.FieldByName for unexported fields due to + // buggy behavior with regard to embeddeding and unexported fields. + // See https://golang.org/issue/4876 for details. + sf = reflect.StructField{} + for i := 0; i < t.NumField() && sf.Name == ""; i++ { + if t.Field(i).Name == name { + sf = t.Field(i) + } + } + } + if sf.Name == "" { + return []string{name}, fmt.Errorf("does not exist") + } + var ss []string + for i := range sf.Index { + ss = append(ss, t.FieldByIndex(sf.Index[:i+1]).Name) + } + if sel == "" { + return ss, nil + } + ssPost, err := canonicalName(sf.Type, sel) + return append(ss, ssPost...), err +} diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/xform.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/xform.go new file mode 100644 index 00000000..9d651553 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/xform.go @@ -0,0 +1,35 @@ +// Copyright 2018, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +package cmpopts + +import ( + "github.com/google/go-cmp/cmp" +) + +type xformFilter struct{ xform cmp.Option } + +func (xf xformFilter) filter(p cmp.Path) bool { + for _, ps := range p { + if t, ok := ps.(cmp.Transform); ok && t.Option() == xf.xform { + return false + } + } + return true +} + +// AcyclicTransformer returns a Transformer with a filter applied that ensures +// that the transformer cannot be recursively applied upon its own output. +// +// An example use case is a transformer that splits a string by lines: +// AcyclicTransformer("SplitLines", func(s string) []string{ +// return strings.Split(s, "\n") +// }) +// +// Had this been an unfiltered Transformer instead, this would result in an +// infinite cycle converting a string to []string to [][]string and so on. +func AcyclicTransformer(name string, xformFunc interface{}) cmp.Option { + xf := xformFilter{cmp.Transformer(name, xformFunc)} + return cmp.FilterPath(xf.filter, xf.xform) +} diff --git a/vendor/github.com/google/go-cmp/cmp/compare.go b/vendor/github.com/google/go-cmp/cmp/compare.go new file mode 100644 index 00000000..580ae209 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/compare.go @@ -0,0 +1,682 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +// Package cmp determines equality of values. +// +// This package is intended to be a more powerful and safer alternative to +// reflect.DeepEqual for comparing whether two values are semantically equal. +// It is intended to only be used in tests, as performance is not a goal and +// it may panic if it cannot compare the values. Its propensity towards +// panicking means that its unsuitable for production environments where a +// spurious panic may be fatal. +// +// The primary features of cmp are: +// +// • When the default behavior of equality does not suit the needs of the test, +// custom equality functions can override the equality operation. +// For example, an equality function may report floats as equal so long as they +// are within some tolerance of each other. +// +// • Types that have an Equal method may use that method to determine equality. +// This allows package authors to determine the equality operation for the types +// that they define. +// +// • If no custom equality functions are used and no Equal method is defined, +// equality is determined by recursively comparing the primitive kinds on both +// values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, unexported +// fields are not compared by default; they result in panics unless suppressed +// by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly +// compared using the Exporter option. +package cmp + +import ( + "fmt" + "reflect" + "strings" + + "github.com/google/go-cmp/cmp/internal/diff" + "github.com/google/go-cmp/cmp/internal/flags" + "github.com/google/go-cmp/cmp/internal/function" + "github.com/google/go-cmp/cmp/internal/value" +) + +// Equal reports whether x and y are equal by recursively applying the +// following rules in the given order to x and y and all of their sub-values: +// +// • Let S be the set of all Ignore, Transformer, and Comparer options that +// remain after applying all path filters, value filters, and type filters. +// If at least one Ignore exists in S, then the comparison is ignored. +// If the number of Transformer and Comparer options in S is greater than one, +// then Equal panics because it is ambiguous which option to use. +// If S contains a single Transformer, then use that to transform the current +// values and recursively call Equal on the output values. +// If S contains a single Comparer, then use that to compare the current values. +// Otherwise, evaluation proceeds to the next rule. +// +// • If the values have an Equal method of the form "(T) Equal(T) bool" or +// "(T) Equal(I) bool" where T is assignable to I, then use the result of +// x.Equal(y) even if x or y is nil. Otherwise, no such method exists and +// evaluation proceeds to the next rule. +// +// • Lastly, try to compare x and y based on their basic kinds. +// Simple kinds like booleans, integers, floats, complex numbers, strings, and +// channels are compared using the equivalent of the == operator in Go. +// Functions are only equal if they are both nil, otherwise they are unequal. +// +// Structs are equal if recursively calling Equal on all fields report equal. +// If a struct contains unexported fields, Equal panics unless an Ignore option +// (e.g., cmpopts.IgnoreUnexported) ignores that field or the Exporter option +// explicitly permits comparing the unexported field. +// +// Slices are equal if they are both nil or both non-nil, where recursively +// calling Equal on all non-ignored slice or array elements report equal. +// Empty non-nil slices and nil slices are not equal; to equate empty slices, +// consider using cmpopts.EquateEmpty. +// +// Maps are equal if they are both nil or both non-nil, where recursively +// calling Equal on all non-ignored map entries report equal. +// Map keys are equal according to the == operator. +// To use custom comparisons for map keys, consider using cmpopts.SortMaps. +// Empty non-nil maps and nil maps are not equal; to equate empty maps, +// consider using cmpopts.EquateEmpty. +// +// Pointers and interfaces are equal if they are both nil or both non-nil, +// where they have the same underlying concrete type and recursively +// calling Equal on the underlying values reports equal. +// +// Before recursing into a pointer, slice element, or map, the current path +// is checked to detect whether the address has already been visited. +// If there is a cycle, then the pointed at values are considered equal +// only if both addresses were previously visited in the same path step. +func Equal(x, y interface{}, opts ...Option) bool { + s := newState(opts) + s.compareAny(rootStep(x, y)) + return s.result.Equal() +} + +// Diff returns a human-readable report of the differences between two values. +// It returns an empty string if and only if Equal returns true for the same +// input values and options. +// +// The output is displayed as a literal in pseudo-Go syntax. +// At the start of each line, a "-" prefix indicates an element removed from x, +// a "+" prefix to indicates an element added to y, and the lack of a prefix +// indicates an element common to both x and y. If possible, the output +// uses fmt.Stringer.String or error.Error methods to produce more humanly +// readable outputs. In such cases, the string is prefixed with either an +// 's' or 'e' character, respectively, to indicate that the method was called. +// +// Do not depend on this output being stable. If you need the ability to +// programmatically interpret the difference, consider using a custom Reporter. +func Diff(x, y interface{}, opts ...Option) string { + s := newState(opts) + + // Optimization: If there are no other reporters, we can optimize for the + // common case where the result is equal (and thus no reported difference). + // This avoids the expensive construction of a difference tree. + if len(s.reporters) == 0 { + s.compareAny(rootStep(x, y)) + if s.result.Equal() { + return "" + } + s.result = diff.Result{} // Reset results + } + + r := new(defaultReporter) + s.reporters = append(s.reporters, reporter{r}) + s.compareAny(rootStep(x, y)) + d := r.String() + if (d == "") != s.result.Equal() { + panic("inconsistent difference and equality results") + } + return d +} + +// rootStep constructs the first path step. If x and y have differing types, +// then they are stored within an empty interface type. +func rootStep(x, y interface{}) PathStep { + vx := reflect.ValueOf(x) + vy := reflect.ValueOf(y) + + // If the inputs are different types, auto-wrap them in an empty interface + // so that they have the same parent type. + var t reflect.Type + if !vx.IsValid() || !vy.IsValid() || vx.Type() != vy.Type() { + t = reflect.TypeOf((*interface{})(nil)).Elem() + if vx.IsValid() { + vvx := reflect.New(t).Elem() + vvx.Set(vx) + vx = vvx + } + if vy.IsValid() { + vvy := reflect.New(t).Elem() + vvy.Set(vy) + vy = vvy + } + } else { + t = vx.Type() + } + + return &pathStep{t, vx, vy} +} + +type state struct { + // These fields represent the "comparison state". + // Calling statelessCompare must not result in observable changes to these. + result diff.Result // The current result of comparison + curPath Path // The current path in the value tree + curPtrs pointerPath // The current set of visited pointers + reporters []reporter // Optional reporters + + // recChecker checks for infinite cycles applying the same set of + // transformers upon the output of itself. + recChecker recChecker + + // dynChecker triggers pseudo-random checks for option correctness. + // It is safe for statelessCompare to mutate this value. + dynChecker dynChecker + + // These fields, once set by processOption, will not change. + exporters []exporter // List of exporters for structs with unexported fields + opts Options // List of all fundamental and filter options +} + +func newState(opts []Option) *state { + // Always ensure a validator option exists to validate the inputs. + s := &state{opts: Options{validator{}}} + s.curPtrs.Init() + s.processOption(Options(opts)) + return s +} + +func (s *state) processOption(opt Option) { + switch opt := opt.(type) { + case nil: + case Options: + for _, o := range opt { + s.processOption(o) + } + case coreOption: + type filtered interface { + isFiltered() bool + } + if fopt, ok := opt.(filtered); ok && !fopt.isFiltered() { + panic(fmt.Sprintf("cannot use an unfiltered option: %v", opt)) + } + s.opts = append(s.opts, opt) + case exporter: + s.exporters = append(s.exporters, opt) + case reporter: + s.reporters = append(s.reporters, opt) + default: + panic(fmt.Sprintf("unknown option %T", opt)) + } +} + +// statelessCompare compares two values and returns the result. +// This function is stateless in that it does not alter the current result, +// or output to any registered reporters. +func (s *state) statelessCompare(step PathStep) diff.Result { + // We do not save and restore curPath and curPtrs because all of the + // compareX methods should properly push and pop from them. + // It is an implementation bug if the contents of the paths differ from + // when calling this function to when returning from it. + + oldResult, oldReporters := s.result, s.reporters + s.result = diff.Result{} // Reset result + s.reporters = nil // Remove reporters to avoid spurious printouts + s.compareAny(step) + res := s.result + s.result, s.reporters = oldResult, oldReporters + return res +} + +func (s *state) compareAny(step PathStep) { + // Update the path stack. + s.curPath.push(step) + defer s.curPath.pop() + for _, r := range s.reporters { + r.PushStep(step) + defer r.PopStep() + } + s.recChecker.Check(s.curPath) + + // Cycle-detection for slice elements (see NOTE in compareSlice). + t := step.Type() + vx, vy := step.Values() + if si, ok := step.(SliceIndex); ok && si.isSlice && vx.IsValid() && vy.IsValid() { + px, py := vx.Addr(), vy.Addr() + if eq, visited := s.curPtrs.Push(px, py); visited { + s.report(eq, reportByCycle) + return + } + defer s.curPtrs.Pop(px, py) + } + + // Rule 1: Check whether an option applies on this node in the value tree. + if s.tryOptions(t, vx, vy) { + return + } + + // Rule 2: Check whether the type has a valid Equal method. + if s.tryMethod(t, vx, vy) { + return + } + + // Rule 3: Compare based on the underlying kind. + switch t.Kind() { + case reflect.Bool: + s.report(vx.Bool() == vy.Bool(), 0) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + s.report(vx.Int() == vy.Int(), 0) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + s.report(vx.Uint() == vy.Uint(), 0) + case reflect.Float32, reflect.Float64: + s.report(vx.Float() == vy.Float(), 0) + case reflect.Complex64, reflect.Complex128: + s.report(vx.Complex() == vy.Complex(), 0) + case reflect.String: + s.report(vx.String() == vy.String(), 0) + case reflect.Chan, reflect.UnsafePointer: + s.report(vx.Pointer() == vy.Pointer(), 0) + case reflect.Func: + s.report(vx.IsNil() && vy.IsNil(), 0) + case reflect.Struct: + s.compareStruct(t, vx, vy) + case reflect.Slice, reflect.Array: + s.compareSlice(t, vx, vy) + case reflect.Map: + s.compareMap(t, vx, vy) + case reflect.Ptr: + s.comparePtr(t, vx, vy) + case reflect.Interface: + s.compareInterface(t, vx, vy) + default: + panic(fmt.Sprintf("%v kind not handled", t.Kind())) + } +} + +func (s *state) tryOptions(t reflect.Type, vx, vy reflect.Value) bool { + // Evaluate all filters and apply the remaining options. + if opt := s.opts.filter(s, t, vx, vy); opt != nil { + opt.apply(s, vx, vy) + return true + } + return false +} + +func (s *state) tryMethod(t reflect.Type, vx, vy reflect.Value) bool { + // Check if this type even has an Equal method. + m, ok := t.MethodByName("Equal") + if !ok || !function.IsType(m.Type, function.EqualAssignable) { + return false + } + + eq := s.callTTBFunc(m.Func, vx, vy) + s.report(eq, reportByMethod) + return true +} + +func (s *state) callTRFunc(f, v reflect.Value, step Transform) reflect.Value { + v = sanitizeValue(v, f.Type().In(0)) + if !s.dynChecker.Next() { + return f.Call([]reflect.Value{v})[0] + } + + // Run the function twice and ensure that we get the same results back. + // We run in goroutines so that the race detector (if enabled) can detect + // unsafe mutations to the input. + c := make(chan reflect.Value) + go detectRaces(c, f, v) + got := <-c + want := f.Call([]reflect.Value{v})[0] + if step.vx, step.vy = got, want; !s.statelessCompare(step).Equal() { + // To avoid false-positives with non-reflexive equality operations, + // we sanity check whether a value is equal to itself. + if step.vx, step.vy = want, want; !s.statelessCompare(step).Equal() { + return want + } + panic(fmt.Sprintf("non-deterministic function detected: %s", function.NameOf(f))) + } + return want +} + +func (s *state) callTTBFunc(f, x, y reflect.Value) bool { + x = sanitizeValue(x, f.Type().In(0)) + y = sanitizeValue(y, f.Type().In(1)) + if !s.dynChecker.Next() { + return f.Call([]reflect.Value{x, y})[0].Bool() + } + + // Swapping the input arguments is sufficient to check that + // f is symmetric and deterministic. + // We run in goroutines so that the race detector (if enabled) can detect + // unsafe mutations to the input. + c := make(chan reflect.Value) + go detectRaces(c, f, y, x) + got := <-c + want := f.Call([]reflect.Value{x, y})[0].Bool() + if !got.IsValid() || got.Bool() != want { + panic(fmt.Sprintf("non-deterministic or non-symmetric function detected: %s", function.NameOf(f))) + } + return want +} + +func detectRaces(c chan<- reflect.Value, f reflect.Value, vs ...reflect.Value) { + var ret reflect.Value + defer func() { + recover() // Ignore panics, let the other call to f panic instead + c <- ret + }() + ret = f.Call(vs)[0] +} + +// sanitizeValue converts nil interfaces of type T to those of type R, +// assuming that T is assignable to R. +// Otherwise, it returns the input value as is. +func sanitizeValue(v reflect.Value, t reflect.Type) reflect.Value { + // TODO(≥go1.10): Workaround for reflect bug (https://golang.org/issue/22143). + if !flags.AtLeastGo110 { + if v.Kind() == reflect.Interface && v.IsNil() && v.Type() != t { + return reflect.New(t).Elem() + } + } + return v +} + +func (s *state) compareStruct(t reflect.Type, vx, vy reflect.Value) { + var addr bool + var vax, vay reflect.Value // Addressable versions of vx and vy + + var mayForce, mayForceInit bool + step := StructField{&structField{}} + for i := 0; i < t.NumField(); i++ { + step.typ = t.Field(i).Type + step.vx = vx.Field(i) + step.vy = vy.Field(i) + step.name = t.Field(i).Name + step.idx = i + step.unexported = !isExported(step.name) + if step.unexported { + if step.name == "_" { + continue + } + // Defer checking of unexported fields until later to give an + // Ignore a chance to ignore the field. + if !vax.IsValid() || !vay.IsValid() { + // For retrieveUnexportedField to work, the parent struct must + // be addressable. Create a new copy of the values if + // necessary to make them addressable. + addr = vx.CanAddr() || vy.CanAddr() + vax = makeAddressable(vx) + vay = makeAddressable(vy) + } + if !mayForceInit { + for _, xf := range s.exporters { + mayForce = mayForce || xf(t) + } + mayForceInit = true + } + step.mayForce = mayForce + step.paddr = addr + step.pvx = vax + step.pvy = vay + step.field = t.Field(i) + } + s.compareAny(step) + } +} + +func (s *state) compareSlice(t reflect.Type, vx, vy reflect.Value) { + isSlice := t.Kind() == reflect.Slice + if isSlice && (vx.IsNil() || vy.IsNil()) { + s.report(vx.IsNil() && vy.IsNil(), 0) + return + } + + // NOTE: It is incorrect to call curPtrs.Push on the slice header pointer + // since slices represents a list of pointers, rather than a single pointer. + // The pointer checking logic must be handled on a per-element basis + // in compareAny. + // + // A slice header (see reflect.SliceHeader) in Go is a tuple of a starting + // pointer P, a length N, and a capacity C. Supposing each slice element has + // a memory size of M, then the slice is equivalent to the list of pointers: + // [P+i*M for i in range(N)] + // + // For example, v[:0] and v[:1] are slices with the same starting pointer, + // but they are clearly different values. Using the slice pointer alone + // violates the assumption that equal pointers implies equal values. + + step := SliceIndex{&sliceIndex{pathStep: pathStep{typ: t.Elem()}, isSlice: isSlice}} + withIndexes := func(ix, iy int) SliceIndex { + if ix >= 0 { + step.vx, step.xkey = vx.Index(ix), ix + } else { + step.vx, step.xkey = reflect.Value{}, -1 + } + if iy >= 0 { + step.vy, step.ykey = vy.Index(iy), iy + } else { + step.vy, step.ykey = reflect.Value{}, -1 + } + return step + } + + // Ignore options are able to ignore missing elements in a slice. + // However, detecting these reliably requires an optimal differencing + // algorithm, for which diff.Difference is not. + // + // Instead, we first iterate through both slices to detect which elements + // would be ignored if standing alone. The index of non-discarded elements + // are stored in a separate slice, which diffing is then performed on. + var indexesX, indexesY []int + var ignoredX, ignoredY []bool + for ix := 0; ix < vx.Len(); ix++ { + ignored := s.statelessCompare(withIndexes(ix, -1)).NumDiff == 0 + if !ignored { + indexesX = append(indexesX, ix) + } + ignoredX = append(ignoredX, ignored) + } + for iy := 0; iy < vy.Len(); iy++ { + ignored := s.statelessCompare(withIndexes(-1, iy)).NumDiff == 0 + if !ignored { + indexesY = append(indexesY, iy) + } + ignoredY = append(ignoredY, ignored) + } + + // Compute an edit-script for slices vx and vy (excluding ignored elements). + edits := diff.Difference(len(indexesX), len(indexesY), func(ix, iy int) diff.Result { + return s.statelessCompare(withIndexes(indexesX[ix], indexesY[iy])) + }) + + // Replay the ignore-scripts and the edit-script. + var ix, iy int + for ix < vx.Len() || iy < vy.Len() { + var e diff.EditType + switch { + case ix < len(ignoredX) && ignoredX[ix]: + e = diff.UniqueX + case iy < len(ignoredY) && ignoredY[iy]: + e = diff.UniqueY + default: + e, edits = edits[0], edits[1:] + } + switch e { + case diff.UniqueX: + s.compareAny(withIndexes(ix, -1)) + ix++ + case diff.UniqueY: + s.compareAny(withIndexes(-1, iy)) + iy++ + default: + s.compareAny(withIndexes(ix, iy)) + ix++ + iy++ + } + } +} + +func (s *state) compareMap(t reflect.Type, vx, vy reflect.Value) { + if vx.IsNil() || vy.IsNil() { + s.report(vx.IsNil() && vy.IsNil(), 0) + return + } + + // Cycle-detection for maps. + if eq, visited := s.curPtrs.Push(vx, vy); visited { + s.report(eq, reportByCycle) + return + } + defer s.curPtrs.Pop(vx, vy) + + // We combine and sort the two map keys so that we can perform the + // comparisons in a deterministic order. + step := MapIndex{&mapIndex{pathStep: pathStep{typ: t.Elem()}}} + for _, k := range value.SortKeys(append(vx.MapKeys(), vy.MapKeys()...)) { + step.vx = vx.MapIndex(k) + step.vy = vy.MapIndex(k) + step.key = k + if !step.vx.IsValid() && !step.vy.IsValid() { + // It is possible for both vx and vy to be invalid if the + // key contained a NaN value in it. + // + // Even with the ability to retrieve NaN keys in Go 1.12, + // there still isn't a sensible way to compare the values since + // a NaN key may map to multiple unordered values. + // The most reasonable way to compare NaNs would be to compare the + // set of values. However, this is impossible to do efficiently + // since set equality is provably an O(n^2) operation given only + // an Equal function. If we had a Less function or Hash function, + // this could be done in O(n*log(n)) or O(n), respectively. + // + // Rather than adding complex logic to deal with NaNs, make it + // the user's responsibility to compare such obscure maps. + const help = "consider providing a Comparer to compare the map" + panic(fmt.Sprintf("%#v has map key with NaNs\n%s", s.curPath, help)) + } + s.compareAny(step) + } +} + +func (s *state) comparePtr(t reflect.Type, vx, vy reflect.Value) { + if vx.IsNil() || vy.IsNil() { + s.report(vx.IsNil() && vy.IsNil(), 0) + return + } + + // Cycle-detection for pointers. + if eq, visited := s.curPtrs.Push(vx, vy); visited { + s.report(eq, reportByCycle) + return + } + defer s.curPtrs.Pop(vx, vy) + + vx, vy = vx.Elem(), vy.Elem() + s.compareAny(Indirect{&indirect{pathStep{t.Elem(), vx, vy}}}) +} + +func (s *state) compareInterface(t reflect.Type, vx, vy reflect.Value) { + if vx.IsNil() || vy.IsNil() { + s.report(vx.IsNil() && vy.IsNil(), 0) + return + } + vx, vy = vx.Elem(), vy.Elem() + if vx.Type() != vy.Type() { + s.report(false, 0) + return + } + s.compareAny(TypeAssertion{&typeAssertion{pathStep{vx.Type(), vx, vy}}}) +} + +func (s *state) report(eq bool, rf resultFlags) { + if rf&reportByIgnore == 0 { + if eq { + s.result.NumSame++ + rf |= reportEqual + } else { + s.result.NumDiff++ + rf |= reportUnequal + } + } + for _, r := range s.reporters { + r.Report(Result{flags: rf}) + } +} + +// recChecker tracks the state needed to periodically perform checks that +// user provided transformers are not stuck in an infinitely recursive cycle. +type recChecker struct{ next int } + +// Check scans the Path for any recursive transformers and panics when any +// recursive transformers are detected. Note that the presence of a +// recursive Transformer does not necessarily imply an infinite cycle. +// As such, this check only activates after some minimal number of path steps. +func (rc *recChecker) Check(p Path) { + const minLen = 1 << 16 + if rc.next == 0 { + rc.next = minLen + } + if len(p) < rc.next { + return + } + rc.next <<= 1 + + // Check whether the same transformer has appeared at least twice. + var ss []string + m := map[Option]int{} + for _, ps := range p { + if t, ok := ps.(Transform); ok { + t := t.Option() + if m[t] == 1 { // Transformer was used exactly once before + tf := t.(*transformer).fnc.Type() + ss = append(ss, fmt.Sprintf("%v: %v => %v", t, tf.In(0), tf.Out(0))) + } + m[t]++ + } + } + if len(ss) > 0 { + const warning = "recursive set of Transformers detected" + const help = "consider using cmpopts.AcyclicTransformer" + set := strings.Join(ss, "\n\t") + panic(fmt.Sprintf("%s:\n\t%s\n%s", warning, set, help)) + } +} + +// dynChecker tracks the state needed to periodically perform checks that +// user provided functions are symmetric and deterministic. +// The zero value is safe for immediate use. +type dynChecker struct{ curr, next int } + +// Next increments the state and reports whether a check should be performed. +// +// Checks occur every Nth function call, where N is a triangular number: +// 0 1 3 6 10 15 21 28 36 45 55 66 78 91 105 120 136 153 171 190 ... +// See https://en.wikipedia.org/wiki/Triangular_number +// +// This sequence ensures that the cost of checks drops significantly as +// the number of functions calls grows larger. +func (dc *dynChecker) Next() bool { + ok := dc.curr == dc.next + if ok { + dc.curr = 0 + dc.next++ + } + dc.curr++ + return ok +} + +// makeAddressable returns a value that is always addressable. +// It returns the input verbatim if it is already addressable, +// otherwise it creates a new value and returns an addressable copy. +func makeAddressable(v reflect.Value) reflect.Value { + if v.CanAddr() { + return v + } + vc := reflect.New(v.Type()).Elem() + vc.Set(v) + return vc +} diff --git a/vendor/github.com/google/go-cmp/cmp/export_panic.go b/vendor/github.com/google/go-cmp/cmp/export_panic.go new file mode 100644 index 00000000..dfa5d213 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/export_panic.go @@ -0,0 +1,15 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +// +build purego + +package cmp + +import "reflect" + +const supportExporters = false + +func retrieveUnexportedField(reflect.Value, reflect.StructField, bool) reflect.Value { + panic("no support for forcibly accessing unexported fields") +} diff --git a/vendor/github.com/google/go-cmp/cmp/export_unsafe.go b/vendor/github.com/google/go-cmp/cmp/export_unsafe.go new file mode 100644 index 00000000..351f1a34 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/export_unsafe.go @@ -0,0 +1,35 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +// +build !purego + +package cmp + +import ( + "reflect" + "unsafe" +) + +const supportExporters = true + +// retrieveUnexportedField uses unsafe to forcibly retrieve any field from +// a struct such that the value has read-write permissions. +// +// The parent struct, v, must be addressable, while f must be a StructField +// describing the field to retrieve. If addr is false, +// then the returned value will be shallowed copied to be non-addressable. +func retrieveUnexportedField(v reflect.Value, f reflect.StructField, addr bool) reflect.Value { + ve := reflect.NewAt(f.Type, unsafe.Pointer(uintptr(unsafe.Pointer(v.UnsafeAddr()))+f.Offset)).Elem() + if !addr { + // A field is addressable if and only if the struct is addressable. + // If the original parent value was not addressable, shallow copy the + // value to make it non-addressable to avoid leaking an implementation + // detail of how forcibly exporting a field works. + if ve.Kind() == reflect.Interface && ve.IsNil() { + return reflect.Zero(f.Type) + } + return reflect.ValueOf(ve.Interface()).Convert(f.Type) + } + return ve +} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go new file mode 100644 index 00000000..fe98dcc6 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go @@ -0,0 +1,17 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +// +build !cmp_debug + +package diff + +var debug debugger + +type debugger struct{} + +func (debugger) Begin(_, _ int, f EqualFunc, _, _ *EditScript) EqualFunc { + return f +} +func (debugger) Update() {} +func (debugger) Finish() {} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go new file mode 100644 index 00000000..597b6ae5 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go @@ -0,0 +1,122 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +// +build cmp_debug + +package diff + +import ( + "fmt" + "strings" + "sync" + "time" +) + +// The algorithm can be seen running in real-time by enabling debugging: +// go test -tags=cmp_debug -v +// +// Example output: +// === RUN TestDifference/#34 +// ┌───────────────────────────────┐ +// │ \ · · · · · · · · · · · · · · │ +// │ · # · · · · · · · · · · · · · │ +// │ · \ · · · · · · · · · · · · · │ +// │ · · \ · · · · · · · · · · · · │ +// │ · · · X # · · · · · · · · · · │ +// │ · · · # \ · · · · · · · · · · │ +// │ · · · · · # # · · · · · · · · │ +// │ · · · · · # \ · · · · · · · · │ +// │ · · · · · · · \ · · · · · · · │ +// │ · · · · · · · · \ · · · · · · │ +// │ · · · · · · · · · \ · · · · · │ +// │ · · · · · · · · · · \ · · # · │ +// │ · · · · · · · · · · · \ # # · │ +// │ · · · · · · · · · · · # # # · │ +// │ · · · · · · · · · · # # # # · │ +// │ · · · · · · · · · # # # # # · │ +// │ · · · · · · · · · · · · · · \ │ +// └───────────────────────────────┘ +// [.Y..M.XY......YXYXY.|] +// +// The grid represents the edit-graph where the horizontal axis represents +// list X and the vertical axis represents list Y. The start of the two lists +// is the top-left, while the ends are the bottom-right. The '·' represents +// an unexplored node in the graph. The '\' indicates that the two symbols +// from list X and Y are equal. The 'X' indicates that two symbols are similar +// (but not exactly equal) to each other. The '#' indicates that the two symbols +// are different (and not similar). The algorithm traverses this graph trying to +// make the paths starting in the top-left and the bottom-right connect. +// +// The series of '.', 'X', 'Y', and 'M' characters at the bottom represents +// the currently established path from the forward and reverse searches, +// separated by a '|' character. + +const ( + updateDelay = 100 * time.Millisecond + finishDelay = 500 * time.Millisecond + ansiTerminal = true // ANSI escape codes used to move terminal cursor +) + +var debug debugger + +type debugger struct { + sync.Mutex + p1, p2 EditScript + fwdPath, revPath *EditScript + grid []byte + lines int +} + +func (dbg *debugger) Begin(nx, ny int, f EqualFunc, p1, p2 *EditScript) EqualFunc { + dbg.Lock() + dbg.fwdPath, dbg.revPath = p1, p2 + top := "┌─" + strings.Repeat("──", nx) + "┐\n" + row := "│ " + strings.Repeat("· ", nx) + "│\n" + btm := "└─" + strings.Repeat("──", nx) + "┘\n" + dbg.grid = []byte(top + strings.Repeat(row, ny) + btm) + dbg.lines = strings.Count(dbg.String(), "\n") + fmt.Print(dbg) + + // Wrap the EqualFunc so that we can intercept each result. + return func(ix, iy int) (r Result) { + cell := dbg.grid[len(top)+iy*len(row):][len("│ ")+len("· ")*ix:][:len("·")] + for i := range cell { + cell[i] = 0 // Zero out the multiple bytes of UTF-8 middle-dot + } + switch r = f(ix, iy); { + case r.Equal(): + cell[0] = '\\' + case r.Similar(): + cell[0] = 'X' + default: + cell[0] = '#' + } + return + } +} + +func (dbg *debugger) Update() { + dbg.print(updateDelay) +} + +func (dbg *debugger) Finish() { + dbg.print(finishDelay) + dbg.Unlock() +} + +func (dbg *debugger) String() string { + dbg.p1, dbg.p2 = *dbg.fwdPath, dbg.p2[:0] + for i := len(*dbg.revPath) - 1; i >= 0; i-- { + dbg.p2 = append(dbg.p2, (*dbg.revPath)[i]) + } + return fmt.Sprintf("%s[%v|%v]\n\n", dbg.grid, dbg.p1, dbg.p2) +} + +func (dbg *debugger) print(d time.Duration) { + if ansiTerminal { + fmt.Printf("\x1b[%dA", dbg.lines) // Reset terminal cursor + } + fmt.Print(dbg) + time.Sleep(d) +} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go b/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go new file mode 100644 index 00000000..730e223e --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go @@ -0,0 +1,392 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +// Package diff implements an algorithm for producing edit-scripts. +// The edit-script is a sequence of operations needed to transform one list +// of symbols into another (or vice-versa). The edits allowed are insertions, +// deletions, and modifications. The summation of all edits is called the +// Levenshtein distance as this problem is well-known in computer science. +// +// This package prioritizes performance over accuracy. That is, the run time +// is more important than obtaining a minimal Levenshtein distance. +package diff + +import ( + "math/rand" + "time" + + "github.com/google/go-cmp/cmp/internal/flags" +) + +// EditType represents a single operation within an edit-script. +type EditType uint8 + +const ( + // Identity indicates that a symbol pair is identical in both list X and Y. + Identity EditType = iota + // UniqueX indicates that a symbol only exists in X and not Y. + UniqueX + // UniqueY indicates that a symbol only exists in Y and not X. + UniqueY + // Modified indicates that a symbol pair is a modification of each other. + Modified +) + +// EditScript represents the series of differences between two lists. +type EditScript []EditType + +// String returns a human-readable string representing the edit-script where +// Identity, UniqueX, UniqueY, and Modified are represented by the +// '.', 'X', 'Y', and 'M' characters, respectively. +func (es EditScript) String() string { + b := make([]byte, len(es)) + for i, e := range es { + switch e { + case Identity: + b[i] = '.' + case UniqueX: + b[i] = 'X' + case UniqueY: + b[i] = 'Y' + case Modified: + b[i] = 'M' + default: + panic("invalid edit-type") + } + } + return string(b) +} + +// stats returns a histogram of the number of each type of edit operation. +func (es EditScript) stats() (s struct{ NI, NX, NY, NM int }) { + for _, e := range es { + switch e { + case Identity: + s.NI++ + case UniqueX: + s.NX++ + case UniqueY: + s.NY++ + case Modified: + s.NM++ + default: + panic("invalid edit-type") + } + } + return +} + +// Dist is the Levenshtein distance and is guaranteed to be 0 if and only if +// lists X and Y are equal. +func (es EditScript) Dist() int { return len(es) - es.stats().NI } + +// LenX is the length of the X list. +func (es EditScript) LenX() int { return len(es) - es.stats().NY } + +// LenY is the length of the Y list. +func (es EditScript) LenY() int { return len(es) - es.stats().NX } + +// EqualFunc reports whether the symbols at indexes ix and iy are equal. +// When called by Difference, the index is guaranteed to be within nx and ny. +type EqualFunc func(ix int, iy int) Result + +// Result is the result of comparison. +// NumSame is the number of sub-elements that are equal. +// NumDiff is the number of sub-elements that are not equal. +type Result struct{ NumSame, NumDiff int } + +// BoolResult returns a Result that is either Equal or not Equal. +func BoolResult(b bool) Result { + if b { + return Result{NumSame: 1} // Equal, Similar + } else { + return Result{NumDiff: 2} // Not Equal, not Similar + } +} + +// Equal indicates whether the symbols are equal. Two symbols are equal +// if and only if NumDiff == 0. If Equal, then they are also Similar. +func (r Result) Equal() bool { return r.NumDiff == 0 } + +// Similar indicates whether two symbols are similar and may be represented +// by using the Modified type. As a special case, we consider binary comparisons +// (i.e., those that return Result{1, 0} or Result{0, 1}) to be similar. +// +// The exact ratio of NumSame to NumDiff to determine similarity may change. +func (r Result) Similar() bool { + // Use NumSame+1 to offset NumSame so that binary comparisons are similar. + return r.NumSame+1 >= r.NumDiff +} + +var randInt = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) + +// Difference reports whether two lists of lengths nx and ny are equal +// given the definition of equality provided as f. +// +// This function returns an edit-script, which is a sequence of operations +// needed to convert one list into the other. The following invariants for +// the edit-script are maintained: +// • eq == (es.Dist()==0) +// • nx == es.LenX() +// • ny == es.LenY() +// +// This algorithm is not guaranteed to be an optimal solution (i.e., one that +// produces an edit-script with a minimal Levenshtein distance). This algorithm +// favors performance over optimality. The exact output is not guaranteed to +// be stable and may change over time. +func Difference(nx, ny int, f EqualFunc) (es EditScript) { + // This algorithm is based on traversing what is known as an "edit-graph". + // See Figure 1 from "An O(ND) Difference Algorithm and Its Variations" + // by Eugene W. Myers. Since D can be as large as N itself, this is + // effectively O(N^2). Unlike the algorithm from that paper, we are not + // interested in the optimal path, but at least some "decent" path. + // + // For example, let X and Y be lists of symbols: + // X = [A B C A B B A] + // Y = [C B A B A C] + // + // The edit-graph can be drawn as the following: + // A B C A B B A + // ┌─────────────┐ + // C │_|_|\|_|_|_|_│ 0 + // B │_|\|_|_|\|\|_│ 1 + // A │\|_|_|\|_|_|\│ 2 + // B │_|\|_|_|\|\|_│ 3 + // A │\|_|_|\|_|_|\│ 4 + // C │ | |\| | | | │ 5 + // └─────────────┘ 6 + // 0 1 2 3 4 5 6 7 + // + // List X is written along the horizontal axis, while list Y is written + // along the vertical axis. At any point on this grid, if the symbol in + // list X matches the corresponding symbol in list Y, then a '\' is drawn. + // The goal of any minimal edit-script algorithm is to find a path from the + // top-left corner to the bottom-right corner, while traveling through the + // fewest horizontal or vertical edges. + // A horizontal edge is equivalent to inserting a symbol from list X. + // A vertical edge is equivalent to inserting a symbol from list Y. + // A diagonal edge is equivalent to a matching symbol between both X and Y. + + // To ensure flexibility in changing the algorithm in the future, + // introduce some degree of deliberate instability. + // This is achieved by fiddling the zigzag iterator to start searching + // the graph starting from the bottom-right versus than the top-left. + // The result may differ depending on the starting search location, + // but still produces a valid edit script. + zigzagInit := randInt // either 0 or 1 + if flags.Deterministic { + zigzagInit = 0 + } + + // Invariants: + // • 0 ≤ fwdPath.X ≤ (fwdFrontier.X, revFrontier.X) ≤ revPath.X ≤ nx + // • 0 ≤ fwdPath.Y ≤ (fwdFrontier.Y, revFrontier.Y) ≤ revPath.Y ≤ ny + // + // In general: + // • fwdFrontier.X < revFrontier.X + // • fwdFrontier.Y < revFrontier.Y + // Unless, it is time for the algorithm to terminate. + fwdPath := path{+1, point{0, 0}, make(EditScript, 0, (nx+ny)/2)} + revPath := path{-1, point{nx, ny}, make(EditScript, 0)} + fwdFrontier := fwdPath.point // Forward search frontier + revFrontier := revPath.point // Reverse search frontier + + // Search budget bounds the cost of searching for better paths. + // The longest sequence of non-matching symbols that can be tolerated is + // approximately the square-root of the search budget. + searchBudget := 4 * (nx + ny) // O(n) + + // The algorithm below is a greedy, meet-in-the-middle algorithm for + // computing sub-optimal edit-scripts between two lists. + // + // The algorithm is approximately as follows: + // • Searching for differences switches back-and-forth between + // a search that starts at the beginning (the top-left corner), and + // a search that starts at the end (the bottom-right corner). The goal of + // the search is connect with the search from the opposite corner. + // • As we search, we build a path in a greedy manner, where the first + // match seen is added to the path (this is sub-optimal, but provides a + // decent result in practice). When matches are found, we try the next pair + // of symbols in the lists and follow all matches as far as possible. + // • When searching for matches, we search along a diagonal going through + // through the "frontier" point. If no matches are found, we advance the + // frontier towards the opposite corner. + // • This algorithm terminates when either the X coordinates or the + // Y coordinates of the forward and reverse frontier points ever intersect. + // + // This algorithm is correct even if searching only in the forward direction + // or in the reverse direction. We do both because it is commonly observed + // that two lists commonly differ because elements were added to the front + // or end of the other list. + // + // Running the tests with the "cmp_debug" build tag prints a visualization + // of the algorithm running in real-time. This is educational for + // understanding how the algorithm works. See debug_enable.go. + f = debug.Begin(nx, ny, f, &fwdPath.es, &revPath.es) + for { + // Forward search from the beginning. + if fwdFrontier.X >= revFrontier.X || fwdFrontier.Y >= revFrontier.Y || searchBudget == 0 { + break + } + for stop1, stop2, i := false, false, zigzagInit; !(stop1 && stop2) && searchBudget > 0; i++ { + // Search in a diagonal pattern for a match. + z := zigzag(i) + p := point{fwdFrontier.X + z, fwdFrontier.Y - z} + switch { + case p.X >= revPath.X || p.Y < fwdPath.Y: + stop1 = true // Hit top-right corner + case p.Y >= revPath.Y || p.X < fwdPath.X: + stop2 = true // Hit bottom-left corner + case f(p.X, p.Y).Equal(): + // Match found, so connect the path to this point. + fwdPath.connect(p, f) + fwdPath.append(Identity) + // Follow sequence of matches as far as possible. + for fwdPath.X < revPath.X && fwdPath.Y < revPath.Y { + if !f(fwdPath.X, fwdPath.Y).Equal() { + break + } + fwdPath.append(Identity) + } + fwdFrontier = fwdPath.point + stop1, stop2 = true, true + default: + searchBudget-- // Match not found + } + debug.Update() + } + // Advance the frontier towards reverse point. + if revPath.X-fwdFrontier.X >= revPath.Y-fwdFrontier.Y { + fwdFrontier.X++ + } else { + fwdFrontier.Y++ + } + + // Reverse search from the end. + if fwdFrontier.X >= revFrontier.X || fwdFrontier.Y >= revFrontier.Y || searchBudget == 0 { + break + } + for stop1, stop2, i := false, false, 0; !(stop1 && stop2) && searchBudget > 0; i++ { + // Search in a diagonal pattern for a match. + z := zigzag(i) + p := point{revFrontier.X - z, revFrontier.Y + z} + switch { + case fwdPath.X >= p.X || revPath.Y < p.Y: + stop1 = true // Hit bottom-left corner + case fwdPath.Y >= p.Y || revPath.X < p.X: + stop2 = true // Hit top-right corner + case f(p.X-1, p.Y-1).Equal(): + // Match found, so connect the path to this point. + revPath.connect(p, f) + revPath.append(Identity) + // Follow sequence of matches as far as possible. + for fwdPath.X < revPath.X && fwdPath.Y < revPath.Y { + if !f(revPath.X-1, revPath.Y-1).Equal() { + break + } + revPath.append(Identity) + } + revFrontier = revPath.point + stop1, stop2 = true, true + default: + searchBudget-- // Match not found + } + debug.Update() + } + // Advance the frontier towards forward point. + if revFrontier.X-fwdPath.X >= revFrontier.Y-fwdPath.Y { + revFrontier.X-- + } else { + revFrontier.Y-- + } + } + + // Join the forward and reverse paths and then append the reverse path. + fwdPath.connect(revPath.point, f) + for i := len(revPath.es) - 1; i >= 0; i-- { + t := revPath.es[i] + revPath.es = revPath.es[:i] + fwdPath.append(t) + } + debug.Finish() + return fwdPath.es +} + +type path struct { + dir int // +1 if forward, -1 if reverse + point // Leading point of the EditScript path + es EditScript +} + +// connect appends any necessary Identity, Modified, UniqueX, or UniqueY types +// to the edit-script to connect p.point to dst. +func (p *path) connect(dst point, f EqualFunc) { + if p.dir > 0 { + // Connect in forward direction. + for dst.X > p.X && dst.Y > p.Y { + switch r := f(p.X, p.Y); { + case r.Equal(): + p.append(Identity) + case r.Similar(): + p.append(Modified) + case dst.X-p.X >= dst.Y-p.Y: + p.append(UniqueX) + default: + p.append(UniqueY) + } + } + for dst.X > p.X { + p.append(UniqueX) + } + for dst.Y > p.Y { + p.append(UniqueY) + } + } else { + // Connect in reverse direction. + for p.X > dst.X && p.Y > dst.Y { + switch r := f(p.X-1, p.Y-1); { + case r.Equal(): + p.append(Identity) + case r.Similar(): + p.append(Modified) + case p.Y-dst.Y >= p.X-dst.X: + p.append(UniqueY) + default: + p.append(UniqueX) + } + } + for p.X > dst.X { + p.append(UniqueX) + } + for p.Y > dst.Y { + p.append(UniqueY) + } + } +} + +func (p *path) append(t EditType) { + p.es = append(p.es, t) + switch t { + case Identity, Modified: + p.add(p.dir, p.dir) + case UniqueX: + p.add(p.dir, 0) + case UniqueY: + p.add(0, p.dir) + } + debug.Update() +} + +type point struct{ X, Y int } + +func (p *point) add(dx, dy int) { p.X += dx; p.Y += dy } + +// zigzag maps a consecutive sequence of integers to a zig-zag sequence. +// [0 1 2 3 4 5 ...] => [0 -1 +1 -2 +2 ...] +func zigzag(x int) int { + if x&1 != 0 { + x = ^x + } + return x >> 1 +} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go b/vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go new file mode 100644 index 00000000..a9e7fc0b --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go @@ -0,0 +1,9 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +package flags + +// Deterministic controls whether the output of Diff should be deterministic. +// This is only used for testing. +var Deterministic bool diff --git a/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_legacy.go b/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_legacy.go new file mode 100644 index 00000000..01aed0a1 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_legacy.go @@ -0,0 +1,10 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +// +build !go1.10 + +package flags + +// AtLeastGo110 reports whether the Go toolchain is at least Go 1.10. +const AtLeastGo110 = false diff --git a/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_recent.go b/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_recent.go new file mode 100644 index 00000000..c0b667f5 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_recent.go @@ -0,0 +1,10 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +// +build go1.10 + +package flags + +// AtLeastGo110 reports whether the Go toolchain is at least Go 1.10. +const AtLeastGo110 = true diff --git a/vendor/github.com/google/go-cmp/cmp/internal/function/func.go b/vendor/github.com/google/go-cmp/cmp/internal/function/func.go new file mode 100644 index 00000000..ace1dbe8 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/function/func.go @@ -0,0 +1,99 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +// Package function provides functionality for identifying function types. +package function + +import ( + "reflect" + "regexp" + "runtime" + "strings" +) + +type funcType int + +const ( + _ funcType = iota + + tbFunc // func(T) bool + ttbFunc // func(T, T) bool + trbFunc // func(T, R) bool + tibFunc // func(T, I) bool + trFunc // func(T) R + + Equal = ttbFunc // func(T, T) bool + EqualAssignable = tibFunc // func(T, I) bool; encapsulates func(T, T) bool + Transformer = trFunc // func(T) R + ValueFilter = ttbFunc // func(T, T) bool + Less = ttbFunc // func(T, T) bool + ValuePredicate = tbFunc // func(T) bool + KeyValuePredicate = trbFunc // func(T, R) bool +) + +var boolType = reflect.TypeOf(true) + +// IsType reports whether the reflect.Type is of the specified function type. +func IsType(t reflect.Type, ft funcType) bool { + if t == nil || t.Kind() != reflect.Func || t.IsVariadic() { + return false + } + ni, no := t.NumIn(), t.NumOut() + switch ft { + case tbFunc: // func(T) bool + if ni == 1 && no == 1 && t.Out(0) == boolType { + return true + } + case ttbFunc: // func(T, T) bool + if ni == 2 && no == 1 && t.In(0) == t.In(1) && t.Out(0) == boolType { + return true + } + case trbFunc: // func(T, R) bool + if ni == 2 && no == 1 && t.Out(0) == boolType { + return true + } + case tibFunc: // func(T, I) bool + if ni == 2 && no == 1 && t.In(0).AssignableTo(t.In(1)) && t.Out(0) == boolType { + return true + } + case trFunc: // func(T) R + if ni == 1 && no == 1 { + return true + } + } + return false +} + +var lastIdentRx = regexp.MustCompile(`[_\p{L}][_\p{L}\p{N}]*$`) + +// NameOf returns the name of the function value. +func NameOf(v reflect.Value) string { + fnc := runtime.FuncForPC(v.Pointer()) + if fnc == nil { + return "" + } + fullName := fnc.Name() // e.g., "long/path/name/mypkg.(*MyType).(long/path/name/mypkg.myMethod)-fm" + + // Method closures have a "-fm" suffix. + fullName = strings.TrimSuffix(fullName, "-fm") + + var name string + for len(fullName) > 0 { + inParen := strings.HasSuffix(fullName, ")") + fullName = strings.TrimSuffix(fullName, ")") + + s := lastIdentRx.FindString(fullName) + if s == "" { + break + } + name = s + "." + name + fullName = strings.TrimSuffix(fullName, s) + + if i := strings.LastIndexByte(fullName, '('); inParen && i >= 0 { + fullName = fullName[:i] + } + fullName = strings.TrimSuffix(fullName, ".") + } + return strings.TrimSuffix(name, ".") +} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/name.go b/vendor/github.com/google/go-cmp/cmp/internal/value/name.go new file mode 100644 index 00000000..8228e7d5 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/name.go @@ -0,0 +1,157 @@ +// Copyright 2020, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +package value + +import ( + "reflect" + "strconv" +) + +// TypeString is nearly identical to reflect.Type.String, +// but has an additional option to specify that full type names be used. +func TypeString(t reflect.Type, qualified bool) string { + return string(appendTypeName(nil, t, qualified, false)) +} + +func appendTypeName(b []byte, t reflect.Type, qualified, elideFunc bool) []byte { + // BUG: Go reflection provides no way to disambiguate two named types + // of the same name and within the same package, + // but declared within the namespace of different functions. + + // Named type. + if t.Name() != "" { + if qualified && t.PkgPath() != "" { + b = append(b, '"') + b = append(b, t.PkgPath()...) + b = append(b, '"') + b = append(b, '.') + b = append(b, t.Name()...) + } else { + b = append(b, t.String()...) + } + return b + } + + // Unnamed type. + switch k := t.Kind(); k { + case reflect.Bool, reflect.String, reflect.UnsafePointer, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, + reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: + b = append(b, k.String()...) + case reflect.Chan: + if t.ChanDir() == reflect.RecvDir { + b = append(b, "<-"...) + } + b = append(b, "chan"...) + if t.ChanDir() == reflect.SendDir { + b = append(b, "<-"...) + } + b = append(b, ' ') + b = appendTypeName(b, t.Elem(), qualified, false) + case reflect.Func: + if !elideFunc { + b = append(b, "func"...) + } + b = append(b, '(') + for i := 0; i < t.NumIn(); i++ { + if i > 0 { + b = append(b, ", "...) + } + if i == t.NumIn()-1 && t.IsVariadic() { + b = append(b, "..."...) + b = appendTypeName(b, t.In(i).Elem(), qualified, false) + } else { + b = appendTypeName(b, t.In(i), qualified, false) + } + } + b = append(b, ')') + switch t.NumOut() { + case 0: + // Do nothing + case 1: + b = append(b, ' ') + b = appendTypeName(b, t.Out(0), qualified, false) + default: + b = append(b, " ("...) + for i := 0; i < t.NumOut(); i++ { + if i > 0 { + b = append(b, ", "...) + } + b = appendTypeName(b, t.Out(i), qualified, false) + } + b = append(b, ')') + } + case reflect.Struct: + b = append(b, "struct{ "...) + for i := 0; i < t.NumField(); i++ { + if i > 0 { + b = append(b, "; "...) + } + sf := t.Field(i) + if !sf.Anonymous { + if qualified && sf.PkgPath != "" { + b = append(b, '"') + b = append(b, sf.PkgPath...) + b = append(b, '"') + b = append(b, '.') + } + b = append(b, sf.Name...) + b = append(b, ' ') + } + b = appendTypeName(b, sf.Type, qualified, false) + if sf.Tag != "" { + b = append(b, ' ') + b = strconv.AppendQuote(b, string(sf.Tag)) + } + } + if b[len(b)-1] == ' ' { + b = b[:len(b)-1] + } else { + b = append(b, ' ') + } + b = append(b, '}') + case reflect.Slice, reflect.Array: + b = append(b, '[') + if k == reflect.Array { + b = strconv.AppendUint(b, uint64(t.Len()), 10) + } + b = append(b, ']') + b = appendTypeName(b, t.Elem(), qualified, false) + case reflect.Map: + b = append(b, "map["...) + b = appendTypeName(b, t.Key(), qualified, false) + b = append(b, ']') + b = appendTypeName(b, t.Elem(), qualified, false) + case reflect.Ptr: + b = append(b, '*') + b = appendTypeName(b, t.Elem(), qualified, false) + case reflect.Interface: + b = append(b, "interface{ "...) + for i := 0; i < t.NumMethod(); i++ { + if i > 0 { + b = append(b, "; "...) + } + m := t.Method(i) + if qualified && m.PkgPath != "" { + b = append(b, '"') + b = append(b, m.PkgPath...) + b = append(b, '"') + b = append(b, '.') + } + b = append(b, m.Name...) + b = appendTypeName(b, m.Type, qualified, true) + } + if b[len(b)-1] == ' ' { + b = b[:len(b)-1] + } else { + b = append(b, ' ') + } + b = append(b, '}') + default: + panic("invalid kind: " + k.String()) + } + return b +} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go new file mode 100644 index 00000000..e9e384a1 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go @@ -0,0 +1,33 @@ +// Copyright 2018, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +// +build purego + +package value + +import "reflect" + +// Pointer is an opaque typed pointer and is guaranteed to be comparable. +type Pointer struct { + p uintptr + t reflect.Type +} + +// PointerOf returns a Pointer from v, which must be a +// reflect.Ptr, reflect.Slice, or reflect.Map. +func PointerOf(v reflect.Value) Pointer { + // NOTE: Storing a pointer as an uintptr is technically incorrect as it + // assumes that the GC implementation does not use a moving collector. + return Pointer{v.Pointer(), v.Type()} +} + +// IsNil reports whether the pointer is nil. +func (p Pointer) IsNil() bool { + return p.p == 0 +} + +// Uintptr returns the pointer as a uintptr. +func (p Pointer) Uintptr() uintptr { + return p.p +} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go new file mode 100644 index 00000000..b50c17ec --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go @@ -0,0 +1,36 @@ +// Copyright 2018, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +// +build !purego + +package value + +import ( + "reflect" + "unsafe" +) + +// Pointer is an opaque typed pointer and is guaranteed to be comparable. +type Pointer struct { + p unsafe.Pointer + t reflect.Type +} + +// PointerOf returns a Pointer from v, which must be a +// reflect.Ptr, reflect.Slice, or reflect.Map. +func PointerOf(v reflect.Value) Pointer { + // The proper representation of a pointer is unsafe.Pointer, + // which is necessary if the GC ever uses a moving collector. + return Pointer{unsafe.Pointer(v.Pointer()), v.Type()} +} + +// IsNil reports whether the pointer is nil. +func (p Pointer) IsNil() bool { + return p.p == nil +} + +// Uintptr returns the pointer as a uintptr. +func (p Pointer) Uintptr() uintptr { + return uintptr(p.p) +} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go b/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go new file mode 100644 index 00000000..24fbae6e --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go @@ -0,0 +1,106 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +package value + +import ( + "fmt" + "math" + "reflect" + "sort" +) + +// SortKeys sorts a list of map keys, deduplicating keys if necessary. +// The type of each value must be comparable. +func SortKeys(vs []reflect.Value) []reflect.Value { + if len(vs) == 0 { + return vs + } + + // Sort the map keys. + sort.SliceStable(vs, func(i, j int) bool { return isLess(vs[i], vs[j]) }) + + // Deduplicate keys (fails for NaNs). + vs2 := vs[:1] + for _, v := range vs[1:] { + if isLess(vs2[len(vs2)-1], v) { + vs2 = append(vs2, v) + } + } + return vs2 +} + +// isLess is a generic function for sorting arbitrary map keys. +// The inputs must be of the same type and must be comparable. +func isLess(x, y reflect.Value) bool { + switch x.Type().Kind() { + case reflect.Bool: + return !x.Bool() && y.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return x.Int() < y.Int() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return x.Uint() < y.Uint() + case reflect.Float32, reflect.Float64: + // NOTE: This does not sort -0 as less than +0 + // since Go maps treat -0 and +0 as equal keys. + fx, fy := x.Float(), y.Float() + return fx < fy || math.IsNaN(fx) && !math.IsNaN(fy) + case reflect.Complex64, reflect.Complex128: + cx, cy := x.Complex(), y.Complex() + rx, ix, ry, iy := real(cx), imag(cx), real(cy), imag(cy) + if rx == ry || (math.IsNaN(rx) && math.IsNaN(ry)) { + return ix < iy || math.IsNaN(ix) && !math.IsNaN(iy) + } + return rx < ry || math.IsNaN(rx) && !math.IsNaN(ry) + case reflect.Ptr, reflect.UnsafePointer, reflect.Chan: + return x.Pointer() < y.Pointer() + case reflect.String: + return x.String() < y.String() + case reflect.Array: + for i := 0; i < x.Len(); i++ { + if isLess(x.Index(i), y.Index(i)) { + return true + } + if isLess(y.Index(i), x.Index(i)) { + return false + } + } + return false + case reflect.Struct: + for i := 0; i < x.NumField(); i++ { + if isLess(x.Field(i), y.Field(i)) { + return true + } + if isLess(y.Field(i), x.Field(i)) { + return false + } + } + return false + case reflect.Interface: + vx, vy := x.Elem(), y.Elem() + if !vx.IsValid() || !vy.IsValid() { + return !vx.IsValid() && vy.IsValid() + } + tx, ty := vx.Type(), vy.Type() + if tx == ty { + return isLess(x.Elem(), y.Elem()) + } + if tx.Kind() != ty.Kind() { + return vx.Kind() < vy.Kind() + } + if tx.String() != ty.String() { + return tx.String() < ty.String() + } + if tx.PkgPath() != ty.PkgPath() { + return tx.PkgPath() < ty.PkgPath() + } + // This can happen in rare situations, so we fallback to just comparing + // the unique pointer for a reflect.Type. This guarantees deterministic + // ordering within a program, but it is obviously not stable. + return reflect.ValueOf(vx.Type()).Pointer() < reflect.ValueOf(vy.Type()).Pointer() + default: + // Must be Func, Map, or Slice; which are not comparable. + panic(fmt.Sprintf("%T is not comparable", x.Type())) + } +} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/zero.go b/vendor/github.com/google/go-cmp/cmp/internal/value/zero.go new file mode 100644 index 00000000..06a8ffd0 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/zero.go @@ -0,0 +1,48 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +package value + +import ( + "math" + "reflect" +) + +// IsZero reports whether v is the zero value. +// This does not rely on Interface and so can be used on unexported fields. +func IsZero(v reflect.Value) bool { + switch v.Kind() { + case reflect.Bool: + return v.Bool() == false + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return math.Float64bits(v.Float()) == 0 + case reflect.Complex64, reflect.Complex128: + return math.Float64bits(real(v.Complex())) == 0 && math.Float64bits(imag(v.Complex())) == 0 + case reflect.String: + return v.String() == "" + case reflect.UnsafePointer: + return v.Pointer() == 0 + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: + return v.IsNil() + case reflect.Array: + for i := 0; i < v.Len(); i++ { + if !IsZero(v.Index(i)) { + return false + } + } + return true + case reflect.Struct: + for i := 0; i < v.NumField(); i++ { + if !IsZero(v.Field(i)) { + return false + } + } + return true + } + return false +} diff --git a/vendor/github.com/google/go-cmp/cmp/options.go b/vendor/github.com/google/go-cmp/cmp/options.go new file mode 100644 index 00000000..abbd2a63 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/options.go @@ -0,0 +1,549 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +package cmp + +import ( + "fmt" + "reflect" + "regexp" + "strings" + + "github.com/google/go-cmp/cmp/internal/function" +) + +// Option configures for specific behavior of Equal and Diff. In particular, +// the fundamental Option functions (Ignore, Transformer, and Comparer), +// configure how equality is determined. +// +// The fundamental options may be composed with filters (FilterPath and +// FilterValues) to control the scope over which they are applied. +// +// The cmp/cmpopts package provides helper functions for creating options that +// may be used with Equal and Diff. +type Option interface { + // filter applies all filters and returns the option that remains. + // Each option may only read s.curPath and call s.callTTBFunc. + // + // An Options is returned only if multiple comparers or transformers + // can apply simultaneously and will only contain values of those types + // or sub-Options containing values of those types. + filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption +} + +// applicableOption represents the following types: +// Fundamental: ignore | validator | *comparer | *transformer +// Grouping: Options +type applicableOption interface { + Option + + // apply executes the option, which may mutate s or panic. + apply(s *state, vx, vy reflect.Value) +} + +// coreOption represents the following types: +// Fundamental: ignore | validator | *comparer | *transformer +// Filters: *pathFilter | *valuesFilter +type coreOption interface { + Option + isCore() +} + +type core struct{} + +func (core) isCore() {} + +// Options is a list of Option values that also satisfies the Option interface. +// Helper comparison packages may return an Options value when packing multiple +// Option values into a single Option. When this package processes an Options, +// it will be implicitly expanded into a flat list. +// +// Applying a filter on an Options is equivalent to applying that same filter +// on all individual options held within. +type Options []Option + +func (opts Options) filter(s *state, t reflect.Type, vx, vy reflect.Value) (out applicableOption) { + for _, opt := range opts { + switch opt := opt.filter(s, t, vx, vy); opt.(type) { + case ignore: + return ignore{} // Only ignore can short-circuit evaluation + case validator: + out = validator{} // Takes precedence over comparer or transformer + case *comparer, *transformer, Options: + switch out.(type) { + case nil: + out = opt + case validator: + // Keep validator + case *comparer, *transformer, Options: + out = Options{out, opt} // Conflicting comparers or transformers + } + } + } + return out +} + +func (opts Options) apply(s *state, _, _ reflect.Value) { + const warning = "ambiguous set of applicable options" + const help = "consider using filters to ensure at most one Comparer or Transformer may apply" + var ss []string + for _, opt := range flattenOptions(nil, opts) { + ss = append(ss, fmt.Sprint(opt)) + } + set := strings.Join(ss, "\n\t") + panic(fmt.Sprintf("%s at %#v:\n\t%s\n%s", warning, s.curPath, set, help)) +} + +func (opts Options) String() string { + var ss []string + for _, opt := range opts { + ss = append(ss, fmt.Sprint(opt)) + } + return fmt.Sprintf("Options{%s}", strings.Join(ss, ", ")) +} + +// FilterPath returns a new Option where opt is only evaluated if filter f +// returns true for the current Path in the value tree. +// +// This filter is called even if a slice element or map entry is missing and +// provides an opportunity to ignore such cases. The filter function must be +// symmetric such that the filter result is identical regardless of whether the +// missing value is from x or y. +// +// The option passed in may be an Ignore, Transformer, Comparer, Options, or +// a previously filtered Option. +func FilterPath(f func(Path) bool, opt Option) Option { + if f == nil { + panic("invalid path filter function") + } + if opt := normalizeOption(opt); opt != nil { + return &pathFilter{fnc: f, opt: opt} + } + return nil +} + +type pathFilter struct { + core + fnc func(Path) bool + opt Option +} + +func (f pathFilter) filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption { + if f.fnc(s.curPath) { + return f.opt.filter(s, t, vx, vy) + } + return nil +} + +func (f pathFilter) String() string { + return fmt.Sprintf("FilterPath(%s, %v)", function.NameOf(reflect.ValueOf(f.fnc)), f.opt) +} + +// FilterValues returns a new Option where opt is only evaluated if filter f, +// which is a function of the form "func(T, T) bool", returns true for the +// current pair of values being compared. If either value is invalid or +// the type of the values is not assignable to T, then this filter implicitly +// returns false. +// +// The filter function must be +// symmetric (i.e., agnostic to the order of the inputs) and +// deterministic (i.e., produces the same result when given the same inputs). +// If T is an interface, it is possible that f is called with two values with +// different concrete types that both implement T. +// +// The option passed in may be an Ignore, Transformer, Comparer, Options, or +// a previously filtered Option. +func FilterValues(f interface{}, opt Option) Option { + v := reflect.ValueOf(f) + if !function.IsType(v.Type(), function.ValueFilter) || v.IsNil() { + panic(fmt.Sprintf("invalid values filter function: %T", f)) + } + if opt := normalizeOption(opt); opt != nil { + vf := &valuesFilter{fnc: v, opt: opt} + if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 { + vf.typ = ti + } + return vf + } + return nil +} + +type valuesFilter struct { + core + typ reflect.Type // T + fnc reflect.Value // func(T, T) bool + opt Option +} + +func (f valuesFilter) filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption { + if !vx.IsValid() || !vx.CanInterface() || !vy.IsValid() || !vy.CanInterface() { + return nil + } + if (f.typ == nil || t.AssignableTo(f.typ)) && s.callTTBFunc(f.fnc, vx, vy) { + return f.opt.filter(s, t, vx, vy) + } + return nil +} + +func (f valuesFilter) String() string { + return fmt.Sprintf("FilterValues(%s, %v)", function.NameOf(f.fnc), f.opt) +} + +// Ignore is an Option that causes all comparisons to be ignored. +// This value is intended to be combined with FilterPath or FilterValues. +// It is an error to pass an unfiltered Ignore option to Equal. +func Ignore() Option { return ignore{} } + +type ignore struct{ core } + +func (ignore) isFiltered() bool { return false } +func (ignore) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { return ignore{} } +func (ignore) apply(s *state, _, _ reflect.Value) { s.report(true, reportByIgnore) } +func (ignore) String() string { return "Ignore()" } + +// validator is a sentinel Option type to indicate that some options could not +// be evaluated due to unexported fields, missing slice elements, or +// missing map entries. Both values are validator only for unexported fields. +type validator struct{ core } + +func (validator) filter(_ *state, _ reflect.Type, vx, vy reflect.Value) applicableOption { + if !vx.IsValid() || !vy.IsValid() { + return validator{} + } + if !vx.CanInterface() || !vy.CanInterface() { + return validator{} + } + return nil +} +func (validator) apply(s *state, vx, vy reflect.Value) { + // Implies missing slice element or map entry. + if !vx.IsValid() || !vy.IsValid() { + s.report(vx.IsValid() == vy.IsValid(), 0) + return + } + + // Unable to Interface implies unexported field without visibility access. + if !vx.CanInterface() || !vy.CanInterface() { + const help = "consider using a custom Comparer; if you control the implementation of type, you can also consider using an Exporter, AllowUnexported, or cmpopts.IgnoreUnexported" + var name string + if t := s.curPath.Index(-2).Type(); t.Name() != "" { + // Named type with unexported fields. + name = fmt.Sprintf("%q.%v", t.PkgPath(), t.Name()) // e.g., "path/to/package".MyType + } else { + // Unnamed type with unexported fields. Derive PkgPath from field. + var pkgPath string + for i := 0; i < t.NumField() && pkgPath == ""; i++ { + pkgPath = t.Field(i).PkgPath + } + name = fmt.Sprintf("%q.(%v)", pkgPath, t.String()) // e.g., "path/to/package".(struct { a int }) + } + panic(fmt.Sprintf("cannot handle unexported field at %#v:\n\t%v\n%s", s.curPath, name, help)) + } + + panic("not reachable") +} + +// identRx represents a valid identifier according to the Go specification. +const identRx = `[_\p{L}][_\p{L}\p{N}]*` + +var identsRx = regexp.MustCompile(`^` + identRx + `(\.` + identRx + `)*$`) + +// Transformer returns an Option that applies a transformation function that +// converts values of a certain type into that of another. +// +// The transformer f must be a function "func(T) R" that converts values of +// type T to those of type R and is implicitly filtered to input values +// assignable to T. The transformer must not mutate T in any way. +// +// To help prevent some cases of infinite recursive cycles applying the +// same transform to the output of itself (e.g., in the case where the +// input and output types are the same), an implicit filter is added such that +// a transformer is applicable only if that exact transformer is not already +// in the tail of the Path since the last non-Transform step. +// For situations where the implicit filter is still insufficient, +// consider using cmpopts.AcyclicTransformer, which adds a filter +// to prevent the transformer from being recursively applied upon itself. +// +// The name is a user provided label that is used as the Transform.Name in the +// transformation PathStep (and eventually shown in the Diff output). +// The name must be a valid identifier or qualified identifier in Go syntax. +// If empty, an arbitrary name is used. +func Transformer(name string, f interface{}) Option { + v := reflect.ValueOf(f) + if !function.IsType(v.Type(), function.Transformer) || v.IsNil() { + panic(fmt.Sprintf("invalid transformer function: %T", f)) + } + if name == "" { + name = function.NameOf(v) + if !identsRx.MatchString(name) { + name = "λ" // Lambda-symbol as placeholder name + } + } else if !identsRx.MatchString(name) { + panic(fmt.Sprintf("invalid name: %q", name)) + } + tr := &transformer{name: name, fnc: reflect.ValueOf(f)} + if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 { + tr.typ = ti + } + return tr +} + +type transformer struct { + core + name string + typ reflect.Type // T + fnc reflect.Value // func(T) R +} + +func (tr *transformer) isFiltered() bool { return tr.typ != nil } + +func (tr *transformer) filter(s *state, t reflect.Type, _, _ reflect.Value) applicableOption { + for i := len(s.curPath) - 1; i >= 0; i-- { + if t, ok := s.curPath[i].(Transform); !ok { + break // Hit most recent non-Transform step + } else if tr == t.trans { + return nil // Cannot directly use same Transform + } + } + if tr.typ == nil || t.AssignableTo(tr.typ) { + return tr + } + return nil +} + +func (tr *transformer) apply(s *state, vx, vy reflect.Value) { + step := Transform{&transform{pathStep{typ: tr.fnc.Type().Out(0)}, tr}} + vvx := s.callTRFunc(tr.fnc, vx, step) + vvy := s.callTRFunc(tr.fnc, vy, step) + step.vx, step.vy = vvx, vvy + s.compareAny(step) +} + +func (tr transformer) String() string { + return fmt.Sprintf("Transformer(%s, %s)", tr.name, function.NameOf(tr.fnc)) +} + +// Comparer returns an Option that determines whether two values are equal +// to each other. +// +// The comparer f must be a function "func(T, T) bool" and is implicitly +// filtered to input values assignable to T. If T is an interface, it is +// possible that f is called with two values of different concrete types that +// both implement T. +// +// The equality function must be: +// • Symmetric: equal(x, y) == equal(y, x) +// • Deterministic: equal(x, y) == equal(x, y) +// • Pure: equal(x, y) does not modify x or y +func Comparer(f interface{}) Option { + v := reflect.ValueOf(f) + if !function.IsType(v.Type(), function.Equal) || v.IsNil() { + panic(fmt.Sprintf("invalid comparer function: %T", f)) + } + cm := &comparer{fnc: v} + if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 { + cm.typ = ti + } + return cm +} + +type comparer struct { + core + typ reflect.Type // T + fnc reflect.Value // func(T, T) bool +} + +func (cm *comparer) isFiltered() bool { return cm.typ != nil } + +func (cm *comparer) filter(_ *state, t reflect.Type, _, _ reflect.Value) applicableOption { + if cm.typ == nil || t.AssignableTo(cm.typ) { + return cm + } + return nil +} + +func (cm *comparer) apply(s *state, vx, vy reflect.Value) { + eq := s.callTTBFunc(cm.fnc, vx, vy) + s.report(eq, reportByFunc) +} + +func (cm comparer) String() string { + return fmt.Sprintf("Comparer(%s)", function.NameOf(cm.fnc)) +} + +// Exporter returns an Option that specifies whether Equal is allowed to +// introspect into the unexported fields of certain struct types. +// +// Users of this option must understand that comparing on unexported fields +// from external packages is not safe since changes in the internal +// implementation of some external package may cause the result of Equal +// to unexpectedly change. However, it may be valid to use this option on types +// defined in an internal package where the semantic meaning of an unexported +// field is in the control of the user. +// +// In many cases, a custom Comparer should be used instead that defines +// equality as a function of the public API of a type rather than the underlying +// unexported implementation. +// +// For example, the reflect.Type documentation defines equality to be determined +// by the == operator on the interface (essentially performing a shallow pointer +// comparison) and most attempts to compare *regexp.Regexp types are interested +// in only checking that the regular expression strings are equal. +// Both of these are accomplished using Comparers: +// +// Comparer(func(x, y reflect.Type) bool { return x == y }) +// Comparer(func(x, y *regexp.Regexp) bool { return x.String() == y.String() }) +// +// In other cases, the cmpopts.IgnoreUnexported option can be used to ignore +// all unexported fields on specified struct types. +func Exporter(f func(reflect.Type) bool) Option { + if !supportExporters { + panic("Exporter is not supported on purego builds") + } + return exporter(f) +} + +type exporter func(reflect.Type) bool + +func (exporter) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { + panic("not implemented") +} + +// AllowUnexported returns an Options that allows Equal to forcibly introspect +// unexported fields of the specified struct types. +// +// See Exporter for the proper use of this option. +func AllowUnexported(types ...interface{}) Option { + m := make(map[reflect.Type]bool) + for _, typ := range types { + t := reflect.TypeOf(typ) + if t.Kind() != reflect.Struct { + panic(fmt.Sprintf("invalid struct type: %T", typ)) + } + m[t] = true + } + return exporter(func(t reflect.Type) bool { return m[t] }) +} + +// Result represents the comparison result for a single node and +// is provided by cmp when calling Result (see Reporter). +type Result struct { + _ [0]func() // Make Result incomparable + flags resultFlags +} + +// Equal reports whether the node was determined to be equal or not. +// As a special case, ignored nodes are considered equal. +func (r Result) Equal() bool { + return r.flags&(reportEqual|reportByIgnore) != 0 +} + +// ByIgnore reports whether the node is equal because it was ignored. +// This never reports true if Equal reports false. +func (r Result) ByIgnore() bool { + return r.flags&reportByIgnore != 0 +} + +// ByMethod reports whether the Equal method determined equality. +func (r Result) ByMethod() bool { + return r.flags&reportByMethod != 0 +} + +// ByFunc reports whether a Comparer function determined equality. +func (r Result) ByFunc() bool { + return r.flags&reportByFunc != 0 +} + +// ByCycle reports whether a reference cycle was detected. +func (r Result) ByCycle() bool { + return r.flags&reportByCycle != 0 +} + +type resultFlags uint + +const ( + _ resultFlags = (1 << iota) / 2 + + reportEqual + reportUnequal + reportByIgnore + reportByMethod + reportByFunc + reportByCycle +) + +// Reporter is an Option that can be passed to Equal. When Equal traverses +// the value trees, it calls PushStep as it descends into each node in the +// tree and PopStep as it ascend out of the node. The leaves of the tree are +// either compared (determined to be equal or not equal) or ignored and reported +// as such by calling the Report method. +func Reporter(r interface { + // PushStep is called when a tree-traversal operation is performed. + // The PathStep itself is only valid until the step is popped. + // The PathStep.Values are valid for the duration of the entire traversal + // and must not be mutated. + // + // Equal always calls PushStep at the start to provide an operation-less + // PathStep used to report the root values. + // + // Within a slice, the exact set of inserted, removed, or modified elements + // is unspecified and may change in future implementations. + // The entries of a map are iterated through in an unspecified order. + PushStep(PathStep) + + // Report is called exactly once on leaf nodes to report whether the + // comparison identified the node as equal, unequal, or ignored. + // A leaf node is one that is immediately preceded by and followed by + // a pair of PushStep and PopStep calls. + Report(Result) + + // PopStep ascends back up the value tree. + // There is always a matching pop call for every push call. + PopStep() +}) Option { + return reporter{r} +} + +type reporter struct{ reporterIface } +type reporterIface interface { + PushStep(PathStep) + Report(Result) + PopStep() +} + +func (reporter) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { + panic("not implemented") +} + +// normalizeOption normalizes the input options such that all Options groups +// are flattened and groups with a single element are reduced to that element. +// Only coreOptions and Options containing coreOptions are allowed. +func normalizeOption(src Option) Option { + switch opts := flattenOptions(nil, Options{src}); len(opts) { + case 0: + return nil + case 1: + return opts[0] + default: + return opts + } +} + +// flattenOptions copies all options in src to dst as a flat list. +// Only coreOptions and Options containing coreOptions are allowed. +func flattenOptions(dst, src Options) Options { + for _, opt := range src { + switch opt := opt.(type) { + case nil: + continue + case Options: + dst = flattenOptions(dst, opt) + case coreOption: + dst = append(dst, opt) + default: + panic(fmt.Sprintf("invalid option type: %T", opt)) + } + } + return dst +} diff --git a/vendor/github.com/google/go-cmp/cmp/path.go b/vendor/github.com/google/go-cmp/cmp/path.go new file mode 100644 index 00000000..603dbb00 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/path.go @@ -0,0 +1,378 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +package cmp + +import ( + "fmt" + "reflect" + "strings" + "unicode" + "unicode/utf8" + + "github.com/google/go-cmp/cmp/internal/value" +) + +// Path is a list of PathSteps describing the sequence of operations to get +// from some root type to the current position in the value tree. +// The first Path element is always an operation-less PathStep that exists +// simply to identify the initial type. +// +// When traversing structs with embedded structs, the embedded struct will +// always be accessed as a field before traversing the fields of the +// embedded struct themselves. That is, an exported field from the +// embedded struct will never be accessed directly from the parent struct. +type Path []PathStep + +// PathStep is a union-type for specific operations to traverse +// a value's tree structure. Users of this package never need to implement +// these types as values of this type will be returned by this package. +// +// Implementations of this interface are +// StructField, SliceIndex, MapIndex, Indirect, TypeAssertion, and Transform. +type PathStep interface { + String() string + + // Type is the resulting type after performing the path step. + Type() reflect.Type + + // Values is the resulting values after performing the path step. + // The type of each valid value is guaranteed to be identical to Type. + // + // In some cases, one or both may be invalid or have restrictions: + // • For StructField, both are not interface-able if the current field + // is unexported and the struct type is not explicitly permitted by + // an Exporter to traverse unexported fields. + // • For SliceIndex, one may be invalid if an element is missing from + // either the x or y slice. + // • For MapIndex, one may be invalid if an entry is missing from + // either the x or y map. + // + // The provided values must not be mutated. + Values() (vx, vy reflect.Value) +} + +var ( + _ PathStep = StructField{} + _ PathStep = SliceIndex{} + _ PathStep = MapIndex{} + _ PathStep = Indirect{} + _ PathStep = TypeAssertion{} + _ PathStep = Transform{} +) + +func (pa *Path) push(s PathStep) { + *pa = append(*pa, s) +} + +func (pa *Path) pop() { + *pa = (*pa)[:len(*pa)-1] +} + +// Last returns the last PathStep in the Path. +// If the path is empty, this returns a non-nil PathStep that reports a nil Type. +func (pa Path) Last() PathStep { + return pa.Index(-1) +} + +// Index returns the ith step in the Path and supports negative indexing. +// A negative index starts counting from the tail of the Path such that -1 +// refers to the last step, -2 refers to the second-to-last step, and so on. +// If index is invalid, this returns a non-nil PathStep that reports a nil Type. +func (pa Path) Index(i int) PathStep { + if i < 0 { + i = len(pa) + i + } + if i < 0 || i >= len(pa) { + return pathStep{} + } + return pa[i] +} + +// String returns the simplified path to a node. +// The simplified path only contains struct field accesses. +// +// For example: +// MyMap.MySlices.MyField +func (pa Path) String() string { + var ss []string + for _, s := range pa { + if _, ok := s.(StructField); ok { + ss = append(ss, s.String()) + } + } + return strings.TrimPrefix(strings.Join(ss, ""), ".") +} + +// GoString returns the path to a specific node using Go syntax. +// +// For example: +// (*root.MyMap["key"].(*mypkg.MyStruct).MySlices)[2][3].MyField +func (pa Path) GoString() string { + var ssPre, ssPost []string + var numIndirect int + for i, s := range pa { + var nextStep PathStep + if i+1 < len(pa) { + nextStep = pa[i+1] + } + switch s := s.(type) { + case Indirect: + numIndirect++ + pPre, pPost := "(", ")" + switch nextStep.(type) { + case Indirect: + continue // Next step is indirection, so let them batch up + case StructField: + numIndirect-- // Automatic indirection on struct fields + case nil: + pPre, pPost = "", "" // Last step; no need for parenthesis + } + if numIndirect > 0 { + ssPre = append(ssPre, pPre+strings.Repeat("*", numIndirect)) + ssPost = append(ssPost, pPost) + } + numIndirect = 0 + continue + case Transform: + ssPre = append(ssPre, s.trans.name+"(") + ssPost = append(ssPost, ")") + continue + } + ssPost = append(ssPost, s.String()) + } + for i, j := 0, len(ssPre)-1; i < j; i, j = i+1, j-1 { + ssPre[i], ssPre[j] = ssPre[j], ssPre[i] + } + return strings.Join(ssPre, "") + strings.Join(ssPost, "") +} + +type pathStep struct { + typ reflect.Type + vx, vy reflect.Value +} + +func (ps pathStep) Type() reflect.Type { return ps.typ } +func (ps pathStep) Values() (vx, vy reflect.Value) { return ps.vx, ps.vy } +func (ps pathStep) String() string { + if ps.typ == nil { + return "" + } + s := ps.typ.String() + if s == "" || strings.ContainsAny(s, "{}\n") { + return "root" // Type too simple or complex to print + } + return fmt.Sprintf("{%s}", s) +} + +// StructField represents a struct field access on a field called Name. +type StructField struct{ *structField } +type structField struct { + pathStep + name string + idx int + + // These fields are used for forcibly accessing an unexported field. + // pvx, pvy, and field are only valid if unexported is true. + unexported bool + mayForce bool // Forcibly allow visibility + paddr bool // Was parent addressable? + pvx, pvy reflect.Value // Parent values (always addressible) + field reflect.StructField // Field information +} + +func (sf StructField) Type() reflect.Type { return sf.typ } +func (sf StructField) Values() (vx, vy reflect.Value) { + if !sf.unexported { + return sf.vx, sf.vy // CanInterface reports true + } + + // Forcibly obtain read-write access to an unexported struct field. + if sf.mayForce { + vx = retrieveUnexportedField(sf.pvx, sf.field, sf.paddr) + vy = retrieveUnexportedField(sf.pvy, sf.field, sf.paddr) + return vx, vy // CanInterface reports true + } + return sf.vx, sf.vy // CanInterface reports false +} +func (sf StructField) String() string { return fmt.Sprintf(".%s", sf.name) } + +// Name is the field name. +func (sf StructField) Name() string { return sf.name } + +// Index is the index of the field in the parent struct type. +// See reflect.Type.Field. +func (sf StructField) Index() int { return sf.idx } + +// SliceIndex is an index operation on a slice or array at some index Key. +type SliceIndex struct{ *sliceIndex } +type sliceIndex struct { + pathStep + xkey, ykey int + isSlice bool // False for reflect.Array +} + +func (si SliceIndex) Type() reflect.Type { return si.typ } +func (si SliceIndex) Values() (vx, vy reflect.Value) { return si.vx, si.vy } +func (si SliceIndex) String() string { + switch { + case si.xkey == si.ykey: + return fmt.Sprintf("[%d]", si.xkey) + case si.ykey == -1: + // [5->?] means "I don't know where X[5] went" + return fmt.Sprintf("[%d->?]", si.xkey) + case si.xkey == -1: + // [?->3] means "I don't know where Y[3] came from" + return fmt.Sprintf("[?->%d]", si.ykey) + default: + // [5->3] means "X[5] moved to Y[3]" + return fmt.Sprintf("[%d->%d]", si.xkey, si.ykey) + } +} + +// Key is the index key; it may return -1 if in a split state +func (si SliceIndex) Key() int { + if si.xkey != si.ykey { + return -1 + } + return si.xkey +} + +// SplitKeys are the indexes for indexing into slices in the +// x and y values, respectively. These indexes may differ due to the +// insertion or removal of an element in one of the slices, causing +// all of the indexes to be shifted. If an index is -1, then that +// indicates that the element does not exist in the associated slice. +// +// Key is guaranteed to return -1 if and only if the indexes returned +// by SplitKeys are not the same. SplitKeys will never return -1 for +// both indexes. +func (si SliceIndex) SplitKeys() (ix, iy int) { return si.xkey, si.ykey } + +// MapIndex is an index operation on a map at some index Key. +type MapIndex struct{ *mapIndex } +type mapIndex struct { + pathStep + key reflect.Value +} + +func (mi MapIndex) Type() reflect.Type { return mi.typ } +func (mi MapIndex) Values() (vx, vy reflect.Value) { return mi.vx, mi.vy } +func (mi MapIndex) String() string { return fmt.Sprintf("[%#v]", mi.key) } + +// Key is the value of the map key. +func (mi MapIndex) Key() reflect.Value { return mi.key } + +// Indirect represents pointer indirection on the parent type. +type Indirect struct{ *indirect } +type indirect struct { + pathStep +} + +func (in Indirect) Type() reflect.Type { return in.typ } +func (in Indirect) Values() (vx, vy reflect.Value) { return in.vx, in.vy } +func (in Indirect) String() string { return "*" } + +// TypeAssertion represents a type assertion on an interface. +type TypeAssertion struct{ *typeAssertion } +type typeAssertion struct { + pathStep +} + +func (ta TypeAssertion) Type() reflect.Type { return ta.typ } +func (ta TypeAssertion) Values() (vx, vy reflect.Value) { return ta.vx, ta.vy } +func (ta TypeAssertion) String() string { return fmt.Sprintf(".(%v)", ta.typ) } + +// Transform is a transformation from the parent type to the current type. +type Transform struct{ *transform } +type transform struct { + pathStep + trans *transformer +} + +func (tf Transform) Type() reflect.Type { return tf.typ } +func (tf Transform) Values() (vx, vy reflect.Value) { return tf.vx, tf.vy } +func (tf Transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) } + +// Name is the name of the Transformer. +func (tf Transform) Name() string { return tf.trans.name } + +// Func is the function pointer to the transformer function. +func (tf Transform) Func() reflect.Value { return tf.trans.fnc } + +// Option returns the originally constructed Transformer option. +// The == operator can be used to detect the exact option used. +func (tf Transform) Option() Option { return tf.trans } + +// pointerPath represents a dual-stack of pointers encountered when +// recursively traversing the x and y values. This data structure supports +// detection of cycles and determining whether the cycles are equal. +// In Go, cycles can occur via pointers, slices, and maps. +// +// The pointerPath uses a map to represent a stack; where descension into a +// pointer pushes the address onto the stack, and ascension from a pointer +// pops the address from the stack. Thus, when traversing into a pointer from +// reflect.Ptr, reflect.Slice element, or reflect.Map, we can detect cycles +// by checking whether the pointer has already been visited. The cycle detection +// uses a seperate stack for the x and y values. +// +// If a cycle is detected we need to determine whether the two pointers +// should be considered equal. The definition of equality chosen by Equal +// requires two graphs to have the same structure. To determine this, both the +// x and y values must have a cycle where the previous pointers were also +// encountered together as a pair. +// +// Semantically, this is equivalent to augmenting Indirect, SliceIndex, and +// MapIndex with pointer information for the x and y values. +// Suppose px and py are two pointers to compare, we then search the +// Path for whether px was ever encountered in the Path history of x, and +// similarly so with py. If either side has a cycle, the comparison is only +// equal if both px and py have a cycle resulting from the same PathStep. +// +// Using a map as a stack is more performant as we can perform cycle detection +// in O(1) instead of O(N) where N is len(Path). +type pointerPath struct { + // mx is keyed by x pointers, where the value is the associated y pointer. + mx map[value.Pointer]value.Pointer + // my is keyed by y pointers, where the value is the associated x pointer. + my map[value.Pointer]value.Pointer +} + +func (p *pointerPath) Init() { + p.mx = make(map[value.Pointer]value.Pointer) + p.my = make(map[value.Pointer]value.Pointer) +} + +// Push indicates intent to descend into pointers vx and vy where +// visited reports whether either has been seen before. If visited before, +// equal reports whether both pointers were encountered together. +// Pop must be called if and only if the pointers were never visited. +// +// The pointers vx and vy must be a reflect.Ptr, reflect.Slice, or reflect.Map +// and be non-nil. +func (p pointerPath) Push(vx, vy reflect.Value) (equal, visited bool) { + px := value.PointerOf(vx) + py := value.PointerOf(vy) + _, ok1 := p.mx[px] + _, ok2 := p.my[py] + if ok1 || ok2 { + equal = p.mx[px] == py && p.my[py] == px // Pointers paired together + return equal, true + } + p.mx[px] = py + p.my[py] = px + return false, false +} + +// Pop ascends from pointers vx and vy. +func (p pointerPath) Pop(vx, vy reflect.Value) { + delete(p.mx, value.PointerOf(vx)) + delete(p.my, value.PointerOf(vy)) +} + +// isExported reports whether the identifier is exported. +func isExported(id string) bool { + r, _ := utf8.DecodeRuneInString(id) + return unicode.IsUpper(r) +} diff --git a/vendor/github.com/google/go-cmp/cmp/report.go b/vendor/github.com/google/go-cmp/cmp/report.go new file mode 100644 index 00000000..aafcb363 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/report.go @@ -0,0 +1,54 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +package cmp + +// defaultReporter implements the reporter interface. +// +// As Equal serially calls the PushStep, Report, and PopStep methods, the +// defaultReporter constructs a tree-based representation of the compared value +// and the result of each comparison (see valueNode). +// +// When the String method is called, the FormatDiff method transforms the +// valueNode tree into a textNode tree, which is a tree-based representation +// of the textual output (see textNode). +// +// Lastly, the textNode.String method produces the final report as a string. +type defaultReporter struct { + root *valueNode + curr *valueNode +} + +func (r *defaultReporter) PushStep(ps PathStep) { + r.curr = r.curr.PushStep(ps) + if r.root == nil { + r.root = r.curr + } +} +func (r *defaultReporter) Report(rs Result) { + r.curr.Report(rs) +} +func (r *defaultReporter) PopStep() { + r.curr = r.curr.PopStep() +} + +// String provides a full report of the differences detected as a structured +// literal in pseudo-Go syntax. String may only be called after the entire tree +// has been traversed. +func (r *defaultReporter) String() string { + assert(r.root != nil && r.curr == nil) + if r.root.NumDiff == 0 { + return "" + } + ptrs := new(pointerReferences) + text := formatOptions{}.FormatDiff(r.root, ptrs) + resolveReferences(text) + return text.String() +} + +func assert(ok bool) { + if !ok { + panic("assertion failure") + } +} diff --git a/vendor/github.com/google/go-cmp/cmp/report_compare.go b/vendor/github.com/google/go-cmp/cmp/report_compare.go new file mode 100644 index 00000000..9e218096 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/report_compare.go @@ -0,0 +1,432 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +package cmp + +import ( + "fmt" + "reflect" + + "github.com/google/go-cmp/cmp/internal/value" +) + +// numContextRecords is the number of surrounding equal records to print. +const numContextRecords = 2 + +type diffMode byte + +const ( + diffUnknown diffMode = 0 + diffIdentical diffMode = ' ' + diffRemoved diffMode = '-' + diffInserted diffMode = '+' +) + +type typeMode int + +const ( + // emitType always prints the type. + emitType typeMode = iota + // elideType never prints the type. + elideType + // autoType prints the type only for composite kinds + // (i.e., structs, slices, arrays, and maps). + autoType +) + +type formatOptions struct { + // DiffMode controls the output mode of FormatDiff. + // + // If diffUnknown, then produce a diff of the x and y values. + // If diffIdentical, then emit values as if they were equal. + // If diffRemoved, then only emit x values (ignoring y values). + // If diffInserted, then only emit y values (ignoring x values). + DiffMode diffMode + + // TypeMode controls whether to print the type for the current node. + // + // As a general rule of thumb, we always print the type of the next node + // after an interface, and always elide the type of the next node after + // a slice or map node. + TypeMode typeMode + + // formatValueOptions are options specific to printing reflect.Values. + formatValueOptions +} + +func (opts formatOptions) WithDiffMode(d diffMode) formatOptions { + opts.DiffMode = d + return opts +} +func (opts formatOptions) WithTypeMode(t typeMode) formatOptions { + opts.TypeMode = t + return opts +} +func (opts formatOptions) WithVerbosity(level int) formatOptions { + opts.VerbosityLevel = level + opts.LimitVerbosity = true + return opts +} +func (opts formatOptions) verbosity() uint { + switch { + case opts.VerbosityLevel < 0: + return 0 + case opts.VerbosityLevel > 16: + return 16 // some reasonable maximum to avoid shift overflow + default: + return uint(opts.VerbosityLevel) + } +} + +const maxVerbosityPreset = 3 + +// verbosityPreset modifies the verbosity settings given an index +// between 0 and maxVerbosityPreset, inclusive. +func verbosityPreset(opts formatOptions, i int) formatOptions { + opts.VerbosityLevel = int(opts.verbosity()) + 2*i + if i > 0 { + opts.AvoidStringer = true + } + if i >= maxVerbosityPreset { + opts.PrintAddresses = true + opts.QualifiedNames = true + } + return opts +} + +// FormatDiff converts a valueNode tree into a textNode tree, where the later +// is a textual representation of the differences detected in the former. +func (opts formatOptions) FormatDiff(v *valueNode, ptrs *pointerReferences) (out textNode) { + if opts.DiffMode == diffIdentical { + opts = opts.WithVerbosity(1) + } else { + opts = opts.WithVerbosity(3) + } + + // Check whether we have specialized formatting for this node. + // This is not necessary, but helpful for producing more readable outputs. + if opts.CanFormatDiffSlice(v) { + return opts.FormatDiffSlice(v) + } + + var parentKind reflect.Kind + if v.parent != nil && v.parent.TransformerName == "" { + parentKind = v.parent.Type.Kind() + } + + // For leaf nodes, format the value based on the reflect.Values alone. + if v.MaxDepth == 0 { + switch opts.DiffMode { + case diffUnknown, diffIdentical: + // Format Equal. + if v.NumDiff == 0 { + outx := opts.FormatValue(v.ValueX, parentKind, ptrs) + outy := opts.FormatValue(v.ValueY, parentKind, ptrs) + if v.NumIgnored > 0 && v.NumSame == 0 { + return textEllipsis + } else if outx.Len() < outy.Len() { + return outx + } else { + return outy + } + } + + // Format unequal. + assert(opts.DiffMode == diffUnknown) + var list textList + outx := opts.WithTypeMode(elideType).FormatValue(v.ValueX, parentKind, ptrs) + outy := opts.WithTypeMode(elideType).FormatValue(v.ValueY, parentKind, ptrs) + for i := 0; i <= maxVerbosityPreset && outx != nil && outy != nil && outx.Equal(outy); i++ { + opts2 := verbosityPreset(opts, i).WithTypeMode(elideType) + outx = opts2.FormatValue(v.ValueX, parentKind, ptrs) + outy = opts2.FormatValue(v.ValueY, parentKind, ptrs) + } + if outx != nil { + list = append(list, textRecord{Diff: '-', Value: outx}) + } + if outy != nil { + list = append(list, textRecord{Diff: '+', Value: outy}) + } + return opts.WithTypeMode(emitType).FormatType(v.Type, list) + case diffRemoved: + return opts.FormatValue(v.ValueX, parentKind, ptrs) + case diffInserted: + return opts.FormatValue(v.ValueY, parentKind, ptrs) + default: + panic("invalid diff mode") + } + } + + // Register slice element to support cycle detection. + if parentKind == reflect.Slice { + ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, true) + defer ptrs.Pop() + defer func() { out = wrapTrunkReferences(ptrRefs, out) }() + } + + // Descend into the child value node. + if v.TransformerName != "" { + out := opts.WithTypeMode(emitType).FormatDiff(v.Value, ptrs) + out = &textWrap{Prefix: "Inverse(" + v.TransformerName + ", ", Value: out, Suffix: ")"} + return opts.FormatType(v.Type, out) + } else { + switch k := v.Type.Kind(); k { + case reflect.Struct, reflect.Array, reflect.Slice: + out = opts.formatDiffList(v.Records, k, ptrs) + out = opts.FormatType(v.Type, out) + case reflect.Map: + // Register map to support cycle detection. + ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, false) + defer ptrs.Pop() + + out = opts.formatDiffList(v.Records, k, ptrs) + out = wrapTrunkReferences(ptrRefs, out) + out = opts.FormatType(v.Type, out) + case reflect.Ptr: + // Register pointer to support cycle detection. + ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, false) + defer ptrs.Pop() + + out = opts.FormatDiff(v.Value, ptrs) + out = wrapTrunkReferences(ptrRefs, out) + out = &textWrap{Prefix: "&", Value: out} + case reflect.Interface: + out = opts.WithTypeMode(emitType).FormatDiff(v.Value, ptrs) + default: + panic(fmt.Sprintf("%v cannot have children", k)) + } + return out + } +} + +func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind, ptrs *pointerReferences) textNode { + // Derive record name based on the data structure kind. + var name string + var formatKey func(reflect.Value) string + switch k { + case reflect.Struct: + name = "field" + opts = opts.WithTypeMode(autoType) + formatKey = func(v reflect.Value) string { return v.String() } + case reflect.Slice, reflect.Array: + name = "element" + opts = opts.WithTypeMode(elideType) + formatKey = func(reflect.Value) string { return "" } + case reflect.Map: + name = "entry" + opts = opts.WithTypeMode(elideType) + formatKey = func(v reflect.Value) string { return formatMapKey(v, false, ptrs) } + } + + maxLen := -1 + if opts.LimitVerbosity { + if opts.DiffMode == diffIdentical { + maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... + } else { + maxLen = (1 << opts.verbosity()) << 1 // 2, 4, 8, 16, 32, 64, etc... + } + opts.VerbosityLevel-- + } + + // Handle unification. + switch opts.DiffMode { + case diffIdentical, diffRemoved, diffInserted: + var list textList + var deferredEllipsis bool // Add final "..." to indicate records were dropped + for _, r := range recs { + if len(list) == maxLen { + deferredEllipsis = true + break + } + + // Elide struct fields that are zero value. + if k == reflect.Struct { + var isZero bool + switch opts.DiffMode { + case diffIdentical: + isZero = value.IsZero(r.Value.ValueX) || value.IsZero(r.Value.ValueY) + case diffRemoved: + isZero = value.IsZero(r.Value.ValueX) + case diffInserted: + isZero = value.IsZero(r.Value.ValueY) + } + if isZero { + continue + } + } + // Elide ignored nodes. + if r.Value.NumIgnored > 0 && r.Value.NumSame+r.Value.NumDiff == 0 { + deferredEllipsis = !(k == reflect.Slice || k == reflect.Array) + if !deferredEllipsis { + list.AppendEllipsis(diffStats{}) + } + continue + } + if out := opts.FormatDiff(r.Value, ptrs); out != nil { + list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) + } + } + if deferredEllipsis { + list.AppendEllipsis(diffStats{}) + } + return &textWrap{Prefix: "{", Value: list, Suffix: "}"} + case diffUnknown: + default: + panic("invalid diff mode") + } + + // Handle differencing. + var numDiffs int + var list textList + var keys []reflect.Value // invariant: len(list) == len(keys) + groups := coalesceAdjacentRecords(name, recs) + maxGroup := diffStats{Name: name} + for i, ds := range groups { + if maxLen >= 0 && numDiffs >= maxLen { + maxGroup = maxGroup.Append(ds) + continue + } + + // Handle equal records. + if ds.NumDiff() == 0 { + // Compute the number of leading and trailing records to print. + var numLo, numHi int + numEqual := ds.NumIgnored + ds.NumIdentical + for numLo < numContextRecords && numLo+numHi < numEqual && i != 0 { + if r := recs[numLo].Value; r.NumIgnored > 0 && r.NumSame+r.NumDiff == 0 { + break + } + numLo++ + } + for numHi < numContextRecords && numLo+numHi < numEqual && i != len(groups)-1 { + if r := recs[numEqual-numHi-1].Value; r.NumIgnored > 0 && r.NumSame+r.NumDiff == 0 { + break + } + numHi++ + } + if numEqual-(numLo+numHi) == 1 && ds.NumIgnored == 0 { + numHi++ // Avoid pointless coalescing of a single equal record + } + + // Format the equal values. + for _, r := range recs[:numLo] { + out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value, ptrs) + list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) + keys = append(keys, r.Key) + } + if numEqual > numLo+numHi { + ds.NumIdentical -= numLo + numHi + list.AppendEllipsis(ds) + for len(keys) < len(list) { + keys = append(keys, reflect.Value{}) + } + } + for _, r := range recs[numEqual-numHi : numEqual] { + out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value, ptrs) + list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) + keys = append(keys, r.Key) + } + recs = recs[numEqual:] + continue + } + + // Handle unequal records. + for _, r := range recs[:ds.NumDiff()] { + switch { + case opts.CanFormatDiffSlice(r.Value): + out := opts.FormatDiffSlice(r.Value) + list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) + keys = append(keys, r.Key) + case r.Value.NumChildren == r.Value.MaxDepth: + outx := opts.WithDiffMode(diffRemoved).FormatDiff(r.Value, ptrs) + outy := opts.WithDiffMode(diffInserted).FormatDiff(r.Value, ptrs) + for i := 0; i <= maxVerbosityPreset && outx != nil && outy != nil && outx.Equal(outy); i++ { + opts2 := verbosityPreset(opts, i) + outx = opts2.WithDiffMode(diffRemoved).FormatDiff(r.Value, ptrs) + outy = opts2.WithDiffMode(diffInserted).FormatDiff(r.Value, ptrs) + } + if outx != nil { + list = append(list, textRecord{Diff: diffRemoved, Key: formatKey(r.Key), Value: outx}) + keys = append(keys, r.Key) + } + if outy != nil { + list = append(list, textRecord{Diff: diffInserted, Key: formatKey(r.Key), Value: outy}) + keys = append(keys, r.Key) + } + default: + out := opts.FormatDiff(r.Value, ptrs) + list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) + keys = append(keys, r.Key) + } + } + recs = recs[ds.NumDiff():] + numDiffs += ds.NumDiff() + } + if maxGroup.IsZero() { + assert(len(recs) == 0) + } else { + list.AppendEllipsis(maxGroup) + for len(keys) < len(list) { + keys = append(keys, reflect.Value{}) + } + } + assert(len(list) == len(keys)) + + // For maps, the default formatting logic uses fmt.Stringer which may + // produce ambiguous output. Avoid calling String to disambiguate. + if k == reflect.Map { + var ambiguous bool + seenKeys := map[string]reflect.Value{} + for i, currKey := range keys { + if currKey.IsValid() { + strKey := list[i].Key + prevKey, seen := seenKeys[strKey] + if seen && prevKey.CanInterface() && currKey.CanInterface() { + ambiguous = prevKey.Interface() != currKey.Interface() + if ambiguous { + break + } + } + seenKeys[strKey] = currKey + } + } + if ambiguous { + for i, k := range keys { + if k.IsValid() { + list[i].Key = formatMapKey(k, true, ptrs) + } + } + } + } + + return &textWrap{Prefix: "{", Value: list, Suffix: "}"} +} + +// coalesceAdjacentRecords coalesces the list of records into groups of +// adjacent equal, or unequal counts. +func coalesceAdjacentRecords(name string, recs []reportRecord) (groups []diffStats) { + var prevCase int // Arbitrary index into which case last occurred + lastStats := func(i int) *diffStats { + if prevCase != i { + groups = append(groups, diffStats{Name: name}) + prevCase = i + } + return &groups[len(groups)-1] + } + for _, r := range recs { + switch rv := r.Value; { + case rv.NumIgnored > 0 && rv.NumSame+rv.NumDiff == 0: + lastStats(1).NumIgnored++ + case rv.NumDiff == 0: + lastStats(1).NumIdentical++ + case rv.NumDiff > 0 && !rv.ValueY.IsValid(): + lastStats(2).NumRemoved++ + case rv.NumDiff > 0 && !rv.ValueX.IsValid(): + lastStats(2).NumInserted++ + default: + lastStats(2).NumModified++ + } + } + return groups +} diff --git a/vendor/github.com/google/go-cmp/cmp/report_references.go b/vendor/github.com/google/go-cmp/cmp/report_references.go new file mode 100644 index 00000000..d620c2c2 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/report_references.go @@ -0,0 +1,264 @@ +// Copyright 2020, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +package cmp + +import ( + "fmt" + "reflect" + "strings" + + "github.com/google/go-cmp/cmp/internal/flags" + "github.com/google/go-cmp/cmp/internal/value" +) + +const ( + pointerDelimPrefix = "⟪" + pointerDelimSuffix = "⟫" +) + +// formatPointer prints the address of the pointer. +func formatPointer(p value.Pointer, withDelims bool) string { + v := p.Uintptr() + if flags.Deterministic { + v = 0xdeadf00f // Only used for stable testing purposes + } + if withDelims { + return pointerDelimPrefix + formatHex(uint64(v)) + pointerDelimSuffix + } + return formatHex(uint64(v)) +} + +// pointerReferences is a stack of pointers visited so far. +type pointerReferences [][2]value.Pointer + +func (ps *pointerReferences) PushPair(vx, vy reflect.Value, d diffMode, deref bool) (pp [2]value.Pointer) { + if deref && vx.IsValid() { + vx = vx.Addr() + } + if deref && vy.IsValid() { + vy = vy.Addr() + } + switch d { + case diffUnknown, diffIdentical: + pp = [2]value.Pointer{value.PointerOf(vx), value.PointerOf(vy)} + case diffRemoved: + pp = [2]value.Pointer{value.PointerOf(vx), value.Pointer{}} + case diffInserted: + pp = [2]value.Pointer{value.Pointer{}, value.PointerOf(vy)} + } + *ps = append(*ps, pp) + return pp +} + +func (ps *pointerReferences) Push(v reflect.Value) (p value.Pointer, seen bool) { + p = value.PointerOf(v) + for _, pp := range *ps { + if p == pp[0] || p == pp[1] { + return p, true + } + } + *ps = append(*ps, [2]value.Pointer{p, p}) + return p, false +} + +func (ps *pointerReferences) Pop() { + *ps = (*ps)[:len(*ps)-1] +} + +// trunkReferences is metadata for a textNode indicating that the sub-tree +// represents the value for either pointer in a pair of references. +type trunkReferences struct{ pp [2]value.Pointer } + +// trunkReference is metadata for a textNode indicating that the sub-tree +// represents the value for the given pointer reference. +type trunkReference struct{ p value.Pointer } + +// leafReference is metadata for a textNode indicating that the value is +// truncated as it refers to another part of the tree (i.e., a trunk). +type leafReference struct{ p value.Pointer } + +func wrapTrunkReferences(pp [2]value.Pointer, s textNode) textNode { + switch { + case pp[0].IsNil(): + return &textWrap{Value: s, Metadata: trunkReference{pp[1]}} + case pp[1].IsNil(): + return &textWrap{Value: s, Metadata: trunkReference{pp[0]}} + case pp[0] == pp[1]: + return &textWrap{Value: s, Metadata: trunkReference{pp[0]}} + default: + return &textWrap{Value: s, Metadata: trunkReferences{pp}} + } +} +func wrapTrunkReference(p value.Pointer, printAddress bool, s textNode) textNode { + var prefix string + if printAddress { + prefix = formatPointer(p, true) + } + return &textWrap{Prefix: prefix, Value: s, Metadata: trunkReference{p}} +} +func makeLeafReference(p value.Pointer, printAddress bool) textNode { + out := &textWrap{Prefix: "(", Value: textEllipsis, Suffix: ")"} + var prefix string + if printAddress { + prefix = formatPointer(p, true) + } + return &textWrap{Prefix: prefix, Value: out, Metadata: leafReference{p}} +} + +// resolveReferences walks the textNode tree searching for any leaf reference +// metadata and resolves each against the corresponding trunk references. +// Since pointer addresses in memory are not particularly readable to the user, +// it replaces each pointer value with an arbitrary and unique reference ID. +func resolveReferences(s textNode) { + var walkNodes func(textNode, func(textNode)) + walkNodes = func(s textNode, f func(textNode)) { + f(s) + switch s := s.(type) { + case *textWrap: + walkNodes(s.Value, f) + case textList: + for _, r := range s { + walkNodes(r.Value, f) + } + } + } + + // Collect all trunks and leaves with reference metadata. + var trunks, leaves []*textWrap + walkNodes(s, func(s textNode) { + if s, ok := s.(*textWrap); ok { + switch s.Metadata.(type) { + case leafReference: + leaves = append(leaves, s) + case trunkReference, trunkReferences: + trunks = append(trunks, s) + } + } + }) + + // No leaf references to resolve. + if len(leaves) == 0 { + return + } + + // Collect the set of all leaf references to resolve. + leafPtrs := make(map[value.Pointer]bool) + for _, leaf := range leaves { + leafPtrs[leaf.Metadata.(leafReference).p] = true + } + + // Collect the set of trunk pointers that are always paired together. + // This allows us to assign a single ID to both pointers for brevity. + // If a pointer in a pair ever occurs by itself or as a different pair, + // then the pair is broken. + pairedTrunkPtrs := make(map[value.Pointer]value.Pointer) + unpair := func(p value.Pointer) { + if !pairedTrunkPtrs[p].IsNil() { + pairedTrunkPtrs[pairedTrunkPtrs[p]] = value.Pointer{} // invalidate other half + } + pairedTrunkPtrs[p] = value.Pointer{} // invalidate this half + } + for _, trunk := range trunks { + switch p := trunk.Metadata.(type) { + case trunkReference: + unpair(p.p) // standalone pointer cannot be part of a pair + case trunkReferences: + p0, ok0 := pairedTrunkPtrs[p.pp[0]] + p1, ok1 := pairedTrunkPtrs[p.pp[1]] + switch { + case !ok0 && !ok1: + // Register the newly seen pair. + pairedTrunkPtrs[p.pp[0]] = p.pp[1] + pairedTrunkPtrs[p.pp[1]] = p.pp[0] + case ok0 && ok1 && p0 == p.pp[1] && p1 == p.pp[0]: + // Exact pair already seen; do nothing. + default: + // Pair conflicts with some other pair; break all pairs. + unpair(p.pp[0]) + unpair(p.pp[1]) + } + } + } + + // Correlate each pointer referenced by leaves to a unique identifier, + // and print the IDs for each trunk that matches those pointers. + var nextID uint + ptrIDs := make(map[value.Pointer]uint) + newID := func() uint { + id := nextID + nextID++ + return id + } + for _, trunk := range trunks { + switch p := trunk.Metadata.(type) { + case trunkReference: + if print := leafPtrs[p.p]; print { + id, ok := ptrIDs[p.p] + if !ok { + id = newID() + ptrIDs[p.p] = id + } + trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id)) + } + case trunkReferences: + print0 := leafPtrs[p.pp[0]] + print1 := leafPtrs[p.pp[1]] + if print0 || print1 { + id0, ok0 := ptrIDs[p.pp[0]] + id1, ok1 := ptrIDs[p.pp[1]] + isPair := pairedTrunkPtrs[p.pp[0]] == p.pp[1] && pairedTrunkPtrs[p.pp[1]] == p.pp[0] + if isPair { + var id uint + assert(ok0 == ok1) // must be seen together or not at all + if ok0 { + assert(id0 == id1) // must have the same ID + id = id0 + } else { + id = newID() + ptrIDs[p.pp[0]] = id + ptrIDs[p.pp[1]] = id + } + trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id)) + } else { + if print0 && !ok0 { + id0 = newID() + ptrIDs[p.pp[0]] = id0 + } + if print1 && !ok1 { + id1 = newID() + ptrIDs[p.pp[1]] = id1 + } + switch { + case print0 && print1: + trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id0)+","+formatReference(id1)) + case print0: + trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id0)) + case print1: + trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id1)) + } + } + } + } + } + + // Update all leaf references with the unique identifier. + for _, leaf := range leaves { + if id, ok := ptrIDs[leaf.Metadata.(leafReference).p]; ok { + leaf.Prefix = updateReferencePrefix(leaf.Prefix, formatReference(id)) + } + } +} + +func formatReference(id uint) string { + return fmt.Sprintf("ref#%d", id) +} + +func updateReferencePrefix(prefix, ref string) string { + if prefix == "" { + return pointerDelimPrefix + ref + pointerDelimSuffix + } + suffix := strings.TrimPrefix(prefix, pointerDelimPrefix) + return pointerDelimPrefix + ref + ": " + suffix +} diff --git a/vendor/github.com/google/go-cmp/cmp/report_reflect.go b/vendor/github.com/google/go-cmp/cmp/report_reflect.go new file mode 100644 index 00000000..2d722ea5 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/report_reflect.go @@ -0,0 +1,353 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +package cmp + +import ( + "fmt" + "reflect" + "strconv" + "strings" + "unicode" + "unicode/utf8" + + "github.com/google/go-cmp/cmp/internal/value" +) + +type formatValueOptions struct { + // AvoidStringer controls whether to avoid calling custom stringer + // methods like error.Error or fmt.Stringer.String. + AvoidStringer bool + + // PrintAddresses controls whether to print the address of all pointers, + // slice elements, and maps. + PrintAddresses bool + + // QualifiedNames controls whether FormatType uses the fully qualified name + // (including the full package path as opposed to just the package name). + QualifiedNames bool + + // VerbosityLevel controls the amount of output to produce. + // A higher value produces more output. A value of zero or lower produces + // no output (represented using an ellipsis). + // If LimitVerbosity is false, then the level is treated as infinite. + VerbosityLevel int + + // LimitVerbosity specifies that formatting should respect VerbosityLevel. + LimitVerbosity bool +} + +// FormatType prints the type as if it were wrapping s. +// This may return s as-is depending on the current type and TypeMode mode. +func (opts formatOptions) FormatType(t reflect.Type, s textNode) textNode { + // Check whether to emit the type or not. + switch opts.TypeMode { + case autoType: + switch t.Kind() { + case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map: + if s.Equal(textNil) { + return s + } + default: + return s + } + if opts.DiffMode == diffIdentical { + return s // elide type for identical nodes + } + case elideType: + return s + } + + // Determine the type label, applying special handling for unnamed types. + typeName := value.TypeString(t, opts.QualifiedNames) + if t.Name() == "" { + // According to Go grammar, certain type literals contain symbols that + // do not strongly bind to the next lexicographical token (e.g., *T). + switch t.Kind() { + case reflect.Chan, reflect.Func, reflect.Ptr: + typeName = "(" + typeName + ")" + } + } + return &textWrap{Prefix: typeName, Value: wrapParens(s)} +} + +// wrapParens wraps s with a set of parenthesis, but avoids it if the +// wrapped node itself is already surrounded by a pair of parenthesis or braces. +// It handles unwrapping one level of pointer-reference nodes. +func wrapParens(s textNode) textNode { + var refNode *textWrap + if s2, ok := s.(*textWrap); ok { + // Unwrap a single pointer reference node. + switch s2.Metadata.(type) { + case leafReference, trunkReference, trunkReferences: + refNode = s2 + if s3, ok := refNode.Value.(*textWrap); ok { + s2 = s3 + } + } + + // Already has delimiters that make parenthesis unnecessary. + hasParens := strings.HasPrefix(s2.Prefix, "(") && strings.HasSuffix(s2.Suffix, ")") + hasBraces := strings.HasPrefix(s2.Prefix, "{") && strings.HasSuffix(s2.Suffix, "}") + if hasParens || hasBraces { + return s + } + } + if refNode != nil { + refNode.Value = &textWrap{Prefix: "(", Value: refNode.Value, Suffix: ")"} + return s + } + return &textWrap{Prefix: "(", Value: s, Suffix: ")"} +} + +// FormatValue prints the reflect.Value, taking extra care to avoid descending +// into pointers already in ptrs. As pointers are visited, ptrs is also updated. +func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind, ptrs *pointerReferences) (out textNode) { + if !v.IsValid() { + return nil + } + t := v.Type() + + // Check slice element for cycles. + if parentKind == reflect.Slice { + ptrRef, visited := ptrs.Push(v.Addr()) + if visited { + return makeLeafReference(ptrRef, false) + } + defer ptrs.Pop() + defer func() { out = wrapTrunkReference(ptrRef, false, out) }() + } + + // Check whether there is an Error or String method to call. + if !opts.AvoidStringer && v.CanInterface() { + // Avoid calling Error or String methods on nil receivers since many + // implementations crash when doing so. + if (t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface) || !v.IsNil() { + var prefix, strVal string + switch v := v.Interface().(type) { + case error: + prefix, strVal = "e", v.Error() + case fmt.Stringer: + prefix, strVal = "s", v.String() + } + if prefix != "" { + maxLen := len(strVal) + if opts.LimitVerbosity { + maxLen = (1 << opts.verbosity()) << 5 // 32, 64, 128, 256, etc... + } + if len(strVal) > maxLen+len(textEllipsis) { + return textLine(prefix + formatString(strVal[:maxLen]) + string(textEllipsis)) + } + return textLine(prefix + formatString(strVal)) + } + } + } + + // Check whether to explicitly wrap the result with the type. + var skipType bool + defer func() { + if !skipType { + out = opts.FormatType(t, out) + } + }() + + switch t.Kind() { + case reflect.Bool: + return textLine(fmt.Sprint(v.Bool())) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return textLine(fmt.Sprint(v.Int())) + case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return textLine(fmt.Sprint(v.Uint())) + case reflect.Uint8: + if parentKind == reflect.Slice || parentKind == reflect.Array { + return textLine(formatHex(v.Uint())) + } + return textLine(fmt.Sprint(v.Uint())) + case reflect.Uintptr: + return textLine(formatHex(v.Uint())) + case reflect.Float32, reflect.Float64: + return textLine(fmt.Sprint(v.Float())) + case reflect.Complex64, reflect.Complex128: + return textLine(fmt.Sprint(v.Complex())) + case reflect.String: + maxLen := v.Len() + if opts.LimitVerbosity { + maxLen = (1 << opts.verbosity()) << 5 // 32, 64, 128, 256, etc... + } + if v.Len() > maxLen+len(textEllipsis) { + return textLine(formatString(v.String()[:maxLen]) + string(textEllipsis)) + } + return textLine(formatString(v.String())) + case reflect.UnsafePointer, reflect.Chan, reflect.Func: + return textLine(formatPointer(value.PointerOf(v), true)) + case reflect.Struct: + var list textList + v := makeAddressable(v) // needed for retrieveUnexportedField + maxLen := v.NumField() + if opts.LimitVerbosity { + maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... + opts.VerbosityLevel-- + } + for i := 0; i < v.NumField(); i++ { + vv := v.Field(i) + if value.IsZero(vv) { + continue // Elide fields with zero values + } + if len(list) == maxLen { + list.AppendEllipsis(diffStats{}) + break + } + sf := t.Field(i) + if supportExporters && !isExported(sf.Name) { + vv = retrieveUnexportedField(v, sf, true) + } + s := opts.WithTypeMode(autoType).FormatValue(vv, t.Kind(), ptrs) + list = append(list, textRecord{Key: sf.Name, Value: s}) + } + return &textWrap{Prefix: "{", Value: list, Suffix: "}"} + case reflect.Slice: + if v.IsNil() { + return textNil + } + fallthrough + case reflect.Array: + maxLen := v.Len() + if opts.LimitVerbosity { + maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... + opts.VerbosityLevel-- + } + var list textList + for i := 0; i < v.Len(); i++ { + if len(list) == maxLen { + list.AppendEllipsis(diffStats{}) + break + } + s := opts.WithTypeMode(elideType).FormatValue(v.Index(i), t.Kind(), ptrs) + list = append(list, textRecord{Value: s}) + } + + out = &textWrap{Prefix: "{", Value: list, Suffix: "}"} + if t.Kind() == reflect.Slice && opts.PrintAddresses { + header := fmt.Sprintf("ptr:%v, len:%d, cap:%d", formatPointer(value.PointerOf(v), false), v.Len(), v.Cap()) + out = &textWrap{Prefix: pointerDelimPrefix + header + pointerDelimSuffix, Value: out} + } + return out + case reflect.Map: + if v.IsNil() { + return textNil + } + + // Check pointer for cycles. + ptrRef, visited := ptrs.Push(v) + if visited { + return makeLeafReference(ptrRef, opts.PrintAddresses) + } + defer ptrs.Pop() + + maxLen := v.Len() + if opts.LimitVerbosity { + maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... + opts.VerbosityLevel-- + } + var list textList + for _, k := range value.SortKeys(v.MapKeys()) { + if len(list) == maxLen { + list.AppendEllipsis(diffStats{}) + break + } + sk := formatMapKey(k, false, ptrs) + sv := opts.WithTypeMode(elideType).FormatValue(v.MapIndex(k), t.Kind(), ptrs) + list = append(list, textRecord{Key: sk, Value: sv}) + } + + out = &textWrap{Prefix: "{", Value: list, Suffix: "}"} + out = wrapTrunkReference(ptrRef, opts.PrintAddresses, out) + return out + case reflect.Ptr: + if v.IsNil() { + return textNil + } + + // Check pointer for cycles. + ptrRef, visited := ptrs.Push(v) + if visited { + out = makeLeafReference(ptrRef, opts.PrintAddresses) + return &textWrap{Prefix: "&", Value: out} + } + defer ptrs.Pop() + + skipType = true // Let the underlying value print the type instead + out = opts.FormatValue(v.Elem(), t.Kind(), ptrs) + out = wrapTrunkReference(ptrRef, opts.PrintAddresses, out) + out = &textWrap{Prefix: "&", Value: out} + return out + case reflect.Interface: + if v.IsNil() { + return textNil + } + // Interfaces accept different concrete types, + // so configure the underlying value to explicitly print the type. + skipType = true // Print the concrete type instead + return opts.WithTypeMode(emitType).FormatValue(v.Elem(), t.Kind(), ptrs) + default: + panic(fmt.Sprintf("%v kind not handled", v.Kind())) + } +} + +// formatMapKey formats v as if it were a map key. +// The result is guaranteed to be a single line. +func formatMapKey(v reflect.Value, disambiguate bool, ptrs *pointerReferences) string { + var opts formatOptions + opts.DiffMode = diffIdentical + opts.TypeMode = elideType + opts.PrintAddresses = disambiguate + opts.AvoidStringer = disambiguate + opts.QualifiedNames = disambiguate + s := opts.FormatValue(v, reflect.Map, ptrs).String() + return strings.TrimSpace(s) +} + +// formatString prints s as a double-quoted or backtick-quoted string. +func formatString(s string) string { + // Use quoted string if it the same length as a raw string literal. + // Otherwise, attempt to use the raw string form. + qs := strconv.Quote(s) + if len(qs) == 1+len(s)+1 { + return qs + } + + // Disallow newlines to ensure output is a single line. + // Only allow printable runes for readability purposes. + rawInvalid := func(r rune) bool { + return r == '`' || r == '\n' || !(unicode.IsPrint(r) || r == '\t') + } + if utf8.ValidString(s) && strings.IndexFunc(s, rawInvalid) < 0 { + return "`" + s + "`" + } + return qs +} + +// formatHex prints u as a hexadecimal integer in Go notation. +func formatHex(u uint64) string { + var f string + switch { + case u <= 0xff: + f = "0x%02x" + case u <= 0xffff: + f = "0x%04x" + case u <= 0xffffff: + f = "0x%06x" + case u <= 0xffffffff: + f = "0x%08x" + case u <= 0xffffffffff: + f = "0x%010x" + case u <= 0xffffffffffff: + f = "0x%012x" + case u <= 0xffffffffffffff: + f = "0x%014x" + case u <= 0xffffffffffffffff: + f = "0x%016x" + } + return fmt.Sprintf(f, u) +} diff --git a/vendor/github.com/google/go-cmp/cmp/report_slices.go b/vendor/github.com/google/go-cmp/cmp/report_slices.go new file mode 100644 index 00000000..35315dad --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/report_slices.go @@ -0,0 +1,448 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +package cmp + +import ( + "bytes" + "fmt" + "reflect" + "strconv" + "strings" + "unicode" + "unicode/utf8" + + "github.com/google/go-cmp/cmp/internal/diff" +) + +// CanFormatDiffSlice reports whether we support custom formatting for nodes +// that are slices of primitive kinds or strings. +func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool { + switch { + case opts.DiffMode != diffUnknown: + return false // Must be formatting in diff mode + case v.NumDiff == 0: + return false // No differences detected + case !v.ValueX.IsValid() || !v.ValueY.IsValid(): + return false // Both values must be valid + case v.Type.Kind() == reflect.Slice && (v.ValueX.Len() == 0 || v.ValueY.Len() == 0): + return false // Both slice values have to be non-empty + case v.NumIgnored > 0: + return false // Some ignore option was used + case v.NumTransformed > 0: + return false // Some transform option was used + case v.NumCompared > 1: + return false // More than one comparison was used + case v.NumCompared == 1 && v.Type.Name() != "": + // The need for cmp to check applicability of options on every element + // in a slice is a significant performance detriment for large []byte. + // The workaround is to specify Comparer(bytes.Equal), + // which enables cmp to compare []byte more efficiently. + // If they differ, we still want to provide batched diffing. + // The logic disallows named types since they tend to have their own + // String method, with nicer formatting than what this provides. + return false + } + + switch t := v.Type; t.Kind() { + case reflect.String: + case reflect.Array, reflect.Slice: + // Only slices of primitive types have specialized handling. + switch t.Elem().Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, + reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: + default: + return false + } + + // If a sufficient number of elements already differ, + // use specialized formatting even if length requirement is not met. + if v.NumDiff > v.NumSame { + return true + } + default: + return false + } + + // Use specialized string diffing for longer slices or strings. + const minLength = 64 + return v.ValueX.Len() >= minLength && v.ValueY.Len() >= minLength +} + +// FormatDiffSlice prints a diff for the slices (or strings) represented by v. +// This provides custom-tailored logic to make printing of differences in +// textual strings and slices of primitive kinds more readable. +func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode { + assert(opts.DiffMode == diffUnknown) + t, vx, vy := v.Type, v.ValueX, v.ValueY + + // Auto-detect the type of the data. + var isLinedText, isText, isBinary bool + var sx, sy string + switch { + case t.Kind() == reflect.String: + sx, sy = vx.String(), vy.String() + isText = true // Initial estimate, verify later + case t.Kind() == reflect.Slice && t.Elem() == reflect.TypeOf(byte(0)): + sx, sy = string(vx.Bytes()), string(vy.Bytes()) + isBinary = true // Initial estimate, verify later + case t.Kind() == reflect.Array: + // Arrays need to be addressable for slice operations to work. + vx2, vy2 := reflect.New(t).Elem(), reflect.New(t).Elem() + vx2.Set(vx) + vy2.Set(vy) + vx, vy = vx2, vy2 + } + if isText || isBinary { + var numLines, lastLineIdx, maxLineLen int + isBinary = !utf8.ValidString(sx) || !utf8.ValidString(sy) + for i, r := range sx + sy { + if !(unicode.IsPrint(r) || unicode.IsSpace(r)) || r == utf8.RuneError { + isBinary = true + break + } + if r == '\n' { + if maxLineLen < i-lastLineIdx { + maxLineLen = i - lastLineIdx + } + lastLineIdx = i + 1 + numLines++ + } + } + isText = !isBinary + isLinedText = isText && numLines >= 4 && maxLineLen <= 1024 + } + + // Format the string into printable records. + var list textList + var delim string + switch { + // If the text appears to be multi-lined text, + // then perform differencing across individual lines. + case isLinedText: + ssx := strings.Split(sx, "\n") + ssy := strings.Split(sy, "\n") + list = opts.formatDiffSlice( + reflect.ValueOf(ssx), reflect.ValueOf(ssy), 1, "line", + func(v reflect.Value, d diffMode) textRecord { + s := formatString(v.Index(0).String()) + return textRecord{Diff: d, Value: textLine(s)} + }, + ) + delim = "\n" + + // If possible, use a custom triple-quote (""") syntax for printing + // differences in a string literal. This format is more readable, + // but has edge-cases where differences are visually indistinguishable. + // This format is avoided under the following conditions: + // • A line starts with `"""` + // • A line starts with "..." + // • A line contains non-printable characters + // • Adjacent different lines differ only by whitespace + // + // For example: + // """ + // ... // 3 identical lines + // foo + // bar + // - baz + // + BAZ + // """ + isTripleQuoted := true + prevRemoveLines := map[string]bool{} + prevInsertLines := map[string]bool{} + var list2 textList + list2 = append(list2, textRecord{Value: textLine(`"""`), ElideComma: true}) + for _, r := range list { + if !r.Value.Equal(textEllipsis) { + line, _ := strconv.Unquote(string(r.Value.(textLine))) + line = strings.TrimPrefix(strings.TrimSuffix(line, "\r"), "\r") // trim leading/trailing carriage returns for legacy Windows endline support + normLine := strings.Map(func(r rune) rune { + if unicode.IsSpace(r) { + return -1 // drop whitespace to avoid visually indistinguishable output + } + return r + }, line) + isPrintable := func(r rune) bool { + return unicode.IsPrint(r) || r == '\t' // specially treat tab as printable + } + isTripleQuoted = !strings.HasPrefix(line, `"""`) && !strings.HasPrefix(line, "...") && strings.TrimFunc(line, isPrintable) == "" + switch r.Diff { + case diffRemoved: + isTripleQuoted = isTripleQuoted && !prevInsertLines[normLine] + prevRemoveLines[normLine] = true + case diffInserted: + isTripleQuoted = isTripleQuoted && !prevRemoveLines[normLine] + prevInsertLines[normLine] = true + } + if !isTripleQuoted { + break + } + r.Value = textLine(line) + r.ElideComma = true + } + if !(r.Diff == diffRemoved || r.Diff == diffInserted) { // start a new non-adjacent difference group + prevRemoveLines = map[string]bool{} + prevInsertLines = map[string]bool{} + } + list2 = append(list2, r) + } + if r := list2[len(list2)-1]; r.Diff == diffIdentical && len(r.Value.(textLine)) == 0 { + list2 = list2[:len(list2)-1] // elide single empty line at the end + } + list2 = append(list2, textRecord{Value: textLine(`"""`), ElideComma: true}) + if isTripleQuoted { + var out textNode = &textWrap{Prefix: "(", Value: list2, Suffix: ")"} + switch t.Kind() { + case reflect.String: + if t != reflect.TypeOf(string("")) { + out = opts.FormatType(t, out) + } + case reflect.Slice: + // Always emit type for slices since the triple-quote syntax + // looks like a string (not a slice). + opts = opts.WithTypeMode(emitType) + out = opts.FormatType(t, out) + } + return out + } + + // If the text appears to be single-lined text, + // then perform differencing in approximately fixed-sized chunks. + // The output is printed as quoted strings. + case isText: + list = opts.formatDiffSlice( + reflect.ValueOf(sx), reflect.ValueOf(sy), 64, "byte", + func(v reflect.Value, d diffMode) textRecord { + s := formatString(v.String()) + return textRecord{Diff: d, Value: textLine(s)} + }, + ) + delim = "" + + // If the text appears to be binary data, + // then perform differencing in approximately fixed-sized chunks. + // The output is inspired by hexdump. + case isBinary: + list = opts.formatDiffSlice( + reflect.ValueOf(sx), reflect.ValueOf(sy), 16, "byte", + func(v reflect.Value, d diffMode) textRecord { + var ss []string + for i := 0; i < v.Len(); i++ { + ss = append(ss, formatHex(v.Index(i).Uint())) + } + s := strings.Join(ss, ", ") + comment := commentString(fmt.Sprintf("%c|%v|", d, formatASCII(v.String()))) + return textRecord{Diff: d, Value: textLine(s), Comment: comment} + }, + ) + + // For all other slices of primitive types, + // then perform differencing in approximately fixed-sized chunks. + // The size of each chunk depends on the width of the element kind. + default: + var chunkSize int + if t.Elem().Kind() == reflect.Bool { + chunkSize = 16 + } else { + switch t.Elem().Bits() { + case 8: + chunkSize = 16 + case 16: + chunkSize = 12 + case 32: + chunkSize = 8 + default: + chunkSize = 8 + } + } + list = opts.formatDiffSlice( + vx, vy, chunkSize, t.Elem().Kind().String(), + func(v reflect.Value, d diffMode) textRecord { + var ss []string + for i := 0; i < v.Len(); i++ { + switch t.Elem().Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + ss = append(ss, fmt.Sprint(v.Index(i).Int())) + case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64: + ss = append(ss, fmt.Sprint(v.Index(i).Uint())) + case reflect.Uint8, reflect.Uintptr: + ss = append(ss, formatHex(v.Index(i).Uint())) + case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: + ss = append(ss, fmt.Sprint(v.Index(i).Interface())) + } + } + s := strings.Join(ss, ", ") + return textRecord{Diff: d, Value: textLine(s)} + }, + ) + } + + // Wrap the output with appropriate type information. + var out textNode = &textWrap{Prefix: "{", Value: list, Suffix: "}"} + if !isText { + // The "{...}" byte-sequence literal is not valid Go syntax for strings. + // Emit the type for extra clarity (e.g. "string{...}"). + if t.Kind() == reflect.String { + opts = opts.WithTypeMode(emitType) + } + return opts.FormatType(t, out) + } + switch t.Kind() { + case reflect.String: + out = &textWrap{Prefix: "strings.Join(", Value: out, Suffix: fmt.Sprintf(", %q)", delim)} + if t != reflect.TypeOf(string("")) { + out = opts.FormatType(t, out) + } + case reflect.Slice: + out = &textWrap{Prefix: "bytes.Join(", Value: out, Suffix: fmt.Sprintf(", %q)", delim)} + if t != reflect.TypeOf([]byte(nil)) { + out = opts.FormatType(t, out) + } + } + return out +} + +// formatASCII formats s as an ASCII string. +// This is useful for printing binary strings in a semi-legible way. +func formatASCII(s string) string { + b := bytes.Repeat([]byte{'.'}, len(s)) + for i := 0; i < len(s); i++ { + if ' ' <= s[i] && s[i] <= '~' { + b[i] = s[i] + } + } + return string(b) +} + +func (opts formatOptions) formatDiffSlice( + vx, vy reflect.Value, chunkSize int, name string, + makeRec func(reflect.Value, diffMode) textRecord, +) (list textList) { + es := diff.Difference(vx.Len(), vy.Len(), func(ix int, iy int) diff.Result { + return diff.BoolResult(vx.Index(ix).Interface() == vy.Index(iy).Interface()) + }) + + appendChunks := func(v reflect.Value, d diffMode) int { + n0 := v.Len() + for v.Len() > 0 { + n := chunkSize + if n > v.Len() { + n = v.Len() + } + list = append(list, makeRec(v.Slice(0, n), d)) + v = v.Slice(n, v.Len()) + } + return n0 - v.Len() + } + + var numDiffs int + maxLen := -1 + if opts.LimitVerbosity { + maxLen = (1 << opts.verbosity()) << 2 // 4, 8, 16, 32, 64, etc... + opts.VerbosityLevel-- + } + + groups := coalesceAdjacentEdits(name, es) + groups = coalesceInterveningIdentical(groups, chunkSize/4) + maxGroup := diffStats{Name: name} + for i, ds := range groups { + if maxLen >= 0 && numDiffs >= maxLen { + maxGroup = maxGroup.Append(ds) + continue + } + + // Print equal. + if ds.NumDiff() == 0 { + // Compute the number of leading and trailing equal bytes to print. + var numLo, numHi int + numEqual := ds.NumIgnored + ds.NumIdentical + for numLo < chunkSize*numContextRecords && numLo+numHi < numEqual && i != 0 { + numLo++ + } + for numHi < chunkSize*numContextRecords && numLo+numHi < numEqual && i != len(groups)-1 { + numHi++ + } + if numEqual-(numLo+numHi) <= chunkSize && ds.NumIgnored == 0 { + numHi = numEqual - numLo // Avoid pointless coalescing of single equal row + } + + // Print the equal bytes. + appendChunks(vx.Slice(0, numLo), diffIdentical) + if numEqual > numLo+numHi { + ds.NumIdentical -= numLo + numHi + list.AppendEllipsis(ds) + } + appendChunks(vx.Slice(numEqual-numHi, numEqual), diffIdentical) + vx = vx.Slice(numEqual, vx.Len()) + vy = vy.Slice(numEqual, vy.Len()) + continue + } + + // Print unequal. + len0 := len(list) + nx := appendChunks(vx.Slice(0, ds.NumIdentical+ds.NumRemoved+ds.NumModified), diffRemoved) + vx = vx.Slice(nx, vx.Len()) + ny := appendChunks(vy.Slice(0, ds.NumIdentical+ds.NumInserted+ds.NumModified), diffInserted) + vy = vy.Slice(ny, vy.Len()) + numDiffs += len(list) - len0 + } + if maxGroup.IsZero() { + assert(vx.Len() == 0 && vy.Len() == 0) + } else { + list.AppendEllipsis(maxGroup) + } + return list +} + +// coalesceAdjacentEdits coalesces the list of edits into groups of adjacent +// equal or unequal counts. +func coalesceAdjacentEdits(name string, es diff.EditScript) (groups []diffStats) { + var prevCase int // Arbitrary index into which case last occurred + lastStats := func(i int) *diffStats { + if prevCase != i { + groups = append(groups, diffStats{Name: name}) + prevCase = i + } + return &groups[len(groups)-1] + } + for _, e := range es { + switch e { + case diff.Identity: + lastStats(1).NumIdentical++ + case diff.UniqueX: + lastStats(2).NumRemoved++ + case diff.UniqueY: + lastStats(2).NumInserted++ + case diff.Modified: + lastStats(2).NumModified++ + } + } + return groups +} + +// coalesceInterveningIdentical coalesces sufficiently short (<= windowSize) +// equal groups into adjacent unequal groups that currently result in a +// dual inserted/removed printout. This acts as a high-pass filter to smooth +// out high-frequency changes within the windowSize. +func coalesceInterveningIdentical(groups []diffStats, windowSize int) []diffStats { + groups, groupsOrig := groups[:0], groups + for i, ds := range groupsOrig { + if len(groups) >= 2 && ds.NumDiff() > 0 { + prev := &groups[len(groups)-2] // Unequal group + curr := &groups[len(groups)-1] // Equal group + next := &groupsOrig[i] // Unequal group + hadX, hadY := prev.NumRemoved > 0, prev.NumInserted > 0 + hasX, hasY := next.NumRemoved > 0, next.NumInserted > 0 + if ((hadX || hasX) && (hadY || hasY)) && curr.NumIdentical <= windowSize { + *prev = prev.Append(*curr).Append(*next) + groups = groups[:len(groups)-1] // Truncate off equal group + continue + } + } + groups = append(groups, ds) + } + return groups +} diff --git a/vendor/github.com/google/go-cmp/cmp/report_text.go b/vendor/github.com/google/go-cmp/cmp/report_text.go new file mode 100644 index 00000000..8b12c05c --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/report_text.go @@ -0,0 +1,431 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +package cmp + +import ( + "bytes" + "fmt" + "math/rand" + "strings" + "time" + "unicode/utf8" + + "github.com/google/go-cmp/cmp/internal/flags" +) + +var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0 + +const maxColumnLength = 80 + +type indentMode int + +func (n indentMode) appendIndent(b []byte, d diffMode) []byte { + // The output of Diff is documented as being unstable to provide future + // flexibility in changing the output for more humanly readable reports. + // This logic intentionally introduces instability to the exact output + // so that users can detect accidental reliance on stability early on, + // rather than much later when an actual change to the format occurs. + if flags.Deterministic || randBool { + // Use regular spaces (U+0020). + switch d { + case diffUnknown, diffIdentical: + b = append(b, " "...) + case diffRemoved: + b = append(b, "- "...) + case diffInserted: + b = append(b, "+ "...) + } + } else { + // Use non-breaking spaces (U+00a0). + switch d { + case diffUnknown, diffIdentical: + b = append(b, "  "...) + case diffRemoved: + b = append(b, "- "...) + case diffInserted: + b = append(b, "+ "...) + } + } + return repeatCount(n).appendChar(b, '\t') +} + +type repeatCount int + +func (n repeatCount) appendChar(b []byte, c byte) []byte { + for ; n > 0; n-- { + b = append(b, c) + } + return b +} + +// textNode is a simplified tree-based representation of structured text. +// Possible node types are textWrap, textList, or textLine. +type textNode interface { + // Len reports the length in bytes of a single-line version of the tree. + // Nested textRecord.Diff and textRecord.Comment fields are ignored. + Len() int + // Equal reports whether the two trees are structurally identical. + // Nested textRecord.Diff and textRecord.Comment fields are compared. + Equal(textNode) bool + // String returns the string representation of the text tree. + // It is not guaranteed that len(x.String()) == x.Len(), + // nor that x.String() == y.String() implies that x.Equal(y). + String() string + + // formatCompactTo formats the contents of the tree as a single-line string + // to the provided buffer. Any nested textRecord.Diff and textRecord.Comment + // fields are ignored. + // + // However, not all nodes in the tree should be collapsed as a single-line. + // If a node can be collapsed as a single-line, it is replaced by a textLine + // node. Since the top-level node cannot replace itself, this also returns + // the current node itself. + // + // This does not mutate the receiver. + formatCompactTo([]byte, diffMode) ([]byte, textNode) + // formatExpandedTo formats the contents of the tree as a multi-line string + // to the provided buffer. In order for column alignment to operate well, + // formatCompactTo must be called before calling formatExpandedTo. + formatExpandedTo([]byte, diffMode, indentMode) []byte +} + +// textWrap is a wrapper that concatenates a prefix and/or a suffix +// to the underlying node. +type textWrap struct { + Prefix string // e.g., "bytes.Buffer{" + Value textNode // textWrap | textList | textLine + Suffix string // e.g., "}" + Metadata interface{} // arbitrary metadata; has no effect on formatting +} + +func (s *textWrap) Len() int { + return len(s.Prefix) + s.Value.Len() + len(s.Suffix) +} +func (s1 *textWrap) Equal(s2 textNode) bool { + if s2, ok := s2.(*textWrap); ok { + return s1.Prefix == s2.Prefix && s1.Value.Equal(s2.Value) && s1.Suffix == s2.Suffix + } + return false +} +func (s *textWrap) String() string { + var d diffMode + var n indentMode + _, s2 := s.formatCompactTo(nil, d) + b := n.appendIndent(nil, d) // Leading indent + b = s2.formatExpandedTo(b, d, n) // Main body + b = append(b, '\n') // Trailing newline + return string(b) +} +func (s *textWrap) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { + n0 := len(b) // Original buffer length + b = append(b, s.Prefix...) + b, s.Value = s.Value.formatCompactTo(b, d) + b = append(b, s.Suffix...) + if _, ok := s.Value.(textLine); ok { + return b, textLine(b[n0:]) + } + return b, s +} +func (s *textWrap) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte { + b = append(b, s.Prefix...) + b = s.Value.formatExpandedTo(b, d, n) + b = append(b, s.Suffix...) + return b +} + +// textList is a comma-separated list of textWrap or textLine nodes. +// The list may be formatted as multi-lines or single-line at the discretion +// of the textList.formatCompactTo method. +type textList []textRecord +type textRecord struct { + Diff diffMode // e.g., 0 or '-' or '+' + Key string // e.g., "MyField" + Value textNode // textWrap | textLine + ElideComma bool // avoid trailing comma + Comment fmt.Stringer // e.g., "6 identical fields" +} + +// AppendEllipsis appends a new ellipsis node to the list if none already +// exists at the end. If cs is non-zero it coalesces the statistics with the +// previous diffStats. +func (s *textList) AppendEllipsis(ds diffStats) { + hasStats := !ds.IsZero() + if len(*s) == 0 || !(*s)[len(*s)-1].Value.Equal(textEllipsis) { + if hasStats { + *s = append(*s, textRecord{Value: textEllipsis, ElideComma: true, Comment: ds}) + } else { + *s = append(*s, textRecord{Value: textEllipsis, ElideComma: true}) + } + return + } + if hasStats { + (*s)[len(*s)-1].Comment = (*s)[len(*s)-1].Comment.(diffStats).Append(ds) + } +} + +func (s textList) Len() (n int) { + for i, r := range s { + n += len(r.Key) + if r.Key != "" { + n += len(": ") + } + n += r.Value.Len() + if i < len(s)-1 { + n += len(", ") + } + } + return n +} + +func (s1 textList) Equal(s2 textNode) bool { + if s2, ok := s2.(textList); ok { + if len(s1) != len(s2) { + return false + } + for i := range s1 { + r1, r2 := s1[i], s2[i] + if !(r1.Diff == r2.Diff && r1.Key == r2.Key && r1.Value.Equal(r2.Value) && r1.Comment == r2.Comment) { + return false + } + } + return true + } + return false +} + +func (s textList) String() string { + return (&textWrap{Prefix: "{", Value: s, Suffix: "}"}).String() +} + +func (s textList) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { + s = append(textList(nil), s...) // Avoid mutating original + + // Determine whether we can collapse this list as a single line. + n0 := len(b) // Original buffer length + var multiLine bool + for i, r := range s { + if r.Diff == diffInserted || r.Diff == diffRemoved { + multiLine = true + } + b = append(b, r.Key...) + if r.Key != "" { + b = append(b, ": "...) + } + b, s[i].Value = r.Value.formatCompactTo(b, d|r.Diff) + if _, ok := s[i].Value.(textLine); !ok { + multiLine = true + } + if r.Comment != nil { + multiLine = true + } + if i < len(s)-1 { + b = append(b, ", "...) + } + } + // Force multi-lined output when printing a removed/inserted node that + // is sufficiently long. + if (d == diffInserted || d == diffRemoved) && len(b[n0:]) > maxColumnLength { + multiLine = true + } + if !multiLine { + return b, textLine(b[n0:]) + } + return b, s +} + +func (s textList) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte { + alignKeyLens := s.alignLens( + func(r textRecord) bool { + _, isLine := r.Value.(textLine) + return r.Key == "" || !isLine + }, + func(r textRecord) int { return utf8.RuneCountInString(r.Key) }, + ) + alignValueLens := s.alignLens( + func(r textRecord) bool { + _, isLine := r.Value.(textLine) + return !isLine || r.Value.Equal(textEllipsis) || r.Comment == nil + }, + func(r textRecord) int { return utf8.RuneCount(r.Value.(textLine)) }, + ) + + // Format lists of simple lists in a batched form. + // If the list is sequence of only textLine values, + // then batch multiple values on a single line. + var isSimple bool + for _, r := range s { + _, isLine := r.Value.(textLine) + isSimple = r.Diff == 0 && r.Key == "" && isLine && r.Comment == nil + if !isSimple { + break + } + } + if isSimple { + n++ + var batch []byte + emitBatch := func() { + if len(batch) > 0 { + b = n.appendIndent(append(b, '\n'), d) + b = append(b, bytes.TrimRight(batch, " ")...) + batch = batch[:0] + } + } + for _, r := range s { + line := r.Value.(textLine) + if len(batch)+len(line)+len(", ") > maxColumnLength { + emitBatch() + } + batch = append(batch, line...) + batch = append(batch, ", "...) + } + emitBatch() + n-- + return n.appendIndent(append(b, '\n'), d) + } + + // Format the list as a multi-lined output. + n++ + for i, r := range s { + b = n.appendIndent(append(b, '\n'), d|r.Diff) + if r.Key != "" { + b = append(b, r.Key+": "...) + } + b = alignKeyLens[i].appendChar(b, ' ') + + b = r.Value.formatExpandedTo(b, d|r.Diff, n) + if !r.ElideComma { + b = append(b, ',') + } + b = alignValueLens[i].appendChar(b, ' ') + + if r.Comment != nil { + b = append(b, " // "+r.Comment.String()...) + } + } + n-- + + return n.appendIndent(append(b, '\n'), d) +} + +func (s textList) alignLens( + skipFunc func(textRecord) bool, + lenFunc func(textRecord) int, +) []repeatCount { + var startIdx, endIdx, maxLen int + lens := make([]repeatCount, len(s)) + for i, r := range s { + if skipFunc(r) { + for j := startIdx; j < endIdx && j < len(s); j++ { + lens[j] = repeatCount(maxLen - lenFunc(s[j])) + } + startIdx, endIdx, maxLen = i+1, i+1, 0 + } else { + if maxLen < lenFunc(r) { + maxLen = lenFunc(r) + } + endIdx = i + 1 + } + } + for j := startIdx; j < endIdx && j < len(s); j++ { + lens[j] = repeatCount(maxLen - lenFunc(s[j])) + } + return lens +} + +// textLine is a single-line segment of text and is always a leaf node +// in the textNode tree. +type textLine []byte + +var ( + textNil = textLine("nil") + textEllipsis = textLine("...") +) + +func (s textLine) Len() int { + return len(s) +} +func (s1 textLine) Equal(s2 textNode) bool { + if s2, ok := s2.(textLine); ok { + return bytes.Equal([]byte(s1), []byte(s2)) + } + return false +} +func (s textLine) String() string { + return string(s) +} +func (s textLine) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { + return append(b, s...), s +} +func (s textLine) formatExpandedTo(b []byte, _ diffMode, _ indentMode) []byte { + return append(b, s...) +} + +type diffStats struct { + Name string + NumIgnored int + NumIdentical int + NumRemoved int + NumInserted int + NumModified int +} + +func (s diffStats) IsZero() bool { + s.Name = "" + return s == diffStats{} +} + +func (s diffStats) NumDiff() int { + return s.NumRemoved + s.NumInserted + s.NumModified +} + +func (s diffStats) Append(ds diffStats) diffStats { + assert(s.Name == ds.Name) + s.NumIgnored += ds.NumIgnored + s.NumIdentical += ds.NumIdentical + s.NumRemoved += ds.NumRemoved + s.NumInserted += ds.NumInserted + s.NumModified += ds.NumModified + return s +} + +// String prints a humanly-readable summary of coalesced records. +// +// Example: +// diffStats{Name: "Field", NumIgnored: 5}.String() => "5 ignored fields" +func (s diffStats) String() string { + var ss []string + var sum int + labels := [...]string{"ignored", "identical", "removed", "inserted", "modified"} + counts := [...]int{s.NumIgnored, s.NumIdentical, s.NumRemoved, s.NumInserted, s.NumModified} + for i, n := range counts { + if n > 0 { + ss = append(ss, fmt.Sprintf("%d %v", n, labels[i])) + } + sum += n + } + + // Pluralize the name (adjusting for some obscure English grammar rules). + name := s.Name + if sum > 1 { + name += "s" + if strings.HasSuffix(name, "ys") { + name = name[:len(name)-2] + "ies" // e.g., "entrys" => "entries" + } + } + + // Format the list according to English grammar (with Oxford comma). + switch n := len(ss); n { + case 0: + return "" + case 1, 2: + return strings.Join(ss, " and ") + " " + name + default: + return strings.Join(ss[:n-1], ", ") + ", and " + ss[n-1] + " " + name + } +} + +type commentString string + +func (s commentString) String() string { return string(s) } diff --git a/vendor/github.com/google/go-cmp/cmp/report_value.go b/vendor/github.com/google/go-cmp/cmp/report_value.go new file mode 100644 index 00000000..83031a7f --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/report_value.go @@ -0,0 +1,121 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +package cmp + +import "reflect" + +// valueNode represents a single node within a report, which is a +// structured representation of the value tree, containing information +// regarding which nodes are equal or not. +type valueNode struct { + parent *valueNode + + Type reflect.Type + ValueX reflect.Value + ValueY reflect.Value + + // NumSame is the number of leaf nodes that are equal. + // All descendants are equal only if NumDiff is 0. + NumSame int + // NumDiff is the number of leaf nodes that are not equal. + NumDiff int + // NumIgnored is the number of leaf nodes that are ignored. + NumIgnored int + // NumCompared is the number of leaf nodes that were compared + // using an Equal method or Comparer function. + NumCompared int + // NumTransformed is the number of non-leaf nodes that were transformed. + NumTransformed int + // NumChildren is the number of transitive descendants of this node. + // This counts from zero; thus, leaf nodes have no descendants. + NumChildren int + // MaxDepth is the maximum depth of the tree. This counts from zero; + // thus, leaf nodes have a depth of zero. + MaxDepth int + + // Records is a list of struct fields, slice elements, or map entries. + Records []reportRecord // If populated, implies Value is not populated + + // Value is the result of a transformation, pointer indirect, of + // type assertion. + Value *valueNode // If populated, implies Records is not populated + + // TransformerName is the name of the transformer. + TransformerName string // If non-empty, implies Value is populated +} +type reportRecord struct { + Key reflect.Value // Invalid for slice element + Value *valueNode +} + +func (parent *valueNode) PushStep(ps PathStep) (child *valueNode) { + vx, vy := ps.Values() + child = &valueNode{parent: parent, Type: ps.Type(), ValueX: vx, ValueY: vy} + switch s := ps.(type) { + case StructField: + assert(parent.Value == nil) + parent.Records = append(parent.Records, reportRecord{Key: reflect.ValueOf(s.Name()), Value: child}) + case SliceIndex: + assert(parent.Value == nil) + parent.Records = append(parent.Records, reportRecord{Value: child}) + case MapIndex: + assert(parent.Value == nil) + parent.Records = append(parent.Records, reportRecord{Key: s.Key(), Value: child}) + case Indirect: + assert(parent.Value == nil && parent.Records == nil) + parent.Value = child + case TypeAssertion: + assert(parent.Value == nil && parent.Records == nil) + parent.Value = child + case Transform: + assert(parent.Value == nil && parent.Records == nil) + parent.Value = child + parent.TransformerName = s.Name() + parent.NumTransformed++ + default: + assert(parent == nil) // Must be the root step + } + return child +} + +func (r *valueNode) Report(rs Result) { + assert(r.MaxDepth == 0) // May only be called on leaf nodes + + if rs.ByIgnore() { + r.NumIgnored++ + } else { + if rs.Equal() { + r.NumSame++ + } else { + r.NumDiff++ + } + } + assert(r.NumSame+r.NumDiff+r.NumIgnored == 1) + + if rs.ByMethod() { + r.NumCompared++ + } + if rs.ByFunc() { + r.NumCompared++ + } + assert(r.NumCompared <= 1) +} + +func (child *valueNode) PopStep() (parent *valueNode) { + if child.parent == nil { + return nil + } + parent = child.parent + parent.NumSame += child.NumSame + parent.NumDiff += child.NumDiff + parent.NumIgnored += child.NumIgnored + parent.NumCompared += child.NumCompared + parent.NumTransformed += child.NumTransformed + parent.NumChildren += child.NumChildren + 1 + if parent.MaxDepth < child.MaxDepth+1 { + parent.MaxDepth = child.MaxDepth + 1 + } + return parent +} diff --git a/vendor/golang.org/x/xerrors/LICENSE b/vendor/golang.org/x/xerrors/LICENSE new file mode 100644 index 00000000..e4a47e17 --- /dev/null +++ b/vendor/golang.org/x/xerrors/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2019 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/xerrors/PATENTS b/vendor/golang.org/x/xerrors/PATENTS new file mode 100644 index 00000000..73309904 --- /dev/null +++ b/vendor/golang.org/x/xerrors/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/xerrors/README b/vendor/golang.org/x/xerrors/README new file mode 100644 index 00000000..aac7867a --- /dev/null +++ b/vendor/golang.org/x/xerrors/README @@ -0,0 +1,2 @@ +This repository holds the transition packages for the new Go 1.13 error values. +See golang.org/design/29934-error-values. diff --git a/vendor/golang.org/x/xerrors/adaptor.go b/vendor/golang.org/x/xerrors/adaptor.go new file mode 100644 index 00000000..4317f248 --- /dev/null +++ b/vendor/golang.org/x/xerrors/adaptor.go @@ -0,0 +1,193 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xerrors + +import ( + "bytes" + "fmt" + "io" + "reflect" + "strconv" +) + +// FormatError calls the FormatError method of f with an errors.Printer +// configured according to s and verb, and writes the result to s. +func FormatError(f Formatter, s fmt.State, verb rune) { + // Assuming this function is only called from the Format method, and given + // that FormatError takes precedence over Format, it cannot be called from + // any package that supports errors.Formatter. It is therefore safe to + // disregard that State may be a specific printer implementation and use one + // of our choice instead. + + // limitations: does not support printing error as Go struct. + + var ( + sep = " " // separator before next error + p = &state{State: s} + direct = true + ) + + var err error = f + + switch verb { + // Note that this switch must match the preference order + // for ordinary string printing (%#v before %+v, and so on). + + case 'v': + if s.Flag('#') { + if stringer, ok := err.(fmt.GoStringer); ok { + io.WriteString(&p.buf, stringer.GoString()) + goto exit + } + // proceed as if it were %v + } else if s.Flag('+') { + p.printDetail = true + sep = "\n - " + } + case 's': + case 'q', 'x', 'X': + // Use an intermediate buffer in the rare cases that precision, + // truncation, or one of the alternative verbs (q, x, and X) are + // specified. + direct = false + + default: + p.buf.WriteString("%!") + p.buf.WriteRune(verb) + p.buf.WriteByte('(') + switch { + case err != nil: + p.buf.WriteString(reflect.TypeOf(f).String()) + default: + p.buf.WriteString("") + } + p.buf.WriteByte(')') + io.Copy(s, &p.buf) + return + } + +loop: + for { + switch v := err.(type) { + case Formatter: + err = v.FormatError((*printer)(p)) + case fmt.Formatter: + v.Format(p, 'v') + break loop + default: + io.WriteString(&p.buf, v.Error()) + break loop + } + if err == nil { + break + } + if p.needColon || !p.printDetail { + p.buf.WriteByte(':') + p.needColon = false + } + p.buf.WriteString(sep) + p.inDetail = false + p.needNewline = false + } + +exit: + width, okW := s.Width() + prec, okP := s.Precision() + + if !direct || (okW && width > 0) || okP { + // Construct format string from State s. + format := []byte{'%'} + if s.Flag('-') { + format = append(format, '-') + } + if s.Flag('+') { + format = append(format, '+') + } + if s.Flag(' ') { + format = append(format, ' ') + } + if okW { + format = strconv.AppendInt(format, int64(width), 10) + } + if okP { + format = append(format, '.') + format = strconv.AppendInt(format, int64(prec), 10) + } + format = append(format, string(verb)...) + fmt.Fprintf(s, string(format), p.buf.String()) + } else { + io.Copy(s, &p.buf) + } +} + +var detailSep = []byte("\n ") + +// state tracks error printing state. It implements fmt.State. +type state struct { + fmt.State + buf bytes.Buffer + + printDetail bool + inDetail bool + needColon bool + needNewline bool +} + +func (s *state) Write(b []byte) (n int, err error) { + if s.printDetail { + if len(b) == 0 { + return 0, nil + } + if s.inDetail && s.needColon { + s.needNewline = true + if b[0] == '\n' { + b = b[1:] + } + } + k := 0 + for i, c := range b { + if s.needNewline { + if s.inDetail && s.needColon { + s.buf.WriteByte(':') + s.needColon = false + } + s.buf.Write(detailSep) + s.needNewline = false + } + if c == '\n' { + s.buf.Write(b[k:i]) + k = i + 1 + s.needNewline = true + } + } + s.buf.Write(b[k:]) + if !s.inDetail { + s.needColon = true + } + } else if !s.inDetail { + s.buf.Write(b) + } + return len(b), nil +} + +// printer wraps a state to implement an xerrors.Printer. +type printer state + +func (s *printer) Print(args ...interface{}) { + if !s.inDetail || s.printDetail { + fmt.Fprint((*state)(s), args...) + } +} + +func (s *printer) Printf(format string, args ...interface{}) { + if !s.inDetail || s.printDetail { + fmt.Fprintf((*state)(s), format, args...) + } +} + +func (s *printer) Detail() bool { + s.inDetail = true + return s.printDetail +} diff --git a/vendor/golang.org/x/xerrors/codereview.cfg b/vendor/golang.org/x/xerrors/codereview.cfg new file mode 100644 index 00000000..3f8b14b6 --- /dev/null +++ b/vendor/golang.org/x/xerrors/codereview.cfg @@ -0,0 +1 @@ +issuerepo: golang/go diff --git a/vendor/golang.org/x/xerrors/doc.go b/vendor/golang.org/x/xerrors/doc.go new file mode 100644 index 00000000..eef99d9d --- /dev/null +++ b/vendor/golang.org/x/xerrors/doc.go @@ -0,0 +1,22 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package xerrors implements functions to manipulate errors. +// +// This package is based on the Go 2 proposal for error values: +// https://golang.org/design/29934-error-values +// +// These functions were incorporated into the standard library's errors package +// in Go 1.13: +// - Is +// - As +// - Unwrap +// +// Also, Errorf's %w verb was incorporated into fmt.Errorf. +// +// Use this package to get equivalent behavior in all supported Go versions. +// +// No other features of this package were included in Go 1.13, and at present +// there are no plans to include any of them. +package xerrors // import "golang.org/x/xerrors" diff --git a/vendor/golang.org/x/xerrors/errors.go b/vendor/golang.org/x/xerrors/errors.go new file mode 100644 index 00000000..e88d3772 --- /dev/null +++ b/vendor/golang.org/x/xerrors/errors.go @@ -0,0 +1,33 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xerrors + +import "fmt" + +// errorString is a trivial implementation of error. +type errorString struct { + s string + frame Frame +} + +// New returns an error that formats as the given text. +// +// The returned error contains a Frame set to the caller's location and +// implements Formatter to show this information when printed with details. +func New(text string) error { + return &errorString{text, Caller(1)} +} + +func (e *errorString) Error() string { + return e.s +} + +func (e *errorString) Format(s fmt.State, v rune) { FormatError(e, s, v) } + +func (e *errorString) FormatError(p Printer) (next error) { + p.Print(e.s) + e.frame.Format(p) + return nil +} diff --git a/vendor/golang.org/x/xerrors/fmt.go b/vendor/golang.org/x/xerrors/fmt.go new file mode 100644 index 00000000..829862dd --- /dev/null +++ b/vendor/golang.org/x/xerrors/fmt.go @@ -0,0 +1,187 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xerrors + +import ( + "fmt" + "strings" + "unicode" + "unicode/utf8" + + "golang.org/x/xerrors/internal" +) + +const percentBangString = "%!" + +// Errorf formats according to a format specifier and returns the string as a +// value that satisfies error. +// +// The returned error includes the file and line number of the caller when +// formatted with additional detail enabled. If the last argument is an error +// the returned error's Format method will return it if the format string ends +// with ": %s", ": %v", or ": %w". If the last argument is an error and the +// format string ends with ": %w", the returned error implements an Unwrap +// method returning it. +// +// If the format specifier includes a %w verb with an error operand in a +// position other than at the end, the returned error will still implement an +// Unwrap method returning the operand, but the error's Format method will not +// return the wrapped error. +// +// It is invalid to include more than one %w verb or to supply it with an +// operand that does not implement the error interface. The %w verb is otherwise +// a synonym for %v. +func Errorf(format string, a ...interface{}) error { + format = formatPlusW(format) + // Support a ": %[wsv]" suffix, which works well with xerrors.Formatter. + wrap := strings.HasSuffix(format, ": %w") + idx, format2, ok := parsePercentW(format) + percentWElsewhere := !wrap && idx >= 0 + if !percentWElsewhere && (wrap || strings.HasSuffix(format, ": %s") || strings.HasSuffix(format, ": %v")) { + err := errorAt(a, len(a)-1) + if err == nil { + return &noWrapError{fmt.Sprintf(format, a...), nil, Caller(1)} + } + // TODO: this is not entirely correct. The error value could be + // printed elsewhere in format if it mixes numbered with unnumbered + // substitutions. With relatively small changes to doPrintf we can + // have it optionally ignore extra arguments and pass the argument + // list in its entirety. + msg := fmt.Sprintf(format[:len(format)-len(": %s")], a[:len(a)-1]...) + frame := Frame{} + if internal.EnableTrace { + frame = Caller(1) + } + if wrap { + return &wrapError{msg, err, frame} + } + return &noWrapError{msg, err, frame} + } + // Support %w anywhere. + // TODO: don't repeat the wrapped error's message when %w occurs in the middle. + msg := fmt.Sprintf(format2, a...) + if idx < 0 { + return &noWrapError{msg, nil, Caller(1)} + } + err := errorAt(a, idx) + if !ok || err == nil { + // Too many %ws or argument of %w is not an error. Approximate the Go + // 1.13 fmt.Errorf message. + return &noWrapError{fmt.Sprintf("%sw(%s)", percentBangString, msg), nil, Caller(1)} + } + frame := Frame{} + if internal.EnableTrace { + frame = Caller(1) + } + return &wrapError{msg, err, frame} +} + +func errorAt(args []interface{}, i int) error { + if i < 0 || i >= len(args) { + return nil + } + err, ok := args[i].(error) + if !ok { + return nil + } + return err +} + +// formatPlusW is used to avoid the vet check that will barf at %w. +func formatPlusW(s string) string { + return s +} + +// Return the index of the only %w in format, or -1 if none. +// Also return a rewritten format string with %w replaced by %v, and +// false if there is more than one %w. +// TODO: handle "%[N]w". +func parsePercentW(format string) (idx int, newFormat string, ok bool) { + // Loosely copied from golang.org/x/tools/go/analysis/passes/printf/printf.go. + idx = -1 + ok = true + n := 0 + sz := 0 + var isW bool + for i := 0; i < len(format); i += sz { + if format[i] != '%' { + sz = 1 + continue + } + // "%%" is not a format directive. + if i+1 < len(format) && format[i+1] == '%' { + sz = 2 + continue + } + sz, isW = parsePrintfVerb(format[i:]) + if isW { + if idx >= 0 { + ok = false + } else { + idx = n + } + // "Replace" the last character, the 'w', with a 'v'. + p := i + sz - 1 + format = format[:p] + "v" + format[p+1:] + } + n++ + } + return idx, format, ok +} + +// Parse the printf verb starting with a % at s[0]. +// Return how many bytes it occupies and whether the verb is 'w'. +func parsePrintfVerb(s string) (int, bool) { + // Assume only that the directive is a sequence of non-letters followed by a single letter. + sz := 0 + var r rune + for i := 1; i < len(s); i += sz { + r, sz = utf8.DecodeRuneInString(s[i:]) + if unicode.IsLetter(r) { + return i + sz, r == 'w' + } + } + return len(s), false +} + +type noWrapError struct { + msg string + err error + frame Frame +} + +func (e *noWrapError) Error() string { + return fmt.Sprint(e) +} + +func (e *noWrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) } + +func (e *noWrapError) FormatError(p Printer) (next error) { + p.Print(e.msg) + e.frame.Format(p) + return e.err +} + +type wrapError struct { + msg string + err error + frame Frame +} + +func (e *wrapError) Error() string { + return fmt.Sprint(e) +} + +func (e *wrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) } + +func (e *wrapError) FormatError(p Printer) (next error) { + p.Print(e.msg) + e.frame.Format(p) + return e.err +} + +func (e *wrapError) Unwrap() error { + return e.err +} diff --git a/vendor/golang.org/x/xerrors/format.go b/vendor/golang.org/x/xerrors/format.go new file mode 100644 index 00000000..1bc9c26b --- /dev/null +++ b/vendor/golang.org/x/xerrors/format.go @@ -0,0 +1,34 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xerrors + +// A Formatter formats error messages. +type Formatter interface { + error + + // FormatError prints the receiver's first error and returns the next error in + // the error chain, if any. + FormatError(p Printer) (next error) +} + +// A Printer formats error messages. +// +// The most common implementation of Printer is the one provided by package fmt +// during Printf (as of Go 1.13). Localization packages such as golang.org/x/text/message +// typically provide their own implementations. +type Printer interface { + // Print appends args to the message output. + Print(args ...interface{}) + + // Printf writes a formatted string. + Printf(format string, args ...interface{}) + + // Detail reports whether error detail is requested. + // After the first call to Detail, all text written to the Printer + // is formatted as additional detail, or ignored when + // detail has not been requested. + // If Detail returns false, the caller can avoid printing the detail at all. + Detail() bool +} diff --git a/vendor/golang.org/x/xerrors/frame.go b/vendor/golang.org/x/xerrors/frame.go new file mode 100644 index 00000000..0de628ec --- /dev/null +++ b/vendor/golang.org/x/xerrors/frame.go @@ -0,0 +1,56 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xerrors + +import ( + "runtime" +) + +// A Frame contains part of a call stack. +type Frame struct { + // Make room for three PCs: the one we were asked for, what it called, + // and possibly a PC for skipPleaseUseCallersFrames. See: + // https://go.googlesource.com/go/+/032678e0fb/src/runtime/extern.go#169 + frames [3]uintptr +} + +// Caller returns a Frame that describes a frame on the caller's stack. +// The argument skip is the number of frames to skip over. +// Caller(0) returns the frame for the caller of Caller. +func Caller(skip int) Frame { + var s Frame + runtime.Callers(skip+1, s.frames[:]) + return s +} + +// location reports the file, line, and function of a frame. +// +// The returned function may be "" even if file and line are not. +func (f Frame) location() (function, file string, line int) { + frames := runtime.CallersFrames(f.frames[:]) + if _, ok := frames.Next(); !ok { + return "", "", 0 + } + fr, ok := frames.Next() + if !ok { + return "", "", 0 + } + return fr.Function, fr.File, fr.Line +} + +// Format prints the stack as error detail. +// It should be called from an error's Format implementation +// after printing any other error detail. +func (f Frame) Format(p Printer) { + if p.Detail() { + function, file, line := f.location() + if function != "" { + p.Printf("%s\n ", function) + } + if file != "" { + p.Printf("%s:%d\n", file, line) + } + } +} diff --git a/vendor/golang.org/x/xerrors/go.mod b/vendor/golang.org/x/xerrors/go.mod new file mode 100644 index 00000000..870d4f61 --- /dev/null +++ b/vendor/golang.org/x/xerrors/go.mod @@ -0,0 +1,3 @@ +module golang.org/x/xerrors + +go 1.11 diff --git a/vendor/golang.org/x/xerrors/internal/internal.go b/vendor/golang.org/x/xerrors/internal/internal.go new file mode 100644 index 00000000..89f4eca5 --- /dev/null +++ b/vendor/golang.org/x/xerrors/internal/internal.go @@ -0,0 +1,8 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package internal + +// EnableTrace indicates whether stack information should be recorded in errors. +var EnableTrace = true diff --git a/vendor/golang.org/x/xerrors/wrap.go b/vendor/golang.org/x/xerrors/wrap.go new file mode 100644 index 00000000..9a3b5103 --- /dev/null +++ b/vendor/golang.org/x/xerrors/wrap.go @@ -0,0 +1,106 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xerrors + +import ( + "reflect" +) + +// A Wrapper provides context around another error. +type Wrapper interface { + // Unwrap returns the next error in the error chain. + // If there is no next error, Unwrap returns nil. + Unwrap() error +} + +// Opaque returns an error with the same error formatting as err +// but that does not match err and cannot be unwrapped. +func Opaque(err error) error { + return noWrapper{err} +} + +type noWrapper struct { + error +} + +func (e noWrapper) FormatError(p Printer) (next error) { + if f, ok := e.error.(Formatter); ok { + return f.FormatError(p) + } + p.Print(e.error) + return nil +} + +// Unwrap returns the result of calling the Unwrap method on err, if err implements +// Unwrap. Otherwise, Unwrap returns nil. +func Unwrap(err error) error { + u, ok := err.(Wrapper) + if !ok { + return nil + } + return u.Unwrap() +} + +// Is reports whether any error in err's chain matches target. +// +// An error is considered to match a target if it is equal to that target or if +// it implements a method Is(error) bool such that Is(target) returns true. +func Is(err, target error) bool { + if target == nil { + return err == target + } + + isComparable := reflect.TypeOf(target).Comparable() + for { + if isComparable && err == target { + return true + } + if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) { + return true + } + // TODO: consider supporing target.Is(err). This would allow + // user-definable predicates, but also may allow for coping with sloppy + // APIs, thereby making it easier to get away with them. + if err = Unwrap(err); err == nil { + return false + } + } +} + +// As finds the first error in err's chain that matches the type to which target +// points, and if so, sets the target to its value and returns true. An error +// matches a type if it is assignable to the target type, or if it has a method +// As(interface{}) bool such that As(target) returns true. As will panic if target +// is not a non-nil pointer to a type which implements error or is of interface type. +// +// The As method should set the target to its value and return true if err +// matches the type to which target points. +func As(err error, target interface{}) bool { + if target == nil { + panic("errors: target cannot be nil") + } + val := reflect.ValueOf(target) + typ := val.Type() + if typ.Kind() != reflect.Ptr || val.IsNil() { + panic("errors: target must be a non-nil pointer") + } + if e := typ.Elem(); e.Kind() != reflect.Interface && !e.Implements(errorType) { + panic("errors: *target must be interface or implement error") + } + targetType := typ.Elem() + for err != nil { + if reflect.TypeOf(err).AssignableTo(targetType) { + val.Elem().Set(reflect.ValueOf(err)) + return true + } + if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) { + return true + } + err = Unwrap(err) + } + return false +} + +var errorType = reflect.TypeOf((*error)(nil)).Elem() diff --git a/vendor/google.golang.org/protobuf/internal/msgfmt/format.go b/vendor/google.golang.org/protobuf/internal/msgfmt/format.go new file mode 100644 index 00000000..9547a530 --- /dev/null +++ b/vendor/google.golang.org/protobuf/internal/msgfmt/format.go @@ -0,0 +1,275 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package msgfmt implements a text marshaler combining the desirable features +// of both the JSON and proto text formats. +// It is optimized for human readability and has no associated deserializer. +package msgfmt + +import ( + "bytes" + "fmt" + "math" + "reflect" + "sort" + "strconv" + "strings" + "time" + + "google.golang.org/protobuf/encoding/protowire" + "google.golang.org/protobuf/internal/detrand" + "google.golang.org/protobuf/internal/genid" + "google.golang.org/protobuf/internal/mapsort" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" +) + +// Format returns a formatted string for the message. +func Format(m proto.Message) string { + return string(appendMessage(nil, m.ProtoReflect())) +} + +func appendValue(b []byte, v protoreflect.Value, fd protoreflect.FieldDescriptor) []byte { + switch v := v.Interface().(type) { + case bool, int32, int64, uint32, uint64, float32, float64: + return append(b, fmt.Sprint(v)...) + case string: + return append(b, strconv.Quote(string(v))...) + case []byte: + return append(b, strconv.Quote(string(v))...) + case protoreflect.EnumNumber: + return appendEnum(b, v, fd.Enum()) + case protoreflect.Message: + return appendMessage(b, v) + case protoreflect.List: + return appendList(b, v, fd) + case protoreflect.Map: + return appendMap(b, v, fd) + default: + panic(fmt.Sprintf("invalid type: %T", v)) + } +} + +func appendEnum(b []byte, v protoreflect.EnumNumber, ed protoreflect.EnumDescriptor) []byte { + if ev := ed.Values().ByNumber(v); ev != nil { + return append(b, ev.Name()...) + } + return strconv.AppendInt(b, int64(v), 10) +} + +func appendMessage(b []byte, m protoreflect.Message) []byte { + if b2 := appendKnownMessage(b, m); b2 != nil { + return b2 + } + + var fds []protoreflect.FieldDescriptor + m.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool { + fds = append(fds, fd) + return true + }) + sort.Slice(fds, func(i, j int) bool { + fdi, fdj := fds[i], fds[j] + switch { + case !fdi.IsExtension() && !fdj.IsExtension(): + return fdi.Index() < fdj.Index() + case fdi.IsExtension() && fdj.IsExtension(): + return fdi.FullName() < fdj.FullName() + default: + return !fdi.IsExtension() && fdj.IsExtension() + } + }) + + b = append(b, '{') + for _, fd := range fds { + k := string(fd.Name()) + if fd.IsExtension() { + k = string("[" + fd.FullName() + "]") + } + + b = append(b, k...) + b = append(b, ':') + b = appendValue(b, m.Get(fd), fd) + b = append(b, delim()...) + } + b = appendUnknown(b, m.GetUnknown()) + b = bytes.TrimRight(b, delim()) + b = append(b, '}') + return b +} + +var protocmpMessageType = reflect.TypeOf(map[string]interface{}(nil)) + +func appendKnownMessage(b []byte, m protoreflect.Message) []byte { + md := m.Descriptor() + fds := md.Fields() + switch md.FullName() { + case genid.Any_message_fullname: + var msgVal protoreflect.Message + url := m.Get(fds.ByNumber(genid.Any_TypeUrl_field_number)).String() + if v := reflect.ValueOf(m); v.Type().ConvertibleTo(protocmpMessageType) { + // For protocmp.Message, directly obtain the sub-message value + // which is stored in structured form, rather than as raw bytes. + m2 := v.Convert(protocmpMessageType).Interface().(map[string]interface{}) + v, ok := m2[string(genid.Any_Value_field_name)].(proto.Message) + if !ok { + return nil + } + msgVal = v.ProtoReflect() + } else { + val := m.Get(fds.ByNumber(genid.Any_Value_field_number)).Bytes() + mt, err := protoregistry.GlobalTypes.FindMessageByURL(url) + if err != nil { + return nil + } + msgVal = mt.New() + err = proto.UnmarshalOptions{AllowPartial: true}.Unmarshal(val, msgVal.Interface()) + if err != nil { + return nil + } + } + + b = append(b, '{') + b = append(b, "["+url+"]"...) + b = append(b, ':') + b = appendMessage(b, msgVal) + b = append(b, '}') + return b + + case genid.Timestamp_message_fullname: + secs := m.Get(fds.ByNumber(genid.Timestamp_Seconds_field_number)).Int() + nanos := m.Get(fds.ByNumber(genid.Timestamp_Nanos_field_number)).Int() + if nanos < 0 || nanos >= 1e9 { + return nil + } + t := time.Unix(secs, nanos).UTC() + x := t.Format("2006-01-02T15:04:05.000000000") // RFC 3339 + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, ".000") + return append(b, x+"Z"...) + + case genid.Duration_message_fullname: + secs := m.Get(fds.ByNumber(genid.Duration_Seconds_field_number)).Int() + nanos := m.Get(fds.ByNumber(genid.Duration_Nanos_field_number)).Int() + if nanos <= -1e9 || nanos >= 1e9 || (secs > 0 && nanos < 0) || (secs < 0 && nanos > 0) { + return nil + } + x := fmt.Sprintf("%d.%09d", secs, int64(math.Abs(float64(nanos)))) + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, ".000") + return append(b, x+"s"...) + + case genid.BoolValue_message_fullname, + genid.Int32Value_message_fullname, + genid.Int64Value_message_fullname, + genid.UInt32Value_message_fullname, + genid.UInt64Value_message_fullname, + genid.FloatValue_message_fullname, + genid.DoubleValue_message_fullname, + genid.StringValue_message_fullname, + genid.BytesValue_message_fullname: + fd := fds.ByNumber(genid.WrapperValue_Value_field_number) + return appendValue(b, m.Get(fd), fd) + } + + return nil +} + +func appendUnknown(b []byte, raw protoreflect.RawFields) []byte { + rs := make(map[protoreflect.FieldNumber][]protoreflect.RawFields) + for len(raw) > 0 { + num, _, n := protowire.ConsumeField(raw) + rs[num] = append(rs[num], raw[:n]) + raw = raw[n:] + } + + var ns []protoreflect.FieldNumber + for n := range rs { + ns = append(ns, n) + } + sort.Slice(ns, func(i, j int) bool { return ns[i] < ns[j] }) + + for _, n := range ns { + var leftBracket, rightBracket string + if len(rs[n]) > 1 { + leftBracket, rightBracket = "[", "]" + } + + b = strconv.AppendInt(b, int64(n), 10) + b = append(b, ':') + b = append(b, leftBracket...) + for _, r := range rs[n] { + num, typ, n := protowire.ConsumeTag(r) + r = r[n:] + switch typ { + case protowire.VarintType: + v, _ := protowire.ConsumeVarint(r) + b = strconv.AppendInt(b, int64(v), 10) + case protowire.Fixed32Type: + v, _ := protowire.ConsumeFixed32(r) + b = append(b, fmt.Sprintf("0x%08x", v)...) + case protowire.Fixed64Type: + v, _ := protowire.ConsumeFixed64(r) + b = append(b, fmt.Sprintf("0x%016x", v)...) + case protowire.BytesType: + v, _ := protowire.ConsumeBytes(r) + b = strconv.AppendQuote(b, string(v)) + case protowire.StartGroupType: + v, _ := protowire.ConsumeGroup(num, r) + b = append(b, '{') + b = appendUnknown(b, v) + b = bytes.TrimRight(b, delim()) + b = append(b, '}') + default: + panic(fmt.Sprintf("invalid type: %v", typ)) + } + b = append(b, delim()...) + } + b = bytes.TrimRight(b, delim()) + b = append(b, rightBracket...) + b = append(b, delim()...) + } + return b +} + +func appendList(b []byte, v protoreflect.List, fd protoreflect.FieldDescriptor) []byte { + b = append(b, '[') + for i := 0; i < v.Len(); i++ { + b = appendValue(b, v.Get(i), fd) + b = append(b, delim()...) + } + b = bytes.TrimRight(b, delim()) + b = append(b, ']') + return b +} + +func appendMap(b []byte, v protoreflect.Map, fd protoreflect.FieldDescriptor) []byte { + var ks []protoreflect.MapKey + mapsort.Range(v, fd.MapKey().Kind(), func(k protoreflect.MapKey, _ protoreflect.Value) bool { + ks = append(ks, k) + return true + }) + + b = append(b, '{') + for _, k := range ks { + b = appendValue(b, k.Value(), fd.MapKey()) + b = append(b, ':') + b = appendValue(b, v.Get(k), fd.MapValue()) + b = append(b, delim()...) + } + b = bytes.TrimRight(b, delim()) + b = append(b, '}') + return b +} + +func delim() string { + // Deliberately introduce instability into the message string to + // discourage users from depending on it. + if detrand.Bool() { + return " " + } + return ", " +} diff --git a/vendor/google.golang.org/protobuf/testing/protocmp/reflect.go b/vendor/google.golang.org/protobuf/testing/protocmp/reflect.go new file mode 100644 index 00000000..a4f9cac2 --- /dev/null +++ b/vendor/google.golang.org/protobuf/testing/protocmp/reflect.go @@ -0,0 +1,261 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package protocmp + +import ( + "reflect" + "sort" + "strconv" + "strings" + + "google.golang.org/protobuf/internal/genid" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/runtime/protoiface" +) + +func reflectValueOf(v interface{}) protoreflect.Value { + switch v := v.(type) { + case Enum: + return protoreflect.ValueOfEnum(v.Number()) + case Message: + return protoreflect.ValueOfMessage(v.ProtoReflect()) + case []byte: + return protoreflect.ValueOfBytes(v) // avoid overlap with reflect.Slice check below + default: + switch rv := reflect.ValueOf(v); { + case rv.Kind() == reflect.Slice: + return protoreflect.ValueOfList(reflectList{rv}) + case rv.Kind() == reflect.Map: + return protoreflect.ValueOfMap(reflectMap{rv}) + default: + return protoreflect.ValueOf(v) + } + } +} + +type reflectMessage Message + +func (m reflectMessage) stringKey(fd protoreflect.FieldDescriptor) string { + if m.Descriptor() != fd.ContainingMessage() { + panic("mismatching containing message") + } + if fd.IsExtension() { + return string("[" + fd.FullName() + "]") + } + return string(fd.Name()) +} + +func (m reflectMessage) Descriptor() protoreflect.MessageDescriptor { + return (Message)(m).Descriptor() +} +func (m reflectMessage) Type() protoreflect.MessageType { + return reflectMessageType{m.Descriptor()} +} +func (m reflectMessage) New() protoreflect.Message { + return m.Type().New() +} +func (m reflectMessage) Interface() protoreflect.ProtoMessage { + return Message(m) +} +func (m reflectMessage) Range(f func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool) { + // Range over populated known fields. + fds := m.Descriptor().Fields() + for i := 0; i < fds.Len(); i++ { + fd := fds.Get(i) + if m.Has(fd) && !f(fd, m.Get(fd)) { + return + } + } + + // Range over populated extension fields. + for _, xd := range m[messageTypeKey].(messageType).xds { + if m.Has(xd) && !f(xd, m.Get(xd)) { + return + } + } +} +func (m reflectMessage) Has(fd protoreflect.FieldDescriptor) bool { + _, ok := m[m.stringKey(fd)] + return ok +} +func (m reflectMessage) Clear(protoreflect.FieldDescriptor) { + panic("invalid mutation of read-only message") +} +func (m reflectMessage) Get(fd protoreflect.FieldDescriptor) protoreflect.Value { + v, ok := m[m.stringKey(fd)] + if !ok { + switch { + case fd.IsList(): + return protoreflect.ValueOfList(reflectList{}) + case fd.IsMap(): + return protoreflect.ValueOfMap(reflectMap{}) + case fd.Message() != nil: + return protoreflect.ValueOfMessage(reflectMessage{ + messageTypeKey: messageType{md: m.Descriptor()}, + }) + default: + return fd.Default() + } + } + + // The transformation may leave Any messages in structured form. + // If so, convert them back to a raw-encoded form. + if fd.FullName() == genid.Any_Value_field_fullname { + if m, ok := v.(Message); ok { + b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m) + if err != nil { + panic("BUG: " + err.Error()) + } + return protoreflect.ValueOfBytes(b) + } + } + + return reflectValueOf(v) +} +func (m reflectMessage) Set(protoreflect.FieldDescriptor, protoreflect.Value) { + panic("invalid mutation of read-only message") +} +func (m reflectMessage) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + panic("invalid mutation of read-only message") +} +func (m reflectMessage) NewField(protoreflect.FieldDescriptor) protoreflect.Value { + panic("not implemented") +} +func (m reflectMessage) WhichOneof(od protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + if m.Descriptor().Oneofs().ByName(od.Name()) != od { + panic("oneof descriptor does not belong to this message") + } + fds := od.Fields() + for i := 0; i < fds.Len(); i++ { + fd := fds.Get(i) + if _, ok := m[m.stringKey(fd)]; ok { + return fd + } + } + return nil +} +func (m reflectMessage) GetUnknown() protoreflect.RawFields { + var nums []protoreflect.FieldNumber + for k := range m { + if len(strings.Trim(k, "0123456789")) == 0 { + n, _ := strconv.ParseUint(k, 10, 32) + nums = append(nums, protoreflect.FieldNumber(n)) + } + } + sort.Slice(nums, func(i, j int) bool { return nums[i] < nums[j] }) + + var raw protoreflect.RawFields + for _, num := range nums { + b, _ := m[strconv.FormatUint(uint64(num), 10)].(protoreflect.RawFields) + raw = append(raw, b...) + } + return raw +} +func (m reflectMessage) SetUnknown(protoreflect.RawFields) { + panic("invalid mutation of read-only message") +} +func (m reflectMessage) IsValid() bool { + invalid, _ := m[messageInvalidKey].(bool) + return !invalid +} +func (m reflectMessage) ProtoMethods() *protoiface.Methods { + return nil +} + +type reflectMessageType struct{ protoreflect.MessageDescriptor } + +func (t reflectMessageType) New() protoreflect.Message { + panic("not implemented") +} +func (t reflectMessageType) Zero() protoreflect.Message { + panic("not implemented") +} +func (t reflectMessageType) Descriptor() protoreflect.MessageDescriptor { + return t.MessageDescriptor +} + +type reflectList struct{ v reflect.Value } + +func (ls reflectList) Len() int { + if !ls.IsValid() { + return 0 + } + return ls.v.Len() +} +func (ls reflectList) Get(i int) protoreflect.Value { + return reflectValueOf(ls.v.Index(i).Interface()) +} +func (ls reflectList) Set(int, protoreflect.Value) { + panic("invalid mutation of read-only list") +} +func (ls reflectList) Append(protoreflect.Value) { + panic("invalid mutation of read-only list") +} +func (ls reflectList) AppendMutable() protoreflect.Value { + panic("invalid mutation of read-only list") +} +func (ls reflectList) Truncate(int) { + panic("invalid mutation of read-only list") +} +func (ls reflectList) NewElement() protoreflect.Value { + panic("not implemented") +} +func (ls reflectList) IsValid() bool { + return ls.v.IsValid() +} + +type reflectMap struct{ v reflect.Value } + +func (ms reflectMap) Len() int { + if !ms.IsValid() { + return 0 + } + return ms.v.Len() +} +func (ms reflectMap) Range(f func(protoreflect.MapKey, protoreflect.Value) bool) { + if !ms.IsValid() { + return + } + ks := ms.v.MapKeys() + for _, k := range ks { + pk := reflectValueOf(k.Interface()).MapKey() + pv := reflectValueOf(ms.v.MapIndex(k).Interface()) + if !f(pk, pv) { + return + } + } +} +func (ms reflectMap) Has(k protoreflect.MapKey) bool { + if !ms.IsValid() { + return false + } + return ms.v.MapIndex(reflect.ValueOf(k.Interface())).IsValid() +} +func (ms reflectMap) Clear(protoreflect.MapKey) { + panic("invalid mutation of read-only list") +} +func (ms reflectMap) Get(k protoreflect.MapKey) protoreflect.Value { + if !ms.IsValid() { + return protoreflect.Value{} + } + v := ms.v.MapIndex(reflect.ValueOf(k.Interface())) + if !v.IsValid() { + return protoreflect.Value{} + } + return reflectValueOf(v.Interface()) +} +func (ms reflectMap) Set(protoreflect.MapKey, protoreflect.Value) { + panic("invalid mutation of read-only list") +} +func (ms reflectMap) Mutable(k protoreflect.MapKey) protoreflect.Value { + panic("invalid mutation of read-only list") +} +func (ms reflectMap) NewValue() protoreflect.Value { + panic("not implemented") +} +func (ms reflectMap) IsValid() bool { + return ms.v.IsValid() +} diff --git a/vendor/google.golang.org/protobuf/testing/protocmp/util.go b/vendor/google.golang.org/protobuf/testing/protocmp/util.go new file mode 100644 index 00000000..668bb2e9 --- /dev/null +++ b/vendor/google.golang.org/protobuf/testing/protocmp/util.go @@ -0,0 +1,679 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package protocmp + +import ( + "bytes" + "fmt" + "math" + "reflect" + "strings" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" +) + +var ( + enumReflectType = reflect.TypeOf(Enum{}) + messageReflectType = reflect.TypeOf(Message{}) +) + +// FilterEnum filters opt to only be applicable on standalone Enums, +// singular fields of enums, list fields of enums, or map fields of enum values, +// where the enum is the same type as the specified enum. +// +// The Go type of the last path step may be an: +// • Enum for singular fields, elements of a repeated field, +// values of a map field, or standalone Enums +// • []Enum for list fields +// • map[K]Enum for map fields +// • interface{} for a Message map entry value +// +// This must be used in conjunction with Transform. +func FilterEnum(enum protoreflect.Enum, opt cmp.Option) cmp.Option { + return FilterDescriptor(enum.Descriptor(), opt) +} + +// FilterMessage filters opt to only be applicable on standalone Messages, +// singular fields of messages, list fields of messages, or map fields of +// message values, where the message is the same type as the specified message. +// +// The Go type of the last path step may be an: +// • Message for singular fields, elements of a repeated field, +// values of a map field, or standalone Messages +// • []Message for list fields +// • map[K]Message for map fields +// • interface{} for a Message map entry value +// +// This must be used in conjunction with Transform. +func FilterMessage(message proto.Message, opt cmp.Option) cmp.Option { + return FilterDescriptor(message.ProtoReflect().Descriptor(), opt) +} + +// FilterField filters opt to only be applicable on the specified field +// in the message. It panics if a field of the given name does not exist. +// +// The Go type of the last path step may be an: +// • T for singular fields +// • []T for list fields +// • map[K]T for map fields +// • interface{} for a Message map entry value +// +// This must be used in conjunction with Transform. +func FilterField(message proto.Message, name protoreflect.Name, opt cmp.Option) cmp.Option { + md := message.ProtoReflect().Descriptor() + return FilterDescriptor(mustFindFieldDescriptor(md, name), opt) +} + +// FilterOneof filters opt to only be applicable on all fields within the +// specified oneof in the message. It panics if a oneof of the given name +// does not exist. +// +// The Go type of the last path step may be an: +// • T for singular fields +// • []T for list fields +// • map[K]T for map fields +// • interface{} for a Message map entry value +// +// This must be used in conjunction with Transform. +func FilterOneof(message proto.Message, name protoreflect.Name, opt cmp.Option) cmp.Option { + md := message.ProtoReflect().Descriptor() + return FilterDescriptor(mustFindOneofDescriptor(md, name), opt) +} + +// FilterDescriptor ignores the specified descriptor. +// +// The following descriptor types may be specified: +// • protoreflect.EnumDescriptor +// • protoreflect.MessageDescriptor +// • protoreflect.FieldDescriptor +// • protoreflect.OneofDescriptor +// +// For the behavior of each, see the corresponding filter function. +// Since this filter accepts a protoreflect.FieldDescriptor, it can be used +// to also filter for extension fields as a protoreflect.ExtensionDescriptor +// is just an alias to protoreflect.FieldDescriptor. +// +// This must be used in conjunction with Transform. +func FilterDescriptor(desc protoreflect.Descriptor, opt cmp.Option) cmp.Option { + f := newNameFilters(desc) + return cmp.FilterPath(f.Filter, opt) +} + +// IgnoreEnums ignores all enums of the specified types. +// It is equivalent to FilterEnum(enum, cmp.Ignore()) for each enum. +// +// This must be used in conjunction with Transform. +func IgnoreEnums(enums ...protoreflect.Enum) cmp.Option { + var ds []protoreflect.Descriptor + for _, e := range enums { + ds = append(ds, e.Descriptor()) + } + return IgnoreDescriptors(ds...) +} + +// IgnoreMessages ignores all messages of the specified types. +// It is equivalent to FilterMessage(message, cmp.Ignore()) for each message. +// +// This must be used in conjunction with Transform. +func IgnoreMessages(messages ...proto.Message) cmp.Option { + var ds []protoreflect.Descriptor + for _, m := range messages { + ds = append(ds, m.ProtoReflect().Descriptor()) + } + return IgnoreDescriptors(ds...) +} + +// IgnoreFields ignores the specified fields in the specified message. +// It is equivalent to FilterField(message, name, cmp.Ignore()) for each field +// in the message. +// +// This must be used in conjunction with Transform. +func IgnoreFields(message proto.Message, names ...protoreflect.Name) cmp.Option { + var ds []protoreflect.Descriptor + md := message.ProtoReflect().Descriptor() + for _, s := range names { + ds = append(ds, mustFindFieldDescriptor(md, s)) + } + return IgnoreDescriptors(ds...) +} + +// IgnoreOneofs ignores fields of the specified oneofs in the specified message. +// It is equivalent to FilterOneof(message, name, cmp.Ignore()) for each oneof +// in the message. +// +// This must be used in conjunction with Transform. +func IgnoreOneofs(message proto.Message, names ...protoreflect.Name) cmp.Option { + var ds []protoreflect.Descriptor + md := message.ProtoReflect().Descriptor() + for _, s := range names { + ds = append(ds, mustFindOneofDescriptor(md, s)) + } + return IgnoreDescriptors(ds...) +} + +// IgnoreDescriptors ignores the specified set of descriptors. +// It is equivalent to FilterDescriptor(desc, cmp.Ignore()) for each descriptor. +// +// This must be used in conjunction with Transform. +func IgnoreDescriptors(descs ...protoreflect.Descriptor) cmp.Option { + return cmp.FilterPath(newNameFilters(descs...).Filter, cmp.Ignore()) +} + +func mustFindFieldDescriptor(md protoreflect.MessageDescriptor, s protoreflect.Name) protoreflect.FieldDescriptor { + d := findDescriptor(md, s) + if fd, ok := d.(protoreflect.FieldDescriptor); ok && fd.Name() == s { + return fd + } + + var suggestion string + switch d.(type) { + case protoreflect.FieldDescriptor: + suggestion = fmt.Sprintf("; consider specifying field %q instead", d.Name()) + case protoreflect.OneofDescriptor: + suggestion = fmt.Sprintf("; consider specifying oneof %q with IgnoreOneofs instead", d.Name()) + } + panic(fmt.Sprintf("message %q has no field %q%s", md.FullName(), s, suggestion)) +} + +func mustFindOneofDescriptor(md protoreflect.MessageDescriptor, s protoreflect.Name) protoreflect.OneofDescriptor { + d := findDescriptor(md, s) + if od, ok := d.(protoreflect.OneofDescriptor); ok && d.Name() == s { + return od + } + + var suggestion string + switch d.(type) { + case protoreflect.OneofDescriptor: + suggestion = fmt.Sprintf("; consider specifying oneof %q instead", d.Name()) + case protoreflect.FieldDescriptor: + suggestion = fmt.Sprintf("; consider specifying field %q with IgnoreFields instead", d.Name()) + } + panic(fmt.Sprintf("message %q has no oneof %q%s", md.FullName(), s, suggestion)) +} + +func findDescriptor(md protoreflect.MessageDescriptor, s protoreflect.Name) protoreflect.Descriptor { + // Exact match. + if fd := md.Fields().ByName(s); fd != nil { + return fd + } + if od := md.Oneofs().ByName(s); od != nil { + return od + } + + // Best-effort match. + // + // It's a common user mistake to use the CameCased field name as it appears + // in the generated Go struct. Instead of complaining that it doesn't exist, + // suggest the real protobuf name that the user may have desired. + normalize := func(s protoreflect.Name) string { + return strings.Replace(strings.ToLower(string(s)), "_", "", -1) + } + for i := 0; i < md.Fields().Len(); i++ { + if fd := md.Fields().Get(i); normalize(fd.Name()) == normalize(s) { + return fd + } + } + for i := 0; i < md.Oneofs().Len(); i++ { + if od := md.Oneofs().Get(i); normalize(od.Name()) == normalize(s) { + return od + } + } + return nil +} + +type nameFilters struct { + names map[protoreflect.FullName]bool +} + +func newNameFilters(descs ...protoreflect.Descriptor) *nameFilters { + f := &nameFilters{names: make(map[protoreflect.FullName]bool)} + for _, d := range descs { + switch d := d.(type) { + case protoreflect.EnumDescriptor: + f.names[d.FullName()] = true + case protoreflect.MessageDescriptor: + f.names[d.FullName()] = true + case protoreflect.FieldDescriptor: + f.names[d.FullName()] = true + case protoreflect.OneofDescriptor: + for i := 0; i < d.Fields().Len(); i++ { + f.names[d.Fields().Get(i).FullName()] = true + } + default: + panic("invalid descriptor type") + } + } + return f +} + +func (f *nameFilters) Filter(p cmp.Path) bool { + vx, vy := p.Last().Values() + return (f.filterValue(vx) && f.filterValue(vy)) || f.filterFields(p) +} + +func (f *nameFilters) filterFields(p cmp.Path) bool { + // Trim off trailing type-assertions so that the filter can match on the + // concrete value held within an interface value. + if _, ok := p.Last().(cmp.TypeAssertion); ok { + p = p[:len(p)-1] + } + + // Filter for Message maps. + mi, ok := p.Index(-1).(cmp.MapIndex) + if !ok { + return false + } + ps := p.Index(-2) + if ps.Type() != messageReflectType { + return false + } + + // Check field name. + vx, vy := ps.Values() + mx := vx.Interface().(Message) + my := vy.Interface().(Message) + k := mi.Key().String() + if f.filterFieldName(mx, k) && f.filterFieldName(my, k) { + return true + } + + // Check field value. + vx, vy = mi.Values() + if f.filterFieldValue(vx) && f.filterFieldValue(vy) { + return true + } + + return false +} + +func (f *nameFilters) filterFieldName(m Message, k string) bool { + if md := m.Descriptor(); md != nil { + switch { + case protoreflect.Name(k).IsValid(): + return f.names[md.Fields().ByName(protoreflect.Name(k)).FullName()] + case strings.HasPrefix(k, "[") && strings.HasSuffix(k, "]"): + return f.names[protoreflect.FullName(k[1:len(k)-1])] + } + } + return false +} + +func (f *nameFilters) filterFieldValue(v reflect.Value) bool { + if !v.IsValid() { + return true // implies missing slice element or map entry + } + v = v.Elem() // map entries are always populated values + switch t := v.Type(); { + case t == enumReflectType || t == messageReflectType: + // Check for singular message or enum field. + return f.filterValue(v) + case t.Kind() == reflect.Slice && (t.Elem() == enumReflectType || t.Elem() == messageReflectType): + // Check for list field of enum or message type. + return f.filterValue(v.Index(0)) + case t.Kind() == reflect.Map && (t.Elem() == enumReflectType || t.Elem() == messageReflectType): + // Check for map field of enum or message type. + return f.filterValue(v.MapIndex(v.MapKeys()[0])) + } + return false +} + +func (f *nameFilters) filterValue(v reflect.Value) bool { + if !v.IsValid() { + return true // implies missing slice element or map entry + } + if !v.CanInterface() { + return false // implies unexported struct field + } + switch v := v.Interface().(type) { + case Enum: + return v.Descriptor() != nil && f.names[v.Descriptor().FullName()] + case Message: + return v.Descriptor() != nil && f.names[v.Descriptor().FullName()] + } + return false +} + +// IgnoreDefaultScalars ignores singular scalars that are unpopulated or +// explicitly set to the default value. +// This option does not effect elements in a list or entries in a map. +// +// This must be used in conjunction with Transform. +func IgnoreDefaultScalars() cmp.Option { + return cmp.FilterPath(func(p cmp.Path) bool { + // Filter for Message maps. + mi, ok := p.Index(-1).(cmp.MapIndex) + if !ok { + return false + } + ps := p.Index(-2) + if ps.Type() != messageReflectType { + return false + } + + // Check whether both fields are default or unpopulated scalars. + vx, vy := ps.Values() + mx := vx.Interface().(Message) + my := vy.Interface().(Message) + k := mi.Key().String() + return isDefaultScalar(mx, k) && isDefaultScalar(my, k) + }, cmp.Ignore()) +} + +func isDefaultScalar(m Message, k string) bool { + if _, ok := m[k]; !ok { + return true + } + + var fd protoreflect.FieldDescriptor + switch mt := m[messageTypeKey].(messageType); { + case protoreflect.Name(k).IsValid(): + fd = mt.md.Fields().ByName(protoreflect.Name(k)) + case strings.HasPrefix(k, "[") && strings.HasSuffix(k, "]"): + fd = mt.xds[protoreflect.FullName(k[1:len(k)-1])] + } + if fd == nil || !fd.Default().IsValid() { + return false + } + switch fd.Kind() { + case protoreflect.BytesKind: + v, ok := m[k].([]byte) + return ok && bytes.Equal(fd.Default().Bytes(), v) + case protoreflect.FloatKind: + v, ok := m[k].(float32) + return ok && equalFloat64(fd.Default().Float(), float64(v)) + case protoreflect.DoubleKind: + v, ok := m[k].(float64) + return ok && equalFloat64(fd.Default().Float(), float64(v)) + case protoreflect.EnumKind: + v, ok := m[k].(Enum) + return ok && fd.Default().Enum() == v.Number() + default: + return reflect.DeepEqual(fd.Default().Interface(), m[k]) + } +} + +func equalFloat64(x, y float64) bool { + return x == y || (math.IsNaN(x) && math.IsNaN(y)) +} + +// IgnoreEmptyMessages ignores messages that are empty or unpopulated. +// It applies to standalone Messages, singular message fields, +// list fields of messages, and map fields of message values. +// +// This must be used in conjunction with Transform. +func IgnoreEmptyMessages() cmp.Option { + return cmp.FilterPath(func(p cmp.Path) bool { + vx, vy := p.Last().Values() + return (isEmptyMessage(vx) && isEmptyMessage(vy)) || isEmptyMessageFields(p) + }, cmp.Ignore()) +} + +func isEmptyMessageFields(p cmp.Path) bool { + // Filter for Message maps. + mi, ok := p.Index(-1).(cmp.MapIndex) + if !ok { + return false + } + ps := p.Index(-2) + if ps.Type() != messageReflectType { + return false + } + + // Check field value. + vx, vy := mi.Values() + if isEmptyMessageFieldValue(vx) && isEmptyMessageFieldValue(vy) { + return true + } + + return false +} + +func isEmptyMessageFieldValue(v reflect.Value) bool { + if !v.IsValid() { + return true // implies missing slice element or map entry + } + v = v.Elem() // map entries are always populated values + switch t := v.Type(); { + case t == messageReflectType: + // Check singular field for empty message. + if !isEmptyMessage(v) { + return false + } + case t.Kind() == reflect.Slice && t.Elem() == messageReflectType: + // Check list field for all empty message elements. + for i := 0; i < v.Len(); i++ { + if !isEmptyMessage(v.Index(i)) { + return false + } + } + case t.Kind() == reflect.Map && t.Elem() == messageReflectType: + // Check map field for all empty message values. + for _, k := range v.MapKeys() { + if !isEmptyMessage(v.MapIndex(k)) { + return false + } + } + default: + return false + } + return true +} + +func isEmptyMessage(v reflect.Value) bool { + if !v.IsValid() { + return true // implies missing slice element or map entry + } + if !v.CanInterface() { + return false // implies unexported struct field + } + if m, ok := v.Interface().(Message); ok { + for k := range m { + if k != messageTypeKey && k != messageInvalidKey { + return false + } + } + return true + } + return false +} + +// IgnoreUnknown ignores unknown fields in all messages. +// +// This must be used in conjunction with Transform. +func IgnoreUnknown() cmp.Option { + return cmp.FilterPath(func(p cmp.Path) bool { + // Filter for Message maps. + mi, ok := p.Index(-1).(cmp.MapIndex) + if !ok { + return false + } + ps := p.Index(-2) + if ps.Type() != messageReflectType { + return false + } + + // Filter for unknown fields (which always have a numeric map key). + return strings.Trim(mi.Key().String(), "0123456789") == "" + }, cmp.Ignore()) +} + +// SortRepeated sorts repeated fields of the specified element type. +// The less function must be of the form "func(T, T) bool" where T is the +// Go element type for the repeated field kind. +// +// The element type T can be one of the following: +// • Go type for a protobuf scalar kind except for an enum +// (i.e., bool, int32, int64, uint32, uint64, float32, float64, string, and []byte) +// • E where E is a concrete enum type that implements protoreflect.Enum +// • M where M is a concrete message type that implement proto.Message +// +// This option only applies to repeated fields within a protobuf message. +// It does not operate on higher-order Go types that seem like a repeated field. +// For example, a []T outside the context of a protobuf message will not be +// handled by this option. To sort Go slices that are not repeated fields, +// consider using "github.com/google/go-cmp/cmp/cmpopts".SortSlices instead. +// +// This must be used in conjunction with Transform. +func SortRepeated(lessFunc interface{}) cmp.Option { + t, ok := checkTTBFunc(lessFunc) + if !ok { + panic(fmt.Sprintf("invalid less function: %T", lessFunc)) + } + + var opt cmp.Option + var sliceType reflect.Type + switch vf := reflect.ValueOf(lessFunc); { + case t.Implements(enumV2Type): + et := reflect.Zero(t).Interface().(protoreflect.Enum).Type() + lessFunc = func(x, y Enum) bool { + vx := reflect.ValueOf(et.New(x.Number())) + vy := reflect.ValueOf(et.New(y.Number())) + return vf.Call([]reflect.Value{vx, vy})[0].Bool() + } + opt = FilterDescriptor(et.Descriptor(), cmpopts.SortSlices(lessFunc)) + sliceType = reflect.SliceOf(enumReflectType) + case t.Implements(messageV2Type): + mt := reflect.Zero(t).Interface().(protoreflect.ProtoMessage).ProtoReflect().Type() + lessFunc = func(x, y Message) bool { + mx := mt.New().Interface() + my := mt.New().Interface() + proto.Merge(mx, x) + proto.Merge(my, y) + vx := reflect.ValueOf(mx) + vy := reflect.ValueOf(my) + return vf.Call([]reflect.Value{vx, vy})[0].Bool() + } + opt = FilterDescriptor(mt.Descriptor(), cmpopts.SortSlices(lessFunc)) + sliceType = reflect.SliceOf(messageReflectType) + default: + switch t { + case reflect.TypeOf(bool(false)): + case reflect.TypeOf(int32(0)): + case reflect.TypeOf(int64(0)): + case reflect.TypeOf(uint32(0)): + case reflect.TypeOf(uint64(0)): + case reflect.TypeOf(float32(0)): + case reflect.TypeOf(float64(0)): + case reflect.TypeOf(string("")): + case reflect.TypeOf([]byte(nil)): + default: + panic(fmt.Sprintf("invalid element type: %v", t)) + } + opt = cmpopts.SortSlices(lessFunc) + sliceType = reflect.SliceOf(t) + } + + return cmp.FilterPath(func(p cmp.Path) bool { + // Filter to only apply to repeated fields within a message. + if t := p.Index(-1).Type(); t == nil || t != sliceType { + return false + } + if t := p.Index(-2).Type(); t == nil || t.Kind() != reflect.Interface { + return false + } + if t := p.Index(-3).Type(); t == nil || t != messageReflectType { + return false + } + return true + }, opt) +} + +func checkTTBFunc(lessFunc interface{}) (reflect.Type, bool) { + switch t := reflect.TypeOf(lessFunc); { + case t == nil: + return nil, false + case t.NumIn() != 2 || t.In(0) != t.In(1) || t.IsVariadic(): + return nil, false + case t.NumOut() != 1 || t.Out(0) != reflect.TypeOf(false): + return nil, false + default: + return t.In(0), true + } +} + +// SortRepeatedFields sorts the specified repeated fields. +// Sorting a repeated field is useful for treating the list as a multiset +// (i.e., a set where each value can appear multiple times). +// It panics if the field does not exist or is not a repeated field. +// +// The sort ordering is as follows: +// • Booleans are sorted where false is sorted before true. +// • Integers are sorted in ascending order. +// • Floating-point numbers are sorted in ascending order according to +// the total ordering defined by IEEE-754 (section 5.10). +// • Strings and bytes are sorted lexicographically in ascending order. +// • Enums are sorted in ascending order based on its numeric value. +// • Messages are sorted according to some arbitrary ordering +// which is undefined and may change in future implementations. +// +// The ordering chosen for repeated messages is unlikely to be aesthetically +// preferred by humans. Consider using a custom sort function: +// +// FilterField(m, "foo_field", SortRepeated(func(x, y *foopb.MyMessage) bool { +// ... // user-provided definition for less +// })) +// +// This must be used in conjunction with Transform. +func SortRepeatedFields(message proto.Message, names ...protoreflect.Name) cmp.Option { + var opts cmp.Options + md := message.ProtoReflect().Descriptor() + for _, name := range names { + fd := mustFindFieldDescriptor(md, name) + if !fd.IsList() { + panic(fmt.Sprintf("message field %q is not repeated", fd.FullName())) + } + + var lessFunc interface{} + switch fd.Kind() { + case protoreflect.BoolKind: + lessFunc = func(x, y bool) bool { return !x && y } + case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: + lessFunc = func(x, y int32) bool { return x < y } + case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: + lessFunc = func(x, y int64) bool { return x < y } + case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: + lessFunc = func(x, y uint32) bool { return x < y } + case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: + lessFunc = func(x, y uint64) bool { return x < y } + case protoreflect.FloatKind: + lessFunc = lessF32 + case protoreflect.DoubleKind: + lessFunc = lessF64 + case protoreflect.StringKind: + lessFunc = func(x, y string) bool { return x < y } + case protoreflect.BytesKind: + lessFunc = func(x, y []byte) bool { return bytes.Compare(x, y) < 0 } + case protoreflect.EnumKind: + lessFunc = func(x, y Enum) bool { return x.Number() < y.Number() } + case protoreflect.MessageKind, protoreflect.GroupKind: + lessFunc = func(x, y Message) bool { return x.String() < y.String() } + default: + panic(fmt.Sprintf("invalid kind: %v", fd.Kind())) + } + opts = append(opts, FilterDescriptor(fd, cmpopts.SortSlices(lessFunc))) + } + return opts +} + +func lessF32(x, y float32) bool { + // Bit-wise implementation of IEEE-754, section 5.10. + xi := int32(math.Float32bits(x)) + yi := int32(math.Float32bits(y)) + xi ^= int32(uint32(xi>>31) >> 1) + yi ^= int32(uint32(yi>>31) >> 1) + return xi < yi +} +func lessF64(x, y float64) bool { + // Bit-wise implementation of IEEE-754, section 5.10. + xi := int64(math.Float64bits(x)) + yi := int64(math.Float64bits(y)) + xi ^= int64(uint64(xi>>63) >> 1) + yi ^= int64(uint64(yi>>63) >> 1) + return xi < yi +} diff --git a/vendor/google.golang.org/protobuf/testing/protocmp/xform.go b/vendor/google.golang.org/protobuf/testing/protocmp/xform.go new file mode 100644 index 00000000..25d1302d --- /dev/null +++ b/vendor/google.golang.org/protobuf/testing/protocmp/xform.go @@ -0,0 +1,332 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package protocmp provides protobuf specific options for the +// "github.com/google/go-cmp/cmp" package. +// +// The primary feature is the Transform option, which transform proto.Message +// types into a Message map that is suitable for cmp to introspect upon. +// All other options in this package must be used in conjunction with Transform. +package protocmp + +import ( + "reflect" + "strconv" + + "github.com/google/go-cmp/cmp" + + "google.golang.org/protobuf/encoding/protowire" + "google.golang.org/protobuf/internal/genid" + "google.golang.org/protobuf/internal/msgfmt" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" + "google.golang.org/protobuf/runtime/protoiface" + "google.golang.org/protobuf/runtime/protoimpl" +) + +var ( + enumV2Type = reflect.TypeOf((*protoreflect.Enum)(nil)).Elem() + messageV1Type = reflect.TypeOf((*protoiface.MessageV1)(nil)).Elem() + messageV2Type = reflect.TypeOf((*proto.Message)(nil)).Elem() +) + +// Enum is a dynamic representation of a protocol buffer enum that is +// suitable for cmp.Equal and cmp.Diff to compare upon. +type Enum struct { + num protoreflect.EnumNumber + ed protoreflect.EnumDescriptor +} + +// Descriptor returns the enum descriptor. +// It returns nil for a zero Enum value. +func (e Enum) Descriptor() protoreflect.EnumDescriptor { + return e.ed +} + +// Number returns the enum value as an integer. +func (e Enum) Number() protoreflect.EnumNumber { + return e.num +} + +// Equal reports whether e1 and e2 represent the same enum value. +func (e1 Enum) Equal(e2 Enum) bool { + if e1.ed.FullName() != e2.ed.FullName() { + return false + } + return e1.num == e2.num +} + +// String returns the name of the enum value if known (e.g., "ENUM_VALUE"), +// otherwise it returns the formatted decimal enum number (e.g., "14"). +func (e Enum) String() string { + if ev := e.ed.Values().ByNumber(e.num); ev != nil { + return string(ev.Name()) + } + return strconv.Itoa(int(e.num)) +} + +const ( + messageTypeKey = "@type" + messageInvalidKey = "@invalid" +) + +type messageType struct { + md protoreflect.MessageDescriptor + xds map[protoreflect.FullName]protoreflect.ExtensionDescriptor +} + +func (t messageType) String() string { + return string(t.md.FullName()) +} + +func (t1 messageType) Equal(t2 messageType) bool { + return t1.md.FullName() == t2.md.FullName() +} + +// Message is a dynamic representation of a protocol buffer message that is +// suitable for cmp.Equal and cmp.Diff to directly operate upon. +// +// Every populated known field (excluding extension fields) is stored in the map +// with the key being the short name of the field (e.g., "field_name") and +// the value determined by the kind and cardinality of the field. +// +// Singular scalars are represented by the same Go type as protoreflect.Value, +// singular messages are represented by the Message type, +// singular enums are represented by the Enum type, +// list fields are represented as a Go slice, and +// map fields are represented as a Go map. +// +// Every populated extension field is stored in the map with the key being the +// full name of the field surrounded by brackets (e.g., "[extension.full.name]") +// and the value determined according to the same rules as known fields. +// +// Every unknown field is stored in the map with the key being the field number +// encoded as a decimal string (e.g., "132") and the value being the raw bytes +// of the encoded field (as the protoreflect.RawFields type). +// +// Message values must not be created by or mutated by users. +type Message map[string]interface{} + +// Descriptor return the message descriptor. +// It returns nil for a zero Message value. +func (m Message) Descriptor() protoreflect.MessageDescriptor { + mt, _ := m[messageTypeKey].(messageType) + return mt.md +} + +// ProtoReflect returns a reflective view of m. +// It only implements the read-only operations of protoreflect.Message. +// Calling any mutating operations on m panics. +func (m Message) ProtoReflect() protoreflect.Message { + return (reflectMessage)(m) +} + +// ProtoMessage is a marker method from the legacy message interface. +func (m Message) ProtoMessage() {} + +// Reset is the required Reset method from the legacy message interface. +func (m Message) Reset() { + panic("invalid mutation of a read-only message") +} + +// String returns a formatted string for the message. +// It is intended for human debugging and has no guarantees about its +// exact format or the stability of its output. +func (m Message) String() string { + switch { + case m == nil: + return "" + case !m.ProtoReflect().IsValid(): + return "" + default: + return msgfmt.Format(m) + } +} + +type option struct{} + +// Transform returns a cmp.Option that converts each proto.Message to a Message. +// The transformation does not mutate nor alias any converted messages. +// +// The google.protobuf.Any message is automatically unmarshaled such that the +// "value" field is a Message representing the underlying message value +// assuming it could be resolved and properly unmarshaled. +// +// This does not directly transform higher-order composite Go types. +// For example, []*foopb.Message is not transformed into []Message, +// but rather the individual message elements of the slice are transformed. +// +// Note that there are currently no custom options for Transform, +// but the use of an unexported type keeps the future open. +func Transform(...option) cmp.Option { + // addrType returns a pointer to t if t isn't a pointer or interface. + addrType := func(t reflect.Type) reflect.Type { + if k := t.Kind(); k == reflect.Interface || k == reflect.Ptr { + return t + } + return reflect.PtrTo(t) + } + + // TODO: Should this transform protoreflect.Enum types to Enum as well? + return cmp.FilterPath(func(p cmp.Path) bool { + ps := p.Last() + if isMessageType(addrType(ps.Type())) { + return true + } + + // Check whether the concrete values of an interface both satisfy + // the Message interface. + if ps.Type().Kind() == reflect.Interface { + vx, vy := ps.Values() + if !vx.IsValid() || vx.IsNil() || !vy.IsValid() || vy.IsNil() { + return false + } + return isMessageType(addrType(vx.Elem().Type())) && isMessageType(addrType(vy.Elem().Type())) + } + + return false + }, cmp.Transformer("protocmp.Transform", func(v interface{}) Message { + // For user convenience, shallow copy the message value if necessary + // in order for it to implement the message interface. + if rv := reflect.ValueOf(v); rv.IsValid() && rv.Kind() != reflect.Ptr && !isMessageType(rv.Type()) { + pv := reflect.New(rv.Type()) + pv.Elem().Set(rv) + v = pv.Interface() + } + + m := protoimpl.X.MessageOf(v) + switch { + case m == nil: + return nil + case !m.IsValid(): + return Message{messageTypeKey: messageType{md: m.Descriptor()}, messageInvalidKey: true} + default: + return transformMessage(m) + } + })) +} + +func isMessageType(t reflect.Type) bool { + // Avoid tranforming the Message itself. + if t == reflect.TypeOf(Message(nil)) || t == reflect.TypeOf((*Message)(nil)) { + return false + } + return t.Implements(messageV1Type) || t.Implements(messageV2Type) +} + +func transformMessage(m protoreflect.Message) Message { + mx := Message{} + mt := messageType{md: m.Descriptor(), xds: make(map[protoreflect.FullName]protoreflect.FieldDescriptor)} + + // Handle known and extension fields. + m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { + s := string(fd.Name()) + if fd.IsExtension() { + s = "[" + string(fd.FullName()) + "]" + mt.xds[fd.FullName()] = fd + } + switch { + case fd.IsList(): + mx[s] = transformList(fd, v.List()) + case fd.IsMap(): + mx[s] = transformMap(fd, v.Map()) + default: + mx[s] = transformSingular(fd, v) + } + return true + }) + + // Handle unknown fields. + for b := m.GetUnknown(); len(b) > 0; { + num, _, n := protowire.ConsumeField(b) + s := strconv.Itoa(int(num)) + b2, _ := mx[s].(protoreflect.RawFields) + mx[s] = append(b2, b[:n]...) + b = b[n:] + } + + // Expand Any messages. + if mt.md.FullName() == genid.Any_message_fullname { + // TODO: Expose Transform option to specify a custom resolver? + s, _ := mx[string(genid.Any_TypeUrl_field_name)].(string) + b, _ := mx[string(genid.Any_Value_field_name)].([]byte) + mt, err := protoregistry.GlobalTypes.FindMessageByURL(s) + if mt != nil && err == nil { + m2 := mt.New() + err := proto.UnmarshalOptions{AllowPartial: true}.Unmarshal(b, m2.Interface()) + if err == nil { + mx[string(genid.Any_Value_field_name)] = transformMessage(m2) + } + } + } + + mx[messageTypeKey] = mt + return mx +} + +func transformList(fd protoreflect.FieldDescriptor, lv protoreflect.List) interface{} { + t := protoKindToGoType(fd.Kind()) + rv := reflect.MakeSlice(reflect.SliceOf(t), lv.Len(), lv.Len()) + for i := 0; i < lv.Len(); i++ { + v := reflect.ValueOf(transformSingular(fd, lv.Get(i))) + rv.Index(i).Set(v) + } + return rv.Interface() +} + +func transformMap(fd protoreflect.FieldDescriptor, mv protoreflect.Map) interface{} { + kfd := fd.MapKey() + vfd := fd.MapValue() + kt := protoKindToGoType(kfd.Kind()) + vt := protoKindToGoType(vfd.Kind()) + rv := reflect.MakeMapWithSize(reflect.MapOf(kt, vt), mv.Len()) + mv.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool { + kv := reflect.ValueOf(transformSingular(kfd, k.Value())) + vv := reflect.ValueOf(transformSingular(vfd, v)) + rv.SetMapIndex(kv, vv) + return true + }) + return rv.Interface() +} + +func transformSingular(fd protoreflect.FieldDescriptor, v protoreflect.Value) interface{} { + switch fd.Kind() { + case protoreflect.EnumKind: + return Enum{num: v.Enum(), ed: fd.Enum()} + case protoreflect.MessageKind, protoreflect.GroupKind: + return transformMessage(v.Message()) + default: + return v.Interface() + } +} + +func protoKindToGoType(k protoreflect.Kind) reflect.Type { + switch k { + case protoreflect.BoolKind: + return reflect.TypeOf(bool(false)) + case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: + return reflect.TypeOf(int32(0)) + case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: + return reflect.TypeOf(int64(0)) + case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: + return reflect.TypeOf(uint32(0)) + case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: + return reflect.TypeOf(uint64(0)) + case protoreflect.FloatKind: + return reflect.TypeOf(float32(0)) + case protoreflect.DoubleKind: + return reflect.TypeOf(float64(0)) + case protoreflect.StringKind: + return reflect.TypeOf(string("")) + case protoreflect.BytesKind: + return reflect.TypeOf([]byte(nil)) + case protoreflect.EnumKind: + return reflect.TypeOf(Enum{}) + case protoreflect.MessageKind, protoreflect.GroupKind: + return reflect.TypeOf(Message{}) + default: + panic("invalid kind") + } +} From f6c1e5cff3cae2f327efdc398782354b1143d071 Mon Sep 17 00:00:00 2001 From: jmpfar Date: Tue, 20 Oct 2020 09:40:17 +0000 Subject: [PATCH 3/5] Removing iSCSI tests from integration tests I've tried the test on a clean GCP Windows 2016/2019 VM and this works. This might be related to the Server Target feature install on the github workflow environment. Might be worth fixing with the other disabled integration tests. Change-Id: If76902118f1a2887caf5c554d339ad1dc13f04a8 --- .github/workflows/windows.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 4a185077..e020a8fe 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -31,7 +31,6 @@ jobs: Write-Output "getting named pipes" [System.IO.Directory]::GetFiles("\\.\\pipe\\") $env:CSI_PROXY_GH_ACTIONS="TRUE" - $env:ENABLE_ISCSI_TESTS="TRUE" go test -v -race ./integrationtests/... unit_tests: strategy: From 0ccb01ab1e21db637f98950a407e312b3cb04844 Mon Sep 17 00:00:00 2001 From: jmpfar Date: Thu, 22 Oct 2020 12:54:11 +0000 Subject: [PATCH 4/5] Fixing code review issues 1. Fixing errors in proto comments 2. Adding missing comments for AuthenticationType enum 3. Renaming DiskIds to DiskIDs to better conform to other apis 4. Documenting lack of initiator selection features in AddTargetPortal 5. Improving multipath related documentation Change-Id: Ic9ec4387a140f522fa2e3a3ad781a439bc2cb918 --- client/api/iscsi/v1alpha1/api.pb.go | 62 ++++++++++++------- client/api/iscsi/v1alpha1/api.proto | 23 +++++-- integrationtests/iscsi_test.go | 4 +- internal/server/iscsi/internal/types.go | 6 +- .../internal/v1alpha1/conversion_generated.go | 4 +- internal/server/iscsi/server.go | 2 +- .../client/api/iscsi/v1alpha1/api.pb.go | 62 ++++++++++++------- .../client/api/iscsi/v1alpha1/api.proto | 23 +++++-- 8 files changed, 118 insertions(+), 68 deletions(-) diff --git a/client/api/iscsi/v1alpha1/api.pb.go b/client/api/iscsi/v1alpha1/api.pb.go index c1d35cac..5e289b13 100644 --- a/client/api/iscsi/v1alpha1/api.pb.go +++ b/client/api/iscsi/v1alpha1/api.pb.go @@ -27,9 +27,13 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package type AuthenticationType int32 const ( - AuthenticationType_NONE AuthenticationType = 0 + // No authentication is used + AuthenticationType_NONE AuthenticationType = 0 + // One way CHAP authentication. The target authenticates the initiator. AuthenticationType_ONE_WAY_CHAP AuthenticationType = 1 - AuthenticationType_MUTUAL_CHAP AuthenticationType = 2 + // Mutual CHAP authentication. The target and initiator authenticate each + // other. + AuthenticationType_MUTUAL_CHAP AuthenticationType = 2 ) var AuthenticationType_name = map[int32]string{ @@ -398,7 +402,7 @@ func (m *ListTargetPortalsResponse) GetTargetPortals() []*TargetPortal { } type ConnectTargetRequest struct { - // Target portal to which the initiator will connect. + // Target portal to which the initiator will connect TargetPortal *TargetPortal `protobuf:"bytes,1,opt,name=target_portal,json=targetPortal,proto3" json:"target_portal,omitempty"` // IQN of the iSCSI Target Iqn string `protobuf:"bytes,2,opt,name=iqn,proto3" json:"iqn,omitempty"` @@ -414,9 +418,13 @@ type ConnectTargetRequest struct { ChapUsername string `protobuf:"bytes,4,opt,name=chap_username,json=chapUsername,proto3" json:"chap_username,omitempty"` // CHAP password used to authenticate the initiator ChapSecret string `protobuf:"bytes,5,opt,name=chap_secret,json=chapSecret,proto3" json:"chap_secret,omitempty"` - // Should enable multipath on the connection + // Set true to enable multipath on the connection // In order for multipath to work on Windows, the Multipath feature - // needs to be installed as well as MPIO should be correctly configured + // needs to be installed as well as MPIO should be correctly configured. + // Setting this true allows different sessions to the same target (IQN) + // NOTE: enabling this without having multipath installed may cause data + // corruption in cases where there are multiple sessions to the same + // target. IsMultipath bool `protobuf:"varint,6,opt,name=is_multipath,json=isMultipath,proto3" json:"is_multipath,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -522,7 +530,7 @@ func (m *ConnectTargetResponse) XXX_DiscardUnknown() { var xxx_messageInfo_ConnectTargetResponse proto.InternalMessageInfo type GetTargetDisksRequest struct { - // Target portal to which the initiator will connect. + // Target portal whose disks will be queried TargetPortal *TargetPortal `protobuf:"bytes,1,opt,name=target_portal,json=targetPortal,proto3" json:"target_portal,omitempty"` // IQN of the iSCSI Target Iqn string `protobuf:"bytes,2,opt,name=iqn,proto3" json:"iqn,omitempty"` @@ -573,7 +581,7 @@ func (m *GetTargetDisksRequest) GetIqn() string { type GetTargetDisksResponse struct { // List composed of disk ids (numbers) that are associated with the // iSCSI target - DiskIds []string `protobuf:"bytes,1,rep,name=diskIds,proto3" json:"diskIds,omitempty"` + DiskIDs []string `protobuf:"bytes,1,rep,name=diskIDs,proto3" json:"diskIDs,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -604,15 +612,15 @@ func (m *GetTargetDisksResponse) XXX_DiscardUnknown() { var xxx_messageInfo_GetTargetDisksResponse proto.InternalMessageInfo -func (m *GetTargetDisksResponse) GetDiskIds() []string { +func (m *GetTargetDisksResponse) GetDiskIDs() []string { if m != nil { - return m.DiskIds + return m.DiskIDs } return nil } type DisconnectTargetRequest struct { - // Target portal to which the initiator will connect. + // Target portal from which initiator will disconnect TargetPortal *TargetPortal `protobuf:"bytes,1,opt,name=target_portal,json=targetPortal,proto3" json:"target_portal,omitempty"` // IQN of the iSCSI Target Iqn string `protobuf:"bytes,2,opt,name=iqn,proto3" json:"iqn,omitempty"` @@ -744,20 +752,20 @@ var fileDescriptor_0438d9bfe30f1df4 = []byte{ 0xcf, 0x01, 0x82, 0xeb, 0x50, 0xe2, 0xa2, 0xdd, 0xf3, 0xba, 0x92, 0x3b, 0x54, 0x9a, 0xca, 0x6c, 0x35, 0x57, 0xcb, 0xeb, 0x45, 0x2e, 0x8e, 0x63, 0x88, 0x2c, 0xc3, 0xe2, 0x40, 0x2a, 0xa2, 0xe2, 0xfc, 0x84, 0xc5, 0x03, 0x16, 0x81, 0x7b, 0x5c, 0xdc, 0x88, 0xa7, 0x49, 0x12, 0x69, 0xc2, 0xd2, - 0xa0, 0x9f, 0xa8, 0xca, 0x0a, 0x3c, 0xeb, 0x70, 0x71, 0x73, 0xd8, 0x89, 0xbb, 0x35, 0xde, 0x12, - 0x13, 0x96, 0x83, 0x26, 0x7f, 0xf2, 0x12, 0xfa, 0x2d, 0x3a, 0xec, 0x29, 0x8c, 0xef, 0x8d, 0x06, - 0x38, 0x5c, 0x43, 0xcc, 0xc3, 0xf4, 0xc9, 0xe9, 0xc9, 0x7e, 0x65, 0x02, 0x2b, 0x50, 0x3a, 0x3d, - 0xd9, 0x6f, 0x7f, 0xd5, 0xbe, 0xb5, 0x77, 0x3f, 0x69, 0x67, 0x95, 0x1c, 0xce, 0x43, 0xf1, 0xb8, - 0x75, 0xde, 0xd2, 0x8e, 0x42, 0x60, 0xb2, 0xf9, 0x67, 0x06, 0x66, 0x0e, 0xfd, 0xd1, 0x87, 0x17, - 0x30, 0x3f, 0x30, 0x2e, 0xb0, 0x9a, 0xea, 0x95, 0xcc, 0x01, 0xa5, 0xae, 0x8f, 0x61, 0x44, 0x65, - 0x9c, 0xc0, 0x6b, 0x58, 0xc8, 0x9a, 0x08, 0xb8, 0xd9, 0x37, 0x1e, 0x33, 0x8d, 0xd4, 0xd7, 0xff, - 0xa2, 0x25, 0x8e, 0x28, 0xe0, 0xf0, 0x63, 0xc7, 0x8d, 0xbe, 0xfd, 0xc8, 0x21, 0xa3, 0xbe, 0x1a, - 0x4f, 0x4a, 0x5c, 0xfc, 0x80, 0xe7, 0x43, 0x53, 0x01, 0x49, 0xdf, 0x78, 0xd4, 0x38, 0x51, 0x37, - 0xc6, 0x72, 0x12, 0x7d, 0x1d, 0xca, 0x0f, 0x5e, 0x03, 0xbe, 0xec, 0xdb, 0x65, 0x4d, 0x0c, 0x75, - 0x6d, 0xe4, 0x79, 0xa2, 0xf9, 0x1d, 0x2a, 0x83, 0x2d, 0x84, 0xeb, 0x03, 0x49, 0xcd, 0x50, 0x26, - 0xe3, 0x28, 0x89, 0x78, 0x0b, 0xe6, 0x1e, 0xbe, 0x1e, 0x4c, 0x45, 0x94, 0xf9, 0x7e, 0xd5, 0xea, - 0x68, 0x42, 0x2c, 0xfb, 0xf1, 0xc3, 0xe5, 0xce, 0x7f, 0x7d, 0xe0, 0x57, 0xb3, 0xc1, 0xef, 0xfd, - 0xf6, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0x6d, 0xe8, 0x73, 0x08, 0x08, 0x00, 0x00, + 0xa0, 0x9f, 0xa8, 0xca, 0x0a, 0x3c, 0xeb, 0x70, 0x71, 0x73, 0xb8, 0x17, 0x77, 0x6b, 0xbc, 0x25, + 0x26, 0x2c, 0x07, 0x4d, 0xfe, 0xe4, 0x25, 0xf4, 0x5b, 0x74, 0xd8, 0x53, 0x18, 0xdf, 0x1b, 0x0d, + 0x70, 0xb8, 0x86, 0x98, 0x87, 0xe9, 0x93, 0xd3, 0x93, 0xfd, 0xca, 0x04, 0x56, 0xa0, 0x74, 0x7a, + 0xb2, 0xdf, 0xfe, 0xaa, 0x7d, 0x6b, 0xef, 0x7e, 0xd2, 0xce, 0x2a, 0x39, 0x9c, 0x87, 0xe2, 0x71, + 0xeb, 0xbc, 0xa5, 0x1d, 0x85, 0xc0, 0x64, 0xf3, 0xcf, 0x0c, 0xcc, 0x1c, 0xfa, 0xa3, 0x0f, 0x2f, + 0x60, 0x7e, 0x60, 0x5c, 0x60, 0x35, 0xd5, 0x2b, 0x99, 0x03, 0x4a, 0x5d, 0x1f, 0xc3, 0x88, 0xca, + 0x38, 0x81, 0xd7, 0xb0, 0x90, 0x35, 0x11, 0x70, 0xb3, 0x6f, 0x3c, 0x66, 0x1a, 0xa9, 0xaf, 0xff, + 0x45, 0x4b, 0x1c, 0x51, 0xc0, 0xe1, 0xc7, 0x8e, 0x1b, 0x7d, 0xfb, 0x91, 0x43, 0x46, 0x7d, 0x35, + 0x9e, 0x94, 0xb8, 0xf8, 0x01, 0xcf, 0x87, 0xa6, 0x02, 0x92, 0xbe, 0xf1, 0xa8, 0x71, 0xa2, 0x6e, + 0x8c, 0xe5, 0x24, 0xfa, 0x3a, 0x94, 0x1f, 0xbc, 0x06, 0x7c, 0xd9, 0xb7, 0xcb, 0x9a, 0x18, 0xea, + 0xda, 0xc8, 0xf3, 0x44, 0xf3, 0x3b, 0x54, 0x06, 0x5b, 0x08, 0xd7, 0x07, 0x92, 0x9a, 0xa1, 0x4c, + 0xc6, 0x51, 0x12, 0xf1, 0x16, 0xcc, 0x3d, 0x7c, 0x3d, 0x98, 0x8a, 0x28, 0xf3, 0xfd, 0xaa, 0xd5, + 0xd1, 0x84, 0x58, 0xf6, 0xe3, 0x87, 0xcb, 0x9d, 0xff, 0xfa, 0xc0, 0xaf, 0x66, 0x83, 0xdf, 0xfb, + 0xed, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xce, 0xb0, 0xe3, 0xcc, 0x08, 0x08, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -774,6 +782,9 @@ const _ = grpc.SupportPackageIsVersion6 type IscsiClient interface { // AddTargetPortal registers an iSCSI target network address for later // discovery. + // AddTargetPortal currently does not support selecting different NICs or + // a different iSCSI initiator (e.g a hardware initiator). This means that + // Windows will select the initiator NIC and instance on its own. AddTargetPortal(ctx context.Context, in *AddTargetPortalRequest, opts ...grpc.CallOption) (*AddTargetPortalResponse, error) // DiscoverTargetPortal initiates discovery on an iSCSI target network address // and returns discovered IQNs. @@ -867,6 +878,9 @@ func (c *iscsiClient) GetTargetDisks(ctx context.Context, in *GetTargetDisksRequ type IscsiServer interface { // AddTargetPortal registers an iSCSI target network address for later // discovery. + // AddTargetPortal currently does not support selecting different NICs or + // a different iSCSI initiator (e.g a hardware initiator). This means that + // Windows will select the initiator NIC and instance on its own. AddTargetPortal(context.Context, *AddTargetPortalRequest) (*AddTargetPortalResponse, error) // DiscoverTargetPortal initiates discovery on an iSCSI target network address // and returns discovered IQNs. diff --git a/client/api/iscsi/v1alpha1/api.proto b/client/api/iscsi/v1alpha1/api.proto index 66de2c19..92e5c080 100644 --- a/client/api/iscsi/v1alpha1/api.proto +++ b/client/api/iscsi/v1alpha1/api.proto @@ -7,6 +7,9 @@ option go_package = "github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alph service Iscsi { // AddTargetPortal registers an iSCSI target network address for later // discovery. + // AddTargetPortal currently does not support selecting different NICs or + // a different iSCSI initiator (e.g a hardware initiator). This means that + // Windows will select the initiator NIC and instance on its own. rpc AddTargetPortal(AddTargetPortalRequest) returns (AddTargetPortalResponse) {} @@ -85,13 +88,17 @@ message ListTargetPortalsResponse { } enum AuthenticationType { + // No authentication is used NONE = 0; + // One way CHAP authentication. The target authenticates the initiator. ONE_WAY_CHAP = 1; + // Mutual CHAP authentication. The target and initiator authenticate each + // other. MUTUAL_CHAP = 2; } message ConnectTargetRequest { - // Target portal to which the initiator will connect. + // Target portal to which the initiator will connect TargetPortal target_portal = 1; // IQN of the iSCSI Target @@ -112,9 +119,13 @@ message ConnectTargetRequest { // CHAP password used to authenticate the initiator string chap_secret = 5; - // Should enable multipath on the connection + // Set true to enable multipath on the connection // In order for multipath to work on Windows, the Multipath feature - // needs to be installed as well as MPIO should be correctly configured + // needs to be installed as well as MPIO should be correctly configured. + // Setting this true allows different sessions to the same target (IQN) + // NOTE: enabling this without having multipath installed may cause data + // corruption in cases where there are multiple sessions to the same + // target. bool is_multipath = 6; } @@ -123,7 +134,7 @@ message ConnectTargetResponse { } message GetTargetDisksRequest { - // Target portal to which the initiator will connect. + // Target portal whose disks will be queried TargetPortal target_portal = 1; // IQN of the iSCSI Target @@ -133,11 +144,11 @@ message GetTargetDisksRequest { message GetTargetDisksResponse { // List composed of disk ids (numbers) that are associated with the // iSCSI target - repeated string diskIds = 1; + repeated string diskIDs = 1; } message DisconnectTargetRequest { - // Target portal to which the initiator will connect. + // Target portal from which initiator will disconnect TargetPortal target_portal = 1; // IQN of the iSCSI Target diff --git a/integrationtests/iscsi_test.go b/integrationtests/iscsi_test.go index c1bb88a0..504144e3 100644 --- a/integrationtests/iscsi_test.go +++ b/integrationtests/iscsi_test.go @@ -102,9 +102,9 @@ func e2e_test(t *testing.T) { tgtDisksReq := &iscsi_api.GetTargetDisksRequest{TargetPortal: tp, Iqn: config.Iqn} tgtDisksResp, err := iscsi.GetTargetDisks(context.TODO(), tgtDisksReq) require.Nil(t, err) - require.Len(t, tgtDisksResp.DiskIds, 1) + require.Len(t, tgtDisksResp.DiskIDs, 1) - diskId := tgtDisksResp.DiskIds[0] + diskId := tgtDisksResp.DiskIDs[0] attachReq := &disk_api.SetAttachStateRequest{DiskID: diskId, IsOnline: true} _, err = disk.SetAttachState(context.TODO(), attachReq) diff --git a/internal/server/iscsi/internal/types.go b/internal/server/iscsi/internal/types.go index e22768a4..61bdb722 100644 --- a/internal/server/iscsi/internal/types.go +++ b/internal/server/iscsi/internal/types.go @@ -50,7 +50,7 @@ type ConnectTargetResponse struct { } type DisconnectTargetRequest struct { - // Target portal to which the initiator will connect. + // Target portal from which initiator will disconnect TargetPortal *TargetPortal // IQN of the iSCSI Target Iqn string @@ -72,7 +72,7 @@ type DiscoverTargetPortalResponse struct { } type GetTargetDisksRequest struct { - // Target portal to which the initiator will connect. + // Target portal whose disks will be queried TargetPortal *TargetPortal // IQN of the iSCSI Target Iqn string @@ -81,7 +81,7 @@ type GetTargetDisksRequest struct { type GetTargetDisksResponse struct { // List composed of disk ids (numbers) that are associated with the // iSCSI target - DiskIds []string + DiskIDs []string } type ListTargetPortalsRequest struct { diff --git a/internal/server/iscsi/internal/v1alpha1/conversion_generated.go b/internal/server/iscsi/internal/v1alpha1/conversion_generated.go index 32f8435a..9aa11fac 100644 --- a/internal/server/iscsi/internal/v1alpha1/conversion_generated.go +++ b/internal/server/iscsi/internal/v1alpha1/conversion_generated.go @@ -278,7 +278,7 @@ func Convert_internal_GetTargetDisksRequest_To_v1alpha1_GetTargetDisksRequest(in } func autoConvert_v1alpha1_GetTargetDisksResponse_To_internal_GetTargetDisksResponse(in *v1alpha1.GetTargetDisksResponse, out *internal.GetTargetDisksResponse) error { - out.DiskIds = *(*[]string)(unsafe.Pointer(&in.DiskIds)) + out.DiskIDs = *(*[]string)(unsafe.Pointer(&in.DiskIDs)) return nil } @@ -288,7 +288,7 @@ func Convert_v1alpha1_GetTargetDisksResponse_To_internal_GetTargetDisksResponse( } func autoConvert_internal_GetTargetDisksResponse_To_v1alpha1_GetTargetDisksResponse(in *internal.GetTargetDisksResponse, out *v1alpha1.GetTargetDisksResponse) error { - out.DiskIds = *(*[]string)(unsafe.Pointer(&in.DiskIds)) + out.DiskIDs = *(*[]string)(unsafe.Pointer(&in.DiskIDs)) return nil } diff --git a/internal/server/iscsi/server.go b/internal/server/iscsi/server.go index fa285da3..132725b3 100644 --- a/internal/server/iscsi/server.go +++ b/internal/server/iscsi/server.go @@ -130,7 +130,7 @@ func (s *Server) GetTargetDisks(context context.Context, request *internal.GetTa result = append(result, d) } - response.DiskIds = result + response.DiskIDs = result return response, nil } diff --git a/vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.pb.go b/vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.pb.go index c1d35cac..5e289b13 100644 --- a/vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.pb.go +++ b/vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.pb.go @@ -27,9 +27,13 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package type AuthenticationType int32 const ( - AuthenticationType_NONE AuthenticationType = 0 + // No authentication is used + AuthenticationType_NONE AuthenticationType = 0 + // One way CHAP authentication. The target authenticates the initiator. AuthenticationType_ONE_WAY_CHAP AuthenticationType = 1 - AuthenticationType_MUTUAL_CHAP AuthenticationType = 2 + // Mutual CHAP authentication. The target and initiator authenticate each + // other. + AuthenticationType_MUTUAL_CHAP AuthenticationType = 2 ) var AuthenticationType_name = map[int32]string{ @@ -398,7 +402,7 @@ func (m *ListTargetPortalsResponse) GetTargetPortals() []*TargetPortal { } type ConnectTargetRequest struct { - // Target portal to which the initiator will connect. + // Target portal to which the initiator will connect TargetPortal *TargetPortal `protobuf:"bytes,1,opt,name=target_portal,json=targetPortal,proto3" json:"target_portal,omitempty"` // IQN of the iSCSI Target Iqn string `protobuf:"bytes,2,opt,name=iqn,proto3" json:"iqn,omitempty"` @@ -414,9 +418,13 @@ type ConnectTargetRequest struct { ChapUsername string `protobuf:"bytes,4,opt,name=chap_username,json=chapUsername,proto3" json:"chap_username,omitempty"` // CHAP password used to authenticate the initiator ChapSecret string `protobuf:"bytes,5,opt,name=chap_secret,json=chapSecret,proto3" json:"chap_secret,omitempty"` - // Should enable multipath on the connection + // Set true to enable multipath on the connection // In order for multipath to work on Windows, the Multipath feature - // needs to be installed as well as MPIO should be correctly configured + // needs to be installed as well as MPIO should be correctly configured. + // Setting this true allows different sessions to the same target (IQN) + // NOTE: enabling this without having multipath installed may cause data + // corruption in cases where there are multiple sessions to the same + // target. IsMultipath bool `protobuf:"varint,6,opt,name=is_multipath,json=isMultipath,proto3" json:"is_multipath,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -522,7 +530,7 @@ func (m *ConnectTargetResponse) XXX_DiscardUnknown() { var xxx_messageInfo_ConnectTargetResponse proto.InternalMessageInfo type GetTargetDisksRequest struct { - // Target portal to which the initiator will connect. + // Target portal whose disks will be queried TargetPortal *TargetPortal `protobuf:"bytes,1,opt,name=target_portal,json=targetPortal,proto3" json:"target_portal,omitempty"` // IQN of the iSCSI Target Iqn string `protobuf:"bytes,2,opt,name=iqn,proto3" json:"iqn,omitempty"` @@ -573,7 +581,7 @@ func (m *GetTargetDisksRequest) GetIqn() string { type GetTargetDisksResponse struct { // List composed of disk ids (numbers) that are associated with the // iSCSI target - DiskIds []string `protobuf:"bytes,1,rep,name=diskIds,proto3" json:"diskIds,omitempty"` + DiskIDs []string `protobuf:"bytes,1,rep,name=diskIDs,proto3" json:"diskIDs,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -604,15 +612,15 @@ func (m *GetTargetDisksResponse) XXX_DiscardUnknown() { var xxx_messageInfo_GetTargetDisksResponse proto.InternalMessageInfo -func (m *GetTargetDisksResponse) GetDiskIds() []string { +func (m *GetTargetDisksResponse) GetDiskIDs() []string { if m != nil { - return m.DiskIds + return m.DiskIDs } return nil } type DisconnectTargetRequest struct { - // Target portal to which the initiator will connect. + // Target portal from which initiator will disconnect TargetPortal *TargetPortal `protobuf:"bytes,1,opt,name=target_portal,json=targetPortal,proto3" json:"target_portal,omitempty"` // IQN of the iSCSI Target Iqn string `protobuf:"bytes,2,opt,name=iqn,proto3" json:"iqn,omitempty"` @@ -744,20 +752,20 @@ var fileDescriptor_0438d9bfe30f1df4 = []byte{ 0xcf, 0x01, 0x82, 0xeb, 0x50, 0xe2, 0xa2, 0xdd, 0xf3, 0xba, 0x92, 0x3b, 0x54, 0x9a, 0xca, 0x6c, 0x35, 0x57, 0xcb, 0xeb, 0x45, 0x2e, 0x8e, 0x63, 0x88, 0x2c, 0xc3, 0xe2, 0x40, 0x2a, 0xa2, 0xe2, 0xfc, 0x84, 0xc5, 0x03, 0x16, 0x81, 0x7b, 0x5c, 0xdc, 0x88, 0xa7, 0x49, 0x12, 0x69, 0xc2, 0xd2, - 0xa0, 0x9f, 0xa8, 0xca, 0x0a, 0x3c, 0xeb, 0x70, 0x71, 0x73, 0xd8, 0x89, 0xbb, 0x35, 0xde, 0x12, - 0x13, 0x96, 0x83, 0x26, 0x7f, 0xf2, 0x12, 0xfa, 0x2d, 0x3a, 0xec, 0x29, 0x8c, 0xef, 0x8d, 0x06, - 0x38, 0x5c, 0x43, 0xcc, 0xc3, 0xf4, 0xc9, 0xe9, 0xc9, 0x7e, 0x65, 0x02, 0x2b, 0x50, 0x3a, 0x3d, - 0xd9, 0x6f, 0x7f, 0xd5, 0xbe, 0xb5, 0x77, 0x3f, 0x69, 0x67, 0x95, 0x1c, 0xce, 0x43, 0xf1, 0xb8, - 0x75, 0xde, 0xd2, 0x8e, 0x42, 0x60, 0xb2, 0xf9, 0x67, 0x06, 0x66, 0x0e, 0xfd, 0xd1, 0x87, 0x17, - 0x30, 0x3f, 0x30, 0x2e, 0xb0, 0x9a, 0xea, 0x95, 0xcc, 0x01, 0xa5, 0xae, 0x8f, 0x61, 0x44, 0x65, - 0x9c, 0xc0, 0x6b, 0x58, 0xc8, 0x9a, 0x08, 0xb8, 0xd9, 0x37, 0x1e, 0x33, 0x8d, 0xd4, 0xd7, 0xff, - 0xa2, 0x25, 0x8e, 0x28, 0xe0, 0xf0, 0x63, 0xc7, 0x8d, 0xbe, 0xfd, 0xc8, 0x21, 0xa3, 0xbe, 0x1a, - 0x4f, 0x4a, 0x5c, 0xfc, 0x80, 0xe7, 0x43, 0x53, 0x01, 0x49, 0xdf, 0x78, 0xd4, 0x38, 0x51, 0x37, - 0xc6, 0x72, 0x12, 0x7d, 0x1d, 0xca, 0x0f, 0x5e, 0x03, 0xbe, 0xec, 0xdb, 0x65, 0x4d, 0x0c, 0x75, - 0x6d, 0xe4, 0x79, 0xa2, 0xf9, 0x1d, 0x2a, 0x83, 0x2d, 0x84, 0xeb, 0x03, 0x49, 0xcd, 0x50, 0x26, - 0xe3, 0x28, 0x89, 0x78, 0x0b, 0xe6, 0x1e, 0xbe, 0x1e, 0x4c, 0x45, 0x94, 0xf9, 0x7e, 0xd5, 0xea, - 0x68, 0x42, 0x2c, 0xfb, 0xf1, 0xc3, 0xe5, 0xce, 0x7f, 0x7d, 0xe0, 0x57, 0xb3, 0xc1, 0xef, 0xfd, - 0xf6, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0x6d, 0xe8, 0x73, 0x08, 0x08, 0x00, 0x00, + 0xa0, 0x9f, 0xa8, 0xca, 0x0a, 0x3c, 0xeb, 0x70, 0x71, 0x73, 0xb8, 0x17, 0x77, 0x6b, 0xbc, 0x25, + 0x26, 0x2c, 0x07, 0x4d, 0xfe, 0xe4, 0x25, 0xf4, 0x5b, 0x74, 0xd8, 0x53, 0x18, 0xdf, 0x1b, 0x0d, + 0x70, 0xb8, 0x86, 0x98, 0x87, 0xe9, 0x93, 0xd3, 0x93, 0xfd, 0xca, 0x04, 0x56, 0xa0, 0x74, 0x7a, + 0xb2, 0xdf, 0xfe, 0xaa, 0x7d, 0x6b, 0xef, 0x7e, 0xd2, 0xce, 0x2a, 0x39, 0x9c, 0x87, 0xe2, 0x71, + 0xeb, 0xbc, 0xa5, 0x1d, 0x85, 0xc0, 0x64, 0xf3, 0xcf, 0x0c, 0xcc, 0x1c, 0xfa, 0xa3, 0x0f, 0x2f, + 0x60, 0x7e, 0x60, 0x5c, 0x60, 0x35, 0xd5, 0x2b, 0x99, 0x03, 0x4a, 0x5d, 0x1f, 0xc3, 0x88, 0xca, + 0x38, 0x81, 0xd7, 0xb0, 0x90, 0x35, 0x11, 0x70, 0xb3, 0x6f, 0x3c, 0x66, 0x1a, 0xa9, 0xaf, 0xff, + 0x45, 0x4b, 0x1c, 0x51, 0xc0, 0xe1, 0xc7, 0x8e, 0x1b, 0x7d, 0xfb, 0x91, 0x43, 0x46, 0x7d, 0x35, + 0x9e, 0x94, 0xb8, 0xf8, 0x01, 0xcf, 0x87, 0xa6, 0x02, 0x92, 0xbe, 0xf1, 0xa8, 0x71, 0xa2, 0x6e, + 0x8c, 0xe5, 0x24, 0xfa, 0x3a, 0x94, 0x1f, 0xbc, 0x06, 0x7c, 0xd9, 0xb7, 0xcb, 0x9a, 0x18, 0xea, + 0xda, 0xc8, 0xf3, 0x44, 0xf3, 0x3b, 0x54, 0x06, 0x5b, 0x08, 0xd7, 0x07, 0x92, 0x9a, 0xa1, 0x4c, + 0xc6, 0x51, 0x12, 0xf1, 0x16, 0xcc, 0x3d, 0x7c, 0x3d, 0x98, 0x8a, 0x28, 0xf3, 0xfd, 0xaa, 0xd5, + 0xd1, 0x84, 0x58, 0xf6, 0xe3, 0x87, 0xcb, 0x9d, 0xff, 0xfa, 0xc0, 0xaf, 0x66, 0x83, 0xdf, 0xfb, + 0xed, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xce, 0xb0, 0xe3, 0xcc, 0x08, 0x08, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -774,6 +782,9 @@ const _ = grpc.SupportPackageIsVersion6 type IscsiClient interface { // AddTargetPortal registers an iSCSI target network address for later // discovery. + // AddTargetPortal currently does not support selecting different NICs or + // a different iSCSI initiator (e.g a hardware initiator). This means that + // Windows will select the initiator NIC and instance on its own. AddTargetPortal(ctx context.Context, in *AddTargetPortalRequest, opts ...grpc.CallOption) (*AddTargetPortalResponse, error) // DiscoverTargetPortal initiates discovery on an iSCSI target network address // and returns discovered IQNs. @@ -867,6 +878,9 @@ func (c *iscsiClient) GetTargetDisks(ctx context.Context, in *GetTargetDisksRequ type IscsiServer interface { // AddTargetPortal registers an iSCSI target network address for later // discovery. + // AddTargetPortal currently does not support selecting different NICs or + // a different iSCSI initiator (e.g a hardware initiator). This means that + // Windows will select the initiator NIC and instance on its own. AddTargetPortal(context.Context, *AddTargetPortalRequest) (*AddTargetPortalResponse, error) // DiscoverTargetPortal initiates discovery on an iSCSI target network address // and returns discovered IQNs. diff --git a/vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.proto b/vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.proto index 66de2c19..92e5c080 100644 --- a/vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.proto +++ b/vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.proto @@ -7,6 +7,9 @@ option go_package = "github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alph service Iscsi { // AddTargetPortal registers an iSCSI target network address for later // discovery. + // AddTargetPortal currently does not support selecting different NICs or + // a different iSCSI initiator (e.g a hardware initiator). This means that + // Windows will select the initiator NIC and instance on its own. rpc AddTargetPortal(AddTargetPortalRequest) returns (AddTargetPortalResponse) {} @@ -85,13 +88,17 @@ message ListTargetPortalsResponse { } enum AuthenticationType { + // No authentication is used NONE = 0; + // One way CHAP authentication. The target authenticates the initiator. ONE_WAY_CHAP = 1; + // Mutual CHAP authentication. The target and initiator authenticate each + // other. MUTUAL_CHAP = 2; } message ConnectTargetRequest { - // Target portal to which the initiator will connect. + // Target portal to which the initiator will connect TargetPortal target_portal = 1; // IQN of the iSCSI Target @@ -112,9 +119,13 @@ message ConnectTargetRequest { // CHAP password used to authenticate the initiator string chap_secret = 5; - // Should enable multipath on the connection + // Set true to enable multipath on the connection // In order for multipath to work on Windows, the Multipath feature - // needs to be installed as well as MPIO should be correctly configured + // needs to be installed as well as MPIO should be correctly configured. + // Setting this true allows different sessions to the same target (IQN) + // NOTE: enabling this without having multipath installed may cause data + // corruption in cases where there are multiple sessions to the same + // target. bool is_multipath = 6; } @@ -123,7 +134,7 @@ message ConnectTargetResponse { } message GetTargetDisksRequest { - // Target portal to which the initiator will connect. + // Target portal whose disks will be queried TargetPortal target_portal = 1; // IQN of the iSCSI Target @@ -133,11 +144,11 @@ message GetTargetDisksRequest { message GetTargetDisksResponse { // List composed of disk ids (numbers) that are associated with the // iSCSI target - repeated string diskIds = 1; + repeated string diskIDs = 1; } message DisconnectTargetRequest { - // Target portal to which the initiator will connect. + // Target portal from which initiator will disconnect TargetPortal target_portal = 1; // IQN of the iSCSI Target From 37b254e5383a7e702332d7f92da433a8ba109545 Mon Sep 17 00:00:00 2001 From: Dan Ilan Date: Fri, 23 Oct 2020 09:27:52 +0000 Subject: [PATCH 5/5] Remove multipath option from API This was decided as currently we don't have a way of specifying different initiators. Effectively making multipath very limited in usage, as well as probably confusing Change-Id: I0cd9b7fa7cdb309dbc29ab1a5a5c64909a429f1e --- client/api/iscsi/v1alpha1/api.pb.go | 102 ++++++++---------- client/api/iscsi/v1alpha1/api.proto | 11 +- internal/os/iscsi/api.go | 6 +- internal/server/iscsi/internal/types.go | 15 +-- .../internal/v1alpha1/conversion_generated.go | 2 - internal/server/iscsi/server.go | 10 +- .../client/api/iscsi/v1alpha1/api.pb.go | 102 ++++++++---------- .../client/api/iscsi/v1alpha1/api.proto | 11 +- 8 files changed, 105 insertions(+), 154 deletions(-) diff --git a/client/api/iscsi/v1alpha1/api.pb.go b/client/api/iscsi/v1alpha1/api.pb.go index 5e289b13..9ba7f6a4 100644 --- a/client/api/iscsi/v1alpha1/api.pb.go +++ b/client/api/iscsi/v1alpha1/api.pb.go @@ -417,15 +417,7 @@ type ConnectTargetRequest struct { // CHAP Username used to authenticate the initiator ChapUsername string `protobuf:"bytes,4,opt,name=chap_username,json=chapUsername,proto3" json:"chap_username,omitempty"` // CHAP password used to authenticate the initiator - ChapSecret string `protobuf:"bytes,5,opt,name=chap_secret,json=chapSecret,proto3" json:"chap_secret,omitempty"` - // Set true to enable multipath on the connection - // In order for multipath to work on Windows, the Multipath feature - // needs to be installed as well as MPIO should be correctly configured. - // Setting this true allows different sessions to the same target (IQN) - // NOTE: enabling this without having multipath installed may cause data - // corruption in cases where there are multiple sessions to the same - // target. - IsMultipath bool `protobuf:"varint,6,opt,name=is_multipath,json=isMultipath,proto3" json:"is_multipath,omitempty"` + ChapSecret string `protobuf:"bytes,5,opt,name=chap_secret,json=chapSecret,proto3" json:"chap_secret,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -491,13 +483,6 @@ func (m *ConnectTargetRequest) GetChapSecret() string { return "" } -func (m *ConnectTargetRequest) GetIsMultipath() bool { - if m != nil { - return m.IsMultipath - } - return false -} - type ConnectTargetResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -723,49 +708,48 @@ func init() { } var fileDescriptor_0438d9bfe30f1df4 = []byte{ - // 671 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xdb, 0x4e, 0xdb, 0x40, - 0x10, 0x25, 0xdc, 0x9a, 0x4c, 0x12, 0x48, 0x47, 0x5c, 0x8c, 0x8b, 0x4a, 0x58, 0x4a, 0x15, 0x55, - 0x22, 0x11, 0xe9, 0x53, 0x55, 0xa1, 0xca, 0x05, 0x44, 0x91, 0xb8, 0xc9, 0x25, 0x2d, 0xa5, 0x52, - 0xa3, 0xc5, 0xd9, 0xe2, 0x15, 0x89, 0x6d, 0xbc, 0x6b, 0x54, 0x3e, 0xa1, 0x1f, 0xd0, 0xff, 0xad, - 0x7c, 0x8d, 0x49, 0x9c, 0xf4, 0xa1, 0xf0, 0xb6, 0x7b, 0xf6, 0xcc, 0x99, 0xd9, 0x99, 0xd9, 0x59, - 0x38, 0xb8, 0xe6, 0xd2, 0xf4, 0xae, 0xea, 0x86, 0xdd, 0x6b, 0xdc, 0x78, 0x57, 0xcc, 0xb5, 0x98, - 0x64, 0x62, 0xcb, 0x10, 0xbc, 0x61, 0x08, 0xbe, 0xe5, 0xb8, 0xf6, 0xaf, 0xfb, 0x86, 0xd1, 0xe5, - 0xcc, 0x92, 0x0d, 0xea, 0xf0, 0x06, 0x17, 0xfe, 0xd1, 0xdd, 0x36, 0xed, 0x3a, 0x26, 0xdd, 0xf6, - 0xa1, 0xba, 0xe3, 0xda, 0xd2, 0xc6, 0x7c, 0x8c, 0x91, 0x2f, 0x50, 0x3a, 0xa7, 0xee, 0x35, 0x93, - 0x67, 0xb6, 0x2b, 0x69, 0x17, 0x37, 0x61, 0x4e, 0x06, 0xfb, 0x36, 0xed, 0x74, 0x5c, 0x26, 0x84, - 0x92, 0xab, 0xe6, 0x6a, 0x05, 0xbd, 0x1c, 0xa2, 0x5a, 0x08, 0xe2, 0x1a, 0x14, 0x23, 0x9a, 0x63, - 0xbb, 0x52, 0x99, 0xac, 0xe6, 0x6a, 0x65, 0x1d, 0x64, 0xa2, 0x44, 0x5a, 0xb0, 0xa4, 0x75, 0x3a, - 0x69, 0x69, 0x9d, 0xdd, 0x7a, 0x4c, 0x48, 0x7c, 0x0f, 0xe5, 0x94, 0x29, 0xed, 0x06, 0x0e, 0x8a, - 0xcd, 0xa5, 0x7a, 0x1c, 0x53, 0xfd, 0x81, 0x55, 0x49, 0xa6, 0x76, 0x64, 0x05, 0x96, 0x87, 0x64, - 0x85, 0x63, 0x5b, 0x82, 0x91, 0x4b, 0x78, 0xb1, 0xc7, 0x85, 0x61, 0xdf, 0x31, 0xf7, 0xd1, 0xdd, - 0x36, 0x61, 0x35, 0x5b, 0x3b, 0xf4, 0x8d, 0x08, 0xd3, 0xfc, 0xd6, 0xf2, 0x73, 0x35, 0x55, 0x2b, - 0xe8, 0xc1, 0x9a, 0x5c, 0xc0, 0x8a, 0xce, 0x7a, 0xf6, 0x1d, 0x7b, 0xf4, 0x68, 0x56, 0x41, 0xcd, - 0x52, 0x8e, 0xf2, 0xa0, 0x82, 0x72, 0xc4, 0x85, 0x4c, 0x9f, 0x89, 0xc8, 0x2d, 0xb9, 0x84, 0x95, - 0x8c, 0xb3, 0xe8, 0x12, 0x3b, 0x49, 0xe9, 0xc3, 0x98, 0xc2, 0xeb, 0x8c, 0x0e, 0xaa, 0x9c, 0x0e, - 0x4a, 0x90, 0xdf, 0x93, 0xb0, 0xb0, 0x6b, 0x5b, 0x16, 0x33, 0x22, 0xfd, 0xc7, 0xb8, 0x2b, 0x56, - 0x60, 0x8a, 0xdf, 0x5a, 0x41, 0x83, 0x15, 0x74, 0x7f, 0x89, 0xef, 0xa0, 0x40, 0x3d, 0x69, 0xb6, - 0xe5, 0xbd, 0xc3, 0x94, 0xa9, 0x6a, 0xae, 0x36, 0xd7, 0x5c, 0xed, 0x4b, 0x69, 0x9e, 0x34, 0x99, - 0x25, 0xb9, 0x41, 0x25, 0xb7, 0xad, 0xf3, 0x7b, 0x87, 0xe9, 0x79, 0x9f, 0xee, 0xaf, 0x70, 0x03, - 0xca, 0x86, 0x49, 0x9d, 0xb6, 0x27, 0x98, 0x6b, 0xd1, 0x1e, 0x53, 0xa6, 0x03, 0xd9, 0x92, 0x0f, - 0xb6, 0x22, 0xcc, 0x6f, 0xed, 0x80, 0x24, 0x98, 0xe1, 0x32, 0xa9, 0xcc, 0x04, 0x14, 0xf0, 0xa1, - 0xcf, 0x01, 0x82, 0xeb, 0x50, 0xe2, 0xa2, 0xdd, 0xf3, 0xba, 0x92, 0x3b, 0x54, 0x9a, 0xca, 0x6c, - 0x35, 0x57, 0xcb, 0xeb, 0x45, 0x2e, 0x8e, 0x63, 0x88, 0x2c, 0xc3, 0xe2, 0x40, 0x2a, 0xa2, 0xe2, - 0xfc, 0x84, 0xc5, 0x03, 0x16, 0x81, 0x7b, 0x5c, 0xdc, 0x88, 0xa7, 0x49, 0x12, 0x69, 0xc2, 0xd2, - 0xa0, 0x9f, 0xa8, 0xca, 0x0a, 0x3c, 0xeb, 0x70, 0x71, 0x73, 0xb8, 0x17, 0x77, 0x6b, 0xbc, 0x25, - 0x26, 0x2c, 0x07, 0x4d, 0xfe, 0xe4, 0x25, 0xf4, 0x5b, 0x74, 0xd8, 0x53, 0x18, 0xdf, 0x1b, 0x0d, - 0x70, 0xb8, 0x86, 0x98, 0x87, 0xe9, 0x93, 0xd3, 0x93, 0xfd, 0xca, 0x04, 0x56, 0xa0, 0x74, 0x7a, - 0xb2, 0xdf, 0xfe, 0xaa, 0x7d, 0x6b, 0xef, 0x7e, 0xd2, 0xce, 0x2a, 0x39, 0x9c, 0x87, 0xe2, 0x71, - 0xeb, 0xbc, 0xa5, 0x1d, 0x85, 0xc0, 0x64, 0xf3, 0xcf, 0x0c, 0xcc, 0x1c, 0xfa, 0xa3, 0x0f, 0x2f, - 0x60, 0x7e, 0x60, 0x5c, 0x60, 0x35, 0xd5, 0x2b, 0x99, 0x03, 0x4a, 0x5d, 0x1f, 0xc3, 0x88, 0xca, - 0x38, 0x81, 0xd7, 0xb0, 0x90, 0x35, 0x11, 0x70, 0xb3, 0x6f, 0x3c, 0x66, 0x1a, 0xa9, 0xaf, 0xff, - 0x45, 0x4b, 0x1c, 0x51, 0xc0, 0xe1, 0xc7, 0x8e, 0x1b, 0x7d, 0xfb, 0x91, 0x43, 0x46, 0x7d, 0x35, - 0x9e, 0x94, 0xb8, 0xf8, 0x01, 0xcf, 0x87, 0xa6, 0x02, 0x92, 0xbe, 0xf1, 0xa8, 0x71, 0xa2, 0x6e, - 0x8c, 0xe5, 0x24, 0xfa, 0x3a, 0x94, 0x1f, 0xbc, 0x06, 0x7c, 0xd9, 0xb7, 0xcb, 0x9a, 0x18, 0xea, - 0xda, 0xc8, 0xf3, 0x44, 0xf3, 0x3b, 0x54, 0x06, 0x5b, 0x08, 0xd7, 0x07, 0x92, 0x9a, 0xa1, 0x4c, - 0xc6, 0x51, 0x12, 0xf1, 0x16, 0xcc, 0x3d, 0x7c, 0x3d, 0x98, 0x8a, 0x28, 0xf3, 0xfd, 0xaa, 0xd5, - 0xd1, 0x84, 0x58, 0xf6, 0xe3, 0x87, 0xcb, 0x9d, 0xff, 0xfa, 0xc0, 0xaf, 0x66, 0x83, 0xdf, 0xfb, - 0xed, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xce, 0xb0, 0xe3, 0xcc, 0x08, 0x08, 0x00, 0x00, + // 649 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0x6d, 0x4f, 0xd3, 0x50, + 0x14, 0xa6, 0xbc, 0x28, 0x3b, 0xac, 0x30, 0x6f, 0x78, 0x29, 0x95, 0xc8, 0xb8, 0x88, 0x59, 0x4c, + 0xd8, 0xc2, 0xfc, 0x64, 0x0c, 0x31, 0x15, 0x08, 0x92, 0x20, 0x90, 0xca, 0x14, 0x67, 0xe2, 0x72, + 0xd7, 0x5d, 0xd7, 0x9b, 0x6d, 0x6d, 0xd7, 0x7b, 0xbb, 0xb8, 0x1f, 0xe2, 0x5f, 0xf4, 0x77, 0x98, + 0xbe, 0xac, 0xeb, 0xb6, 0x6e, 0x7e, 0x70, 0x7c, 0xeb, 0x7d, 0xee, 0x73, 0x9e, 0x73, 0xee, 0x79, + 0x2b, 0x5c, 0x36, 0x99, 0x30, 0xbd, 0x7a, 0xd1, 0xb0, 0x3b, 0xa5, 0x96, 0x57, 0xa7, 0xae, 0x45, + 0x05, 0xe5, 0xc7, 0x06, 0x67, 0x25, 0x83, 0xb3, 0x63, 0xc7, 0xb5, 0x7f, 0xf5, 0x4b, 0x46, 0x9b, + 0x51, 0x4b, 0x94, 0x88, 0xc3, 0x4a, 0x8c, 0xfb, 0x57, 0xbd, 0x13, 0xd2, 0x76, 0x4c, 0x72, 0xe2, + 0x43, 0x45, 0xc7, 0xb5, 0x85, 0x8d, 0x56, 0x07, 0x18, 0xfe, 0x02, 0xd9, 0x7b, 0xe2, 0x36, 0xa9, + 0xb8, 0xb3, 0x5d, 0x41, 0xda, 0xe8, 0x08, 0xd6, 0x45, 0x70, 0xae, 0x91, 0x46, 0xc3, 0xa5, 0x9c, + 0x2b, 0x52, 0x5e, 0x2a, 0x64, 0x74, 0x39, 0x44, 0xb5, 0x10, 0x44, 0xfb, 0xb0, 0x16, 0xd1, 0x1c, + 0xdb, 0x15, 0xca, 0x62, 0x5e, 0x2a, 0xc8, 0x3a, 0x88, 0x58, 0x09, 0x57, 0x60, 0x5b, 0x6b, 0x34, + 0x92, 0xd2, 0x3a, 0xed, 0x7a, 0x94, 0x0b, 0xf4, 0x0e, 0xe4, 0x84, 0x29, 0x69, 0x07, 0x0e, 0xd6, + 0xca, 0xdb, 0xc5, 0x41, 0x4c, 0xc5, 0x11, 0xab, 0xac, 0x48, 0x9c, 0xf0, 0x2e, 0xec, 0x4c, 0xc8, + 0x72, 0xc7, 0xb6, 0x38, 0xc5, 0x55, 0x78, 0x7e, 0xce, 0xb8, 0x61, 0xf7, 0xa8, 0x3b, 0x77, 0xb7, + 0x65, 0xd8, 0x4b, 0xd7, 0x0e, 0x7d, 0x23, 0x04, 0xcb, 0xac, 0x6b, 0xf9, 0xb9, 0x5a, 0x2a, 0x64, + 0xf4, 0xe0, 0x1b, 0x3f, 0xc0, 0xae, 0x4e, 0x3b, 0x76, 0x8f, 0xce, 0x3d, 0x9a, 0x3d, 0x50, 0xd3, + 0x94, 0xa3, 0x3c, 0xa8, 0xa0, 0x5c, 0x33, 0x2e, 0x92, 0x77, 0x3c, 0x72, 0x8b, 0xab, 0xb0, 0x9b, + 0x72, 0x17, 0x3d, 0xe2, 0x34, 0x2e, 0x7d, 0x18, 0x53, 0xf8, 0x9c, 0xe9, 0x41, 0xc9, 0xc9, 0xa0, + 0x38, 0xfe, 0x23, 0xc1, 0xe6, 0x99, 0x6d, 0x59, 0xd4, 0x88, 0xf4, 0xe7, 0xf1, 0x56, 0x94, 0x83, + 0x25, 0xd6, 0xb5, 0x82, 0x06, 0xcb, 0xe8, 0xfe, 0x27, 0x7a, 0x0b, 0x19, 0xe2, 0x09, 0xb3, 0x26, + 0xfa, 0x0e, 0x55, 0x96, 0xf2, 0x52, 0x61, 0xbd, 0xbc, 0x37, 0x94, 0xd2, 0x3c, 0x61, 0x52, 0x4b, + 0x30, 0x83, 0x08, 0x66, 0x5b, 0xf7, 0x7d, 0x87, 0xea, 0xab, 0x3e, 0xdd, 0xff, 0x42, 0x87, 0x20, + 0x1b, 0x26, 0x71, 0x6a, 0x1e, 0xa7, 0xae, 0x45, 0x3a, 0x54, 0x59, 0x0e, 0x64, 0xb3, 0x3e, 0x58, + 0x89, 0x30, 0xbf, 0xb5, 0x03, 0x12, 0xa7, 0x86, 0x4b, 0x85, 0xb2, 0x12, 0x50, 0xc0, 0x87, 0x3e, + 0x07, 0x08, 0xde, 0x81, 0xad, 0xb1, 0x77, 0x46, 0x99, 0xff, 0x09, 0x5b, 0x97, 0x34, 0x02, 0xcf, + 0x19, 0x6f, 0xf1, 0xc7, 0xc9, 0x00, 0x2e, 0xc3, 0xf6, 0xb8, 0x9f, 0xa8, 0x84, 0x0a, 0x3c, 0x6d, + 0x30, 0xde, 0xba, 0x3a, 0x1f, 0xb4, 0xe2, 0xe0, 0x88, 0x4d, 0xd8, 0x09, 0x3a, 0xf8, 0xd1, 0xeb, + 0xe3, 0xf7, 0xdf, 0xa4, 0xa7, 0x30, 0xbe, 0xd7, 0x1a, 0xa0, 0xc9, 0x02, 0xa1, 0x55, 0x58, 0xbe, + 0xb9, 0xbd, 0xb9, 0xc8, 0x2d, 0xa0, 0x1c, 0x64, 0x6f, 0x6f, 0x2e, 0x6a, 0x5f, 0xb5, 0x6f, 0xb5, + 0xb3, 0x8f, 0xda, 0x5d, 0x4e, 0x42, 0x1b, 0xb0, 0xf6, 0xa9, 0x72, 0x5f, 0xd1, 0xae, 0x43, 0x60, + 0xb1, 0xfc, 0x7b, 0x05, 0x56, 0xae, 0xfc, 0xbd, 0x86, 0x1e, 0x60, 0x63, 0x6c, 0x17, 0xa0, 0x7c, + 0xa2, 0x11, 0x52, 0xb7, 0x8f, 0x7a, 0x30, 0x83, 0x11, 0x95, 0x71, 0x01, 0x35, 0x61, 0x33, 0x6d, + 0xdc, 0xd1, 0xd1, 0xd0, 0x78, 0xc6, 0xaa, 0x51, 0x5f, 0xfd, 0x8b, 0x16, 0x3b, 0x22, 0x80, 0x26, + 0x27, 0x19, 0x1d, 0x0e, 0xed, 0xa7, 0x6e, 0x10, 0xf5, 0xe5, 0x6c, 0x52, 0xec, 0xe2, 0x07, 0x3c, + 0x9b, 0x18, 0x79, 0x84, 0x87, 0xc6, 0xd3, 0x76, 0x85, 0x7a, 0x38, 0x93, 0x13, 0xeb, 0xeb, 0x20, + 0x8f, 0x4c, 0x03, 0x7a, 0x31, 0xb4, 0x4b, 0x5b, 0x07, 0xea, 0xfe, 0xd4, 0xfb, 0x58, 0xf3, 0x3b, + 0xe4, 0xc6, 0x5b, 0x08, 0x1d, 0x8c, 0x25, 0x35, 0x45, 0x19, 0xcf, 0xa2, 0xc4, 0xe2, 0x15, 0x58, + 0x1f, 0x9d, 0x1e, 0x94, 0x88, 0x28, 0x75, 0x7e, 0xd5, 0xfc, 0x74, 0xc2, 0x40, 0xf6, 0xc3, 0xfb, + 0xea, 0xe9, 0x7f, 0xfd, 0x9d, 0xeb, 0x4f, 0x82, 0x5f, 0xf3, 0x9b, 0xbf, 0x01, 0x00, 0x00, 0xff, + 0xff, 0xbb, 0x3d, 0x8f, 0x5c, 0xe5, 0x07, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/client/api/iscsi/v1alpha1/api.proto b/client/api/iscsi/v1alpha1/api.proto index 92e5c080..b667f8da 100644 --- a/client/api/iscsi/v1alpha1/api.proto +++ b/client/api/iscsi/v1alpha1/api.proto @@ -90,8 +90,10 @@ message ListTargetPortalsResponse { enum AuthenticationType { // No authentication is used NONE = 0; + // One way CHAP authentication. The target authenticates the initiator. ONE_WAY_CHAP = 1; + // Mutual CHAP authentication. The target and initiator authenticate each // other. MUTUAL_CHAP = 2; @@ -118,15 +120,6 @@ message ConnectTargetRequest { // CHAP password used to authenticate the initiator string chap_secret = 5; - - // Set true to enable multipath on the connection - // In order for multipath to work on Windows, the Multipath feature - // needs to be installed as well as MPIO should be correctly configured. - // Setting this true allows different sessions to the same target (IQN) - // NOTE: enabling this without having multipath installed may cause data - // corruption in cases where there are multiple sessions to the same - // target. - bool is_multipath = 6; } message ConnectTargetResponse { diff --git a/internal/os/iscsi/api.go b/internal/os/iscsi/api.go index 13433044..80e02f97 100644 --- a/internal/os/iscsi/api.go +++ b/internal/os/iscsi/api.go @@ -100,7 +100,7 @@ func (APIImplementor) RemoveTargetPortal(portal *TargetPortal) error { return nil } -func (APIImplementor) ConnectTarget(portal *TargetPortal, iqn string, isMultipath bool, +func (APIImplementor) ConnectTarget(portal *TargetPortal, iqn string, authType string, chapUser string, chapSecret string) error { // Not using InputObject as Connect-IscsiTarget's InputObject does not work. // This is due to being a static WMI method together with a bug in the @@ -108,8 +108,7 @@ func (APIImplementor) ConnectTarget(portal *TargetPortal, iqn string, isMultipat cmdLine := fmt.Sprintf( `Connect-IscsiTarget -TargetPortalAddress ${Env:iscsi_tp_address}` + ` -TargetPortalPortNumber ${Env:iscsi_tp_port} -NodeAddress ${Env:iscsi_target_iqn}` + - ` -AuthenticationType ${Env:iscsi_auth_type}` + - ` -IsMultipathEnabled $([System.Convert]::ToBoolean(${Env:iscsi_is_multipath}))`) + ` -AuthenticationType ${Env:iscsi_auth_type}`) if chapUser != "" { cmdLine += fmt.Sprintf(` -ChapUsername ${Env:iscsi_chap_user}`) @@ -127,7 +126,6 @@ func (APIImplementor) ConnectTarget(portal *TargetPortal, iqn string, isMultipat fmt.Sprintf("iscsi_auth_type=%s", authType), fmt.Sprintf("iscsi_chap_user=%s", chapUser), fmt.Sprintf("iscsi_chap_secret=%s", chapSecret), - fmt.Sprintf("iscsi_is_multipath=%t", isMultipath), ) out, err := cmd.CombinedOutput() diff --git a/internal/server/iscsi/internal/types.go b/internal/server/iscsi/internal/types.go index 61bdb722..6abb0239 100644 --- a/internal/server/iscsi/internal/types.go +++ b/internal/server/iscsi/internal/types.go @@ -12,9 +12,15 @@ type AddTargetPortalResponse struct { type AuthenticationType uint32 const ( - NONE = 0 + // No authentication is used + NONE = 0 + + // One way CHAP authentication. The target authenticates the initiator. ONE_WAY_CHAP = 1 - MUTUAL_CHAP = 2 + + // Mutual CHAP authentication. The target and initiator authenticate each + // other. + MUTUAL_CHAP = 2 ) type ConnectTargetRequest struct { @@ -38,11 +44,6 @@ type ConnectTargetRequest struct { // CHAP password used to authenticate the initiator ChapSecret string - - // Should enable multipath on the connection - // In order for multipath to work on Windows, the Multipath feature - // needs to be installed as well as MPIO should be correctly configured - IsMultipath bool } type ConnectTargetResponse struct { diff --git a/internal/server/iscsi/internal/v1alpha1/conversion_generated.go b/internal/server/iscsi/internal/v1alpha1/conversion_generated.go index 9aa11fac..34d64bc3 100644 --- a/internal/server/iscsi/internal/v1alpha1/conversion_generated.go +++ b/internal/server/iscsi/internal/v1alpha1/conversion_generated.go @@ -77,7 +77,6 @@ func autoConvert_v1alpha1_ConnectTargetRequest_To_internal_ConnectTargetRequest( out.AuthType = internal.AuthenticationType(in.AuthType) out.ChapUsername = in.ChapUsername out.ChapSecret = in.ChapSecret - out.IsMultipath = in.IsMultipath return nil } @@ -100,7 +99,6 @@ func autoConvert_internal_ConnectTargetRequest_To_v1alpha1_ConnectTargetRequest( out.AuthType = v1alpha1.AuthenticationType(in.AuthType) out.ChapUsername = in.ChapUsername out.ChapSecret = in.ChapSecret - out.IsMultipath = in.IsMultipath return nil } diff --git a/internal/server/iscsi/server.go b/internal/server/iscsi/server.go index 132725b3..d1daac74 100644 --- a/internal/server/iscsi/server.go +++ b/internal/server/iscsi/server.go @@ -21,8 +21,8 @@ type API interface { DiscoverTargetPortal(portal *iscsi.TargetPortal) ([]string, error) ListTargetPortals() ([]iscsi.TargetPortal, error) RemoveTargetPortal(portal *iscsi.TargetPortal) error - ConnectTarget(portal *iscsi.TargetPortal, iqn string, isMultipath bool, - authType string, chapUser string, chapSecret string) error + ConnectTarget(portal *iscsi.TargetPortal, iqn string, authType string, + chapUser string, chapSecret string) error DisconnectTarget(portal *iscsi.TargetPortal, iqn string) error GetTargetDisks(portal *iscsi.TargetPortal, iqn string) ([]string, error) } @@ -68,8 +68,8 @@ func AuthTypeToString(authType internal.AuthenticationType) (string, error) { func (s *Server) ConnectTarget(context context.Context, req *internal.ConnectTargetRequest, version apiversion.Version) (*internal.ConnectTargetResponse, error) { klog.V(4).Infof("calling ConnectTarget with portal %s:%d and iqn %s"+ - " multipath=%v auth=%v chapuser=%v", req.TargetPortal.TargetAddress, - req.TargetPortal.TargetPort, req.Iqn, req.IsMultipath, req.AuthType, req.ChapUsername) + " auth=%v chapuser=%v", req.TargetPortal.TargetAddress, + req.TargetPortal.TargetPort, req.Iqn, req.AuthType, req.ChapUsername) response := &internal.ConnectTargetResponse{} authType, err := AuthTypeToString(req.AuthType) @@ -79,7 +79,7 @@ func (s *Server) ConnectTarget(context context.Context, req *internal.ConnectTar } err = s.hostAPI.ConnectTarget(s.requestTPtoAPITP(req.TargetPortal), req.Iqn, - req.IsMultipath, authType, req.ChapUsername, req.ChapSecret) + authType, req.ChapUsername, req.ChapSecret) if err != nil { klog.Errorf("failed ConnectTarget %v", err) return response, err diff --git a/vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.pb.go b/vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.pb.go index 5e289b13..9ba7f6a4 100644 --- a/vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.pb.go +++ b/vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.pb.go @@ -417,15 +417,7 @@ type ConnectTargetRequest struct { // CHAP Username used to authenticate the initiator ChapUsername string `protobuf:"bytes,4,opt,name=chap_username,json=chapUsername,proto3" json:"chap_username,omitempty"` // CHAP password used to authenticate the initiator - ChapSecret string `protobuf:"bytes,5,opt,name=chap_secret,json=chapSecret,proto3" json:"chap_secret,omitempty"` - // Set true to enable multipath on the connection - // In order for multipath to work on Windows, the Multipath feature - // needs to be installed as well as MPIO should be correctly configured. - // Setting this true allows different sessions to the same target (IQN) - // NOTE: enabling this without having multipath installed may cause data - // corruption in cases where there are multiple sessions to the same - // target. - IsMultipath bool `protobuf:"varint,6,opt,name=is_multipath,json=isMultipath,proto3" json:"is_multipath,omitempty"` + ChapSecret string `protobuf:"bytes,5,opt,name=chap_secret,json=chapSecret,proto3" json:"chap_secret,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -491,13 +483,6 @@ func (m *ConnectTargetRequest) GetChapSecret() string { return "" } -func (m *ConnectTargetRequest) GetIsMultipath() bool { - if m != nil { - return m.IsMultipath - } - return false -} - type ConnectTargetResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -723,49 +708,48 @@ func init() { } var fileDescriptor_0438d9bfe30f1df4 = []byte{ - // 671 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xdb, 0x4e, 0xdb, 0x40, - 0x10, 0x25, 0xdc, 0x9a, 0x4c, 0x12, 0x48, 0x47, 0x5c, 0x8c, 0x8b, 0x4a, 0x58, 0x4a, 0x15, 0x55, - 0x22, 0x11, 0xe9, 0x53, 0x55, 0xa1, 0xca, 0x05, 0x44, 0x91, 0xb8, 0xc9, 0x25, 0x2d, 0xa5, 0x52, - 0xa3, 0xc5, 0xd9, 0xe2, 0x15, 0x89, 0x6d, 0xbc, 0x6b, 0x54, 0x3e, 0xa1, 0x1f, 0xd0, 0xff, 0xad, - 0x7c, 0x8d, 0x49, 0x9c, 0xf4, 0xa1, 0xf0, 0xb6, 0x7b, 0xf6, 0xcc, 0x99, 0xd9, 0x99, 0xd9, 0x59, - 0x38, 0xb8, 0xe6, 0xd2, 0xf4, 0xae, 0xea, 0x86, 0xdd, 0x6b, 0xdc, 0x78, 0x57, 0xcc, 0xb5, 0x98, - 0x64, 0x62, 0xcb, 0x10, 0xbc, 0x61, 0x08, 0xbe, 0xe5, 0xb8, 0xf6, 0xaf, 0xfb, 0x86, 0xd1, 0xe5, - 0xcc, 0x92, 0x0d, 0xea, 0xf0, 0x06, 0x17, 0xfe, 0xd1, 0xdd, 0x36, 0xed, 0x3a, 0x26, 0xdd, 0xf6, - 0xa1, 0xba, 0xe3, 0xda, 0xd2, 0xc6, 0x7c, 0x8c, 0x91, 0x2f, 0x50, 0x3a, 0xa7, 0xee, 0x35, 0x93, - 0x67, 0xb6, 0x2b, 0x69, 0x17, 0x37, 0x61, 0x4e, 0x06, 0xfb, 0x36, 0xed, 0x74, 0x5c, 0x26, 0x84, - 0x92, 0xab, 0xe6, 0x6a, 0x05, 0xbd, 0x1c, 0xa2, 0x5a, 0x08, 0xe2, 0x1a, 0x14, 0x23, 0x9a, 0x63, - 0xbb, 0x52, 0x99, 0xac, 0xe6, 0x6a, 0x65, 0x1d, 0x64, 0xa2, 0x44, 0x5a, 0xb0, 0xa4, 0x75, 0x3a, - 0x69, 0x69, 0x9d, 0xdd, 0x7a, 0x4c, 0x48, 0x7c, 0x0f, 0xe5, 0x94, 0x29, 0xed, 0x06, 0x0e, 0x8a, - 0xcd, 0xa5, 0x7a, 0x1c, 0x53, 0xfd, 0x81, 0x55, 0x49, 0xa6, 0x76, 0x64, 0x05, 0x96, 0x87, 0x64, - 0x85, 0x63, 0x5b, 0x82, 0x91, 0x4b, 0x78, 0xb1, 0xc7, 0x85, 0x61, 0xdf, 0x31, 0xf7, 0xd1, 0xdd, - 0x36, 0x61, 0x35, 0x5b, 0x3b, 0xf4, 0x8d, 0x08, 0xd3, 0xfc, 0xd6, 0xf2, 0x73, 0x35, 0x55, 0x2b, - 0xe8, 0xc1, 0x9a, 0x5c, 0xc0, 0x8a, 0xce, 0x7a, 0xf6, 0x1d, 0x7b, 0xf4, 0x68, 0x56, 0x41, 0xcd, - 0x52, 0x8e, 0xf2, 0xa0, 0x82, 0x72, 0xc4, 0x85, 0x4c, 0x9f, 0x89, 0xc8, 0x2d, 0xb9, 0x84, 0x95, - 0x8c, 0xb3, 0xe8, 0x12, 0x3b, 0x49, 0xe9, 0xc3, 0x98, 0xc2, 0xeb, 0x8c, 0x0e, 0xaa, 0x9c, 0x0e, - 0x4a, 0x90, 0xdf, 0x93, 0xb0, 0xb0, 0x6b, 0x5b, 0x16, 0x33, 0x22, 0xfd, 0xc7, 0xb8, 0x2b, 0x56, - 0x60, 0x8a, 0xdf, 0x5a, 0x41, 0x83, 0x15, 0x74, 0x7f, 0x89, 0xef, 0xa0, 0x40, 0x3d, 0x69, 0xb6, - 0xe5, 0xbd, 0xc3, 0x94, 0xa9, 0x6a, 0xae, 0x36, 0xd7, 0x5c, 0xed, 0x4b, 0x69, 0x9e, 0x34, 0x99, - 0x25, 0xb9, 0x41, 0x25, 0xb7, 0xad, 0xf3, 0x7b, 0x87, 0xe9, 0x79, 0x9f, 0xee, 0xaf, 0x70, 0x03, - 0xca, 0x86, 0x49, 0x9d, 0xb6, 0x27, 0x98, 0x6b, 0xd1, 0x1e, 0x53, 0xa6, 0x03, 0xd9, 0x92, 0x0f, - 0xb6, 0x22, 0xcc, 0x6f, 0xed, 0x80, 0x24, 0x98, 0xe1, 0x32, 0xa9, 0xcc, 0x04, 0x14, 0xf0, 0xa1, - 0xcf, 0x01, 0x82, 0xeb, 0x50, 0xe2, 0xa2, 0xdd, 0xf3, 0xba, 0x92, 0x3b, 0x54, 0x9a, 0xca, 0x6c, - 0x35, 0x57, 0xcb, 0xeb, 0x45, 0x2e, 0x8e, 0x63, 0x88, 0x2c, 0xc3, 0xe2, 0x40, 0x2a, 0xa2, 0xe2, - 0xfc, 0x84, 0xc5, 0x03, 0x16, 0x81, 0x7b, 0x5c, 0xdc, 0x88, 0xa7, 0x49, 0x12, 0x69, 0xc2, 0xd2, - 0xa0, 0x9f, 0xa8, 0xca, 0x0a, 0x3c, 0xeb, 0x70, 0x71, 0x73, 0xb8, 0x17, 0x77, 0x6b, 0xbc, 0x25, - 0x26, 0x2c, 0x07, 0x4d, 0xfe, 0xe4, 0x25, 0xf4, 0x5b, 0x74, 0xd8, 0x53, 0x18, 0xdf, 0x1b, 0x0d, - 0x70, 0xb8, 0x86, 0x98, 0x87, 0xe9, 0x93, 0xd3, 0x93, 0xfd, 0xca, 0x04, 0x56, 0xa0, 0x74, 0x7a, - 0xb2, 0xdf, 0xfe, 0xaa, 0x7d, 0x6b, 0xef, 0x7e, 0xd2, 0xce, 0x2a, 0x39, 0x9c, 0x87, 0xe2, 0x71, - 0xeb, 0xbc, 0xa5, 0x1d, 0x85, 0xc0, 0x64, 0xf3, 0xcf, 0x0c, 0xcc, 0x1c, 0xfa, 0xa3, 0x0f, 0x2f, - 0x60, 0x7e, 0x60, 0x5c, 0x60, 0x35, 0xd5, 0x2b, 0x99, 0x03, 0x4a, 0x5d, 0x1f, 0xc3, 0x88, 0xca, - 0x38, 0x81, 0xd7, 0xb0, 0x90, 0x35, 0x11, 0x70, 0xb3, 0x6f, 0x3c, 0x66, 0x1a, 0xa9, 0xaf, 0xff, - 0x45, 0x4b, 0x1c, 0x51, 0xc0, 0xe1, 0xc7, 0x8e, 0x1b, 0x7d, 0xfb, 0x91, 0x43, 0x46, 0x7d, 0x35, - 0x9e, 0x94, 0xb8, 0xf8, 0x01, 0xcf, 0x87, 0xa6, 0x02, 0x92, 0xbe, 0xf1, 0xa8, 0x71, 0xa2, 0x6e, - 0x8c, 0xe5, 0x24, 0xfa, 0x3a, 0x94, 0x1f, 0xbc, 0x06, 0x7c, 0xd9, 0xb7, 0xcb, 0x9a, 0x18, 0xea, - 0xda, 0xc8, 0xf3, 0x44, 0xf3, 0x3b, 0x54, 0x06, 0x5b, 0x08, 0xd7, 0x07, 0x92, 0x9a, 0xa1, 0x4c, - 0xc6, 0x51, 0x12, 0xf1, 0x16, 0xcc, 0x3d, 0x7c, 0x3d, 0x98, 0x8a, 0x28, 0xf3, 0xfd, 0xaa, 0xd5, - 0xd1, 0x84, 0x58, 0xf6, 0xe3, 0x87, 0xcb, 0x9d, 0xff, 0xfa, 0xc0, 0xaf, 0x66, 0x83, 0xdf, 0xfb, - 0xed, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xce, 0xb0, 0xe3, 0xcc, 0x08, 0x08, 0x00, 0x00, + // 649 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0x6d, 0x4f, 0xd3, 0x50, + 0x14, 0xa6, 0xbc, 0x28, 0x3b, 0xac, 0x30, 0x6f, 0x78, 0x29, 0x95, 0xc8, 0xb8, 0x88, 0x59, 0x4c, + 0xd8, 0xc2, 0xfc, 0x64, 0x0c, 0x31, 0x15, 0x08, 0x92, 0x20, 0x90, 0xca, 0x14, 0x67, 0xe2, 0x72, + 0xd7, 0x5d, 0xd7, 0x9b, 0x6d, 0x6d, 0xd7, 0x7b, 0xbb, 0xb8, 0x1f, 0xe2, 0x5f, 0xf4, 0x77, 0x98, + 0xbe, 0xac, 0xeb, 0xb6, 0x6e, 0x7e, 0x70, 0x7c, 0xeb, 0x7d, 0xee, 0x73, 0x9e, 0x73, 0xee, 0x79, + 0x2b, 0x5c, 0x36, 0x99, 0x30, 0xbd, 0x7a, 0xd1, 0xb0, 0x3b, 0xa5, 0x96, 0x57, 0xa7, 0xae, 0x45, + 0x05, 0xe5, 0xc7, 0x06, 0x67, 0x25, 0x83, 0xb3, 0x63, 0xc7, 0xb5, 0x7f, 0xf5, 0x4b, 0x46, 0x9b, + 0x51, 0x4b, 0x94, 0x88, 0xc3, 0x4a, 0x8c, 0xfb, 0x57, 0xbd, 0x13, 0xd2, 0x76, 0x4c, 0x72, 0xe2, + 0x43, 0x45, 0xc7, 0xb5, 0x85, 0x8d, 0x56, 0x07, 0x18, 0xfe, 0x02, 0xd9, 0x7b, 0xe2, 0x36, 0xa9, + 0xb8, 0xb3, 0x5d, 0x41, 0xda, 0xe8, 0x08, 0xd6, 0x45, 0x70, 0xae, 0x91, 0x46, 0xc3, 0xa5, 0x9c, + 0x2b, 0x52, 0x5e, 0x2a, 0x64, 0x74, 0x39, 0x44, 0xb5, 0x10, 0x44, 0xfb, 0xb0, 0x16, 0xd1, 0x1c, + 0xdb, 0x15, 0xca, 0x62, 0x5e, 0x2a, 0xc8, 0x3a, 0x88, 0x58, 0x09, 0x57, 0x60, 0x5b, 0x6b, 0x34, + 0x92, 0xd2, 0x3a, 0xed, 0x7a, 0x94, 0x0b, 0xf4, 0x0e, 0xe4, 0x84, 0x29, 0x69, 0x07, 0x0e, 0xd6, + 0xca, 0xdb, 0xc5, 0x41, 0x4c, 0xc5, 0x11, 0xab, 0xac, 0x48, 0x9c, 0xf0, 0x2e, 0xec, 0x4c, 0xc8, + 0x72, 0xc7, 0xb6, 0x38, 0xc5, 0x55, 0x78, 0x7e, 0xce, 0xb8, 0x61, 0xf7, 0xa8, 0x3b, 0x77, 0xb7, + 0x65, 0xd8, 0x4b, 0xd7, 0x0e, 0x7d, 0x23, 0x04, 0xcb, 0xac, 0x6b, 0xf9, 0xb9, 0x5a, 0x2a, 0x64, + 0xf4, 0xe0, 0x1b, 0x3f, 0xc0, 0xae, 0x4e, 0x3b, 0x76, 0x8f, 0xce, 0x3d, 0x9a, 0x3d, 0x50, 0xd3, + 0x94, 0xa3, 0x3c, 0xa8, 0xa0, 0x5c, 0x33, 0x2e, 0x92, 0x77, 0x3c, 0x72, 0x8b, 0xab, 0xb0, 0x9b, + 0x72, 0x17, 0x3d, 0xe2, 0x34, 0x2e, 0x7d, 0x18, 0x53, 0xf8, 0x9c, 0xe9, 0x41, 0xc9, 0xc9, 0xa0, + 0x38, 0xfe, 0x23, 0xc1, 0xe6, 0x99, 0x6d, 0x59, 0xd4, 0x88, 0xf4, 0xe7, 0xf1, 0x56, 0x94, 0x83, + 0x25, 0xd6, 0xb5, 0x82, 0x06, 0xcb, 0xe8, 0xfe, 0x27, 0x7a, 0x0b, 0x19, 0xe2, 0x09, 0xb3, 0x26, + 0xfa, 0x0e, 0x55, 0x96, 0xf2, 0x52, 0x61, 0xbd, 0xbc, 0x37, 0x94, 0xd2, 0x3c, 0x61, 0x52, 0x4b, + 0x30, 0x83, 0x08, 0x66, 0x5b, 0xf7, 0x7d, 0x87, 0xea, 0xab, 0x3e, 0xdd, 0xff, 0x42, 0x87, 0x20, + 0x1b, 0x26, 0x71, 0x6a, 0x1e, 0xa7, 0xae, 0x45, 0x3a, 0x54, 0x59, 0x0e, 0x64, 0xb3, 0x3e, 0x58, + 0x89, 0x30, 0xbf, 0xb5, 0x03, 0x12, 0xa7, 0x86, 0x4b, 0x85, 0xb2, 0x12, 0x50, 0xc0, 0x87, 0x3e, + 0x07, 0x08, 0xde, 0x81, 0xad, 0xb1, 0x77, 0x46, 0x99, 0xff, 0x09, 0x5b, 0x97, 0x34, 0x02, 0xcf, + 0x19, 0x6f, 0xf1, 0xc7, 0xc9, 0x00, 0x2e, 0xc3, 0xf6, 0xb8, 0x9f, 0xa8, 0x84, 0x0a, 0x3c, 0x6d, + 0x30, 0xde, 0xba, 0x3a, 0x1f, 0xb4, 0xe2, 0xe0, 0x88, 0x4d, 0xd8, 0x09, 0x3a, 0xf8, 0xd1, 0xeb, + 0xe3, 0xf7, 0xdf, 0xa4, 0xa7, 0x30, 0xbe, 0xd7, 0x1a, 0xa0, 0xc9, 0x02, 0xa1, 0x55, 0x58, 0xbe, + 0xb9, 0xbd, 0xb9, 0xc8, 0x2d, 0xa0, 0x1c, 0x64, 0x6f, 0x6f, 0x2e, 0x6a, 0x5f, 0xb5, 0x6f, 0xb5, + 0xb3, 0x8f, 0xda, 0x5d, 0x4e, 0x42, 0x1b, 0xb0, 0xf6, 0xa9, 0x72, 0x5f, 0xd1, 0xae, 0x43, 0x60, + 0xb1, 0xfc, 0x7b, 0x05, 0x56, 0xae, 0xfc, 0xbd, 0x86, 0x1e, 0x60, 0x63, 0x6c, 0x17, 0xa0, 0x7c, + 0xa2, 0x11, 0x52, 0xb7, 0x8f, 0x7a, 0x30, 0x83, 0x11, 0x95, 0x71, 0x01, 0x35, 0x61, 0x33, 0x6d, + 0xdc, 0xd1, 0xd1, 0xd0, 0x78, 0xc6, 0xaa, 0x51, 0x5f, 0xfd, 0x8b, 0x16, 0x3b, 0x22, 0x80, 0x26, + 0x27, 0x19, 0x1d, 0x0e, 0xed, 0xa7, 0x6e, 0x10, 0xf5, 0xe5, 0x6c, 0x52, 0xec, 0xe2, 0x07, 0x3c, + 0x9b, 0x18, 0x79, 0x84, 0x87, 0xc6, 0xd3, 0x76, 0x85, 0x7a, 0x38, 0x93, 0x13, 0xeb, 0xeb, 0x20, + 0x8f, 0x4c, 0x03, 0x7a, 0x31, 0xb4, 0x4b, 0x5b, 0x07, 0xea, 0xfe, 0xd4, 0xfb, 0x58, 0xf3, 0x3b, + 0xe4, 0xc6, 0x5b, 0x08, 0x1d, 0x8c, 0x25, 0x35, 0x45, 0x19, 0xcf, 0xa2, 0xc4, 0xe2, 0x15, 0x58, + 0x1f, 0x9d, 0x1e, 0x94, 0x88, 0x28, 0x75, 0x7e, 0xd5, 0xfc, 0x74, 0xc2, 0x40, 0xf6, 0xc3, 0xfb, + 0xea, 0xe9, 0x7f, 0xfd, 0x9d, 0xeb, 0x4f, 0x82, 0x5f, 0xf3, 0x9b, 0xbf, 0x01, 0x00, 0x00, 0xff, + 0xff, 0xbb, 0x3d, 0x8f, 0x5c, 0xe5, 0x07, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.proto b/vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.proto index 92e5c080..b667f8da 100644 --- a/vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.proto +++ b/vendor/github.com/kubernetes-csi/csi-proxy/client/api/iscsi/v1alpha1/api.proto @@ -90,8 +90,10 @@ message ListTargetPortalsResponse { enum AuthenticationType { // No authentication is used NONE = 0; + // One way CHAP authentication. The target authenticates the initiator. ONE_WAY_CHAP = 1; + // Mutual CHAP authentication. The target and initiator authenticate each // other. MUTUAL_CHAP = 2; @@ -118,15 +120,6 @@ message ConnectTargetRequest { // CHAP password used to authenticate the initiator string chap_secret = 5; - - // Set true to enable multipath on the connection - // In order for multipath to work on Windows, the Multipath feature - // needs to be installed as well as MPIO should be correctly configured. - // Setting this true allows different sessions to the same target (IQN) - // NOTE: enabling this without having multipath installed may cause data - // corruption in cases where there are multiple sessions to the same - // target. - bool is_multipath = 6; } message ConnectTargetResponse {