From d3a702657ea66fc09eb5438f94b747fcc429a590 Mon Sep 17 00:00:00 2001 From: Jing Xu Date: Fri, 31 Jan 2020 12:58:35 -0800 Subject: [PATCH] SMB APIs Add SMB APIs to csi-proxy and integration test --- client/api/smb/v1alpha1/api.pb.go | 370 ++++++++++++++++++ client/api/smb/v1alpha1/api.proto | 57 +++ .../groups/smb/v1alpha1/client_generated.go | 60 +++ cmd/server/main.go | 8 + go.mod | 1 + go.sum | 5 + integrationtests/smb_test.go | 198 ++++++++++ internal/os/smb/api.go | 46 +++ internal/server/smb/api_group_generated.go | 27 ++ internal/server/smb/internal/types.go | 19 + .../server/smb/internal/types_generated.go | 20 + .../smb/internal/v1alpha1/conversion.go | 3 + .../internal/v1alpha1/conversion_generated.go | 92 +++++ .../smb/internal/v1alpha1/server_generated.go | 66 ++++ internal/server/smb/server.go | 54 +++ internal/server/smb/server_test.go | 63 +++ 16 files changed, 1089 insertions(+) create mode 100644 client/api/smb/v1alpha1/api.pb.go create mode 100644 client/api/smb/v1alpha1/api.proto create mode 100644 client/groups/smb/v1alpha1/client_generated.go create mode 100644 integrationtests/smb_test.go create mode 100644 internal/os/smb/api.go create mode 100644 internal/server/smb/api_group_generated.go create mode 100644 internal/server/smb/internal/types.go create mode 100644 internal/server/smb/internal/types_generated.go create mode 100644 internal/server/smb/internal/v1alpha1/conversion.go create mode 100644 internal/server/smb/internal/v1alpha1/conversion_generated.go create mode 100644 internal/server/smb/internal/v1alpha1/server_generated.go create mode 100644 internal/server/smb/server.go create mode 100644 internal/server/smb/server_test.go diff --git a/client/api/smb/v1alpha1/api.pb.go b/client/api/smb/v1alpha1/api.pb.go new file mode 100644 index 00000000..0bce3231 --- /dev/null +++ b/client/api/smb/v1alpha1/api.pb.go @@ -0,0 +1,370 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: github.com/kubernetes-csi/csi-proxy/client/api/smb/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 NewSmbGlobalMappingRequest struct { + // A remote SMB share to mount + // All unicode characters allowed in SMB server name specifications are + // permitted except for restrictions below + // + // Restrictions: + // SMB remote path specified in the format: \\server-name\sharename, \\server.fqdn\sharename or \\a.b.c.d\sharename + // If not an IP address, share name has to be a valid DNS name. + // UNC specifications to local paths or prefix: \\?\ is not allowed. + // Characters: + [ ] " / : ; | < > , ? * = $ are not allowed. + RemotePath string `protobuf:"bytes,1,opt,name=remote_path,json=remotePath,proto3" json:"remote_path,omitempty"` + // Username credential associated with the share + Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"` + // Password credential associated with the share + Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *NewSmbGlobalMappingRequest) Reset() { *m = NewSmbGlobalMappingRequest{} } +func (m *NewSmbGlobalMappingRequest) String() string { return proto.CompactTextString(m) } +func (*NewSmbGlobalMappingRequest) ProtoMessage() {} +func (*NewSmbGlobalMappingRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_33320d52df402875, []int{0} +} + +func (m *NewSmbGlobalMappingRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_NewSmbGlobalMappingRequest.Unmarshal(m, b) +} +func (m *NewSmbGlobalMappingRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_NewSmbGlobalMappingRequest.Marshal(b, m, deterministic) +} +func (m *NewSmbGlobalMappingRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_NewSmbGlobalMappingRequest.Merge(m, src) +} +func (m *NewSmbGlobalMappingRequest) XXX_Size() int { + return xxx_messageInfo_NewSmbGlobalMappingRequest.Size(m) +} +func (m *NewSmbGlobalMappingRequest) XXX_DiscardUnknown() { + xxx_messageInfo_NewSmbGlobalMappingRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_NewSmbGlobalMappingRequest proto.InternalMessageInfo + +func (m *NewSmbGlobalMappingRequest) GetRemotePath() string { + if m != nil { + return m.RemotePath + } + return "" +} + +func (m *NewSmbGlobalMappingRequest) GetUsername() string { + if m != nil { + return m.Username + } + return "" +} + +func (m *NewSmbGlobalMappingRequest) GetPassword() string { + if m != nil { + return m.Password + } + return "" +} + +type NewSmbGlobalMappingResponse struct { + // Windows error code + // Success is represented as 0 + Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *NewSmbGlobalMappingResponse) Reset() { *m = NewSmbGlobalMappingResponse{} } +func (m *NewSmbGlobalMappingResponse) String() string { return proto.CompactTextString(m) } +func (*NewSmbGlobalMappingResponse) ProtoMessage() {} +func (*NewSmbGlobalMappingResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_33320d52df402875, []int{1} +} + +func (m *NewSmbGlobalMappingResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_NewSmbGlobalMappingResponse.Unmarshal(m, b) +} +func (m *NewSmbGlobalMappingResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_NewSmbGlobalMappingResponse.Marshal(b, m, deterministic) +} +func (m *NewSmbGlobalMappingResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_NewSmbGlobalMappingResponse.Merge(m, src) +} +func (m *NewSmbGlobalMappingResponse) XXX_Size() int { + return xxx_messageInfo_NewSmbGlobalMappingResponse.Size(m) +} +func (m *NewSmbGlobalMappingResponse) XXX_DiscardUnknown() { + xxx_messageInfo_NewSmbGlobalMappingResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_NewSmbGlobalMappingResponse proto.InternalMessageInfo + +func (m *NewSmbGlobalMappingResponse) GetError() string { + if m != nil { + return m.Error + } + return "" +} + +type RemoveSmbGlobalMappingRequest struct { + // A remote SMB share mapping to remove + // All unicode characters allowed in SMB server name specifications are + // permitted except for restrictions below + // + // Restrictions: + // SMB share specified in the format: \\server-name\sharename, \\server.fqdn\sharename or \\a.b.c.d\sharename + // If not an IP address, share name has to be a valid DNS name. + // UNC specifications to local paths or prefix: \\?\ is not allowed. + // Characters: + [ ] " / : ; | < > , ? * = $ are not allowed. + RemotePath string `protobuf:"bytes,1,opt,name=remote_path,json=remotePath,proto3" json:"remote_path,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RemoveSmbGlobalMappingRequest) Reset() { *m = RemoveSmbGlobalMappingRequest{} } +func (m *RemoveSmbGlobalMappingRequest) String() string { return proto.CompactTextString(m) } +func (*RemoveSmbGlobalMappingRequest) ProtoMessage() {} +func (*RemoveSmbGlobalMappingRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_33320d52df402875, []int{2} +} + +func (m *RemoveSmbGlobalMappingRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RemoveSmbGlobalMappingRequest.Unmarshal(m, b) +} +func (m *RemoveSmbGlobalMappingRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RemoveSmbGlobalMappingRequest.Marshal(b, m, deterministic) +} +func (m *RemoveSmbGlobalMappingRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveSmbGlobalMappingRequest.Merge(m, src) +} +func (m *RemoveSmbGlobalMappingRequest) XXX_Size() int { + return xxx_messageInfo_RemoveSmbGlobalMappingRequest.Size(m) +} +func (m *RemoveSmbGlobalMappingRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveSmbGlobalMappingRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveSmbGlobalMappingRequest proto.InternalMessageInfo + +func (m *RemoveSmbGlobalMappingRequest) GetRemotePath() string { + if m != nil { + return m.RemotePath + } + return "" +} + +type RemoveSmbGlobalMappingResponse struct { + // Windows error code + // Success is represented as 0 + Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RemoveSmbGlobalMappingResponse) Reset() { *m = RemoveSmbGlobalMappingResponse{} } +func (m *RemoveSmbGlobalMappingResponse) String() string { return proto.CompactTextString(m) } +func (*RemoveSmbGlobalMappingResponse) ProtoMessage() {} +func (*RemoveSmbGlobalMappingResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_33320d52df402875, []int{3} +} + +func (m *RemoveSmbGlobalMappingResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RemoveSmbGlobalMappingResponse.Unmarshal(m, b) +} +func (m *RemoveSmbGlobalMappingResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RemoveSmbGlobalMappingResponse.Marshal(b, m, deterministic) +} +func (m *RemoveSmbGlobalMappingResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveSmbGlobalMappingResponse.Merge(m, src) +} +func (m *RemoveSmbGlobalMappingResponse) XXX_Size() int { + return xxx_messageInfo_RemoveSmbGlobalMappingResponse.Size(m) +} +func (m *RemoveSmbGlobalMappingResponse) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveSmbGlobalMappingResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveSmbGlobalMappingResponse proto.InternalMessageInfo + +func (m *RemoveSmbGlobalMappingResponse) GetError() string { + if m != nil { + return m.Error + } + return "" +} + +func init() { + proto.RegisterType((*NewSmbGlobalMappingRequest)(nil), "v1alpha1.NewSmbGlobalMappingRequest") + proto.RegisterType((*NewSmbGlobalMappingResponse)(nil), "v1alpha1.NewSmbGlobalMappingResponse") + proto.RegisterType((*RemoveSmbGlobalMappingRequest)(nil), "v1alpha1.RemoveSmbGlobalMappingRequest") + proto.RegisterType((*RemoveSmbGlobalMappingResponse)(nil), "v1alpha1.RemoveSmbGlobalMappingResponse") +} + +func init() { proto.RegisterFile("client/api/smb/v1alpha1/api.proto", fileDescriptor_33320d52df402875) } + +var fileDescriptor_33320d52df402875 = []byte{ + // 252 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x52, 0x4d, 0x4b, 0xc3, 0x40, + 0x10, 0x35, 0x16, 0xa5, 0x8e, 0xb7, 0x55, 0x24, 0x44, 0xfc, 0x5a, 0x14, 0x7b, 0x4a, 0xa8, 0x05, + 0xcf, 0xde, 0x3c, 0x29, 0x92, 0xfe, 0x00, 0x99, 0xb5, 0x83, 0x09, 0x64, 0x33, 0xe3, 0xee, 0xa6, + 0xfd, 0xaf, 0xfe, 0x1a, 0x69, 0x63, 0xf4, 0x92, 0xb6, 0xd0, 0xe3, 0x7b, 0x6f, 0x78, 0xef, 0xf1, + 0x18, 0xb8, 0xf9, 0xa8, 0x4a, 0xaa, 0x43, 0x86, 0x52, 0x66, 0xde, 0x9a, 0x6c, 0x3e, 0xc6, 0x4a, + 0x0a, 0x1c, 0x2f, 0x89, 0x54, 0x1c, 0x07, 0x56, 0xc3, 0x8e, 0xd3, 0x0d, 0x24, 0xaf, 0xb4, 0x98, + 0x5a, 0xf3, 0x5c, 0xb1, 0xc1, 0xea, 0x05, 0x45, 0xca, 0xfa, 0x33, 0xa7, 0xaf, 0x86, 0x7c, 0x50, + 0x57, 0x70, 0xec, 0xc8, 0x72, 0xa0, 0x77, 0xc1, 0x50, 0xc4, 0xd1, 0x75, 0x34, 0x3a, 0xca, 0xa1, + 0xa5, 0xde, 0x30, 0x14, 0x2a, 0x81, 0x61, 0xe3, 0xc9, 0xd5, 0x68, 0x29, 0xde, 0x5f, 0xa9, 0x7f, + 0x78, 0xa9, 0x09, 0x7a, 0xbf, 0x60, 0x37, 0x8b, 0x07, 0xad, 0xd6, 0x61, 0x3d, 0x81, 0xf3, 0xde, + 0x58, 0x2f, 0x5c, 0x7b, 0x52, 0xa7, 0x70, 0x40, 0xce, 0xb1, 0xfb, 0x4d, 0x6c, 0x81, 0x7e, 0x82, + 0x8b, 0x9c, 0x2c, 0xcf, 0x69, 0xd7, 0xba, 0xfa, 0x11, 0x2e, 0xd7, 0x39, 0x6c, 0x4a, 0x7e, 0xf8, + 0x8e, 0x60, 0x30, 0xb5, 0x46, 0xcd, 0xe0, 0xa4, 0xa7, 0xb6, 0xba, 0x4d, 0xbb, 0x3d, 0xd3, 0xf5, + 0x63, 0x26, 0x77, 0x5b, 0xae, 0xda, 0x06, 0x7a, 0x4f, 0x59, 0x38, 0xeb, 0x6f, 0xa9, 0xee, 0xff, + 0x2d, 0x36, 0x2e, 0x91, 0x8c, 0xb6, 0x1f, 0x76, 0x71, 0xe6, 0x70, 0xf5, 0x13, 0x93, 0x9f, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x3c, 0xc6, 0x1d, 0x35, 0x38, 0x02, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// 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.SupportPackageIsVersion4 + +// SmbClient is the client API for Smb service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type SmbClient interface { + // NewSmbGlobalMapping creates an SMB mapping on the SMB client to an SMB share. + NewSmbGlobalMapping(ctx context.Context, in *NewSmbGlobalMappingRequest, opts ...grpc.CallOption) (*NewSmbGlobalMappingResponse, error) + // RemoveSmbGlobalMapping removes the SMB mapping to an SMB share. + RemoveSmbGlobalMapping(ctx context.Context, in *RemoveSmbGlobalMappingRequest, opts ...grpc.CallOption) (*RemoveSmbGlobalMappingResponse, error) +} + +type smbClient struct { + cc *grpc.ClientConn +} + +func NewSmbClient(cc *grpc.ClientConn) SmbClient { + return &smbClient{cc} +} + +func (c *smbClient) NewSmbGlobalMapping(ctx context.Context, in *NewSmbGlobalMappingRequest, opts ...grpc.CallOption) (*NewSmbGlobalMappingResponse, error) { + out := new(NewSmbGlobalMappingResponse) + err := c.cc.Invoke(ctx, "/v1alpha1.Smb/NewSmbGlobalMapping", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *smbClient) RemoveSmbGlobalMapping(ctx context.Context, in *RemoveSmbGlobalMappingRequest, opts ...grpc.CallOption) (*RemoveSmbGlobalMappingResponse, error) { + out := new(RemoveSmbGlobalMappingResponse) + err := c.cc.Invoke(ctx, "/v1alpha1.Smb/RemoveSmbGlobalMapping", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// SmbServer is the server API for Smb service. +type SmbServer interface { + // NewSmbGlobalMapping creates an SMB mapping on the SMB client to an SMB share. + NewSmbGlobalMapping(context.Context, *NewSmbGlobalMappingRequest) (*NewSmbGlobalMappingResponse, error) + // RemoveSmbGlobalMapping removes the SMB mapping to an SMB share. + RemoveSmbGlobalMapping(context.Context, *RemoveSmbGlobalMappingRequest) (*RemoveSmbGlobalMappingResponse, error) +} + +// UnimplementedSmbServer can be embedded to have forward compatible implementations. +type UnimplementedSmbServer struct { +} + +func (*UnimplementedSmbServer) NewSmbGlobalMapping(ctx context.Context, req *NewSmbGlobalMappingRequest) (*NewSmbGlobalMappingResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method NewSmbGlobalMapping not implemented") +} +func (*UnimplementedSmbServer) RemoveSmbGlobalMapping(ctx context.Context, req *RemoveSmbGlobalMappingRequest) (*RemoveSmbGlobalMappingResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveSmbGlobalMapping not implemented") +} + +func RegisterSmbServer(s *grpc.Server, srv SmbServer) { + s.RegisterService(&_Smb_serviceDesc, srv) +} + +func _Smb_NewSmbGlobalMapping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(NewSmbGlobalMappingRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SmbServer).NewSmbGlobalMapping(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1alpha1.Smb/NewSmbGlobalMapping", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SmbServer).NewSmbGlobalMapping(ctx, req.(*NewSmbGlobalMappingRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Smb_RemoveSmbGlobalMapping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveSmbGlobalMappingRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SmbServer).RemoveSmbGlobalMapping(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1alpha1.Smb/RemoveSmbGlobalMapping", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SmbServer).RemoveSmbGlobalMapping(ctx, req.(*RemoveSmbGlobalMappingRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Smb_serviceDesc = grpc.ServiceDesc{ + ServiceName: "v1alpha1.Smb", + HandlerType: (*SmbServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "NewSmbGlobalMapping", + Handler: _Smb_NewSmbGlobalMapping_Handler, + }, + { + MethodName: "RemoveSmbGlobalMapping", + Handler: _Smb_RemoveSmbGlobalMapping_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "client/api/smb/v1alpha1/api.proto", +} diff --git a/client/api/smb/v1alpha1/api.proto b/client/api/smb/v1alpha1/api.proto new file mode 100644 index 00000000..defb768c --- /dev/null +++ b/client/api/smb/v1alpha1/api.proto @@ -0,0 +1,57 @@ +syntax = "proto3"; + +package v1alpha1; + +service Smb { + // NewSmbGlobalMapping creates an SMB mapping on the SMB client to an SMB share. + rpc NewSmbGlobalMapping(NewSmbGlobalMappingRequest) returns (NewSmbGlobalMappingResponse) {} + + // RemoveSmbGlobalMapping removes the SMB mapping to an SMB share. + rpc RemoveSmbGlobalMapping(RemoveSmbGlobalMappingRequest) returns (RemoveSmbGlobalMappingResponse) {} +} + + +message NewSmbGlobalMappingRequest { + // A remote SMB share to mount + // All unicode characters allowed in SMB server name specifications are + // permitted except for restrictions below + // + // Restrictions: + // SMB remote path specified in the format: \\server-name\sharename, \\server.fqdn\sharename or \\a.b.c.d\sharename + // If not an IP address, share name has to be a valid DNS name. + // UNC specifications to local paths or prefix: \\?\ is not allowed. + // Characters: + [ ] " / : ; | < > , ? * = $ are not allowed. + string remote_path = 1; + + // Username credential associated with the share + string username = 2; + + // Password credential associated with the share + string password = 3; +} + +message NewSmbGlobalMappingResponse { + // Windows error code + // Success is represented as 0 + string error = 1; +} + + +message RemoveSmbGlobalMappingRequest { + // A remote SMB share mapping to remove + // All unicode characters allowed in SMB server name specifications are + // permitted except for restrictions below + // + // Restrictions: + // SMB share specified in the format: \\server-name\sharename, \\server.fqdn\sharename or \\a.b.c.d\sharename + // If not an IP address, share name has to be a valid DNS name. + // UNC specifications to local paths or prefix: \\?\ is not allowed. + // Characters: + [ ] " / : ; | < > , ? * = $ are not allowed. + string remote_path = 1; +} + +message RemoveSmbGlobalMappingResponse { + // Windows error code + // Success is represented as 0 + string error = 1; +} \ No newline at end of file diff --git a/client/groups/smb/v1alpha1/client_generated.go b/client/groups/smb/v1alpha1/client_generated.go new file mode 100644 index 00000000..4cdc0e83 --- /dev/null +++ b/client/groups/smb/v1alpha1/client_generated.go @@ -0,0 +1,60 @@ +// 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/smb/v1alpha1" + "github.com/kubernetes-csi/csi-proxy/client/apiversion" + "google.golang.org/grpc" +) + +const groupName = "smb" + +var version = apiversion.NewVersionOrPanic("v1alpha1") + +type Client struct { + client v1alpha1.SmbClient + connection *grpc.ClientConn +} + +// NewClient returns a client to make calls to the smb 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.NewSmbClient(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.SmbClient = &Client{} + +func (w *Client) NewSmbGlobalMapping(context context.Context, request *v1alpha1.NewSmbGlobalMappingRequest, opts ...grpc.CallOption) (*v1alpha1.NewSmbGlobalMappingResponse, error) { + return w.client.NewSmbGlobalMapping(context, request, opts...) +} + +func (w *Client) RemoveSmbGlobalMapping(context context.Context, request *v1alpha1.RemoveSmbGlobalMappingRequest, opts ...grpc.CallOption) (*v1alpha1.RemoveSmbGlobalMappingResponse, error) { + return w.client.RemoveSmbGlobalMapping(context, request, opts...) +} diff --git a/cmd/server/main.go b/cmd/server/main.go index 1b884c38..164fecac 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -5,10 +5,12 @@ import ( diskapi "github.com/kubernetes-csi/csi-proxy/internal/os/disk" filesystemapi "github.com/kubernetes-csi/csi-proxy/internal/os/filesystem" + smbapi "github.com/kubernetes-csi/csi-proxy/internal/os/smb" 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" + smbsrv "github.com/kubernetes-csi/csi-proxy/internal/server/smb" srvtypes "github.com/kubernetes-csi/csi-proxy/internal/server/types" volumesrv "github.com/kubernetes-csi/csi-proxy/internal/server/volume" "k8s.io/klog" @@ -51,9 +53,15 @@ func apiGroups() ([]srvtypes.APIGroup, error) { return []srvtypes.APIGroup{}, err } + smbsrv, err := smbsrv.NewServer(smbapi.New()) + if err != nil { + return []srvtypes.APIGroup{}, err + } + return []srvtypes.APIGroup{ fssrv, disksrv, volumesrv, + smbsrv, }, nil } diff --git a/go.mod b/go.mod index b3a6cf55..e7b8388e 100644 --- a/go.mod +++ b/go.mod @@ -28,4 +28,5 @@ require ( google.golang.org/grpc v1.27.1 k8s.io/gengo v0.0.0-00010101000000-000000000000 k8s.io/klog v1.0.0 + k8s.io/utils v0.0.0-20200124190032-861946025e34 // indirect ) diff --git a/go.sum b/go.sum index b7cd6383..1f816a49 100644 --- a/go.sum +++ b/go.sum @@ -30,11 +30,13 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/wk8/gengo v0.0.0-20191007012548-3d2530bfe606 h1:i5JAp+X9ISMaGt0R/9nsiFuNyYN0hXSK9KtphOk/r94= @@ -93,5 +95,8 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/utils v0.0.0-20200124190032-861946025e34 h1:HjlUD6M0K3P8nRXmr2B9o4F9dUy9TCj/aEpReeyi6+k= +k8s.io/utils v0.0.0-20200124190032-861946025e34/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= diff --git a/integrationtests/smb_test.go b/integrationtests/smb_test.go new file mode 100644 index 00000000..b77ffccd --- /dev/null +++ b/integrationtests/smb_test.go @@ -0,0 +1,198 @@ +package integrationtests + +import ( + "context" + "fmt" + "io/ioutil" + "math/rand" + "os" + "os/exec" + "strings" + "time" + + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/kubernetes-csi/csi-proxy/client/api/smb/v1alpha1" + v1alpha1client "github.com/kubernetes-csi/csi-proxy/client/groups/smb/v1alpha1" +) + +const letterset = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + +var seededRand *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano())) + +func stringWithCharset(length int, charset string) string { + b := make([]byte, length) + for i := range b { + b[i] = charset[seededRand.Intn(len(charset))] + } + return string(b) +} + +// RandomString generates a random string with specified length +func randomString(length int) string { + return stringWithCharset(length, letterset) +} + +func setupUser(username, password string) error { + cmdLine := fmt.Sprintf(`$PWord = ConvertTo-SecureString $Env:password -AsPlainText -Force` + + `;New-Localuser -name $Env:username -accountneverexpires -password $PWord`) + cmd := exec.Command("powershell", "/c", cmdLine) + cmd.Env = append(os.Environ(), + fmt.Sprintf("username=%s", username), + fmt.Sprintf("password=%s", password)) + if output, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("setupUser failed: %v, output: %q", err, string(output)) + } + return nil +} + +func removeUser(username string) error { + cmdLine := fmt.Sprintf(`Remove-Localuser -name $Env:username`) + cmd := exec.Command("powershell", "/c", cmdLine) + cmd.Env = append(os.Environ(), + fmt.Sprintf("username=%s", username)) + if output, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("setupUser failed: %v, output: %q", err, string(output)) + } + return nil +} + +func setupSmbShare(shareName, localPath, username string) error { + if err := os.MkdirAll(localPath, 0755); err != nil { + return fmt.Errorf("setupSmbShare failed to create local path %q: %v", localPath, err) + } + cmdLine := fmt.Sprintf(`New-SMBShare -Name $Env:sharename -Path $Env:path -fullaccess $Env:username`) + cmd := exec.Command("powershell", "/c", cmdLine) + cmd.Env = append(os.Environ(), + fmt.Sprintf("sharename=%s", shareName), + fmt.Sprintf("path=%s", localPath), + fmt.Sprintf("username=%s", username)) + if output, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("setupSmbShare failed: %v, output: %q", err, string(output)) + } + + return nil +} + +func removeSmbShare(shareName string) error { + cmdLine := fmt.Sprintf(`Remove-SMBShare -Name $Env:sharename -Force`) + cmd := exec.Command("powershell", "/c", cmdLine) + cmd.Env = append(os.Environ(), + fmt.Sprintf("sharename=%s", shareName)) + if output, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("setupSmbShare failed: %v, output: %q", err, string(output)) + } + return nil +} + +func getSmbGlobalMapping(remotePath string) error { + // use PowerShell Environment Variables to store user input string to prevent command line injection + // https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-5.1 + cmdLine := fmt.Sprintf(`(Get-SmbGlobalMapping -RemotePath $Env:smbremotepath).Status`) + + cmd := exec.Command("powershell", "/c", cmdLine) + cmd.Env = append(os.Environ(), + fmt.Sprintf("smbremotepath=%s", remotePath)) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("Get-SmbGlobalMapping failed: %v, output: %q", err, string(output)) + } + if !strings.Contains(string(output), "OK") { + return fmt.Errorf("Get-SmbGlobalMapping return status %q instead of OK", string(output)) + } + return nil +} + +func writeReadFile(path string) error { + fileName := path + "\\hello.txt" + f, err := os.Create(fileName) + if err != nil { + return fmt.Errorf("create file %q failed: %v", fileName, err) + } + defer f.Close() + fileContent := "Hello World" + _, err = f.WriteString(fileContent) + f.Sync() + + dat, err := ioutil.ReadFile(fileName) + if err != nil { + return fmt.Errorf("read file %q failed: %v", fileName, err) + } + if fileContent != string(dat) { + return fmt.Errorf("read content of file %q failed: expected %q, got %q", fileName, fileContent, string(dat)) + } + fmt.Print(string(dat)) + + return err +} + +func TestSmbAPIGroup(t *testing.T) { + t.Run("Smb positive", func(t *testing.T) { + client, err := v1alpha1client.NewClient() + if err != nil { + t.Fatalf("Fail to get smb API group client %v", err) + } + defer client.Close() + + username := randomString(5) + password := randomString(10) + "!" + sharePath := fmt.Sprintf("C:\\smbshare%s", randomString(5)) + smbShare := randomString(6) + + localPath := fmt.Sprintf("C:\\localpath%s", randomString(5)) + + if err := setupUser(username, password); err != nil { + t.Fatalf("TestSmbAPIGroup %v", err) + } + defer removeUser(username) + + if err := setupSmbShare(smbShare, sharePath, username); err != nil { + t.Fatalf("TestSmbAPIGroup %v", err) + } + defer removeSmbShare(smbShare) + + hostname, err := os.Hostname() + assert.Nil(t, err) + + username = "domain\\" + username + remotePath := "\\\\" + hostname + "\\" + smbShare + // simulate Mount SMB operations around staging a volume on a node + mountSmbShareReq := &v1alpha1.NewSmbGlobalMappingRequest{ + RemotePath: remotePath, + Username: username, + Password: password, + } + mountSmbShareRsp, err := client.NewSmbGlobalMapping(context.Background(), mountSmbShareReq) + if err != nil { + t.Fatalf("TestSmbAPIGroup %v", err) + } + if !assert.Equal(t, "", mountSmbShareRsp.Error) { + t.Fatalf("TestSmbAPIGroup %v", mountSmbShareRsp.Error) + } + + err = getSmbGlobalMapping(remotePath) + assert.Nil(t, err) + + err = writeReadFile(remotePath) + assert.Nil(t, err) + + unmountSmbShareReq := &v1alpha1.RemoveSmbGlobalMappingRequest{ + RemotePath: remotePath, + } + unmountSmbShareRsp, err := client.RemoveSmbGlobalMapping(context.Background(), unmountSmbShareReq) + if err != nil { + t.Fatalf("TestSmbAPIGroup %v", err) + } + if !assert.Equal(t, "", unmountSmbShareRsp.Error) { + t.Fatalf("TestSmbAPIGroup %v", mountSmbShareRsp.Error) + } + err = getSmbGlobalMapping(remotePath) + assert.NotNil(t, err) + + err = writeReadFile(localPath) + assert.NotNil(t, err) + + }) +} diff --git a/internal/os/smb/api.go b/internal/os/smb/api.go new file mode 100644 index 00000000..83232385 --- /dev/null +++ b/internal/os/smb/api.go @@ -0,0 +1,46 @@ +package smb + +import ( + "fmt" + "os" + "os/exec" + + "k8s.io/klog" +) + +type APIImplementor struct{} + +func New() APIImplementor { + return APIImplementor{} +} + +func (APIImplementor) NewSmbGlobalMapping(remotePath, username, password string) error { + klog.V(4).Infof("NewSmbGlobalMapping: remotePath:%q", remotePath) + + // use PowerShell Environment Variables to store user input string to prevent command line injection + // https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-5.1 + cmdLine := fmt.Sprintf(`$PWord = ConvertTo-SecureString -String $Env:smbpassword -AsPlainText -Force` + + `;$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Env:smbuser, $PWord` + + `;New-SmbGlobalMapping -RemotePath $Env:smbremotepath -Credential $Credential`) + + cmd := exec.Command("powershell", "/c", cmdLine) + cmd.Env = append(os.Environ(), + fmt.Sprintf("smbuser=%s", username), + fmt.Sprintf("smbpassword=%s", password), + fmt.Sprintf("smbremotepath=%s", remotePath)) + if output, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("SmbGlobaNewSmbGlobalMappinglMapping failed: %v, output: %q", err, string(output)) + } + return nil +} + +func (APIImplementor) RemoveSmbGlobalMapping(remotePath string) error { + klog.V(4).Infof("RemoveSmbGlobalMapping remotePath (%q)", remotePath) + cmd := exec.Command("powershell", "/c", `Remove-SmbGlobalMapping -RemotePath $Env:smbremotepath -Force`) + cmd.Env = append(os.Environ(), fmt.Sprintf("smbremotepath=%s", remotePath)) + if output, err := cmd.CombinedOutput(); err != nil { + klog.Errorf("Remove-SmbGlobalMapping failed: %v, output: %q", err, output) + return fmt.Errorf("UnmountSmbShare failed: %v, output: %q", err, output) + } + return nil +} diff --git a/internal/server/smb/api_group_generated.go b/internal/server/smb/api_group_generated.go new file mode 100644 index 00000000..b7064889 --- /dev/null +++ b/internal/server/smb/api_group_generated.go @@ -0,0 +1,27 @@ +// Code generated by csi-proxy-api-gen. DO NOT EDIT. + +package smb + +import ( + "github.com/kubernetes-csi/csi-proxy/client/apiversion" + "github.com/kubernetes-csi/csi-proxy/internal/server/smb/internal" + "github.com/kubernetes-csi/csi-proxy/internal/server/smb/internal/v1alpha1" + srvtypes "github.com/kubernetes-csi/csi-proxy/internal/server/types" +) + +const name = "smb" + +// 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/smb/internal/types.go b/internal/server/smb/internal/types.go new file mode 100644 index 00000000..72efff90 --- /dev/null +++ b/internal/server/smb/internal/types.go @@ -0,0 +1,19 @@ +package internal + +type NewSmbGlobalMappingRequest struct { + RemotePath string + Username string + Password string +} + +type NewSmbGlobalMappingResponse struct { + Error string +} + +type RemoveSmbGlobalMappingRequest struct { + RemotePath string +} + +type RemoveSmbGlobalMappingResponse struct { + Error string +} diff --git a/internal/server/smb/internal/types_generated.go b/internal/server/smb/internal/types_generated.go new file mode 100644 index 00000000..fb2d5306 --- /dev/null +++ b/internal/server/smb/internal/types_generated.go @@ -0,0 +1,20 @@ +// 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 { + NewSmbGlobalMapping(context.Context, *NewSmbGlobalMappingRequest, apiversion.Version) (*NewSmbGlobalMappingResponse, error) + RemoveSmbGlobalMapping(context.Context, *RemoveSmbGlobalMappingRequest, apiversion.Version) (*RemoveSmbGlobalMappingResponse, error) +} diff --git a/internal/server/smb/internal/v1alpha1/conversion.go b/internal/server/smb/internal/v1alpha1/conversion.go new file mode 100644 index 00000000..23e83731 --- /dev/null +++ b/internal/server/smb/internal/v1alpha1/conversion.go @@ -0,0 +1,3 @@ +package v1alpha1 + +// Add manual conversion functions here to override automatic conversion functions diff --git a/internal/server/smb/internal/v1alpha1/conversion_generated.go b/internal/server/smb/internal/v1alpha1/conversion_generated.go new file mode 100644 index 00000000..7e3c7601 --- /dev/null +++ b/internal/server/smb/internal/v1alpha1/conversion_generated.go @@ -0,0 +1,92 @@ +// Code generated by csi-proxy-api-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/kubernetes-csi/csi-proxy/client/api/smb/v1alpha1" + internal "github.com/kubernetes-csi/csi-proxy/internal/server/smb/internal" +) + +func autoConvert_v1alpha1_NewSmbGlobalMappingRequest_To_internal_NewSmbGlobalMappingRequest(in *v1alpha1.NewSmbGlobalMappingRequest, out *internal.NewSmbGlobalMappingRequest) error { + out.RemotePath = in.RemotePath + out.Username = in.Username + out.Password = in.Password + return nil +} + +// Convert_v1alpha1_NewSmbGlobalMappingRequest_To_internal_NewSmbGlobalMappingRequest is an autogenerated conversion function. +func Convert_v1alpha1_NewSmbGlobalMappingRequest_To_internal_NewSmbGlobalMappingRequest(in *v1alpha1.NewSmbGlobalMappingRequest, out *internal.NewSmbGlobalMappingRequest) error { + return autoConvert_v1alpha1_NewSmbGlobalMappingRequest_To_internal_NewSmbGlobalMappingRequest(in, out) +} + +func autoConvert_internal_NewSmbGlobalMappingRequest_To_v1alpha1_NewSmbGlobalMappingRequest(in *internal.NewSmbGlobalMappingRequest, out *v1alpha1.NewSmbGlobalMappingRequest) error { + out.RemotePath = in.RemotePath + out.Username = in.Username + out.Password = in.Password + return nil +} + +// Convert_internal_NewSmbGlobalMappingRequest_To_v1alpha1_NewSmbGlobalMappingRequest is an autogenerated conversion function. +func Convert_internal_NewSmbGlobalMappingRequest_To_v1alpha1_NewSmbGlobalMappingRequest(in *internal.NewSmbGlobalMappingRequest, out *v1alpha1.NewSmbGlobalMappingRequest) error { + return autoConvert_internal_NewSmbGlobalMappingRequest_To_v1alpha1_NewSmbGlobalMappingRequest(in, out) +} + +func autoConvert_v1alpha1_NewSmbGlobalMappingResponse_To_internal_NewSmbGlobalMappingResponse(in *v1alpha1.NewSmbGlobalMappingResponse, out *internal.NewSmbGlobalMappingResponse) error { + out.Error = in.Error + return nil +} + +// Convert_v1alpha1_NewSmbGlobalMappingResponse_To_internal_NewSmbGlobalMappingResponse is an autogenerated conversion function. +func Convert_v1alpha1_NewSmbGlobalMappingResponse_To_internal_NewSmbGlobalMappingResponse(in *v1alpha1.NewSmbGlobalMappingResponse, out *internal.NewSmbGlobalMappingResponse) error { + return autoConvert_v1alpha1_NewSmbGlobalMappingResponse_To_internal_NewSmbGlobalMappingResponse(in, out) +} + +func autoConvert_internal_NewSmbGlobalMappingResponse_To_v1alpha1_NewSmbGlobalMappingResponse(in *internal.NewSmbGlobalMappingResponse, out *v1alpha1.NewSmbGlobalMappingResponse) error { + out.Error = in.Error + return nil +} + +// Convert_internal_NewSmbGlobalMappingResponse_To_v1alpha1_NewSmbGlobalMappingResponse is an autogenerated conversion function. +func Convert_internal_NewSmbGlobalMappingResponse_To_v1alpha1_NewSmbGlobalMappingResponse(in *internal.NewSmbGlobalMappingResponse, out *v1alpha1.NewSmbGlobalMappingResponse) error { + return autoConvert_internal_NewSmbGlobalMappingResponse_To_v1alpha1_NewSmbGlobalMappingResponse(in, out) +} + +func autoConvert_v1alpha1_RemoveSmbGlobalMappingRequest_To_internal_RemoveSmbGlobalMappingRequest(in *v1alpha1.RemoveSmbGlobalMappingRequest, out *internal.RemoveSmbGlobalMappingRequest) error { + out.RemotePath = in.RemotePath + return nil +} + +// Convert_v1alpha1_RemoveSmbGlobalMappingRequest_To_internal_RemoveSmbGlobalMappingRequest is an autogenerated conversion function. +func Convert_v1alpha1_RemoveSmbGlobalMappingRequest_To_internal_RemoveSmbGlobalMappingRequest(in *v1alpha1.RemoveSmbGlobalMappingRequest, out *internal.RemoveSmbGlobalMappingRequest) error { + return autoConvert_v1alpha1_RemoveSmbGlobalMappingRequest_To_internal_RemoveSmbGlobalMappingRequest(in, out) +} + +func autoConvert_internal_RemoveSmbGlobalMappingRequest_To_v1alpha1_RemoveSmbGlobalMappingRequest(in *internal.RemoveSmbGlobalMappingRequest, out *v1alpha1.RemoveSmbGlobalMappingRequest) error { + out.RemotePath = in.RemotePath + return nil +} + +// Convert_internal_RemoveSmbGlobalMappingRequest_To_v1alpha1_RemoveSmbGlobalMappingRequest is an autogenerated conversion function. +func Convert_internal_RemoveSmbGlobalMappingRequest_To_v1alpha1_RemoveSmbGlobalMappingRequest(in *internal.RemoveSmbGlobalMappingRequest, out *v1alpha1.RemoveSmbGlobalMappingRequest) error { + return autoConvert_internal_RemoveSmbGlobalMappingRequest_To_v1alpha1_RemoveSmbGlobalMappingRequest(in, out) +} + +func autoConvert_v1alpha1_RemoveSmbGlobalMappingResponse_To_internal_RemoveSmbGlobalMappingResponse(in *v1alpha1.RemoveSmbGlobalMappingResponse, out *internal.RemoveSmbGlobalMappingResponse) error { + out.Error = in.Error + return nil +} + +// Convert_v1alpha1_RemoveSmbGlobalMappingResponse_To_internal_RemoveSmbGlobalMappingResponse is an autogenerated conversion function. +func Convert_v1alpha1_RemoveSmbGlobalMappingResponse_To_internal_RemoveSmbGlobalMappingResponse(in *v1alpha1.RemoveSmbGlobalMappingResponse, out *internal.RemoveSmbGlobalMappingResponse) error { + return autoConvert_v1alpha1_RemoveSmbGlobalMappingResponse_To_internal_RemoveSmbGlobalMappingResponse(in, out) +} + +func autoConvert_internal_RemoveSmbGlobalMappingResponse_To_v1alpha1_RemoveSmbGlobalMappingResponse(in *internal.RemoveSmbGlobalMappingResponse, out *v1alpha1.RemoveSmbGlobalMappingResponse) error { + out.Error = in.Error + return nil +} + +// Convert_internal_RemoveSmbGlobalMappingResponse_To_v1alpha1_RemoveSmbGlobalMappingResponse is an autogenerated conversion function. +func Convert_internal_RemoveSmbGlobalMappingResponse_To_v1alpha1_RemoveSmbGlobalMappingResponse(in *internal.RemoveSmbGlobalMappingResponse, out *v1alpha1.RemoveSmbGlobalMappingResponse) error { + return autoConvert_internal_RemoveSmbGlobalMappingResponse_To_v1alpha1_RemoveSmbGlobalMappingResponse(in, out) +} diff --git a/internal/server/smb/internal/v1alpha1/server_generated.go b/internal/server/smb/internal/v1alpha1/server_generated.go new file mode 100644 index 00000000..b8abecdc --- /dev/null +++ b/internal/server/smb/internal/v1alpha1/server_generated.go @@ -0,0 +1,66 @@ +// Code generated by csi-proxy-api-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + + "github.com/kubernetes-csi/csi-proxy/client/api/smb/v1alpha1" + "github.com/kubernetes-csi/csi-proxy/client/apiversion" + "github.com/kubernetes-csi/csi-proxy/internal/server/smb/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.RegisterSmbServer(grpcServer, s) +} + +func (s *versionedAPI) NewSmbGlobalMapping(context context.Context, versionedRequest *v1alpha1.NewSmbGlobalMappingRequest) (*v1alpha1.NewSmbGlobalMappingResponse, error) { + request := &internal.NewSmbGlobalMappingRequest{} + if err := Convert_v1alpha1_NewSmbGlobalMappingRequest_To_internal_NewSmbGlobalMappingRequest(versionedRequest, request); err != nil { + return nil, err + } + + response, err := s.apiGroupServer.NewSmbGlobalMapping(context, request, version) + if err != nil { + return nil, err + } + + versionedResponse := &v1alpha1.NewSmbGlobalMappingResponse{} + if err := Convert_internal_NewSmbGlobalMappingResponse_To_v1alpha1_NewSmbGlobalMappingResponse(response, versionedResponse); err != nil { + return nil, err + } + + return versionedResponse, err +} + +func (s *versionedAPI) RemoveSmbGlobalMapping(context context.Context, versionedRequest *v1alpha1.RemoveSmbGlobalMappingRequest) (*v1alpha1.RemoveSmbGlobalMappingResponse, error) { + request := &internal.RemoveSmbGlobalMappingRequest{} + if err := Convert_v1alpha1_RemoveSmbGlobalMappingRequest_To_internal_RemoveSmbGlobalMappingRequest(versionedRequest, request); err != nil { + return nil, err + } + + response, err := s.apiGroupServer.RemoveSmbGlobalMapping(context, request, version) + if err != nil { + return nil, err + } + + versionedResponse := &v1alpha1.RemoveSmbGlobalMappingResponse{} + if err := Convert_internal_RemoveSmbGlobalMappingResponse_To_v1alpha1_RemoveSmbGlobalMappingResponse(response, versionedResponse); err != nil { + return nil, err + } + + return versionedResponse, err +} diff --git a/internal/server/smb/server.go b/internal/server/smb/server.go new file mode 100644 index 00000000..4e123c90 --- /dev/null +++ b/internal/server/smb/server.go @@ -0,0 +1,54 @@ +package smb + +import ( + "context" + "fmt" + + "github.com/kubernetes-csi/csi-proxy/client/apiversion" + "github.com/kubernetes-csi/csi-proxy/internal/server/smb/internal" +) + +type Server struct { + hostAPI API +} + +type API interface { + NewSmbGlobalMapping(remotePath, username, password string) error + RemoveSmbGlobalMapping(remotePath string) error +} + +func NewServer(hostAPI API) (*Server, error) { + return &Server{ + hostAPI: hostAPI, + }, nil +} + +func (s *Server) NewSmbGlobalMapping(context context.Context, request *internal.NewSmbGlobalMappingRequest, version apiversion.Version) (*internal.NewSmbGlobalMappingResponse, error) { + response := &internal.NewSmbGlobalMappingResponse{} + remotePath := request.RemotePath + + if remotePath == "" { + return response, fmt.Errorf("remote path is empty") + } + + err := s.hostAPI.NewSmbGlobalMapping(remotePath, request.Username, request.Password) + if err != nil { + return response, err + } + return response, nil +} + +func (s *Server) RemoveSmbGlobalMapping(context context.Context, request *internal.RemoveSmbGlobalMappingRequest, version apiversion.Version) (*internal.RemoveSmbGlobalMappingResponse, error) { + response := &internal.RemoveSmbGlobalMappingResponse{} + remotePath := request.RemotePath + + if remotePath == "" { + return response, fmt.Errorf("remote path is empty") + } + + err := s.hostAPI.RemoveSmbGlobalMapping(remotePath) + if err != nil { + return response, err + } + return response, nil +} diff --git a/internal/server/smb/server_test.go b/internal/server/smb/server_test.go new file mode 100644 index 00000000..b69689ff --- /dev/null +++ b/internal/server/smb/server_test.go @@ -0,0 +1,63 @@ +package smb + +import ( + "context" + "testing" + + "github.com/kubernetes-csi/csi-proxy/client/apiversion" + "github.com/kubernetes-csi/csi-proxy/internal/server/smb/internal" +) + +type fakeSmbAPI struct{} + +func (fakeSmbAPI) NewSmbGlobalMapping(remotePath, username, password string) error { + return nil +} + +func (fakeSmbAPI) RemoveSmbGlobalMapping(remotePath string) error { + return nil +} + +func TestNewSmbGlobalMapping(t *testing.T) { + v1alpha1, err := apiversion.NewVersion("v1alpha1") + testCases := []struct { + remote string + username string + password string + version apiversion.Version + expectError bool + }{ + { + remote: "", + username: "", + password: "", + version: v1alpha1, + expectError: true, + }, + { + remote: "\\test\\path", + username: "", + password: "", + version: v1alpha1, + expectError: false, + }, + } + srv, err := NewServer(&fakeSmbAPI{}) + if err != nil { + t.Fatalf("Smb Server could not be initialized for testing: %v", err) + } + for _, tc := range testCases { + req := &internal.NewSmbGlobalMappingRequest{ + RemotePath: tc.remote, + Username: tc.username, + Password: tc.password, + } + response, _ := srv.NewSmbGlobalMapping(context.TODO(), req, tc.version) + if tc.expectError && response.Error == "" { + t.Errorf("Expected error but NewSmbGlobalMapping returned a nil error") + } + if !tc.expectError && response.Error != "" { + t.Errorf("Expected no errors but NewSmbGlobalMapping returned error: %s", response.Error) + } + } +}