add prune and remove unused packages

This commit is contained in:
Michelle Au
2019-03-08 14:54:43 -08:00
parent f59b58d164
commit 8c0accad66
17240 changed files with 27 additions and 4750030 deletions

View File

@@ -1,22 +0,0 @@
# Mock CSI Driver
Extremely simple mock driver used to test `csi-sanity` based on `rexray/gocsi/mock`.
It can be used for testing of Container Orchestrators that implement client side
of CSI interface.
```
Usage of mock:
-disable-attach
Disables RPC_PUBLISH_UNPUBLISH_VOLUME capability.
-name string
CSI driver name. (default "io.kubernetes.storage.mock")
```
It prints all received CSI messages to stdout encoded as json, so a test can check that
CO sent the right CSI message.
Example of such output:
```
gRPCCall: {"Method":"/csi.v0.Controller/ControllerGetCapabilities","Request":{},"Response":{"capabilities":[{"Type":{"Rpc":{"type":1}}},{"Type":{"Rpc":{"type":3}}},{"Type":{"Rpc":{"type":4}}},{"Type":{"Rpc":{"type":6}}},{"Type":{"Rpc":{"type":5}}},{"Type":{"Rpc":{"type":2}}}]},"Error":""}
gRPCCall: {"Method":"/csi.v0.Controller/ControllerPublishVolume","Request":{"volume_id":"12","node_id":"some-fake-node-id","volume_capability":{"AccessType":{"Mount":{}},"access_mode":{"mode":1}}},"Response":null,"Error":"rpc error: code = NotFound desc = Not matching Node ID some-fake-node-id to Mock Node ID io.kubernetes.storage.mock"}
```

View File

@@ -1,89 +0,0 @@
package cache
import (
"strings"
"sync"
"github.com/container-storage-interface/spec/lib/go/csi"
)
type SnapshotCache interface {
Add(snapshot Snapshot)
Delete(i int)
List(ready bool) []csi.Snapshot
FindSnapshot(k, v string) (int, Snapshot)
}
type Snapshot struct {
Name string
Parameters map[string]string
SnapshotCSI csi.Snapshot
}
type snapshotCache struct {
snapshotsRWL sync.RWMutex
snapshots []Snapshot
}
func NewSnapshotCache() SnapshotCache {
return &snapshotCache{
snapshots: make([]Snapshot, 0),
}
}
func (snap *snapshotCache) Add(snapshot Snapshot) {
snap.snapshotsRWL.Lock()
defer snap.snapshotsRWL.Unlock()
snap.snapshots = append(snap.snapshots, snapshot)
}
func (snap *snapshotCache) Delete(i int) {
snap.snapshotsRWL.Lock()
defer snap.snapshotsRWL.Unlock()
copy(snap.snapshots[i:], snap.snapshots[i+1:])
snap.snapshots = snap.snapshots[:len(snap.snapshots)-1]
}
func (snap *snapshotCache) List(ready bool) []csi.Snapshot {
snap.snapshotsRWL.RLock()
defer snap.snapshotsRWL.RUnlock()
snapshots := make([]csi.Snapshot, 0)
for _, v := range snap.snapshots {
if v.SnapshotCSI.GetReadyToUse() {
snapshots = append(snapshots, v.SnapshotCSI)
}
}
return snapshots
}
func (snap *snapshotCache) FindSnapshot(k, v string) (int, Snapshot) {
snap.snapshotsRWL.RLock()
defer snap.snapshotsRWL.RUnlock()
snapshotIdx := -1
for i, vi := range snap.snapshots {
switch k {
case "id":
if strings.EqualFold(v, vi.SnapshotCSI.GetSnapshotId()) {
return i, vi
}
case "sourceVolumeId":
if strings.EqualFold(v, vi.SnapshotCSI.SourceVolumeId) {
return i, vi
}
case "name":
if vi.Name == v {
return i, vi
}
}
}
return snapshotIdx, Snapshot{}
}

View File

