mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 10:33:35 +00:00
cleanup: move pkg/ to internal/
The internal/ directory in Go has a special meaning, and indicates that those packages are not meant for external consumption. Ceph-CSI does provide public APIs for other projects to consume. There is no plan to keep the API of the internally used packages stable. Closes: #903 Signed-off-by: Niels de Vos <ndevos@redhat.com>
This commit is contained in:
committed by
mergify[bot]
parent
d0abc3f5e6
commit
32839948ef
83
internal/csi-common/controllerserver-default.go
Normal file
83
internal/csi-common/controllerserver-default.go
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package csicommon
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/ceph/ceph-csi/internal/util"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// DefaultControllerServer points to default driver
|
||||
type DefaultControllerServer struct {
|
||||
Driver *CSIDriver
|
||||
}
|
||||
|
||||
// ControllerPublishVolume publish volume on node
|
||||
func (cs *DefaultControllerServer) ControllerPublishVolume(ctx context.Context, req *csi.ControllerPublishVolumeRequest) (*csi.ControllerPublishVolumeResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
}
|
||||
|
||||
// ControllerUnpublishVolume unpublish on node
|
||||
func (cs *DefaultControllerServer) ControllerUnpublishVolume(ctx context.Context, req *csi.ControllerUnpublishVolumeRequest) (*csi.ControllerUnpublishVolumeResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
}
|
||||
|
||||
// ControllerExpandVolume expand volume
|
||||
func (cs *DefaultControllerServer) ControllerExpandVolume(ctx context.Context, req *csi.ControllerExpandVolumeRequest) (*csi.ControllerExpandVolumeResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
}
|
||||
|
||||
// ListVolumes lists volumes
|
||||
func (cs *DefaultControllerServer) ListVolumes(ctx context.Context, req *csi.ListVolumesRequest) (*csi.ListVolumesResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
}
|
||||
|
||||
// GetCapacity get volume capacity
|
||||
func (cs *DefaultControllerServer) GetCapacity(ctx context.Context, req *csi.GetCapacityRequest) (*csi.GetCapacityResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
}
|
||||
|
||||
// ControllerGetCapabilities implements the default GRPC callout.
|
||||
// Default supports all capabilities
|
||||
func (cs *DefaultControllerServer) ControllerGetCapabilities(ctx context.Context, req *csi.ControllerGetCapabilitiesRequest) (*csi.ControllerGetCapabilitiesResponse, error) {
|
||||
klog.V(5).Infof(util.Log(ctx, "Using default ControllerGetCapabilities"))
|
||||
|
||||
return &csi.ControllerGetCapabilitiesResponse{
|
||||
Capabilities: cs.Driver.cap,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateSnapshot creates snapshot
|
||||
func (cs *DefaultControllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateSnapshotRequest) (*csi.CreateSnapshotResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
}
|
||||
|
||||
// DeleteSnapshot deletes snapshot
|
||||
func (cs *DefaultControllerServer) DeleteSnapshot(ctx context.Context, req *csi.DeleteSnapshotRequest) (*csi.DeleteSnapshotResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
}
|
||||
|
||||
// ListSnapshots lists snapshots
|
||||
func (cs *DefaultControllerServer) ListSnapshots(ctx context.Context, req *csi.ListSnapshotsRequest) (*csi.ListSnapshotsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
}
|
109
internal/csi-common/driver.go
Normal file
109
internal/csi-common/driver.go
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package csicommon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// CSIDriver stores driver information
|
||||
type CSIDriver struct {
|
||||
name string
|
||||
nodeID string
|
||||
version string
|
||||
// topology constraints that this nodeserver will advertise
|
||||
topology map[string]string
|
||||
cap []*csi.ControllerServiceCapability
|
||||
vc []*csi.VolumeCapability_AccessMode
|
||||
}
|
||||
|
||||
// NewCSIDriver Creates a NewCSIDriver object. Assumes vendor
|
||||
// version is equal to driver version & does not support optional
|
||||
// driver plugin info manifest field. Refer to CSI spec for more details.
|
||||
func NewCSIDriver(name, v, nodeID string) *CSIDriver {
|
||||
if name == "" {
|
||||
klog.Errorf("Driver name missing")
|
||||
return nil
|
||||
}
|
||||
|
||||
if nodeID == "" {
|
||||
klog.Errorf("NodeID missing")
|
||||
return nil
|
||||
}
|
||||
// TODO version format and validation
|
||||
if v == "" {
|
||||
klog.Errorf("Version argument missing")
|
||||
return nil
|
||||
}
|
||||
|
||||
driver := CSIDriver{
|
||||
name: name,
|
||||
version: v,
|
||||
nodeID: nodeID,
|
||||
}
|
||||
|
||||
return &driver
|
||||
}
|
||||
|
||||
// ValidateControllerServiceRequest validates the controller
|
||||
// plugin capabilities
|
||||
func (d *CSIDriver) ValidateControllerServiceRequest(c csi.ControllerServiceCapability_RPC_Type) error {
|
||||
if c == csi.ControllerServiceCapability_RPC_UNKNOWN {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, cap := range d.cap {
|
||||
if c == cap.GetRpc().GetType() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return status.Error(codes.InvalidArgument, fmt.Sprintf("%s", c)) //nolint
|
||||
}
|
||||
|
||||
// AddControllerServiceCapabilities stores the controller capabilities
|
||||
// in driver object
|
||||
func (d *CSIDriver) AddControllerServiceCapabilities(cl []csi.ControllerServiceCapability_RPC_Type) {
|
||||
var csc []*csi.ControllerServiceCapability
|
||||
|
||||
for _, c := range cl {
|
||||
klog.V(1).Infof("Enabling controller service capability: %v", c.String())
|
||||
csc = append(csc, NewControllerServiceCapability(c))
|
||||
}
|
||||
|
||||
d.cap = csc
|
||||
}
|
||||
|
||||
// AddVolumeCapabilityAccessModes stores volume access modes
|
||||
func (d *CSIDriver) AddVolumeCapabilityAccessModes(vc []csi.VolumeCapability_AccessMode_Mode) []*csi.VolumeCapability_AccessMode {
|
||||
var vca []*csi.VolumeCapability_AccessMode
|
||||
for _, c := range vc {
|
||||
klog.V(1).Infof("Enabling volume access mode: %v", c.String())
|
||||
vca = append(vca, NewVolumeCapabilityAccessMode(c))
|
||||
}
|
||||
d.vc = vca
|
||||
return vca
|
||||
}
|
||||
|
||||
// GetVolumeCapabilityAccessModes returns access modes
|
||||
func (d *CSIDriver) GetVolumeCapabilityAccessModes() []*csi.VolumeCapability_AccessMode {
|
||||
return d.vc
|
||||
}
|
72
internal/csi-common/identityserver-default.go
Normal file
72
internal/csi-common/identityserver-default.go
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package csicommon
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/ceph/ceph-csi/internal/util"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// DefaultIdentityServer stores driver object
|
||||
type DefaultIdentityServer struct {
|
||||
Driver *CSIDriver
|
||||
}
|
||||
|
||||
// GetPluginInfo returns plugin information
|
||||
func (ids *DefaultIdentityServer) GetPluginInfo(ctx context.Context, req *csi.GetPluginInfoRequest) (*csi.GetPluginInfoResponse, error) {
|
||||
klog.V(5).Infof(util.Log(ctx, "Using default GetPluginInfo"))
|
||||
|
||||
if ids.Driver.name == "" {
|
||||
return nil, status.Error(codes.Unavailable, "Driver name not configured")
|
||||
}
|
||||
|
||||
if ids.Driver.version == "" {
|
||||
return nil, status.Error(codes.Unavailable, "Driver is missing version")
|
||||
}
|
||||
|
||||
return &csi.GetPluginInfoResponse{
|
||||
Name: ids.Driver.name,
|
||||
VendorVersion: ids.Driver.version,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Probe returns empty response
|
||||
func (ids *DefaultIdentityServer) Probe(ctx context.Context, req *csi.ProbeRequest) (*csi.ProbeResponse, error) {
|
||||
return &csi.ProbeResponse{}, nil
|
||||
}
|
||||
|
||||
// GetPluginCapabilities returns plugin capabilities
|
||||
func (ids *DefaultIdentityServer) GetPluginCapabilities(ctx context.Context, req *csi.GetPluginCapabilitiesRequest) (*csi.GetPluginCapabilitiesResponse, error) {
|
||||
klog.V(5).Infof(util.Log(ctx, "Using default capabilities"))
|
||||
return &csi.GetPluginCapabilitiesResponse{
|
||||
Capabilities: []*csi.PluginCapability{
|
||||
{
|
||||
Type: &csi.PluginCapability_Service_{
|
||||
Service: &csi.PluginCapability_Service{
|
||||
Type: csi.PluginCapability_Service_CONTROLLER_SERVICE,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
197
internal/csi-common/nodeserver-default.go
Normal file
197
internal/csi-common/nodeserver-default.go
Normal file
@ -0,0 +1,197 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package csicommon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/ceph/ceph-csi/internal/util"
|
||||
|
||||
"context"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
csipbv1 "github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"k8s.io/klog"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
)
|
||||
|
||||
// DefaultNodeServer stores driver object
|
||||
type DefaultNodeServer struct {
|
||||
Driver *CSIDriver
|
||||
Type string
|
||||
}
|
||||
|
||||
// NodeStageVolume returns unimplemented response
|
||||
func (ns *DefaultNodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
}
|
||||
|
||||
// NodeUnstageVolume returns unimplemented response
|
||||
func (ns *DefaultNodeServer) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstageVolumeRequest) (*csi.NodeUnstageVolumeResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
}
|
||||
|
||||
// NodeExpandVolume returns unimplemented response
|
||||
func (ns *DefaultNodeServer) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandVolumeRequest) (*csi.NodeExpandVolumeResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
}
|
||||
|
||||
// NodeGetInfo returns node ID
|
||||
func (ns *DefaultNodeServer) NodeGetInfo(ctx context.Context, req *csi.NodeGetInfoRequest) (*csi.NodeGetInfoResponse, error) {
|
||||
klog.V(5).Infof(util.Log(ctx, "Using default NodeGetInfo"))
|
||||
|
||||
csiTopology := &csi.Topology{
|
||||
Segments: ns.Driver.topology,
|
||||
}
|
||||
|
||||
return &csi.NodeGetInfoResponse{
|
||||
NodeId: ns.Driver.nodeID,
|
||||
AccessibleTopology: csiTopology,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NodeGetCapabilities returns RPC unknow capability
|
||||
func (ns *DefaultNodeServer) NodeGetCapabilities(ctx context.Context, req *csi.NodeGetCapabilitiesRequest) (*csi.NodeGetCapabilitiesResponse, error) {
|
||||
klog.V(5).Infof(util.Log(ctx, "Using default NodeGetCapabilities"))
|
||||
|
||||
return &csi.NodeGetCapabilitiesResponse{
|
||||
Capabilities: []*csi.NodeServiceCapability{
|
||||
{
|
||||
Type: &csi.NodeServiceCapability_Rpc{
|
||||
Rpc: &csi.NodeServiceCapability_RPC{
|
||||
Type: csi.NodeServiceCapability_RPC_UNKNOWN,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NodeGetVolumeStats returns volume stats
|
||||
func (ns *DefaultNodeServer) NodeGetVolumeStats(ctx context.Context, req *csi.NodeGetVolumeStatsRequest) (*csi.NodeGetVolumeStatsResponse, error) {
|
||||
var err error
|
||||
targetPath := req.GetVolumePath()
|
||||
if targetPath == "" {
|
||||
err = fmt.Errorf("targetpath %v is empty", targetPath)
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
/*
|
||||
volID := req.GetVolumeId()
|
||||
|
||||
TODO: Map the volumeID to the targetpath.
|
||||
|
||||
CephFS:
|
||||
we need secret to connect to the ceph cluster to get the volumeID from volume
|
||||
Name, however `secret` field/option is not available in NodeGetVolumeStats spec,
|
||||
Below issue covers this request and once its available, we can do the validation
|
||||
as per the spec.
|
||||
|
||||
https://github.com/container-storage-interface/spec/issues/371
|
||||
|
||||
RBD:
|
||||
Below issue covers this request for RBD and once its available, we can do the validation
|
||||
as per the spec.
|
||||
|
||||
https://github.com/ceph/ceph-csi/issues/511
|
||||
|
||||
*/
|
||||
|
||||
isMnt, err := util.IsMountPoint(targetPath)
|
||||
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "targetpath %s doesnot exist", targetPath)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if !isMnt {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "targetpath %s is not mounted", targetPath)
|
||||
}
|
||||
|
||||
cephMetricsProvider := volume.NewMetricsStatFS(targetPath)
|
||||
volMetrics, volMetErr := cephMetricsProvider.GetMetrics()
|
||||
if volMetErr != nil {
|
||||
return nil, status.Error(codes.Internal, volMetErr.Error())
|
||||
}
|
||||
|
||||
available, ok := (*(volMetrics.Available)).AsInt64()
|
||||
if !ok {
|
||||
klog.Errorf(util.Log(ctx, "failed to fetch available bytes"))
|
||||
}
|
||||
capacity, ok := (*(volMetrics.Capacity)).AsInt64()
|
||||
if !ok {
|
||||
klog.Errorf(util.Log(ctx, "failed to fetch capacity bytes"))
|
||||
return nil, status.Error(codes.Unknown, "failed to fetch capacity bytes")
|
||||
}
|
||||
used, ok := (*(volMetrics.Used)).AsInt64()
|
||||
if !ok {
|
||||
klog.Errorf(util.Log(ctx, "failed to fetch used bytes"))
|
||||
}
|
||||
inodes, ok := (*(volMetrics.Inodes)).AsInt64()
|
||||
if !ok {
|
||||
klog.Errorf(util.Log(ctx, "failed to fetch available inodes"))
|
||||
return nil, status.Error(codes.Unknown, "failed to fetch available inodes")
|
||||
}
|
||||
inodesFree, ok := (*(volMetrics.InodesFree)).AsInt64()
|
||||
if !ok {
|
||||
klog.Errorf(util.Log(ctx, "failed to fetch free inodes"))
|
||||
}
|
||||
|
||||
inodesUsed, ok := (*(volMetrics.InodesUsed)).AsInt64()
|
||||
if !ok {
|
||||
klog.Errorf(util.Log(ctx, "failed to fetch used inodes"))
|
||||
}
|
||||
return &csi.NodeGetVolumeStatsResponse{
|
||||
Usage: []*csi.VolumeUsage{
|
||||
{
|
||||
Available: available,
|
||||
Total: capacity,
|
||||
Used: used,
|
||||
Unit: csipbv1.VolumeUsage_BYTES,
|
||||
},
|
||||
{
|
||||
Available: inodesFree,
|
||||
Total: inodes,
|
||||
Used: inodesUsed,
|
||||
Unit: csipbv1.VolumeUsage_INODES,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ConstructMountOptions returns only unique mount options in slice
|
||||
func ConstructMountOptions(mountOptions []string, volCap *csi.VolumeCapability) []string {
|
||||
if m := volCap.GetMount(); m != nil {
|
||||
hasOption := func(options []string, opt string) bool {
|
||||
for _, o := range options {
|
||||
if o == opt {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
for _, f := range m.MountFlags {
|
||||
if !hasOption(mountOptions, f) {
|
||||
mountOptions = append(mountOptions, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
return mountOptions
|
||||
}
|
143
internal/csi-common/server.go
Normal file
143
internal/csi-common/server.go
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package csicommon
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"google.golang.org/grpc"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// NonBlockingGRPCServer defines Non blocking GRPC server interfaces
|
||||
type NonBlockingGRPCServer interface {
|
||||
// Start services at the endpoint
|
||||
Start(endpoint, hstOptions string, ids csi.IdentityServer, cs csi.ControllerServer, ns csi.NodeServer, metrics bool)
|
||||
// Waits for the service to stop
|
||||
Wait()
|
||||
// Stops the service gracefully
|
||||
Stop()
|
||||
// Stops the service forcefully
|
||||
ForceStop()
|
||||
}
|
||||
|
||||
// NewNonBlockingGRPCServer return non-blocking GRPC
|
||||
func NewNonBlockingGRPCServer() NonBlockingGRPCServer {
|
||||
return &nonBlockingGRPCServer{}
|
||||
}
|
||||
|
||||
// NonBlocking server
|
||||
type nonBlockingGRPCServer struct {
|
||||
wg sync.WaitGroup
|
||||
server *grpc.Server
|
||||
}
|
||||
|
||||
// Start start service on endpoint
|
||||
func (s *nonBlockingGRPCServer) Start(endpoint, hstOptions string, ids csi.IdentityServer, cs csi.ControllerServer, ns csi.NodeServer, metrics bool) {
|
||||
s.wg.Add(1)
|
||||
go s.serve(endpoint, hstOptions, ids, cs, ns, metrics)
|
||||
}
|
||||
|
||||
// Wait blocks until the WaitGroup counter
|
||||
func (s *nonBlockingGRPCServer) Wait() {
|
||||
s.wg.Wait()
|
||||
}
|
||||
|
||||
// GracefulStop stops the gRPC server gracefully.
|
||||
func (s *nonBlockingGRPCServer) Stop() {
|
||||
s.server.GracefulStop()
|
||||
}
|
||||
|
||||
// Stop stops the gRPC server.
|
||||
func (s *nonBlockingGRPCServer) ForceStop() {
|
||||
s.server.Stop()
|
||||
}
|
||||
|
||||
func (s *nonBlockingGRPCServer) serve(endpoint, hstOptions string, ids csi.IdentityServer, cs csi.ControllerServer, ns csi.NodeServer, metrics bool) {
|
||||
proto, addr, err := parseEndpoint(endpoint)
|
||||
if err != nil {
|
||||
klog.Fatal(err.Error())
|
||||
}
|
||||
|
||||
if proto == "unix" {
|
||||
addr = "/" + addr
|
||||
if e := os.Remove(addr); e != nil && !os.IsNotExist(e) {
|
||||
klog.Fatalf("Failed to remove %s, error: %s", addr, e.Error())
|
||||
}
|
||||
}
|
||||
|
||||
listener, err := net.Listen(proto, addr)
|
||||
if err != nil {
|
||||
klog.Fatalf("Failed to listen: %v", err)
|
||||
}
|
||||
|
||||
middleWare := []grpc.UnaryServerInterceptor{contextIDInjector, logGRPC, panicHandler}
|
||||
if metrics {
|
||||
middleWare = append(middleWare, grpc_prometheus.UnaryServerInterceptor)
|
||||
}
|
||||
opts := []grpc.ServerOption{
|
||||
grpc_middleware.WithUnaryServerChain(middleWare...),
|
||||
}
|
||||
|
||||
server := grpc.NewServer(opts...)
|
||||
s.server = server
|
||||
|
||||
if ids != nil {
|
||||
csi.RegisterIdentityServer(server, ids)
|
||||
}
|
||||
if cs != nil {
|
||||
csi.RegisterControllerServer(server, cs)
|
||||
}
|
||||
if ns != nil {
|
||||
csi.RegisterNodeServer(server, ns)
|
||||
}
|
||||
klog.V(1).Infof("Listening for connections on address: %#v", listener.Addr())
|
||||
if metrics {
|
||||
ho := strings.Split(hstOptions, ",")
|
||||
if len(ho) != 3 {
|
||||
klog.Fatalf("invalid histogram options provided: %v", hstOptions)
|
||||
}
|
||||
start, e := strconv.ParseFloat(ho[0], 32)
|
||||
if e != nil {
|
||||
klog.Fatalf("failed to parse histogram start value: %v", e)
|
||||
}
|
||||
factor, e := strconv.ParseFloat(ho[1], 32)
|
||||
if err != nil {
|
||||
klog.Fatalf("failed to parse histogram factor value: %v", e)
|
||||
}
|
||||
count, e := strconv.Atoi(ho[2])
|
||||
if err != nil {
|
||||
klog.Fatalf("failed to parse histogram count value: %v", e)
|
||||
}
|
||||
buckets := prometheus.ExponentialBuckets(start, factor, count)
|
||||
bktOptios := grpc_prometheus.WithHistogramBuckets(buckets)
|
||||
grpc_prometheus.EnableHandlingTimeHistogram(bktOptios)
|
||||
grpc_prometheus.Register(server)
|
||||
}
|
||||
err = server.Serve(listener)
|
||||
if err != nil {
|
||||
klog.Fatalf("Failed to server: %v", err)
|
||||
}
|
||||
}
|
179
internal/csi-common/utils.go
Normal file
179
internal/csi-common/utils.go
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package csicommon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/ceph/ceph-csi/internal/util"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/kubernetes-csi/csi-lib-utils/protosanitizer"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
func parseEndpoint(ep string) (string, string, error) {
|
||||
if strings.HasPrefix(strings.ToLower(ep), "unix://") || strings.HasPrefix(strings.ToLower(ep), "tcp://") {
|
||||
s := strings.SplitN(ep, "://", 2)
|
||||
if s[1] != "" {
|
||||
return s[0], s[1], nil
|
||||
}
|
||||
}
|
||||
return "", "", fmt.Errorf("invalid endpoint: %v", ep)
|
||||
}
|
||||
|
||||
// NewVolumeCapabilityAccessMode returns volume access mode
|
||||
func NewVolumeCapabilityAccessMode(mode csi.VolumeCapability_AccessMode_Mode) *csi.VolumeCapability_AccessMode {
|
||||
return &csi.VolumeCapability_AccessMode{Mode: mode}
|
||||
}
|
||||
|
||||
// NewDefaultNodeServer initializes default node server
|
||||
func NewDefaultNodeServer(d *CSIDriver, t string, topology map[string]string) *DefaultNodeServer {
|
||||
d.topology = topology
|
||||
return &DefaultNodeServer{
|
||||
Driver: d,
|
||||
Type: t,
|
||||
}
|
||||
}
|
||||
|
||||
// NewDefaultIdentityServer initializes default identity servier
|
||||
func NewDefaultIdentityServer(d *CSIDriver) *DefaultIdentityServer {
|
||||
return &DefaultIdentityServer{
|
||||
Driver: d,
|
||||
}
|
||||
}
|
||||
|
||||
// NewDefaultControllerServer initializes default controller server
|
||||
func NewDefaultControllerServer(d *CSIDriver) *DefaultControllerServer {
|
||||
return &DefaultControllerServer{
|
||||
Driver: d,
|
||||
}
|
||||
}
|
||||
|
||||
// NewControllerServiceCapability returns controller capabilities
|
||||
func NewControllerServiceCapability(ctrlCap csi.ControllerServiceCapability_RPC_Type) *csi.ControllerServiceCapability {
|
||||
return &csi.ControllerServiceCapability{
|
||||
Type: &csi.ControllerServiceCapability_Rpc{
|
||||
Rpc: &csi.ControllerServiceCapability_RPC{
|
||||
Type: ctrlCap,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// RunNodePublishServer starts node server
|
||||
func RunNodePublishServer(endpoint, hstOption string, d *CSIDriver, ns csi.NodeServer, m bool) {
|
||||
ids := NewDefaultIdentityServer(d)
|
||||
|
||||
s := NewNonBlockingGRPCServer()
|
||||
s.Start(endpoint, hstOption, ids, nil, ns, m)
|
||||
s.Wait()
|
||||
}
|
||||
|
||||
// RunControllerPublishServer starts controller server
|
||||
func RunControllerPublishServer(endpoint, hstOption string, d *CSIDriver, cs csi.ControllerServer, m bool) {
|
||||
ids := NewDefaultIdentityServer(d)
|
||||
|
||||
s := NewNonBlockingGRPCServer()
|
||||
s.Start(endpoint, hstOption, ids, cs, nil, m)
|
||||
s.Wait()
|
||||
}
|
||||
|
||||
// RunControllerandNodePublishServer starts both controller and node server
|
||||
func RunControllerandNodePublishServer(endpoint, hstOption string, d *CSIDriver, cs csi.ControllerServer, ns csi.NodeServer, m bool) {
|
||||
ids := NewDefaultIdentityServer(d)
|
||||
|
||||
s := NewNonBlockingGRPCServer()
|
||||
s.Start(endpoint, hstOption, ids, cs, ns, m)
|
||||
s.Wait()
|
||||
}
|
||||
|
||||
func getReqID(req interface{}) string {
|
||||
// if req is nil empty string will be returned
|
||||
reqID := ""
|
||||
switch r := req.(type) {
|
||||
case *csi.CreateVolumeRequest:
|
||||
reqID = r.Name
|
||||
|
||||
case *csi.DeleteVolumeRequest:
|
||||
reqID = r.VolumeId
|
||||
|
||||
case *csi.CreateSnapshotRequest:
|
||||
reqID = r.Name
|
||||
case *csi.DeleteSnapshotRequest:
|
||||
reqID = r.SnapshotId
|
||||
|
||||
case *csi.ControllerExpandVolumeRequest:
|
||||
reqID = r.VolumeId
|
||||
|
||||
case *csi.NodeStageVolumeRequest:
|
||||
reqID = r.VolumeId
|
||||
case *csi.NodeUnstageVolumeRequest:
|
||||
reqID = r.VolumeId
|
||||
|
||||
case *csi.NodePublishVolumeRequest:
|
||||
reqID = r.VolumeId
|
||||
case *csi.NodeUnpublishVolumeRequest:
|
||||
reqID = r.VolumeId
|
||||
|
||||
case *csi.NodeExpandVolumeRequest:
|
||||
reqID = r.VolumeId
|
||||
}
|
||||
return reqID
|
||||
}
|
||||
|
||||
var id uint64
|
||||
|
||||
func contextIDInjector(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
|
||||
atomic.AddUint64(&id, 1)
|
||||
ctx = context.WithValue(ctx, util.CtxKey, id)
|
||||
reqID := getReqID(req)
|
||||
if reqID != "" {
|
||||
ctx = context.WithValue(ctx, util.ReqID, reqID)
|
||||
}
|
||||
return handler(ctx, req)
|
||||
}
|
||||
|
||||
func logGRPC(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
klog.V(3).Infof(util.Log(ctx, "GRPC call: %s"), info.FullMethod)
|
||||
klog.V(5).Infof(util.Log(ctx, "GRPC request: %s"), protosanitizer.StripSecrets(req))
|
||||
resp, err := handler(ctx, req)
|
||||
if err != nil {
|
||||
klog.Errorf(util.Log(ctx, "GRPC error: %v"), err)
|
||||
} else {
|
||||
klog.V(5).Infof(util.Log(ctx, "GRPC response: %s"), protosanitizer.StripSecrets(resp))
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func panicHandler(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
klog.Errorf("panic occurred: %v", r)
|
||||
debug.PrintStack()
|
||||
err = status.Errorf(codes.Internal, "panic %v", r)
|
||||
}
|
||||
}()
|
||||
return handler(ctx, req)
|
||||
}
|
71
internal/csi-common/utils_test.go
Normal file
71
internal/csi-common/utils_test.go
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
Copyright 2019 ceph-csi authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package csicommon
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
)
|
||||
|
||||
var fakeID = "fake-id"
|
||||
|
||||
func TestGetReqID(t *testing.T) {
|
||||
req := []interface{}{
|
||||
&csi.CreateVolumeRequest{
|
||||
Name: fakeID,
|
||||
},
|
||||
&csi.DeleteVolumeRequest{
|
||||
VolumeId: fakeID,
|
||||
},
|
||||
&csi.CreateSnapshotRequest{
|
||||
Name: fakeID,
|
||||
},
|
||||
&csi.DeleteSnapshotRequest{
|
||||
SnapshotId: fakeID,
|
||||
},
|
||||
|
||||
&csi.ControllerExpandVolumeRequest{
|
||||
VolumeId: fakeID,
|
||||
},
|
||||
&csi.NodeStageVolumeRequest{
|
||||
VolumeId: fakeID,
|
||||
},
|
||||
&csi.NodeUnstageVolumeRequest{
|
||||
VolumeId: fakeID,
|
||||
},
|
||||
&csi.NodePublishVolumeRequest{
|
||||
VolumeId: fakeID,
|
||||
},
|
||||
&csi.NodeUnpublishVolumeRequest{
|
||||
VolumeId: fakeID,
|
||||
},
|
||||
&csi.NodeExpandVolumeRequest{
|
||||
VolumeId: fakeID,
|
||||
},
|
||||
}
|
||||
for _, r := range req {
|
||||
if got := getReqID(r); got != fakeID {
|
||||
t.Errorf("getReqID() = %v, want %v", got, fakeID)
|
||||
}
|
||||
}
|
||||
|
||||
// test for nil request
|
||||
if got := getReqID(nil); got != "" {
|
||||
t.Errorf("getReqID() = %v, want empty string", got)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user