@@ -1,95 +0,0 @@
/*
Copyright 2018 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 main
import (
"flag"
"fmt"
"net"
"os"
"os/signal"
"strings"
"syscall"
"github.com/kubernetes-csi/csi-test/driver"
"github.com/kubernetes-csi/csi-test/mock/service"
)
func main() {
var config service.Config
flag.BoolVar(&config.DisableAttach, "disable-attach", false, "Disables RPC_PUBLISH_UNPUBLISH_VOLUME capability.")
flag.StringVar(&config.DriverName, "name", service.Name, "CSI driver name.")
flag.Int64Var(&config.AttachLimit, "attach-limit", 0, "number of attachable volumes on a node")
flag.Parse()
endpoint := os.Getenv("CSI_ENDPOINT")
if len(endpoint) == 0 {
fmt.Println("CSI_ENDPOINT must be defined and must be a path")
os.Exit(1)
}
if strings.Contains(endpoint, ":") {
fmt.Println("CSI_ENDPOINT must be a unix path")
os.Exit(1)
}
// Create mock driver
s := service.New(config)
servers := &driver.CSIDriverServers{
Controller: s,
Identity: s,
Node: s,
}
d := driver.NewCSIDriver(servers)
// If creds is enabled, set the default creds.
setCreds := os.Getenv("CSI_ENABLE_CREDS")
if len(setCreds) > 0 && setCreds == "true" {
d.SetDefaultCreds()
}
// Listen
os.Remove(endpoint)
l, err := net.Listen("unix", endpoint)
if err != nil {
fmt.Printf("Error: Unable to listen on %s socket: %v\n",
endpoint,
err)
os.Exit(1)
}
defer os.Remove(endpoint)
// Start server
if err := d.Start(l); err != nil {
fmt.Printf("Error: Unable to start mock CSI server: %v\n",
err)
os.Exit(1)
}
fmt.Println("mock driver started")
// Wait for signal
sigc := make(chan os.Signal, 1)
sigs := []os.Signal{
syscall.SIGTERM,
syscall.SIGHUP,
syscall.SIGINT,
syscall.SIGQUIT,
}
signal.Notify(sigc, sigs...)
<-sigc
d.Stop()
fmt.Println("mock driver stopped")
}

View File

@@ -1,16 +0,0 @@
CreateVolumeSecret:
secretKey: secretval1
DeleteVolumeSecret:
secretKey: secretval2
ControllerPublishVolumeSecret:
secretKey: secretval3
ControllerUnpublishVolumeSecret:
secretKey: secretval4
NodeStageVolumeSecret:
secretKey: secretval5
NodePublishVolumeSecret:
secretKey: secretval6
CreateSnapshotSecret:
secretKey: secretval7
DeleteSnapshotSecret:
secretKey: secretval8

View File

@@ -1,577 +0,0 @@
package service
import (
"fmt"
"math"
"path"
"reflect"
"strconv"
log "github.com/sirupsen/logrus"
"golang.org/x/net/context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/container-storage-interface/spec/lib/go/csi"
)
const (
MaxStorageCapacity = tib
ReadOnlyKey = "readonly"
)
func (s *service) CreateVolume(
ctx context.Context,
req *csi.CreateVolumeRequest) (
*csi.CreateVolumeResponse, error) {
if len(req.Name) == 0 {
return nil, status.Error(codes.InvalidArgument, "Volume Name cannot be empty")
}
if req.VolumeCapabilities == nil {
return nil, status.Error(codes.InvalidArgument, "Volume Capabilities cannot be empty")
}
// Check to see if the volume already exists.
if i, v := s.findVolByName(ctx, req.Name); i >= 0 {
// Requested volume name already exists, need to check if the existing volume's
// capacity is more or equal to new request's capacity.
if v.GetCapacityBytes() < req.GetCapacityRange().GetRequiredBytes() {
return nil, status.Error(codes.AlreadyExists,
fmt.Sprintf("Volume with name %s already exists", req.GetName()))
}
return &csi.CreateVolumeResponse{Volume: &v}, nil
}
// If no capacity is specified then use 100GiB
capacity := gib100
if cr := req.CapacityRange; cr != nil {
if rb := cr.RequiredBytes; rb > 0 {
capacity = rb
}
if lb := cr.LimitBytes; lb > 0 {
capacity = lb
}
}
// Check for maximum available capacity
if capacity >= MaxStorageCapacity {
return nil, status.Errorf(codes.OutOfRange, "Requested capacity %d exceeds maximum allowed %d", capacity, MaxStorageCapacity)
}
// Create the volume and add it to the service's in-mem volume slice.
v := s.newVolume(req.Name, capacity)
s.volsRWL.Lock()
defer s.volsRWL.Unlock()
s.vols = append(s.vols, v)
MockVolumes[v.GetVolumeId()] = Volume{
VolumeCSI: v,
NodeID: "",
ISStaged: false,
ISPublished: false,
StageTargetPath: "",
TargetPath: "",
}
return &csi.CreateVolumeResponse{Volume: &v}, nil
}
func (s *service) DeleteVolume(
ctx context.Context,
req *csi.DeleteVolumeRequest) (
*csi.DeleteVolumeResponse, error) {
s.volsRWL.Lock()
defer s.volsRWL.Unlock()
// If the volume is not specified, return error
if len(req.VolumeId) == 0 {
return nil, status.Error(codes.InvalidArgument, "Volume ID cannot be empty")
}
// If the volume does not exist then return an idempotent response.
i, _ := s.findVolNoLock("id", req.VolumeId)
if i < 0 {
return &csi.DeleteVolumeResponse{}, nil
}
// This delete logic preserves order and prevents potential memory
// leaks. The slice's elements may not be pointers, but the structs
// themselves have fields that are.
copy(s.vols[i:], s.vols[i+1:])
s.vols[len(s.vols)-1] = csi.Volume{}
s.vols = s.vols[:len(s.vols)-1]
log.WithField("volumeID", req.VolumeId).Debug("mock delete volume")
return &csi.DeleteVolumeResponse{}, nil
}
func (s *service) ControllerPublishVolume(
ctx context.Context,
req *csi.ControllerPublishVolumeRequest) (
*csi.ControllerPublishVolumeResponse, error) {
if s.config.DisableAttach {
return nil, status.Error(codes.Unimplemented, "ControllerPublish is not supported")
}
if len(req.VolumeId) == 0 {
return nil, status.Error(codes.InvalidArgument, "Volume ID cannot be empty")
}
if len(req.NodeId) == 0 {
return nil, status.Error(codes.InvalidArgument, "Node ID cannot be empty")
}
if req.VolumeCapability == nil {
return nil, status.Error(codes.InvalidArgument, "Volume Capabilities cannot be empty")
}
if req.NodeId != s.nodeID {
return nil, status.Errorf(codes.NotFound, "Not matching Node ID %s to Mock Node ID %s", req.NodeId, s.nodeID)
}
s.volsRWL.Lock()
defer s.volsRWL.Unlock()
i, v := s.findVolNoLock("id", req.VolumeId)
if i < 0 {
return nil, status.Error(codes.NotFound, req.VolumeId)
}
// devPathKey is the key in the volume's attributes that is set to a
// mock device path if the volume has been published by the controller
// to the specified node.
devPathKey := path.Join(req.NodeId, "dev")
// Check to see if the volume is already published.
if device := v.VolumeContext[devPathKey]; device != "" {
var volRo bool
var roVal string
if ro, ok := v.VolumeContext[ReadOnlyKey]; ok {
roVal = ro
}
if roVal == "true" {
volRo = true
} else {
volRo = false
}
// Check if readonly flag is compatible with the publish request.
if req.GetReadonly() != volRo {
return nil, status.Error(codes.AlreadyExists, "Volume published but has incompatible readonly flag")
}
return &csi.ControllerPublishVolumeResponse{
PublishContext: map[string]string{
"device": device,
"readonly": roVal,
},
}, nil
}
var roVal string
if req.GetReadonly() {
roVal = "true"
} else {
roVal = "false"
}
// Publish the volume.
device := "/dev/mock"
v.VolumeContext[devPathKey] = device
v.VolumeContext[ReadOnlyKey] = roVal
s.vols[i] = v
return &csi.ControllerPublishVolumeResponse{
PublishContext: map[string]string{
"device": device,
"readonly": roVal,
},
}, nil
}
func (s *service) ControllerUnpublishVolume(
ctx context.Context,
req *csi.ControllerUnpublishVolumeRequest) (
*csi.ControllerUnpublishVolumeResponse, error) {
if s.config.DisableAttach {
return nil, status.Error(codes.Unimplemented, "ControllerPublish is not supported")
}
if len(req.VolumeId) == 0 {
return nil, status.Error(codes.InvalidArgument, "Volume ID cannot be empty")
}
nodeID := req.NodeId
if len(nodeID) == 0 {
// If node id is empty, no failure as per Spec
nodeID = s.nodeID
}
if req.NodeId != s.nodeID {
return nil, status.Errorf(codes.NotFound, "Node ID %s does not match to expected Node ID %s", req.NodeId, s.nodeID)
}
s.volsRWL.Lock()
defer s.volsRWL.Unlock()
i, v := s.findVolNoLock("id", req.VolumeId)
if i < 0 {
return nil, status.Error(codes.NotFound, req.VolumeId)
}
// devPathKey is the key in the volume's attributes that is set to a
// mock device path if the volume has been published by the controller
// to the specified node.
devPathKey := path.Join(nodeID, "dev")
// Check to see if the volume is already unpublished.
if v.VolumeContext[devPathKey] == "" {
return &csi.ControllerUnpublishVolumeResponse{}, nil
}
// Unpublish the volume.
delete(v.VolumeContext, devPathKey)
delete(v.VolumeContext, ReadOnlyKey)
s.vols[i] = v
return &csi.ControllerUnpublishVolumeResponse{}, nil
}
func (s *service) ValidateVolumeCapabilities(
ctx context.Context,
req *csi.ValidateVolumeCapabilitiesRequest) (
*csi.ValidateVolumeCapabilitiesResponse, error) {
if len(req.GetVolumeId()) == 0 {
return nil, status.Error(codes.InvalidArgument, "Volume ID cannot be empty")
}
if len(req.VolumeCapabilities) == 0 {
return nil, status.Error(codes.InvalidArgument, req.VolumeId)
}
i, _ := s.findVolNoLock("id", req.VolumeId)
if i < 0 {
return nil, status.Error(codes.NotFound, req.VolumeId)
}
return &csi.ValidateVolumeCapabilitiesResponse{
Confirmed: &csi.ValidateVolumeCapabilitiesResponse_Confirmed{
VolumeContext: req.GetVolumeContext(),
VolumeCapabilities: req.GetVolumeCapabilities(),
Parameters: req.GetParameters(),
},
}, nil
}
func (s *service) ListVolumes(
ctx context.Context,
req *csi.ListVolumesRequest) (
*csi.ListVolumesResponse, error) {
// Copy the mock volumes into a new slice in order to avoid
// locking the service's volume slice for the duration of the
// ListVolumes RPC.
var vols []csi.Volume
func() {
s.volsRWL.RLock()
defer s.volsRWL.RUnlock()
vols = make([]csi.Volume, len(s.vols))
copy(vols, s.vols)
}()
var (
ulenVols = int32(len(vols))
maxEntries = req.MaxEntries
startingToken int32
)
if v := req.StartingToken; v != "" {
i, err := strconv.ParseUint(v, 10, 32)
if err != nil {
return nil, status.Errorf(
codes.InvalidArgument,
"startingToken=%d !< int32=%d",
startingToken, math.MaxUint32)
}
startingToken = int32(i)
}
if startingToken > ulenVols {
return nil, status.Errorf(
codes.InvalidArgument,
"startingToken=%d > len(vols)=%d",
startingToken, ulenVols)
}
// Discern the number of remaining entries.
rem := ulenVols - startingToken
// If maxEntries is 0 or greater than the number of remaining entries then
// set maxEntries to the number of remaining entries.
if maxEntries == 0 || maxEntries > rem {
maxEntries = rem
}
var (
i int
j = startingToken
entries = make(
[]*csi.ListVolumesResponse_Entry,
maxEntries)
)
for i = 0; i < len(entries); i++ {
entries[i] = &csi.ListVolumesResponse_Entry{
Volume: &vols[j],
}
j++
}
var nextToken string
if n := startingToken + int32(i); n < ulenVols {
nextToken = fmt.Sprintf("%d", n)
}
return &csi.ListVolumesResponse{
Entries: entries,
NextToken: nextToken,
}, nil
}
func (s *service) GetCapacity(
ctx context.Context,
req *csi.GetCapacityRequest) (
*csi.GetCapacityResponse, error) {
return &csi.GetCapacityResponse{
AvailableCapacity: MaxStorageCapacity,
}, nil
}
func (s *service) ControllerGetCapabilities(
ctx context.Context,
req *csi.ControllerGetCapabilitiesRequest) (
*csi.ControllerGetCapabilitiesResponse, error) {
caps := []*csi.ControllerServiceCapability{
{
Type: &csi.ControllerServiceCapability_Rpc{
Rpc: &csi.ControllerServiceCapability_RPC{
Type: csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME,
},
},
},
{
Type: &csi.ControllerServiceCapability_Rpc{
Rpc: &csi.ControllerServiceCapability_RPC{
Type: csi.ControllerServiceCapability_RPC_LIST_VOLUMES,
},
},
},
{
Type: &csi.ControllerServiceCapability_Rpc{
Rpc: &csi.ControllerServiceCapability_RPC{
Type: csi.ControllerServiceCapability_RPC_GET_CAPACITY,
},
},
},
{
Type: &csi.ControllerServiceCapability_Rpc{
Rpc: &csi.ControllerServiceCapability_RPC{
Type: csi.ControllerServiceCapability_RPC_LIST_SNAPSHOTS,
},
},
},
{
Type: &csi.ControllerServiceCapability_Rpc{
Rpc: &csi.ControllerServiceCapability_RPC{
Type: csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT,
},
},
},
}
if !s.config.DisableAttach {
caps = append(caps, &csi.ControllerServiceCapability{
Type: &csi.ControllerServiceCapability_Rpc{
Rpc: &csi.ControllerServiceCapability_RPC{
Type: csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME,
},
},
})
}
return &csi.ControllerGetCapabilitiesResponse{
Capabilities: caps,
}, nil
}
func (s *service) CreateSnapshot(ctx context.Context,
req *csi.CreateSnapshotRequest) (*csi.CreateSnapshotResponse, error) {
// Check arguments
if len(req.GetName()) == 0 {
return nil, status.Error(codes.InvalidArgument, "Snapshot Name cannot be empty")
}
if len(req.GetSourceVolumeId()) == 0 {
return nil, status.Error(codes.InvalidArgument, "Snapshot SourceVolumeId cannot be empty")
}
// Check to see if the snapshot already exists.
if i, v := s.snapshots.FindSnapshot("name", req.GetName()); i >= 0 {
// Requested snapshot name already exists
if v.SnapshotCSI.GetSourceVolumeId() != req.GetSourceVolumeId() || !reflect.DeepEqual(v.Parameters, req.GetParameters()) {
return nil, status.Error(codes.AlreadyExists,
fmt.Sprintf("Snapshot with name %s already exists", req.GetName()))
}
return &csi.CreateSnapshotResponse{Snapshot: &v.SnapshotCSI}, nil
}
// Create the snapshot and add it to the service's in-mem snapshot slice.
snapshot := s.newSnapshot(req.GetName(), req.GetSourceVolumeId(), req.GetParameters())
s.snapshots.Add(snapshot)
return &csi.CreateSnapshotResponse{Snapshot: &snapshot.SnapshotCSI}, nil
}
func (s *service) DeleteSnapshot(ctx context.Context,
req *csi.DeleteSnapshotRequest) (*csi.DeleteSnapshotResponse, error) {
// If the snapshot is not specified, return error
if len(req.SnapshotId) == 0 {
return nil, status.Error(codes.InvalidArgument, "Snapshot ID cannot be empty")
}
// If the snapshot does not exist then return an idempotent response.
i, _ := s.snapshots.FindSnapshot("id", req.SnapshotId)
if i < 0 {
return &csi.DeleteSnapshotResponse{}, nil
}
// This delete logic preserves order and prevents potential memory
// leaks. The slice's elements may not be pointers, but the structs
// themselves have fields that are.
s.snapshots.Delete(i)
log.WithField("SnapshotId", req.SnapshotId).Debug("mock delete snapshot")
return &csi.DeleteSnapshotResponse{}, nil
}
func (s *service) ListSnapshots(ctx context.Context,
req *csi.ListSnapshotsRequest) (*csi.ListSnapshotsResponse, error) {
// case 1: SnapshotId is not empty, return snapshots that match the snapshot id.
if len(req.GetSnapshotId()) != 0 {
return getSnapshotById(s, req)
}
// case 2: SourceVolumeId is not empty, return snapshots that match the source volume id.
if len(req.GetSourceVolumeId()) != 0 {
return getSnapshotByVolumeId(s, req)
}
// case 3: no parameter is set, so we return all the snapshots.
return getAllSnapshots(s, req)
}
func getSnapshotById(s *service, req *csi.ListSnapshotsRequest) (*csi.ListSnapshotsResponse, error) {
if len(req.GetSnapshotId()) != 0 {
i, snapshot := s.snapshots.FindSnapshot("id", req.GetSnapshotId())
if i < 0 {
return &csi.ListSnapshotsResponse{}, nil
}
if len(req.GetSourceVolumeId()) != 0 {
if snapshot.SnapshotCSI.GetSourceVolumeId() != req.GetSourceVolumeId() {
return &csi.ListSnapshotsResponse{}, nil
}
}
return &csi.ListSnapshotsResponse{
Entries: []*csi.ListSnapshotsResponse_Entry{
{
Snapshot: &snapshot.SnapshotCSI,
},
},
}, nil
}
return nil, nil
}
func getSnapshotByVolumeId(s *service, req *csi.ListSnapshotsRequest) (*csi.ListSnapshotsResponse, error) {
if len(req.GetSourceVolumeId()) != 0 {
i, snapshot := s.snapshots.FindSnapshot("sourceVolumeId", req.SourceVolumeId)
if i < 0 {
return &csi.ListSnapshotsResponse{}, nil
}
return &csi.ListSnapshotsResponse{
Entries: []*csi.ListSnapshotsResponse_Entry{
{
Snapshot: &snapshot.SnapshotCSI,
},
},
}, nil
}
return nil, nil
}
func getAllSnapshots(s *service, req *csi.ListSnapshotsRequest) (*csi.ListSnapshotsResponse, error) {
// Copy the mock snapshots into a new slice in order to avoid
// locking the service's snapshot slice for the duration of the
// ListSnapshots RPC.
readyToUse := true
snapshots := s.snapshots.List(readyToUse)
var (
ulenSnapshots = int32(len(snapshots))
maxEntries = req.MaxEntries
startingToken int32
)
if v := req.StartingToken; v != "" {
i, err := strconv.ParseUint(v, 10, 32)
if err != nil {
return nil, status.Errorf(
codes.Aborted,
"startingToken=%d !< int32=%d",
startingToken, math.MaxUint32)
}
startingToken = int32(i)
}
if startingToken > ulenSnapshots {
return nil, status.Errorf(
codes.Aborted,
"startingToken=%d > len(snapshots)=%d",
startingToken, ulenSnapshots)
}
// Discern the number of remaining entries.
rem := ulenSnapshots - startingToken
// If maxEntries is 0 or greater than the number of remaining entries then
// set maxEntries to the number of remaining entries.
if maxEntries == 0 || maxEntries > rem {
maxEntries = rem
}
var (
i int
j = startingToken
entries = make(
[]*csi.ListSnapshotsResponse_Entry,
maxEntries)
)
for i = 0; i < len(entries); i++ {
entries[i] = &csi.ListSnapshotsResponse_Entry{
Snapshot: &snapshots[j],
}
j++
}
var nextToken string
if n := startingToken + int32(i); n < ulenSnapshots {
nextToken = fmt.Sprintf("%d", n)
}
return &csi.ListSnapshotsResponse{
Entries: entries,
NextToken: nextToken,
}, nil
}

View File

@@ -1,48 +0,0 @@
package service
import (
"golang.org/x/net/context"
"github.com/container-storage-interface/spec/lib/go/csi"
"github.com/golang/protobuf/ptypes/wrappers"
)
func (s *service) GetPluginInfo(
ctx context.Context,
req *csi.GetPluginInfoRequest) (
*csi.GetPluginInfoResponse, error) {
return &csi.GetPluginInfoResponse{
Name: s.config.DriverName,
VendorVersion: VendorVersion,
Manifest: Manifest,
}, nil
}
func (s *service) Probe(
ctx context.Context,
req *csi.ProbeRequest) (
*csi.ProbeResponse, error) {
return &csi.ProbeResponse{
Ready: &wrappers.BoolValue{Value: true},
}, nil
}
func (s *service) GetPluginCapabilities(
ctx context.Context,
req *csi.GetPluginCapabilitiesRequest) (
*csi.GetPluginCapabilitiesResponse, error) {
return &csi.GetPluginCapabilitiesResponse{
Capabilities: []*csi.PluginCapability{
{
Type: &csi.PluginCapability_Service_{
Service: &csi.PluginCapability_Service{
Type: csi.PluginCapability_Service_CONTROLLER_SERVICE,
},
},
},
},
}, nil
}

View File

@@ -1,244 +0,0 @@
package service
import (
"path"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"golang.org/x/net/context"
"github.com/container-storage-interface/spec/lib/go/csi"
)
func (s *service) NodeStageVolume(
ctx context.Context,
req *csi.NodeStageVolumeRequest) (
*csi.NodeStageVolumeResponse, error) {
device, ok := req.PublishContext["device"]
if !ok {
if s.config.DisableAttach {
device = "mock device"
} else {
return nil, status.Error(
codes.InvalidArgument,
"stage volume info 'device' key required")
}
}
if len(req.GetVolumeId()) == 0 {
return nil, status.Error(codes.InvalidArgument, "Volume ID cannot be empty")
}
if len(req.GetStagingTargetPath()) == 0 {
return nil, status.Error(codes.InvalidArgument, "Staging Target Path cannot be empty")
}
if req.GetVolumeCapability() == nil {
return nil, status.Error(codes.InvalidArgument, "Volume Capability cannot be empty")
}
s.volsRWL.Lock()
defer s.volsRWL.Unlock()
i, v := s.findVolNoLock("id", req.VolumeId)
if i < 0 {
return nil, status.Error(codes.NotFound, req.VolumeId)
}
// nodeStgPathKey is the key in the volume's attributes that is set to a
// mock stage path if the volume has been published by the node
nodeStgPathKey := path.Join(s.nodeID, req.StagingTargetPath)
// Check to see if the volume has already been staged.
if v.VolumeContext[nodeStgPathKey] != "" {
// TODO: Check for the capabilities to be equal. Return "ALREADY_EXISTS"
// if the capabilities don't match.
return &csi.NodeStageVolumeResponse{}, nil
}
// Stage the volume.
v.VolumeContext[nodeStgPathKey] = device
s.vols[i] = v
return &csi.NodeStageVolumeResponse{}, nil
}
func (s *service) NodeUnstageVolume(
ctx context.Context,
req *csi.NodeUnstageVolumeRequest) (
*csi.NodeUnstageVolumeResponse, error) {
if len(req.GetVolumeId()) == 0 {
return nil, status.Error(codes.InvalidArgument, "Volume ID cannot be empty")
}
if len(req.GetStagingTargetPath()) == 0 {
return nil, status.Error(codes.InvalidArgument, "Staging Target Path cannot be empty")
}
s.volsRWL.Lock()
defer s.volsRWL.Unlock()
i, v := s.findVolNoLock("id", req.VolumeId)
if i < 0 {
return nil, status.Error(codes.NotFound, req.VolumeId)
}
// nodeStgPathKey is the key in the volume's attributes that is set to a
// mock stage path if the volume has been published by the node
nodeStgPathKey := path.Join(s.nodeID, req.StagingTargetPath)
// Check to see if the volume has already been unstaged.
if v.VolumeContext[nodeStgPathKey] == "" {
return &csi.NodeUnstageVolumeResponse{}, nil
}
// Unpublish the volume.
delete(v.VolumeContext, nodeStgPathKey)
s.vols[i] = v
return &csi.NodeUnstageVolumeResponse{}, nil
}
func (s *service) NodePublishVolume(
ctx context.Context,
req *csi.NodePublishVolumeRequest) (
*csi.NodePublishVolumeResponse, error) {
device, ok := req.PublishContext["device"]
if !ok {
if s.config.DisableAttach {
device = "mock device"
} else {
return nil, status.Error(
codes.InvalidArgument,
"stage volume info 'device' key required")
}
}
if len(req.GetVolumeId()) == 0 {
return nil, status.Error(codes.InvalidArgument, "Volume ID cannot be empty")
}
if len(req.GetTargetPath()) == 0 {
return nil, status.Error(codes.InvalidArgument, "Target Path cannot be empty")
}
if req.GetVolumeCapability() == nil {
return nil, status.Error(codes.InvalidArgument, "Volume Capability cannot be empty")
}
s.volsRWL.Lock()
defer s.volsRWL.Unlock()
i, v := s.findVolNoLock("id", req.VolumeId)
if i < 0 {
return nil, status.Error(codes.NotFound, req.VolumeId)
}
// nodeMntPathKey is the key in the volume's attributes that is set to a
// mock mount path if the volume has been published by the node
nodeMntPathKey := path.Join(s.nodeID, req.TargetPath)
// Check to see if the volume has already been published.
if v.VolumeContext[nodeMntPathKey] != "" {
// Requests marked Readonly fail due to volumes published by
// the Mock driver supporting only RW mode.
if req.Readonly {
return nil, status.Error(codes.AlreadyExists, req.VolumeId)
}
return &csi.NodePublishVolumeResponse{}, nil
}
// Publish the volume.
if req.GetStagingTargetPath() != "" {
v.VolumeContext[nodeMntPathKey] = req.GetStagingTargetPath()
} else {
v.VolumeContext[nodeMntPathKey] = device
}
s.vols[i] = v
return &csi.NodePublishVolumeResponse{}, nil
}
func (s *service) NodeUnpublishVolume(
ctx context.Context,
req *csi.NodeUnpublishVolumeRequest) (
*csi.NodeUnpublishVolumeResponse, error) {
if len(req.GetVolumeId()) == 0 {
return nil, status.Error(codes.InvalidArgument, "Volume ID cannot be empty")
}
if len(req.GetTargetPath()) == 0 {
return nil, status.Error(codes.InvalidArgument, "Target Path cannot be empty")
}
s.volsRWL.Lock()
defer s.volsRWL.Unlock()
i, v := s.findVolNoLock("id", req.VolumeId)
if i < 0 {
return nil, status.Error(codes.NotFound, req.VolumeId)
}
// nodeMntPathKey is the key in the volume's attributes that is set to a
// mock mount path if the volume has been published by the node
nodeMntPathKey := path.Join(s.nodeID, req.TargetPath)
// Check to see if the volume has already been unpublished.
if v.VolumeContext[nodeMntPathKey] == "" {
return &csi.NodeUnpublishVolumeResponse{}, nil
}
// Unpublish the volume.
delete(v.VolumeContext, nodeMntPathKey)
s.vols[i] = v
return &csi.NodeUnpublishVolumeResponse{}, nil
}
func (s *service) NodeGetCapabilities(
ctx context.Context,
req *csi.NodeGetCapabilitiesRequest) (
*csi.NodeGetCapabilitiesResponse, error) {
return &csi.NodeGetCapabilitiesResponse{
Capabilities: []*csi.NodeServiceCapability{
{
Type: &csi.NodeServiceCapability_Rpc{
Rpc: &csi.NodeServiceCapability_RPC{
Type: csi.NodeServiceCapability_RPC_UNKNOWN,
},
},
},
{
Type: &csi.NodeServiceCapability_Rpc{
Rpc: &csi.NodeServiceCapability_RPC{
Type: csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME,
},
},
},
},
}, nil
}
func (s *service) NodeGetInfo(ctx context.Context,
req *csi.NodeGetInfoRequest) (*csi.NodeGetInfoResponse, error) {
csiNodeResponse := &csi.NodeGetInfoResponse{
NodeId: s.nodeID,
}
if s.config.AttachLimit > 0 {
csiNodeResponse.MaxVolumesPerNode = s.config.AttachLimit
}
return csiNodeResponse, nil
}
func (s *service) NodeGetVolumeStats(ctx context.Context,
req *csi.NodeGetVolumeStatsRequest) (*csi.NodeGetVolumeStatsResponse, error) {
return &csi.NodeGetVolumeStatsResponse{}, nil
}

View File

@@ -1,147 +0,0 @@
package service
import (
"fmt"
"strings"
"sync"
"sync/atomic"
"github.com/container-storage-interface/spec/lib/go/csi"
"github.com/kubernetes-csi/csi-test/mock/cache"
"golang.org/x/net/context"
"github.com/golang/protobuf/ptypes"
)
const (
// Name is the name of the CSI plug-in.
Name = "io.kubernetes.storage.mock"
// VendorVersion is the version returned by GetPluginInfo.
VendorVersion = "0.3.0"
)
// Manifest is the SP's manifest.
var Manifest = map[string]string{
"url": "https://github.com/kubernetes-csi/csi-test/mock",
}
type Config struct {
DisableAttach bool
DriverName string
AttachLimit int64
}
// Service is the CSI Mock service provider.
type Service interface {
csi.ControllerServer
csi.IdentityServer
csi.NodeServer
}
type service struct {
sync.Mutex
nodeID string
vols []csi.Volume
volsRWL sync.RWMutex
volsNID uint64
snapshots cache.SnapshotCache
snapshotsNID uint64
config Config
}
type Volume struct {
sync.Mutex
VolumeCSI csi.Volume
NodeID string
ISStaged bool
ISPublished bool
StageTargetPath string
TargetPath string
}
var MockVolumes map[string]Volume
// New returns a new Service.
func New(config Config) Service {
s := &service{
nodeID: config.DriverName,
config: config,
}
s.snapshots = cache.NewSnapshotCache()
s.vols = []csi.Volume{
s.newVolume("Mock Volume 1", gib100),
s.newVolume("Mock Volume 2", gib100),
s.newVolume("Mock Volume 3", gib100),
}
MockVolumes = map[string]Volume{}
s.snapshots.Add(s.newSnapshot("Mock Snapshot 1", "1", map[string]string{"Description": "snapshot 1"}))
s.snapshots.Add(s.newSnapshot("Mock Snapshot 2", "2", map[string]string{"Description": "snapshot 2"}))
s.snapshots.Add(s.newSnapshot("Mock Snapshot 3", "3", map[string]string{"Description": "snapshot 3"}))
return s
}
const (
kib int64 = 1024
mib int64 = kib * 1024
gib int64 = mib * 1024
gib100 int64 = gib * 100
tib int64 = gib * 1024
tib100 int64 = tib * 100
)
func (s *service) newVolume(name string, capcity int64) csi.Volume {
return csi.Volume{
VolumeId: fmt.Sprintf("%d", atomic.AddUint64(&s.volsNID, 1)),
VolumeContext: map[string]string{"name": name},
CapacityBytes: capcity,
}
}
func (s *service) findVol(k, v string) (volIdx int, volInfo csi.Volume) {
s.volsRWL.RLock()
defer s.volsRWL.RUnlock()
return s.findVolNoLock(k, v)
}
func (s *service) findVolNoLock(k, v string) (volIdx int, volInfo csi.Volume) {
volIdx = -1
for i, vi := range s.vols {
switch k {
case "id":
if strings.EqualFold(v, vi.GetVolumeId()) {
return i, vi
}
case "name":
if n, ok := vi.VolumeContext["name"]; ok && strings.EqualFold(v, n) {
return i, vi
}
}
}
return
}
func (s *service) findVolByName(
ctx context.Context, name string) (int, csi.Volume) {
return s.findVol("name", name)
}
func (s *service) newSnapshot(name, sourceVolumeId string, parameters map[string]string) cache.Snapshot {
ptime := ptypes.TimestampNow()
return cache.Snapshot{
Name: name,
Parameters: parameters,
SnapshotCSI: csi.Snapshot{
SnapshotId: fmt.Sprintf("%d", atomic.AddUint64(&s.snapshotsNID, 1)),
CreationTime: ptime,
SourceVolumeId: sourceVolumeId,
ReadyToUse: true,
},
}
}