mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 02:43:36 +00:00
vendor files
This commit is contained in:
97
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/README.md
generated
vendored
Normal file
97
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/README.md
generated
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
# CSI Cinder driver
|
||||
|
||||
## Kubernetes
|
||||
|
||||
### Requirements
|
||||
|
||||
The following feature gates and runtime config have to be enabled to deploy the driver.
|
||||
|
||||
```
|
||||
FEATURE_GATES=CSIPersistentVolume=true,MountPropagation=true
|
||||
RUNTIME_CONFIG="storage.k8s.io/v1alpha1=true"
|
||||
```
|
||||
|
||||
Mountprogpation requires support for privileged containers. So, make sure privileged containers are enabled in the cluster.
|
||||
|
||||
### Example local-up-cluster.sh
|
||||
|
||||
```ALLOW_PRIVILEGED=true FEATURE_GATES=CSIPersistentVolume=true,MountPropagation=true RUNTIME_CONFIG="storage.k8s.io/v1alpha1=true" LOG_LEVEL=5 hack/local-up-cluster.sh```
|
||||
|
||||
### Deploy
|
||||
|
||||
Encode your ```cloud.conf``` file content using base64.
|
||||
|
||||
```base64 -w 0 cloud.conf```
|
||||
|
||||
Update ```cloud.conf``` configuration in ```deploy/kubernetes/csi-secret-cinderplugin.yaml``` file
|
||||
by using the result of the above command.
|
||||
|
||||
```kubectl -f deploy/kubernetes create```
|
||||
|
||||
### Example Nginx application
|
||||
|
||||
```kubectl -f examples/kubernetes/nginx.yaml create```
|
||||
|
||||
## Using CSC tool
|
||||
|
||||
### Start Cinder driver
|
||||
```
|
||||
$ sudo ./_output/cinderplugin --endpoint tcp://127.0.0.1:10000 --cloud-config /etc/cloud.conf --nodeid CSINodeID
|
||||
```
|
||||
|
||||
### Test
|
||||
Get ```csc``` tool from https://github.com/chakri-nelluri/gocsi/tree/master/csc
|
||||
|
||||
#### Get plugin info
|
||||
```
|
||||
$ csc identity plugin-info --endpoint tcp://127.0.0.1:10000
|
||||
"csi-cinderplugin" "0.1.0"
|
||||
```
|
||||
|
||||
#### Get supported versions
|
||||
```
|
||||
$ csc identity supported-versions --endpoint tcp://127.0.0.1:10000
|
||||
0.1.0
|
||||
```
|
||||
|
||||
#### Create a volume
|
||||
```
|
||||
$ csc controller new --endpoint tcp://127.0.0.1:10000 CSIVolumeName
|
||||
CSIVolumeID
|
||||
```
|
||||
|
||||
#### Delete a volume
|
||||
```
|
||||
$ csc controller del --endpoint tcp://127.0.0.1:10000 CSIVolumeID
|
||||
CSIVolumeID
|
||||
```
|
||||
|
||||
#### ControllerPublish a volume
|
||||
```
|
||||
$ csc controller publish --endpoint tcp://127.0.0.1:10000 --node-id=CSINodeID CSIVolumeID
|
||||
CSIVolumeID "DevicePath"="/dev/xxx"
|
||||
```
|
||||
|
||||
#### ControllerUnpublish a volume
|
||||
```
|
||||
$ csc controller unpublish --endpoint tcp://127.0.0.1:10000 --node-id=CSINodeID CSIVolumeID
|
||||
CSIVolumeID
|
||||
```
|
||||
|
||||
#### NodePublish a volume
|
||||
```
|
||||
$ csc node publish --endpoint tcp://127.0.0.1:10000 --target-path /mnt/cinder --pub-info DevicePath="/dev/xxx" CSIVolumeID
|
||||
CSIVolumeID
|
||||
```
|
||||
|
||||
#### NodeUnpublish a volume
|
||||
```
|
||||
$ csc node unpublish --endpoint tcp://127.0.0.1:10000 --target-path /mnt/cinder CSIVolumeID
|
||||
CSIVolumeID
|
||||
```
|
||||
|
||||
#### Get NodeID
|
||||
```
|
||||
$ csc node get-id --endpoint tcp://127.0.0.1:10000
|
||||
CSINodeID
|
||||
```
|
172
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/controllerserver.go
generated
vendored
Normal file
172
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/controllerserver.go
generated
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
/*
|
||||
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 cinder
|
||||
|
||||
import (
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/golang/glog"
|
||||
"github.com/kubernetes-csi/drivers/pkg/cinder/openstack"
|
||||
csicommon "github.com/kubernetes-csi/drivers/pkg/csi-common"
|
||||
"github.com/pborman/uuid"
|
||||
"golang.org/x/net/context"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
)
|
||||
|
||||
type controllerServer struct {
|
||||
*csicommon.DefaultControllerServer
|
||||
}
|
||||
|
||||
func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) {
|
||||
|
||||
// Volume Name
|
||||
volName := req.GetName()
|
||||
if len(volName) == 0 {
|
||||
volName = uuid.NewUUID().String()
|
||||
}
|
||||
|
||||
// Volume Size - Default is 1 GiB
|
||||
volSizeBytes := int64(1 * 1024 * 1024 * 1024)
|
||||
if req.GetCapacityRange() != nil {
|
||||
volSizeBytes = int64(req.GetCapacityRange().GetRequiredBytes())
|
||||
}
|
||||
volSizeGB := int(volume.RoundUpSize(volSizeBytes, 1024*1024*1024))
|
||||
|
||||
// Volume Type
|
||||
volType := req.GetParameters()["type"]
|
||||
|
||||
// Volume Availability - Default is nova
|
||||
volAvailability := req.GetParameters()["availability"]
|
||||
|
||||
// Get OpenStack Provider
|
||||
cloud, err := openstack.GetOpenStackProvider()
|
||||
if err != nil {
|
||||
glog.V(3).Infof("Failed to GetOpenStackProvider: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Volume Create
|
||||
resID, resAvailability, err := cloud.CreateVolume(volName, volSizeGB, volType, volAvailability, nil)
|
||||
if err != nil {
|
||||
glog.V(3).Infof("Failed to CreateVolume: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
glog.V(4).Infof("Create volume %s in Availability Zone: %s", resID, resAvailability)
|
||||
|
||||
return &csi.CreateVolumeResponse{
|
||||
VolumeInfo: &csi.VolumeInfo{
|
||||
Id: resID,
|
||||
Attributes: map[string]string{
|
||||
"availability": resAvailability,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (cs *controllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest) (*csi.DeleteVolumeResponse, error) {
|
||||
|
||||
// Get OpenStack Provider
|
||||
cloud, err := openstack.GetOpenStackProvider()
|
||||
if err != nil {
|
||||
glog.V(3).Infof("Failed to GetOpenStackProvider: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Volume Delete
|
||||
volID := req.GetVolumeId()
|
||||
err = cloud.DeleteVolume(volID)
|
||||
if err != nil {
|
||||
glog.V(3).Infof("Failed to DeleteVolume: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
glog.V(4).Infof("Delete volume %s", volID)
|
||||
|
||||
return &csi.DeleteVolumeResponse{}, nil
|
||||
}
|
||||
|
||||
func (cs *controllerServer) ControllerPublishVolume(ctx context.Context, req *csi.ControllerPublishVolumeRequest) (*csi.ControllerPublishVolumeResponse, error) {
|
||||
|
||||
// Get OpenStack Provider
|
||||
cloud, err := openstack.GetOpenStackProvider()
|
||||
if err != nil {
|
||||
glog.V(3).Infof("Failed to GetOpenStackProvider: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Volume Attach
|
||||
instanceID := req.GetNodeId()
|
||||
volumeID := req.GetVolumeId()
|
||||
|
||||
_, err = cloud.AttachVolume(instanceID, volumeID)
|
||||
if err != nil {
|
||||
glog.V(3).Infof("Failed to AttachVolume: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = cloud.WaitDiskAttached(instanceID, volumeID)
|
||||
if err != nil {
|
||||
glog.V(3).Infof("Failed to WaitDiskAttached: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
devicePath, err := cloud.GetAttachmentDiskPath(instanceID, volumeID)
|
||||
if err != nil {
|
||||
glog.V(3).Infof("Failed to GetAttachmentDiskPath: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
glog.V(4).Infof("ControllerPublishVolume %s on %s", volumeID, instanceID)
|
||||
|
||||
// Publish Volume Info
|
||||
pvInfo := map[string]string{}
|
||||
pvInfo["DevicePath"] = devicePath
|
||||
|
||||
return &csi.ControllerPublishVolumeResponse{
|
||||
PublishVolumeInfo: pvInfo,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (cs *controllerServer) ControllerUnpublishVolume(ctx context.Context, req *csi.ControllerUnpublishVolumeRequest) (*csi.ControllerUnpublishVolumeResponse, error) {
|
||||
|
||||
// Get OpenStack Provider
|
||||
cloud, err := openstack.GetOpenStackProvider()
|
||||
if err != nil {
|
||||
glog.V(3).Infof("Failed to GetOpenStackProvider: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Volume Detach
|
||||
instanceID := req.GetNodeId()
|
||||
volumeID := req.GetVolumeId()
|
||||
|
||||
err = cloud.DetachVolume(instanceID, volumeID)
|
||||
if err != nil {
|
||||
glog.V(3).Infof("Failed to DetachVolume: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = cloud.WaitDiskDetached(instanceID, volumeID)
|
||||
if err != nil {
|
||||
glog.V(3).Infof("Failed to WaitDiskDetached: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
glog.V(4).Infof("ControllerUnpublishVolume %s on %s", volumeID, instanceID)
|
||||
|
||||
return &csi.ControllerUnpublishVolumeResponse{}, nil
|
||||
}
|
176
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/controllerserver_test.go
generated
vendored
Normal file
176
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/controllerserver_test.go
generated
vendored
Normal file
@ -0,0 +1,176 @@
|
||||
/*
|
||||
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 cinder
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/kubernetes-csi/drivers/pkg/cinder/openstack"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
var fakeCs *controllerServer
|
||||
|
||||
// Init Controller Server
|
||||
func init() {
|
||||
if fakeCs == nil {
|
||||
d := NewDriver(fakeNodeID, fakeEndpoint, fakeConfig)
|
||||
fakeCs = NewControllerServer(d)
|
||||
}
|
||||
}
|
||||
|
||||
// Test CreateVolume
|
||||
func TestCreateVolume(t *testing.T) {
|
||||
|
||||
// mock OpenStack
|
||||
osmock := new(openstack.OpenStackMock)
|
||||
// CreateVolume(name string, size int, vtype, availability string, tags *map[string]string) (string, string, error)
|
||||
osmock.On("CreateVolume", fakeVolName, mock.AnythingOfType("int"), fakeVolType, fakeAvailability, (*map[string]string)(nil)).Return(fakeVolID, fakeAvailability, nil)
|
||||
openstack.OsInstance = osmock
|
||||
|
||||
// Init assert
|
||||
assert := assert.New(t)
|
||||
|
||||
// Fake request
|
||||
fakeReq := &csi.CreateVolumeRequest{
|
||||
Version: &version,
|
||||
Name: fakeVolName,
|
||||
VolumeCapabilities: nil,
|
||||
}
|
||||
|
||||
// Invoke CreateVolume
|
||||
actualRes, err := fakeCs.CreateVolume(fakeCtx, fakeReq)
|
||||
if err != nil {
|
||||
t.Errorf("failed to CreateVolume: %v", err)
|
||||
}
|
||||
|
||||
// Assert
|
||||
assert.NotNil(actualRes.VolumeInfo)
|
||||
|
||||
assert.NotEqual(0, len(actualRes.VolumeInfo.Id), "Volume Id is nil")
|
||||
|
||||
assert.Equal(fakeAvailability, actualRes.VolumeInfo.Attributes["availability"])
|
||||
}
|
||||
|
||||
// Test DeleteVolume
|
||||
func TestDeleteVolume(t *testing.T) {
|
||||
|
||||
// mock OpenStack
|
||||
osmock := new(openstack.OpenStackMock)
|
||||
// DeleteVolume(volumeID string) error
|
||||
osmock.On("DeleteVolume", fakeVolID).Return(nil)
|
||||
openstack.OsInstance = osmock
|
||||
|
||||
// Init assert
|
||||
assert := assert.New(t)
|
||||
|
||||
// Fake request
|
||||
fakeReq := &csi.DeleteVolumeRequest{
|
||||
Version: &version,
|
||||
VolumeId: fakeVolID,
|
||||
}
|
||||
|
||||
// Expected Result
|
||||
expectedRes := &csi.DeleteVolumeResponse{}
|
||||
|
||||
// Invoke DeleteVolume
|
||||
actualRes, err := fakeCs.DeleteVolume(fakeCtx, fakeReq)
|
||||
if err != nil {
|
||||
t.Errorf("failed to DeleteVolume: %v", err)
|
||||
}
|
||||
|
||||
// Assert
|
||||
assert.Equal(expectedRes, actualRes)
|
||||
}
|
||||
|
||||
// Test ControllerPublishVolume
|
||||
func TestControllerPublishVolume(t *testing.T) {
|
||||
|
||||
// mock OpenStack
|
||||
osmock := new(openstack.OpenStackMock)
|
||||
// AttachVolume(instanceID, volumeID string) (string, error)
|
||||
osmock.On("AttachVolume", fakeNodeID, fakeVolID).Return(fakeVolID, nil)
|
||||
// WaitDiskAttached(instanceID string, volumeID string) error
|
||||
osmock.On("WaitDiskAttached", fakeNodeID, fakeVolID).Return(nil)
|
||||
// GetAttachmentDiskPath(instanceID, volumeID string) (string, error)
|
||||
osmock.On("GetAttachmentDiskPath", fakeNodeID, fakeVolID).Return(fakeDevicePath, nil)
|
||||
openstack.OsInstance = osmock
|
||||
|
||||
// Init assert
|
||||
assert := assert.New(t)
|
||||
|
||||
// Fake request
|
||||
fakeReq := &csi.ControllerPublishVolumeRequest{
|
||||
Version: &version,
|
||||
VolumeId: fakeVolID,
|
||||
NodeId: fakeNodeID,
|
||||
VolumeCapability: nil,
|
||||
Readonly: false,
|
||||
}
|
||||
|
||||
// Expected Result
|
||||
expectedRes := &csi.ControllerPublishVolumeResponse{
|
||||
PublishVolumeInfo: map[string]string{
|
||||
"DevicePath": fakeDevicePath,
|
||||
},
|
||||
}
|
||||
|
||||
// Invoke ControllerPublishVolume
|
||||
actualRes, err := fakeCs.ControllerPublishVolume(fakeCtx, fakeReq)
|
||||
if err != nil {
|
||||
t.Errorf("failed to ControllerPublishVolume: %v", err)
|
||||
}
|
||||
|
||||
// Assert
|
||||
assert.Equal(expectedRes, actualRes)
|
||||
}
|
||||
|
||||
// Test ControllerUnpublishVolume
|
||||
func TestControllerUnpublishVolume(t *testing.T) {
|
||||
|
||||
// mock OpenStack
|
||||
osmock := new(openstack.OpenStackMock)
|
||||
// DetachVolume(instanceID, volumeID string) error
|
||||
osmock.On("DetachVolume", fakeNodeID, fakeVolID).Return(nil)
|
||||
// WaitDiskDetached(instanceID string, volumeID string) error
|
||||
osmock.On("WaitDiskDetached", fakeNodeID, fakeVolID).Return(nil)
|
||||
openstack.OsInstance = osmock
|
||||
|
||||
// Init assert
|
||||
assert := assert.New(t)
|
||||
|
||||
// Fake request
|
||||
fakeReq := &csi.ControllerUnpublishVolumeRequest{
|
||||
Version: &version,
|
||||
VolumeId: fakeVolID,
|
||||
NodeId: fakeNodeID,
|
||||
}
|
||||
|
||||
// Expected Result
|
||||
expectedRes := &csi.ControllerUnpublishVolumeResponse{}
|
||||
|
||||
// Invoke ControllerUnpublishVolume
|
||||
actualRes, err := fakeCs.ControllerUnpublishVolume(fakeCtx, fakeReq)
|
||||
if err != nil {
|
||||
t.Errorf("failed to ControllerUnpublishVolume: %v", err)
|
||||
}
|
||||
|
||||
// Assert
|
||||
assert.Equal(expectedRes, actualRes)
|
||||
}
|
71
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/deploy/kubernetes/csi-attacher-cinderplugin.yaml
generated
vendored
Normal file
71
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/deploy/kubernetes/csi-attacher-cinderplugin.yaml
generated
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
# This YAML file contains attacher & csi driver API objects,
|
||||
# which are necessary to run external csi attacher for cinder.
|
||||
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: csi-attacher-cinderplugin
|
||||
labels:
|
||||
app: csi-attacher-cinderplugin
|
||||
spec:
|
||||
selector:
|
||||
app: csi-attacher-cinderplugin
|
||||
ports:
|
||||
- name: dummy
|
||||
port: 12345
|
||||
|
||||
---
|
||||
kind: StatefulSet
|
||||
apiVersion: apps/v1beta1
|
||||
metadata:
|
||||
name: csi-attacher-cinderplugin
|
||||
spec:
|
||||
serviceName: "csi-attacher-cinderplugin"
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: csi-attacher-cinderplugin
|
||||
spec:
|
||||
serviceAccount: csi-attacher
|
||||
containers:
|
||||
- name: csi-attacher
|
||||
image: docker.io/k8scsi/csi-attacher
|
||||
args:
|
||||
- "--v=5"
|
||||
- "--csi-address=$(ADDRESS)"
|
||||
env:
|
||||
- name: ADDRESS
|
||||
value: /var/lib/csi/sockets/pluginproxy/csi.sock
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
volumeMounts:
|
||||
- name: socket-dir
|
||||
mountPath: /var/lib/csi/sockets/pluginproxy/
|
||||
- name: cinder
|
||||
image: docker.io/k8scsi/cinderplugin
|
||||
args :
|
||||
- "--nodeid=$(NODE_ID)"
|
||||
- "--endpoint=$(CSI_ENDPOINT)"
|
||||
- "--cloud-config=$(CLOUD_CONFIG)"
|
||||
env:
|
||||
- name: NODE_ID
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.nodeName
|
||||
- name: CSI_ENDPOINT
|
||||
value: unix://plugin/csi.sock
|
||||
- name: CLOUD_CONFIG
|
||||
value: /etc/config/cloud.conf
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
volumeMounts:
|
||||
- name: socket-dir
|
||||
mountPath: /plugin
|
||||
- name: secret-cinderplugin
|
||||
mountPath: /etc/config
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: socket-dir
|
||||
emptyDir:
|
||||
- name: secret-cinderplugin
|
||||
secret:
|
||||
secretName: csi-secret-cinderplugin
|
37
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/deploy/kubernetes/csi-attacher-rbac.yaml
generated
vendored
Normal file
37
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/deploy/kubernetes/csi-attacher-rbac.yaml
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
# This YAML file contains RBAC API objects,
|
||||
# which are necessary to run external csi attacher for cinder.
|
||||
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: csi-attacher
|
||||
|
||||
---
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: external-attacher-runner
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["persistentvolumes"]
|
||||
verbs: ["get", "list", "watch", "update"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: ["storage.k8s.io"]
|
||||
resources: ["volumeattachments"]
|
||||
verbs: ["get", "list", "watch", "update"]
|
||||
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: csi-attacher-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: csi-attacher
|
||||
namespace: default
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: external-attacher-runner
|
||||
apiGroup: rbac.authorization.k8s.io
|
90
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/deploy/kubernetes/csi-nodeplugin-cinderplugin.yaml
generated
vendored
Normal file
90
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/deploy/kubernetes/csi-nodeplugin-cinderplugin.yaml
generated
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
# This YAML file contains driver-registrar & csi driver nodeplugin API objects,
|
||||
# which are necessary to run csi nodeplugin for cinder.
|
||||
|
||||
kind: DaemonSet
|
||||
apiVersion: apps/v1beta2
|
||||
metadata:
|
||||
name: csi-nodeplugin-cinderplugin
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: csi-nodeplugin-cinderplugin
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: csi-nodeplugin-cinderplugin
|
||||
spec:
|
||||
serviceAccount: csi-nodeplugin
|
||||
hostNetwork: true
|
||||
containers:
|
||||
- name: driver-registrar
|
||||
image: docker.io/k8scsi/driver-registrar
|
||||
args:
|
||||
- "--v=5"
|
||||
- "--csi-address=$(ADDRESS)"
|
||||
env:
|
||||
- name: ADDRESS
|
||||
value: /plugin/csi.sock
|
||||
- name: KUBE_NODE_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.nodeName
|
||||
volumeMounts:
|
||||
- name: plugin-dir
|
||||
mountPath: /plugin
|
||||
- name: cinder
|
||||
securityContext:
|
||||
privileged: true
|
||||
capabilities:
|
||||
add: ["SYS_ADMIN"]
|
||||
allowPrivilegeEscalation: true
|
||||
image: docker.io/k8scsi/cinderplugin
|
||||
args :
|
||||
- "--nodeid=$(NODE_ID)"
|
||||
- "--endpoint=$(CSI_ENDPOINT)"
|
||||
- "--cloud-config=$(CLOUD_CONFIG)"
|
||||
env:
|
||||
- name: NODE_ID
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.nodeName
|
||||
- name: CSI_ENDPOINT
|
||||
value: unix://plugin/csi.sock
|
||||
- name: CLOUD_CONFIG
|
||||
value: /etc/config/cloud.conf
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
volumeMounts:
|
||||
- name: plugin-dir
|
||||
mountPath: /plugin
|
||||
- name: pods-mount-dir
|
||||
mountPath: /var/lib/kubelet/pods
|
||||
mountPropagation: "Bidirectional"
|
||||
- name: pods-cloud-data
|
||||
mountPath: /var/lib/cloud/data
|
||||
readOnly: true
|
||||
- name: pods-probe-dir
|
||||
mountPath: /dev
|
||||
mountPropagation: "HostToContainer"
|
||||
- name: secret-cinderplugin
|
||||
mountPath: /etc/config
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: plugin-dir
|
||||
hostPath:
|
||||
path: /var/lib/kubelet/plugins/csi-cinderplugin
|
||||
type: DirectoryOrCreate
|
||||
- name: pods-mount-dir
|
||||
hostPath:
|
||||
path: /var/lib/kubelet/pods
|
||||
type: Directory
|
||||
- name: pods-cloud-data
|
||||
hostPath:
|
||||
path: /var/lib/cloud/data
|
||||
type: Directory
|
||||
- name: pods-probe-dir
|
||||
hostPath:
|
||||
path: /dev
|
||||
type: Directory
|
||||
- name: secret-cinderplugin
|
||||
secret:
|
||||
secretName: csi-secret-cinderplugin
|
35
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/deploy/kubernetes/csi-nodeplugin-rbac.yaml
generated
vendored
Normal file
35
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/deploy/kubernetes/csi-nodeplugin-rbac.yaml
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
# This YAML defines all API objects to create RBAC roles for csi node plugin.
|
||||
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: csi-nodeplugin
|
||||
|
||||
---
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: csi-nodeplugin
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["persistentvolumes"]
|
||||
verbs: ["get", "list", "watch", "update"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["get", "list", "watch", "update"]
|
||||
- apiGroups: ["storage.k8s.io"]
|
||||
resources: ["volumeattachments"]
|
||||
verbs: ["get", "list", "watch", "update"]
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: csi-nodeplugin
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: csi-nodeplugin
|
||||
namespace: default
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: csi-nodeplugin
|
||||
apiGroup: rbac.authorization.k8s.io
|
71
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/deploy/kubernetes/csi-provisioner-cinderplugin.yaml
generated
vendored
Normal file
71
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/deploy/kubernetes/csi-provisioner-cinderplugin.yaml
generated
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
# This YAML file contains attacher & csi driver API objects,
|
||||
# which are necessary to run external csi provisioner for cinder.
|
||||
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: csi-provisioner-cinderplugin
|
||||
labels:
|
||||
app: csi-provisioner-cinderplugin
|
||||
spec:
|
||||
selector:
|
||||
app: csi-provisioner-cinderplugin
|
||||
ports:
|
||||
- name: dummy
|
||||
port: 12345
|
||||
|
||||
---
|
||||
kind: StatefulSet
|
||||
apiVersion: apps/v1beta1
|
||||
metadata:
|
||||
name: csi-provisioner-cinderplugin
|
||||
spec:
|
||||
serviceName: "csi-provisioner-cinderplugin"
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: csi-provisioner-cinderplugin
|
||||
spec:
|
||||
serviceAccount: csi-provisioner
|
||||
containers:
|
||||
- name: csi-provisioner
|
||||
image: docker.io/k8scsi/csi-provisioner
|
||||
args:
|
||||
- "--provisioner=csi-cinderplugin"
|
||||
- "--csi-address=$(ADDRESS)"
|
||||
env:
|
||||
- name: ADDRESS
|
||||
value: /var/lib/csi/sockets/pluginproxy/csi.sock
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
volumeMounts:
|
||||
- name: socket-dir
|
||||
mountPath: /var/lib/csi/sockets/pluginproxy/
|
||||
- name: cinder
|
||||
image: docker.io/k8scsi/cinderplugin
|
||||
args :
|
||||
- "--nodeid=$(NODE_ID)"
|
||||
- "--endpoint=$(CSI_ENDPOINT)"
|
||||
- "--cloud-config=$(CLOUD_CONFIG)"
|
||||
env:
|
||||
- name: NODE_ID
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.nodeName
|
||||
- name: CSI_ENDPOINT
|
||||
value: unix://plugin/csi.sock
|
||||
- name: CLOUD_CONFIG
|
||||
value: /etc/config/cloud.conf
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
volumeMounts:
|
||||
- name: socket-dir
|
||||
mountPath: /plugin
|
||||
- name: secret-cinderplugin
|
||||
mountPath: /etc/config
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: socket-dir
|
||||
emptyDir:
|
||||
- name: secret-cinderplugin
|
||||
secret:
|
||||
secretName: csi-secret-cinderplugin
|
41
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/deploy/kubernetes/csi-provisioner-rbac.yaml
generated
vendored
Normal file
41
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/deploy/kubernetes/csi-provisioner-rbac.yaml
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
# This YAML file contains RBAC API objects,
|
||||
# which are necessary to run external csi provisioner for cinder.
|
||||
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: csi-provisioner
|
||||
|
||||
---
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: external-provisioner-runner
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["persistentvolumes"]
|
||||
verbs: ["get", "list", "watch", "create", "delete"]
|
||||
- apiGroups: [""]
|
||||
resources: ["persistentvolumeclaims"]
|
||||
verbs: ["get", "list", "watch", "update"]
|
||||
- apiGroups: ["storage.k8s.io"]
|
||||
resources: ["storageclasses"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["events"]
|
||||
verbs: ["list", "watch", "create", "update", "patch"]
|
||||
|
||||
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: csi-provisioner-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: csi-provisioner
|
||||
namespace: default
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: external-provisioner-runner
|
||||
apiGroup: rbac.authorization.k8s.io
|
9
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/deploy/kubernetes/csi-secret-cinderplugin.yaml
generated
vendored
Normal file
9
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/deploy/kubernetes/csi-secret-cinderplugin.yaml
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
# This YAML file contains secret objects,
|
||||
# which are necessary to run csi cinder plugin.
|
||||
|
||||
kind: Secret
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: csi-secret-cinderplugin
|
||||
data:
|
||||
cloud.conf: W0dsb2JhbF0KdXNlcm5hbWU9dXNlcgpwYXNzd29yZD1wYXNzCmF1dGgtdXJsPWh0dHBzOi8vPGtleXN0b25lX2lwPi9pZGVudGl0eS92Mwp0ZW5hbnQtaWQ9Yzg2OTE2OGE4Mjg4NDdmMzlmN2YwNmVkZDczMDU2MzcKZG9tYWluLWlkPTJhNzNiOGY1OTdjMDQ1NTFhMGZkYzhlOTU1NDRiZThhCg==
|
13
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/dockerfile/Dockerfile
generated
vendored
Normal file
13
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/dockerfile/Dockerfile
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
# Based on centos
|
||||
FROM centos:7.4.1708
|
||||
LABEL maintainers="Kubernetes Authors"
|
||||
LABEL description="Cinder CSI Plugin"
|
||||
|
||||
# Copy cinderplugin from build directory
|
||||
COPY cinderplugin /cinderplugin
|
||||
|
||||
# Install e4fsprogs for format
|
||||
RUN yum -y install e4fsprogs
|
||||
|
||||
# Define default command
|
||||
ENTRYPOINT ["/cinderplugin"]
|
90
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/driver.go
generated
vendored
Normal file
90
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/driver.go
generated
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
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 cinder
|
||||
|
||||
import (
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/golang/glog"
|
||||
|
||||
"github.com/kubernetes-csi/drivers/pkg/cinder/openstack"
|
||||
"github.com/kubernetes-csi/drivers/pkg/csi-common"
|
||||
)
|
||||
|
||||
type driver struct {
|
||||
csiDriver *csicommon.CSIDriver
|
||||
endpoint string
|
||||
cloudconfig string
|
||||
|
||||
ids *csicommon.DefaultIdentityServer
|
||||
cs *controllerServer
|
||||
ns *nodeServer
|
||||
|
||||
cap []*csi.VolumeCapability_AccessMode
|
||||
cscap []*csi.ControllerServiceCapability
|
||||
}
|
||||
|
||||
const (
|
||||
driverName = "csi-cinderplugin"
|
||||
)
|
||||
|
||||
var (
|
||||
version = csi.Version{
|
||||
Minor: 1,
|
||||
}
|
||||
)
|
||||
|
||||
func GetSupportedVersions() []*csi.Version {
|
||||
return []*csi.Version{&version}
|
||||
}
|
||||
|
||||
func NewDriver(nodeID, endpoint string, cloudconfig string) *driver {
|
||||
glog.Infof("Driver: %v version: %v", driverName, csicommon.GetVersionString(&version))
|
||||
|
||||
d := &driver{}
|
||||
|
||||
d.endpoint = endpoint
|
||||
d.cloudconfig = cloudconfig
|
||||
|
||||
csiDriver := csicommon.NewCSIDriver(driverName, &version, GetSupportedVersions(), nodeID)
|
||||
csiDriver.AddControllerServiceCapabilities(
|
||||
[]csi.ControllerServiceCapability_RPC_Type{
|
||||
csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME,
|
||||
csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME,
|
||||
})
|
||||
csiDriver.AddVolumeCapabilityAccessModes([]csi.VolumeCapability_AccessMode_Mode{csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER})
|
||||
|
||||
d.csiDriver = csiDriver
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
func NewControllerServer(d *driver) *controllerServer {
|
||||
return &controllerServer{
|
||||
DefaultControllerServer: csicommon.NewDefaultControllerServer(d.csiDriver),
|
||||
}
|
||||
}
|
||||
|
||||
func NewNodeServer(d *driver) *nodeServer {
|
||||
return &nodeServer{
|
||||
DefaultNodeServer: csicommon.NewDefaultNodeServer(d.csiDriver),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *driver) Run() {
|
||||
openstack.InitOpenStackProvider(d.cloudconfig)
|
||||
csicommon.RunControllerandNodePublishServer(d.endpoint, d.csiDriver, NewControllerServer(d), NewNodeServer(d))
|
||||
}
|
6
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/etc/cloud.conf
generated
vendored
Normal file
6
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/etc/cloud.conf
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
[Global]
|
||||
username=user
|
||||
password=pass
|
||||
auth-url=https://<keystone_ip>/identity/v3
|
||||
tenant-id=c869168a828847f39f7f06edd7305637
|
||||
domain-id=2a73b8f597c04551a0fdc8e95544be8a
|
44
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/examples/kubernetes/nginx.yaml
generated
vendored
Normal file
44
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/examples/kubernetes/nginx.yaml
generated
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
# This YAML file contains nginx & csi cinder driver objects,
|
||||
# which are necessary to run nginx with csi cinder driver.
|
||||
|
||||
apiVersion: storage.k8s.io/v1
|
||||
kind: StorageClass
|
||||
metadata:
|
||||
name: csi-sc-cinderplugin
|
||||
provisioner: csi-cinderplugin
|
||||
parameters:
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: csi-pvc-cinderplugin
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
storageClassName: csi-sc-cinderplugin
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
protocol: TCP
|
||||
volumeMounts:
|
||||
- mountPath: /var/lib/www/html
|
||||
name: csi-data-cinderplugin
|
||||
volumes:
|
||||
- name: csi-data-cinderplugin
|
||||
persistentVolumeClaim:
|
||||
claimName: csi-pvc-cinderplugin
|
||||
readOnly: false
|
32
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/fake.go
generated
vendored
Normal file
32
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/fake.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
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 cinder
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var fakeNodeID = "CSINodeID"
|
||||
var fakeEndpoint = "tcp://127.0.0.1:10000"
|
||||
var fakeConfig = "/etc/cloud.conf"
|
||||
var fakeCtx = context.Background()
|
||||
var fakeVolName = "CSIVolumeName"
|
||||
var fakeVolID = "CSIVolumeID"
|
||||
var fakeVolType = "lvmdriver-1"
|
||||
var fakeAvailability = "nova"
|
||||
var fakeDevicePath = "/dev/xxx"
|
||||
var fakeTargetPath = "/mnt/cinder"
|
161
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/mount/mount.go
generated
vendored
Normal file
161
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/mount/mount.go
generated
vendored
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
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 mount
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
"k8s.io/kubernetes/pkg/volume/util"
|
||||
utilexec "k8s.io/utils/exec"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
const (
|
||||
probeVolumeDuration = 1 * time.Second
|
||||
probeVolumeTimeout = 60 * time.Second
|
||||
instanceIDFile = "/var/lib/cloud/data/instance-id"
|
||||
)
|
||||
|
||||
type IMount interface {
|
||||
ScanForAttach(devicePath string) error
|
||||
IsLikelyNotMountPointAttach(targetpath string) (bool, error)
|
||||
FormatAndMount(source string, target string, fstype string, options []string) error
|
||||
IsLikelyNotMountPointDetach(targetpath string) (bool, error)
|
||||
UnmountPath(mountPath string) error
|
||||
GetInstanceID() (string, error)
|
||||
}
|
||||
|
||||
type Mount struct {
|
||||
}
|
||||
|
||||
var MInstance IMount = nil
|
||||
|
||||
func GetMountProvider() (IMount, error) {
|
||||
|
||||
if MInstance == nil {
|
||||
MInstance = &Mount{}
|
||||
}
|
||||
return MInstance, nil
|
||||
}
|
||||
|
||||
// probeVolume probes volume in compute
|
||||
func probeVolume() error {
|
||||
// rescan scsi bus
|
||||
scsi_path := "/sys/class/scsi_host/"
|
||||
if dirs, err := ioutil.ReadDir(scsi_path); err == nil {
|
||||
for _, f := range dirs {
|
||||
name := scsi_path + f.Name() + "/scan"
|
||||
data := []byte("- - -")
|
||||
ioutil.WriteFile(name, data, 0666)
|
||||
}
|
||||
}
|
||||
|
||||
executor := utilexec.New()
|
||||
args := []string{"trigger"}
|
||||
cmd := executor.Command("udevadm", args...)
|
||||
_, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
glog.V(3).Infof("error running udevadm trigger %v\n", err)
|
||||
return err
|
||||
}
|
||||
glog.V(4).Infof("Successfully probed all attachments")
|
||||
return nil
|
||||
}
|
||||
|
||||
// ScanForAttach
|
||||
func (m *Mount) ScanForAttach(devicePath string) error {
|
||||
ticker := time.NewTicker(probeVolumeDuration)
|
||||
defer ticker.Stop()
|
||||
timer := time.NewTimer(probeVolumeTimeout)
|
||||
defer timer.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
glog.V(5).Infof("Checking Cinder disk %q is attached.", devicePath)
|
||||
probeVolume()
|
||||
|
||||
exists, err := util.PathExists(devicePath)
|
||||
if exists && err == nil {
|
||||
return nil
|
||||
} else {
|
||||
glog.V(3).Infof("Could not find attached Cinder disk %s", devicePath)
|
||||
}
|
||||
case <-timer.C:
|
||||
return fmt.Errorf("Could not find attached Cinder disk %s. Timeout waiting for mount paths to be created.", devicePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FormatAndMount
|
||||
func (m *Mount) FormatAndMount(source string, target string, fstype string, options []string) error {
|
||||
diskMounter := &mount.SafeFormatAndMount{Interface: mount.New(""), Exec: mount.NewOsExec()}
|
||||
return diskMounter.FormatAndMount(source, target, fstype, options)
|
||||
}
|
||||
|
||||
// IsLikelyNotMountPointAttach
|
||||
func (m *Mount) IsLikelyNotMountPointAttach(targetpath string) (bool, error) {
|
||||
notMnt, err := mount.New("").IsLikelyNotMountPoint(targetpath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = os.MkdirAll(targetpath, 0750)
|
||||
if err == nil {
|
||||
notMnt = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return notMnt, err
|
||||
}
|
||||
|
||||
// IsLikelyNotMountPointDetach
|
||||
func (m *Mount) IsLikelyNotMountPointDetach(targetpath string) (bool, error) {
|
||||
notMnt, err := mount.New("").IsLikelyNotMountPoint(targetpath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return notMnt, fmt.Errorf("targetpath not found")
|
||||
} else {
|
||||
return notMnt, err
|
||||
}
|
||||
}
|
||||
return notMnt, nil
|
||||
}
|
||||
|
||||
// UnmountPath
|
||||
func (m *Mount) UnmountPath(mountPath string) error {
|
||||
return util.UnmountPath(mountPath, mount.New(""))
|
||||
}
|
||||
|
||||
// GetInstanceID from file
|
||||
func (m *Mount) GetInstanceID() (string, error) {
|
||||
// Try to find instance ID on the local filesystem (created by cloud-init)
|
||||
idBytes, err := ioutil.ReadFile(instanceIDFile)
|
||||
if err == nil {
|
||||
instanceID := string(idBytes)
|
||||
instanceID = strings.TrimSpace(instanceID)
|
||||
glog.V(3).Infof("Got instance id from %s: %s", instanceIDFile, instanceID)
|
||||
if instanceID != "" {
|
||||
return instanceID, nil
|
||||
}
|
||||
}
|
||||
return "", err
|
||||
}
|
130
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/mount/mount_mock.go
generated
vendored
Normal file
130
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/mount/mount_mock.go
generated
vendored
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
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 mount
|
||||
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
|
||||
// MountMock is an autogenerated mock type for the IMount type
|
||||
// ORIGINALLY GENERATED BY mockery with hand edits
|
||||
type MountMock struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// FormatAndMount provides a mock function with given fields: source, target, fstype, options
|
||||
func (_m *MountMock) FormatAndMount(source string, target string, fstype string, options []string) error {
|
||||
ret := _m.Called(source, target, fstype, options)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, string, string, []string) error); ok {
|
||||
r0 = rf(source, target, fstype, options)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// GetInstanceID provides a mock function with given fields:
|
||||
func (_m *MountMock) GetInstanceID() (string, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 string
|
||||
if rf, ok := ret.Get(0).(func() string); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func() error); ok {
|
||||
r1 = rf()
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// IsLikelyNotMountPointAttach provides a mock function with given fields: targetpath
|
||||
func (_m *MountMock) IsLikelyNotMountPointAttach(targetpath string) (bool, error) {
|
||||
ret := _m.Called(targetpath)
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func(string) bool); ok {
|
||||
r0 = rf(targetpath)
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(targetpath)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// IsLikelyNotMountPointDetach provides a mock function with given fields: targetpath
|
||||
func (_m *MountMock) IsLikelyNotMountPointDetach(targetpath string) (bool, error) {
|
||||
ret := _m.Called(targetpath)
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func(string) bool); ok {
|
||||
r0 = rf(targetpath)
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(targetpath)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// ScanForAttach provides a mock function with given fields: devicePath
|
||||
func (_m *MountMock) ScanForAttach(devicePath string) error {
|
||||
ret := _m.Called(devicePath)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string) error); ok {
|
||||
r0 = rf(devicePath)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// UnmountPath provides a mock function with given fields: mountPath
|
||||
func (_m *MountMock) UnmountPath(mountPath string) error {
|
||||
ret := _m.Called(mountPath)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string) error); ok {
|
||||
r0 = rf(mountPath)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
132
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/nodeserver.go
generated
vendored
Normal file
132
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/nodeserver.go
generated
vendored
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
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 cinder
|
||||
|
||||
import (
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/kubernetes-csi/drivers/pkg/cinder/mount"
|
||||
csicommon "github.com/kubernetes-csi/drivers/pkg/csi-common"
|
||||
)
|
||||
|
||||
type nodeServer struct {
|
||||
*csicommon.DefaultNodeServer
|
||||
}
|
||||
|
||||
func (ns *nodeServer) GetNodeID(ctx context.Context, req *csi.GetNodeIDRequest) (*csi.GetNodeIDResponse, error) {
|
||||
|
||||
// Get Mount Provider
|
||||
m, err := mount.GetMountProvider()
|
||||
if err != nil {
|
||||
glog.V(3).Infof("Failed to GetMountProvider: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nodeID, err := m.GetInstanceID()
|
||||
if err != nil {
|
||||
glog.V(3).Infof("Failed to GetInstanceID: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(nodeID) > 0 {
|
||||
return &csi.GetNodeIDResponse{
|
||||
NodeId: nodeID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Using default function
|
||||
return ns.DefaultNodeServer.GetNodeID(ctx, req)
|
||||
}
|
||||
|
||||
func (ns *nodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) {
|
||||
|
||||
targetPath := req.GetTargetPath()
|
||||
fsType := req.GetVolumeCapability().GetMount().GetFsType()
|
||||
devicePath := req.GetPublishVolumeInfo()["DevicePath"]
|
||||
|
||||
// Get Mount Provider
|
||||
m, err := mount.GetMountProvider()
|
||||
if err != nil {
|
||||
glog.V(3).Infof("Failed to GetMountProvider: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Device Scan
|
||||
err = m.ScanForAttach(devicePath)
|
||||
if err != nil {
|
||||
glog.V(3).Infof("Failed to ScanForAttach: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Verify whether mounted
|
||||
notMnt, err := m.IsLikelyNotMountPointAttach(targetPath)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
// Volume Mount
|
||||
if notMnt {
|
||||
// Get Options
|
||||
var options []string
|
||||
if req.GetReadonly() {
|
||||
options = append(options, "ro")
|
||||
} else {
|
||||
options = append(options, "rw")
|
||||
}
|
||||
mountFlags := req.GetVolumeCapability().GetMount().GetMountFlags()
|
||||
options = append(options, mountFlags...)
|
||||
|
||||
// Mount
|
||||
err = m.FormatAndMount(devicePath, targetPath, fsType, options)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return &csi.NodePublishVolumeResponse{}, nil
|
||||
}
|
||||
|
||||
func (ns *nodeServer) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpublishVolumeRequest) (*csi.NodeUnpublishVolumeResponse, error) {
|
||||
|
||||
targetPath := req.GetTargetPath()
|
||||
|
||||
// Get Mount Provider
|
||||
m, err := mount.GetMountProvider()
|
||||
if err != nil {
|
||||
glog.V(3).Infof("Failed to GetMountProvider: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
notMnt, err := m.IsLikelyNotMountPointDetach(targetPath)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
if notMnt {
|
||||
return nil, status.Error(codes.NotFound, "Volume not mounted")
|
||||
}
|
||||
|
||||
err = m.UnmountPath(req.GetTargetPath())
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
return &csi.NodeUnpublishVolumeResponse{}, nil
|
||||
}
|
142
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/nodeserver_test.go
generated
vendored
Normal file
142
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/nodeserver_test.go
generated
vendored
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
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 cinder
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/kubernetes-csi/drivers/pkg/cinder/mount"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
var fakeNs *nodeServer
|
||||
|
||||
// Init Node Server
|
||||
func init() {
|
||||
if fakeNs == nil {
|
||||
d := NewDriver(fakeNodeID, fakeEndpoint, fakeConfig)
|
||||
fakeNs = NewNodeServer(d)
|
||||
}
|
||||
}
|
||||
|
||||
// Test GetNodeID
|
||||
func TestGetNodeID(t *testing.T) {
|
||||
|
||||
// mock MountMock
|
||||
mmock := new(mount.MountMock)
|
||||
// GetInstanceID() (string, error)
|
||||
mmock.On("GetInstanceID").Return(fakeNodeID, nil)
|
||||
mount.MInstance = mmock
|
||||
|
||||
// Init assert
|
||||
assert := assert.New(t)
|
||||
|
||||
// Expected Result
|
||||
expectedRes := &csi.GetNodeIDResponse{
|
||||
NodeId: fakeNodeID,
|
||||
}
|
||||
|
||||
// Fake request
|
||||
fakeReq := &csi.GetNodeIDRequest{
|
||||
Version: &version,
|
||||
}
|
||||
|
||||
// Invoke GetNodeID
|
||||
actualRes, err := fakeNs.GetNodeID(fakeCtx, fakeReq)
|
||||
if err != nil {
|
||||
t.Errorf("failed to GetNodeID: %v", err)
|
||||
}
|
||||
|
||||
// Assert
|
||||
assert.Equal(expectedRes, actualRes)
|
||||
}
|
||||
|
||||
// Test NodePublishVolume
|
||||
func TestNodePublishVolume(t *testing.T) {
|
||||
|
||||
// mock MountMock
|
||||
mmock := new(mount.MountMock)
|
||||
// ScanForAttach(devicePath string) error
|
||||
mmock.On("ScanForAttach", fakeDevicePath).Return(nil)
|
||||
// IsLikelyNotMountPointAttach(targetpath string) (bool, error)
|
||||
mmock.On("IsLikelyNotMountPointAttach", fakeTargetPath).Return(true, nil)
|
||||
// FormatAndMount(source string, target string, fstype string, options []string) error
|
||||
mmock.On("FormatAndMount", fakeDevicePath, fakeTargetPath, mock.AnythingOfType("string"), []string{"rw"}).Return(nil)
|
||||
mount.MInstance = mmock
|
||||
|
||||
// Init assert
|
||||
assert := assert.New(t)
|
||||
|
||||
// Expected Result
|
||||
expectedRes := &csi.NodePublishVolumeResponse{}
|
||||
|
||||
// Fake request
|
||||
fakeReq := &csi.NodePublishVolumeRequest{
|
||||
Version: &version,
|
||||
VolumeId: fakeVolID,
|
||||
PublishVolumeInfo: map[string]string{"DevicePath": fakeDevicePath},
|
||||
TargetPath: fakeTargetPath,
|
||||
VolumeCapability: nil,
|
||||
Readonly: false,
|
||||
}
|
||||
|
||||
// Invoke NodePublishVolume
|
||||
actualRes, err := fakeNs.NodePublishVolume(fakeCtx, fakeReq)
|
||||
if err != nil {
|
||||
t.Errorf("failed to NodePublishVolume: %v", err)
|
||||
}
|
||||
|
||||
// Assert
|
||||
assert.Equal(expectedRes, actualRes)
|
||||
}
|
||||
|
||||
// Test NodeUnpublishVolume
|
||||
func TestNodeUnpublishVolume(t *testing.T) {
|
||||
|
||||
// mock MountMock
|
||||
mmock := new(mount.MountMock)
|
||||
|
||||
// IsLikelyNotMountPointDetach(targetpath string) (bool, error)
|
||||
mmock.On("IsLikelyNotMountPointDetach", fakeTargetPath).Return(false, nil)
|
||||
// UnmountPath(mountPath string) error
|
||||
mmock.On("UnmountPath", fakeTargetPath).Return(nil)
|
||||
mount.MInstance = mmock
|
||||
|
||||
// Init assert
|
||||
assert := assert.New(t)
|
||||
|
||||
// Expected Result
|
||||
expectedRes := &csi.NodeUnpublishVolumeResponse{}
|
||||
|
||||
// Fake request
|
||||
fakeReq := &csi.NodeUnpublishVolumeRequest{
|
||||
Version: &version,
|
||||
VolumeId: fakeVolID,
|
||||
TargetPath: fakeTargetPath,
|
||||
}
|
||||
|
||||
// Invoke NodeUnpublishVolume
|
||||
actualRes, err := fakeNs.NodeUnpublishVolume(fakeCtx, fakeReq)
|
||||
if err != nil {
|
||||
t.Errorf("failed to NodeUnpublishVolume: %v", err)
|
||||
}
|
||||
|
||||
// Assert
|
||||
assert.Equal(expectedRes, actualRes)
|
||||
}
|
163
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/openstack/openstack.go
generated
vendored
Normal file
163
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/openstack/openstack.go
generated
vendored
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
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 openstack
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/openstack"
|
||||
"gopkg.in/gcfg.v1"
|
||||
)
|
||||
|
||||
type IOpenStack interface {
|
||||
CreateVolume(name string, size int, vtype, availability string, tags *map[string]string) (string, string, error)
|
||||
DeleteVolume(volumeID string) error
|
||||
AttachVolume(instanceID, volumeID string) (string, error)
|
||||
WaitDiskAttached(instanceID string, volumeID string) error
|
||||
DetachVolume(instanceID, volumeID string) error
|
||||
WaitDiskDetached(instanceID string, volumeID string) error
|
||||
GetAttachmentDiskPath(instanceID, volumeID string) (string, error)
|
||||
}
|
||||
|
||||
type OpenStack struct {
|
||||
compute *gophercloud.ServiceClient
|
||||
blockstorage *gophercloud.ServiceClient
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Global struct {
|
||||
AuthUrl string `gcfg:"auth-url"`
|
||||
Username string
|
||||
UserId string `gcfg:"user-id"`
|
||||
Password string
|
||||
TenantId string `gcfg:"tenant-id"`
|
||||
TenantName string `gcfg:"tenant-name"`
|
||||
DomainId string `gcfg:"domain-id"`
|
||||
DomainName string `gcfg:"domain-name"`
|
||||
Region string
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg Config) toAuthOptions() gophercloud.AuthOptions {
|
||||
return gophercloud.AuthOptions{
|
||||
IdentityEndpoint: cfg.Global.AuthUrl,
|
||||
Username: cfg.Global.Username,
|
||||
UserID: cfg.Global.UserId,
|
||||
Password: cfg.Global.Password,
|
||||
TenantID: cfg.Global.TenantId,
|
||||
TenantName: cfg.Global.TenantName,
|
||||
DomainID: cfg.Global.DomainId,
|
||||
DomainName: cfg.Global.DomainName,
|
||||
|
||||
// Persistent service, so we need to be able to renew tokens.
|
||||
AllowReauth: true,
|
||||
}
|
||||
}
|
||||
|
||||
func GetConfigFromFile(configFilePath string) (gophercloud.AuthOptions, gophercloud.EndpointOpts, error) {
|
||||
// Get config from file
|
||||
var authOpts gophercloud.AuthOptions
|
||||
var epOpts gophercloud.EndpointOpts
|
||||
config, err := os.Open(configFilePath)
|
||||
if err != nil {
|
||||
glog.V(3).Infof("Failed to open OpenStack configuration file: %v", err)
|
||||
return authOpts, epOpts, err
|
||||
}
|
||||
defer config.Close()
|
||||
|
||||
// Read configuration
|
||||
var cfg Config
|
||||
err = gcfg.ReadInto(&cfg, config)
|
||||
if err != nil {
|
||||
glog.V(3).Infof("Failed to read OpenStack configuration file: %v", err)
|
||||
return authOpts, epOpts, err
|
||||
}
|
||||
|
||||
authOpts = cfg.toAuthOptions()
|
||||
epOpts = gophercloud.EndpointOpts{
|
||||
Region: cfg.Global.Region,
|
||||
}
|
||||
|
||||
return authOpts, epOpts, nil
|
||||
}
|
||||
|
||||
func GetConfigFromEnv() (gophercloud.AuthOptions, gophercloud.EndpointOpts, error) {
|
||||
// Get config from env
|
||||
authOpts, err := openstack.AuthOptionsFromEnv()
|
||||
var epOpts gophercloud.EndpointOpts
|
||||
if err != nil {
|
||||
glog.V(3).Infof("Failed to read OpenStack configuration from env: %v", err)
|
||||
return authOpts, epOpts, err
|
||||
}
|
||||
|
||||
epOpts = gophercloud.EndpointOpts{
|
||||
Region: os.Getenv("OS_REGION_NAME"),
|
||||
}
|
||||
|
||||
return authOpts, epOpts, nil
|
||||
}
|
||||
|
||||
var OsInstance IOpenStack = nil
|
||||
var configFile string = "/etc/cloud.conf"
|
||||
|
||||
func InitOpenStackProvider(cfg string) {
|
||||
configFile = cfg
|
||||
glog.V(2).Infof("InitOpenStackProvider configFile: %s", configFile)
|
||||
}
|
||||
|
||||
func GetOpenStackProvider() (IOpenStack, error) {
|
||||
|
||||
if OsInstance == nil {
|
||||
// Get config from file
|
||||
authOpts, epOpts, err := GetConfigFromFile(configFile)
|
||||
if err != nil {
|
||||
// Get config from env
|
||||
authOpts, epOpts, err = GetConfigFromEnv()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Authenticate Client
|
||||
provider, err := openstack.AuthenticatedClient(authOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Init Nova ServiceClient
|
||||
computeclient, err := openstack.NewComputeV2(provider, epOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Init Cinder ServiceClient
|
||||
blockstorageclient, err := openstack.NewBlockStorageV3(provider, epOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Init OpenStack
|
||||
OsInstance = &OpenStack{
|
||||
compute: computeclient,
|
||||
blockstorage: blockstorageclient,
|
||||
}
|
||||
}
|
||||
|
||||
return OsInstance, nil
|
||||
}
|
151
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/openstack/openstack_mock.go
generated
vendored
Normal file
151
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/openstack/openstack_mock.go
generated
vendored
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
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 openstack
|
||||
|
||||
import "github.com/stretchr/testify/mock"
|
||||
|
||||
// OpenStackMock is an autogenerated mock type for the IOpenStack type
|
||||
// ORIGINALLY GENERATED BY mockery with hand edits
|
||||
type OpenStackMock struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// AttachVolume provides a mock function with given fields: instanceID, volumeID
|
||||
func (_m *OpenStackMock) AttachVolume(instanceID string, volumeID string) (string, error) {
|
||||
ret := _m.Called(instanceID, volumeID)
|
||||
|
||||
var r0 string
|
||||
if rf, ok := ret.Get(0).(func(string, string) string); ok {
|
||||
r0 = rf(instanceID, volumeID)
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string, string) error); ok {
|
||||
r1 = rf(instanceID, volumeID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CreateVolume provides a mock function with given fields: name, size, vtype, availability, tags
|
||||
func (_m *OpenStackMock) CreateVolume(name string, size int, vtype string, availability string, tags *map[string]string) (string, string, error) {
|
||||
ret := _m.Called(name, size, vtype, availability, tags)
|
||||
|
||||
var r0 string
|
||||
if rf, ok := ret.Get(0).(func(string, int, string, string, *map[string]string) string); ok {
|
||||
r0 = rf(name, size, vtype, availability, tags)
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
var r1 string
|
||||
if rf, ok := ret.Get(1).(func(string, int, string, string, *map[string]string) string); ok {
|
||||
r1 = rf(name, size, vtype, availability, tags)
|
||||
} else {
|
||||
r1 = ret.Get(1).(string)
|
||||
}
|
||||
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(2).(func(string, int, string, string, *map[string]string) error); ok {
|
||||
r2 = rf(name, size, vtype, availability, tags)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// DeleteVolume provides a mock function with given fields: volumeID
|
||||
func (_m *OpenStackMock) DeleteVolume(volumeID string) error {
|
||||
ret := _m.Called(volumeID)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string) error); ok {
|
||||
r0 = rf(volumeID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// DetachVolume provides a mock function with given fields: instanceID, volumeID
|
||||
func (_m *OpenStackMock) DetachVolume(instanceID string, volumeID string) error {
|
||||
ret := _m.Called(instanceID, volumeID)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, string) error); ok {
|
||||
r0 = rf(instanceID, volumeID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// GetAttachmentDiskPath provides a mock function with given fields: instanceID, volumeID
|
||||
func (_m *OpenStackMock) GetAttachmentDiskPath(instanceID string, volumeID string) (string, error) {
|
||||
ret := _m.Called(instanceID, volumeID)
|
||||
|
||||
var r0 string
|
||||
if rf, ok := ret.Get(0).(func(string, string) string); ok {
|
||||
r0 = rf(instanceID, volumeID)
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string, string) error); ok {
|
||||
r1 = rf(instanceID, volumeID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// WaitDiskAttached provides a mock function with given fields: instanceID, volumeID
|
||||
func (_m *OpenStackMock) WaitDiskAttached(instanceID string, volumeID string) error {
|
||||
ret := _m.Called(instanceID, volumeID)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, string) error); ok {
|
||||
r0 = rf(instanceID, volumeID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// WaitDiskDetached provides a mock function with given fields: instanceID, volumeID
|
||||
func (_m *OpenStackMock) WaitDiskDetached(instanceID string, volumeID string) error {
|
||||
ret := _m.Called(instanceID, volumeID)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, string) error); ok {
|
||||
r0 = rf(instanceID, volumeID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
119
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/openstack/openstack_test.go
generated
vendored
Normal file
119
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/openstack/openstack_test.go
generated
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
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 openstack
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var fakeFileName = "cloud.conf"
|
||||
var fakeUserName = "user"
|
||||
var fakePassword = "pass"
|
||||
var fakeAuthUrl = "https://169.254.169.254/identity/v3"
|
||||
var fakeTenantID = "c869168a828847f39f7f06edd7305637"
|
||||
var fakeDomainID = "2a73b8f597c04551a0fdc8e95544be8a"
|
||||
var fakeRegion = "RegionOne"
|
||||
|
||||
// Test GetConfigFromFile
|
||||
func TestGetConfigFromFile(t *testing.T) {
|
||||
// init file
|
||||
var fakeFileContent = `
|
||||
[Global]
|
||||
username=` + fakeUserName + `
|
||||
password=` + fakePassword + `
|
||||
auth-url=` + fakeAuthUrl + `
|
||||
tenant-id=` + fakeTenantID + `
|
||||
domain-id=` + fakeDomainID + `
|
||||
region=` + fakeRegion + `
|
||||
`
|
||||
|
||||
f, err := os.Create(fakeFileName)
|
||||
if err != nil {
|
||||
t.Errorf("failed to create file: %v", err)
|
||||
}
|
||||
|
||||
_, err = f.WriteString(fakeFileContent)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
t.Errorf("failed to write file: %v", err)
|
||||
}
|
||||
defer os.Remove(fakeFileName)
|
||||
|
||||
// Init assert
|
||||
assert := assert.New(t)
|
||||
|
||||
expectedAuthOpts := gophercloud.AuthOptions{
|
||||
IdentityEndpoint: fakeAuthUrl,
|
||||
Username: fakeUserName,
|
||||
Password: fakePassword,
|
||||
TenantID: fakeTenantID,
|
||||
DomainID: fakeDomainID,
|
||||
AllowReauth: true,
|
||||
}
|
||||
expectedEpOpts := gophercloud.EndpointOpts{
|
||||
Region: fakeRegion,
|
||||
}
|
||||
|
||||
// Invoke GetConfigFromFile
|
||||
actualAuthOpts, actualEpOpts, err := GetConfigFromFile(fakeFileName)
|
||||
if err != nil {
|
||||
t.Errorf("failed to GetConfigFromFile: %v", err)
|
||||
}
|
||||
|
||||
// Assert
|
||||
assert.Equal(expectedAuthOpts, actualAuthOpts)
|
||||
assert.Equal(expectedEpOpts, actualEpOpts)
|
||||
}
|
||||
|
||||
// Test GetConfigFromEnv
|
||||
func TestGetConfigFromEnv(t *testing.T) {
|
||||
// init env
|
||||
os.Setenv("OS_AUTH_URL", fakeAuthUrl)
|
||||
os.Setenv("OS_USERNAME", fakeUserName)
|
||||
os.Setenv("OS_PASSWORD", fakePassword)
|
||||
os.Setenv("OS_TENANT_ID", fakeTenantID)
|
||||
os.Setenv("OS_DOMAIN_ID", fakeDomainID)
|
||||
os.Setenv("OS_REGION_NAME", fakeRegion)
|
||||
|
||||
// Init assert
|
||||
assert := assert.New(t)
|
||||
|
||||
expectedAuthOpts := gophercloud.AuthOptions{
|
||||
IdentityEndpoint: fakeAuthUrl,
|
||||
Username: fakeUserName,
|
||||
Password: fakePassword,
|
||||
TenantID: fakeTenantID,
|
||||
DomainID: fakeDomainID,
|
||||
}
|
||||
expectedEpOpts := gophercloud.EndpointOpts{
|
||||
Region: fakeRegion,
|
||||
}
|
||||
|
||||
// Invoke GetConfigFromEnv
|
||||
actualAuthOpts, actualEpOpts, err := GetConfigFromEnv()
|
||||
if err != nil {
|
||||
t.Errorf("failed to GetConfigFromEnv: %v", err)
|
||||
}
|
||||
|
||||
// Assert
|
||||
assert.Equal(expectedAuthOpts, actualAuthOpts)
|
||||
assert.Equal(expectedEpOpts, actualEpOpts)
|
||||
}
|
253
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/openstack/openstack_volumes.go
generated
vendored
Normal file
253
vendor/github.com/kubernetes-csi/drivers/pkg/cinder/openstack/openstack_volumes.go
generated
vendored
Normal file
@ -0,0 +1,253 @@
|
||||
/*
|
||||
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 openstack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes"
|
||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
const (
|
||||
VolumeAvailableStatus = "available"
|
||||
VolumeInUseStatus = "in-use"
|
||||
VolumeDeletedStatus = "deleted"
|
||||
VolumeErrorStatus = "error"
|
||||
operationFinishInitDelay = 1 * time.Second
|
||||
operationFinishFactor = 1.1
|
||||
operationFinishSteps = 10
|
||||
diskAttachInitDelay = 1 * time.Second
|
||||
diskAttachFactor = 1.2
|
||||
diskAttachSteps = 15
|
||||
diskDetachInitDelay = 1 * time.Second
|
||||
diskDetachFactor = 1.2
|
||||
diskDetachSteps = 13
|
||||
)
|
||||
|
||||
type Volume struct {
|
||||
// ID of the instance, to which this volume is attached. "" if not attached
|
||||
AttachedServerId string
|
||||
// Device file path
|
||||
AttachedDevice string
|
||||
// Unique identifier for the volume.
|
||||
ID string
|
||||
// Human-readable display name for the volume.
|
||||
Name string
|
||||
// Current status of the volume.
|
||||
Status string
|
||||
// Volume size in GB
|
||||
Size int
|
||||
}
|
||||
|
||||
// CreateVolume creates a volume of given size
|
||||
func (os *OpenStack) CreateVolume(name string, size int, vtype, availability string, tags *map[string]string) (string, string, error) {
|
||||
opts := &volumes.CreateOpts{
|
||||
Name: name,
|
||||
Size: size,
|
||||
VolumeType: vtype,
|
||||
AvailabilityZone: availability,
|
||||
}
|
||||
if tags != nil {
|
||||
opts.Metadata = *tags
|
||||
}
|
||||
|
||||
vol, err := volumes.Create(os.blockstorage, opts).Extract()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return vol.ID, vol.AvailabilityZone, nil
|
||||
}
|
||||
|
||||
// DeleteVolume delete a volume
|
||||
func (os *OpenStack) DeleteVolume(volumeID string) error {
|
||||
used, err := os.diskIsUsed(volumeID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if used {
|
||||
return fmt.Errorf("Cannot delete the volume %q, it's still attached to a node", volumeID)
|
||||
}
|
||||
|
||||
err = volumes.Delete(os.blockstorage, volumeID).ExtractErr()
|
||||
return err
|
||||
}
|
||||
|
||||
// GetVolume retrieves Volume by its ID.
|
||||
func (os *OpenStack) GetVolume(volumeID string) (Volume, error) {
|
||||
|
||||
vol, err := volumes.Get(os.blockstorage, volumeID).Extract()
|
||||
if err != nil {
|
||||
return Volume{}, err
|
||||
}
|
||||
|
||||
volume := Volume{
|
||||
ID: vol.ID,
|
||||
Name: vol.Name,
|
||||
Status: vol.Status,
|
||||
}
|
||||
|
||||
if len(vol.Attachments) > 0 {
|
||||
volume.AttachedServerId = vol.Attachments[0].ServerID
|
||||
volume.AttachedDevice = vol.Attachments[0].Device
|
||||
}
|
||||
|
||||
return volume, nil
|
||||
}
|
||||
|
||||
// AttachVolume attaches given cinder volume to the compute
|
||||
func (os *OpenStack) AttachVolume(instanceID, volumeID string) (string, error) {
|
||||
volume, err := os.GetVolume(volumeID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if volume.AttachedServerId != "" {
|
||||
if instanceID == volume.AttachedServerId {
|
||||
glog.V(4).Infof("Disk %s is already attached to instance %s", volumeID, instanceID)
|
||||
return volume.ID, nil
|
||||
}
|
||||
return "", fmt.Errorf("disk %s is attached to a different instance (%s)", volumeID, volume.AttachedServerId)
|
||||
}
|
||||
|
||||
_, err = volumeattach.Create(os.compute, instanceID, &volumeattach.CreateOpts{
|
||||
VolumeID: volume.ID,
|
||||
}).Extract()
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to attach %s volume to %s compute: %v", volumeID, instanceID, err)
|
||||
}
|
||||
glog.V(2).Infof("Successfully attached %s volume to %s compute", volumeID, instanceID)
|
||||
return volume.ID, nil
|
||||
}
|
||||
|
||||
// WaitDiskAttached waits for attched
|
||||
func (os *OpenStack) WaitDiskAttached(instanceID string, volumeID string) error {
|
||||
backoff := wait.Backoff{
|
||||
Duration: diskAttachInitDelay,
|
||||
Factor: diskAttachFactor,
|
||||
Steps: diskAttachSteps,
|
||||
}
|
||||
|
||||
err := wait.ExponentialBackoff(backoff, func() (bool, error) {
|
||||
attached, err := os.diskIsAttached(instanceID, volumeID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return attached, nil
|
||||
})
|
||||
|
||||
if err == wait.ErrWaitTimeout {
|
||||
err = fmt.Errorf("Volume %q failed to be attached within the alloted time", volumeID)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// DetachVolume detaches given cinder volume from the compute
|
||||
func (os *OpenStack) DetachVolume(instanceID, volumeID string) error {
|
||||
volume, err := os.GetVolume(volumeID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if volume.Status == VolumeAvailableStatus {
|
||||
glog.V(2).Infof("volume: %s has been detached from compute: %s ", volume.ID, instanceID)
|
||||
return nil
|
||||
}
|
||||
|
||||
if volume.Status != VolumeInUseStatus {
|
||||
return fmt.Errorf("can not detach volume %s, its status is %s", volume.Name, volume.Status)
|
||||
}
|
||||
|
||||
if volume.AttachedServerId != instanceID {
|
||||
return fmt.Errorf("disk: %s has no attachments or is not attached to compute: %s", volume.Name, instanceID)
|
||||
} else {
|
||||
err = volumeattach.Delete(os.compute, instanceID, volume.ID).ExtractErr()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete volume %s from compute %s attached %v", volume.ID, instanceID, err)
|
||||
}
|
||||
glog.V(2).Infof("Successfully detached volume: %s from compute: %s", volume.ID, instanceID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WaitDiskDetached waits for detached
|
||||
func (os *OpenStack) WaitDiskDetached(instanceID string, volumeID string) error {
|
||||
backoff := wait.Backoff{
|
||||
Duration: diskDetachInitDelay,
|
||||
Factor: diskDetachFactor,
|
||||
Steps: diskDetachSteps,
|
||||
}
|
||||
|
||||
err := wait.ExponentialBackoff(backoff, func() (bool, error) {
|
||||
attached, err := os.diskIsAttached(instanceID, volumeID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return !attached, nil
|
||||
})
|
||||
|
||||
if err == wait.ErrWaitTimeout {
|
||||
err = fmt.Errorf("Volume %q failed to detach within the alloted time", volumeID)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// GetAttachmentDiskPath gets device path of attached volume to the compute
|
||||
func (os *OpenStack) GetAttachmentDiskPath(instanceID, volumeID string) (string, error) {
|
||||
volume, err := os.GetVolume(volumeID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if volume.Status != VolumeInUseStatus {
|
||||
return "", fmt.Errorf("can not get device path of volume %s, its status is %s ", volume.Name, volume.Status)
|
||||
}
|
||||
if volume.AttachedServerId != "" {
|
||||
if instanceID == volume.AttachedServerId {
|
||||
return volume.AttachedDevice, nil
|
||||
} else {
|
||||
return "", fmt.Errorf("disk %q is attached to a different compute: %q, should be detached before proceeding", volumeID, volume.AttachedServerId)
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("volume %s has no ServerId", volumeID)
|
||||
}
|
||||
|
||||
// diskIsAttached queries if a volume is attached to a compute instance
|
||||
func (os *OpenStack) diskIsAttached(instanceID, volumeID string) (bool, error) {
|
||||
volume, err := os.GetVolume(volumeID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return instanceID == volume.AttachedServerId, nil
|
||||
}
|
||||
|
||||
// diskIsUsed returns true a disk is attached to any node.
|
||||
func (os *OpenStack) diskIsUsed(volumeID string) (bool, error) {
|
||||
volume, err := os.GetVolume(volumeID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return volume.AttachedServerId != "", nil
|
||||
}
|
96
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/controllerserver-default.go
generated
vendored
Normal file
96
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/controllerserver-default.go
generated
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
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 (
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type DefaultControllerServer struct {
|
||||
Driver *CSIDriver
|
||||
}
|
||||
|
||||
func (cs *DefaultControllerServer) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
}
|
||||
|
||||
func (cs *DefaultControllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest) (*csi.DeleteVolumeResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
}
|
||||
|
||||
func (cs *DefaultControllerServer) ControllerPublishVolume(ctx context.Context, req *csi.ControllerPublishVolumeRequest) (*csi.ControllerPublishVolumeResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
}
|
||||
|
||||
func (cs *DefaultControllerServer) ControllerUnpublishVolume(ctx context.Context, req *csi.ControllerUnpublishVolumeRequest) (*csi.ControllerUnpublishVolumeResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
}
|
||||
|
||||
func (cs *DefaultControllerServer) ValidateVolumeCapabilities(ctx context.Context, req *csi.ValidateVolumeCapabilitiesRequest) (*csi.ValidateVolumeCapabilitiesResponse, error) {
|
||||
glog.V(5).Infof("Using default ValidateVolumeCapabilities")
|
||||
|
||||
for _, c := range req.GetVolumeCapabilities() {
|
||||
found := false
|
||||
for _, c1 := range cs.Driver.vc {
|
||||
if c1.GetMode() == c.GetAccessMode().GetMode() {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return &csi.ValidateVolumeCapabilitiesResponse{
|
||||
Supported: false,
|
||||
Message: "Driver doesnot support mode:" + c.GetAccessMode().GetMode().String(),
|
||||
}, status.Error(codes.InvalidArgument, "Driver doesnot support mode:"+c.GetAccessMode().GetMode().String())
|
||||
}
|
||||
// TODO: Ignoring mount & block tyeps for now.
|
||||
}
|
||||
|
||||
return &csi.ValidateVolumeCapabilitiesResponse{
|
||||
Supported: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (cs *DefaultControllerServer) ListVolumes(ctx context.Context, req *csi.ListVolumesRequest) (*csi.ListVolumesResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
}
|
||||
|
||||
func (cs *DefaultControllerServer) GetCapacity(ctx context.Context, req *csi.GetCapacityRequest) (*csi.GetCapacityResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
}
|
||||
|
||||
func (cs *DefaultControllerServer) ControllerProbe(ctx context.Context, req *csi.ControllerProbeRequest) (*csi.ControllerProbeResponse, error) {
|
||||
glog.V(5).Infof("Using default ControllerProbe")
|
||||
|
||||
if err := cs.Driver.ValidateControllerServiceRequest(req.Version, csi.ControllerServiceCapability_RPC_UNKNOWN); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &csi.ControllerProbeResponse{}, nil
|
||||
}
|
||||
|
||||
// ControllerGetCapabilities implements the default GRPC callout.
|
||||
// Default supports all capabilities
|
||||
func (cs *DefaultControllerServer) ControllerGetCapabilities(ctx context.Context, req *csi.ControllerGetCapabilitiesRequest) (*csi.ControllerGetCapabilitiesResponse, error) {
|
||||
glog.V(5).Infof("Using default ControllerGetCapabilities")
|
||||
|
||||
return &csi.ControllerGetCapabilitiesResponse{
|
||||
Capabilities: cs.Driver.cap,
|
||||
}, nil
|
||||
}
|
137
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/driver.go
generated
vendored
Normal file
137
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/driver.go
generated
vendored
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
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 (
|
||||
"github.com/golang/glog"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
)
|
||||
|
||||
type CSIDriver struct {
|
||||
name string
|
||||
nodeID string
|
||||
version *csi.Version
|
||||
supVers []*csi.Version
|
||||
cap []*csi.ControllerServiceCapability
|
||||
vc []*csi.VolumeCapability_AccessMode
|
||||
}
|
||||
|
||||
// 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 string, v *csi.Version, supVers []*csi.Version, nodeID string) *CSIDriver {
|
||||
if name == "" {
|
||||
glog.Errorf("Driver name missing")
|
||||
return nil
|
||||
}
|
||||
|
||||
if nodeID == "" {
|
||||
glog.Errorf("NodeID missing")
|
||||
return nil
|
||||
}
|
||||
|
||||
if v == nil {
|
||||
glog.Errorf("Version argument missing")
|
||||
return nil
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, sv := range supVers {
|
||||
if sv.GetMajor() == v.GetMajor() && sv.GetMinor() == v.GetMinor() && sv.GetPatch() == v.GetPatch() {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
supVers = append(supVers, v)
|
||||
}
|
||||
|
||||
driver := CSIDriver{
|
||||
name: name,
|
||||
version: v,
|
||||
supVers: supVers,
|
||||
nodeID: nodeID,
|
||||
}
|
||||
|
||||
return &driver
|
||||
}
|
||||
|
||||
func (d *CSIDriver) CheckVersion(v *csi.Version) error {
|
||||
if v == nil {
|
||||
return status.Error(codes.InvalidArgument, "Version missing")
|
||||
}
|
||||
|
||||
// Assumes always backward compatible
|
||||
for _, sv := range d.supVers {
|
||||
if v.Major == sv.Major && v.Minor <= sv.Minor {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return status.Error(codes.InvalidArgument, "Unsupported version: "+GetVersionString(v))
|
||||
}
|
||||
|
||||
func (d *CSIDriver) ValidateControllerServiceRequest(v *csi.Version, c csi.ControllerServiceCapability_RPC_Type) error {
|
||||
if v == nil {
|
||||
return status.Error(codes.InvalidArgument, "Version not specified")
|
||||
}
|
||||
|
||||
if err := d.CheckVersion(v); err != nil {
|
||||
return status.Error(codes.InvalidArgument, "Unsupported version")
|
||||
}
|
||||
|
||||
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, "Unsupported version: "+GetVersionString(v))
|
||||
}
|
||||
|
||||
func (d *CSIDriver) AddControllerServiceCapabilities(cl []csi.ControllerServiceCapability_RPC_Type) {
|
||||
var csc []*csi.ControllerServiceCapability
|
||||
|
||||
for _, c := range cl {
|
||||
glog.Infof("Enabling controller service capability: %v", c.String())
|
||||
csc = append(csc, NewControllerServiceCapability(c))
|
||||
}
|
||||
|
||||
d.cap = csc
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (d *CSIDriver) AddVolumeCapabilityAccessModes(vc []csi.VolumeCapability_AccessMode_Mode) []*csi.VolumeCapability_AccessMode {
|
||||
var vca []*csi.VolumeCapability_AccessMode
|
||||
for _, c := range vc {
|
||||
glog.Infof("Enabling volume access mode: %v", c.String())
|
||||
vca = append(vca, NewVolumeCapabilityAccessMode(c))
|
||||
}
|
||||
d.vc = vca
|
||||
return vca
|
||||
}
|
||||
|
||||
func (d *CSIDriver) GetVolumeCapabilityAccessModes() []*csi.VolumeCapability_AccessMode {
|
||||
return d.vc
|
||||
}
|
181
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/driver_test.go
generated
vendored
Normal file
181
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/driver_test.go
generated
vendored
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
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 (
|
||||
"testing"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
const (
|
||||
fakeDriverName = "fake"
|
||||
fakeNodeID = "fakeNodeID"
|
||||
)
|
||||
|
||||
var (
|
||||
fakeVersion = csi.Version{Major: 5, Minor: 2, Patch: 0}
|
||||
fakeVersionsSupported = []*csi.Version{
|
||||
&csi.Version{
|
||||
Major: 4, Minor: 0, Patch: 0,
|
||||
},
|
||||
&csi.Version{
|
||||
Major: 4, Minor: 1, Patch: 0,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func NewFakeDriver() *CSIDriver {
|
||||
fakeVersion = csi.Version{Major: 5, Minor: 2, Patch: 0}
|
||||
fakeVersionsSupported = []*csi.Version{
|
||||
&csi.Version{
|
||||
Major: 4, Minor: 0, Patch: 0,
|
||||
},
|
||||
&csi.Version{
|
||||
Major: 4, Minor: 1, Patch: 0,
|
||||
},
|
||||
}
|
||||
|
||||
driver := NewCSIDriver(fakeDriverName, &fakeVersion, fakeVersionsSupported, fakeNodeID)
|
||||
|
||||
return driver
|
||||
}
|
||||
|
||||
func TestNewFakeDriver(t *testing.T) {
|
||||
// Test New fake driver with invalid arguments.
|
||||
d := NewCSIDriver("", &fakeVersion, fakeVersionsSupported, fakeNodeID)
|
||||
assert.Nil(t, d)
|
||||
}
|
||||
|
||||
func TestCheckVersion(t *testing.T) {
|
||||
|
||||
driver := NewFakeDriver()
|
||||
|
||||
// Exact version
|
||||
v := csi.Version{
|
||||
Major: 5,
|
||||
Minor: 1,
|
||||
Patch: 0,
|
||||
}
|
||||
err := driver.CheckVersion(&v)
|
||||
assert.NoError(t, err)
|
||||
|
||||
//Supported version
|
||||
v = csi.Version{
|
||||
Major: 4,
|
||||
Minor: 0,
|
||||
Patch: 0,
|
||||
}
|
||||
err = driver.CheckVersion(&v)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Unsupported version
|
||||
v = csi.Version{
|
||||
Major: 6,
|
||||
Minor: 0,
|
||||
Patch: 0,
|
||||
}
|
||||
err = driver.CheckVersion(&v)
|
||||
s, ok := status.FromError(err)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, s.Code(), codes.InvalidArgument)
|
||||
|
||||
// Supported minor version
|
||||
v = csi.Version{
|
||||
Major: 5,
|
||||
Minor: 1,
|
||||
Patch: 0,
|
||||
}
|
||||
err = driver.CheckVersion(&v)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Unsupported minor version
|
||||
v = csi.Version{
|
||||
Major: 5,
|
||||
Minor: 3,
|
||||
Patch: 0,
|
||||
}
|
||||
err = driver.CheckVersion(&v)
|
||||
s, ok = status.FromError(err)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, s.Code(), codes.InvalidArgument)
|
||||
}
|
||||
|
||||
func TestGetVolumeCapabilityAccessModes(t *testing.T) {
|
||||
|
||||
d := NewFakeDriver()
|
||||
|
||||
// Test no volume access modes.
|
||||
// REVISIT: Do we need to support any default access modes.
|
||||
c := d.GetVolumeCapabilityAccessModes()
|
||||
assert.Zero(t, len(c))
|
||||
|
||||
// Test driver with access modes.
|
||||
d.AddVolumeCapabilityAccessModes([]csi.VolumeCapability_AccessMode_Mode{csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER})
|
||||
modes := d.GetVolumeCapabilityAccessModes()
|
||||
assert.Equal(t, 1, len(modes))
|
||||
assert.Equal(t, modes[0].GetMode(), csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER)
|
||||
}
|
||||
|
||||
func TestValidateControllerServiceRequest(t *testing.T) {
|
||||
d := NewFakeDriver()
|
||||
|
||||
v := csi.Version{
|
||||
Major: 5,
|
||||
Minor: 0,
|
||||
Patch: 0,
|
||||
}
|
||||
|
||||
// Valid requests which require no capabilities
|
||||
err := d.ValidateControllerServiceRequest(&v, csi.ControllerServiceCapability_RPC_UNKNOWN)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test controller service publish/unpublish not supported
|
||||
err = d.ValidateControllerServiceRequest(&v, csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME)
|
||||
s, ok := status.FromError(err)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, s.Code(), codes.InvalidArgument)
|
||||
|
||||
// Add controller service publish & unpublish request
|
||||
d.AddControllerServiceCapabilities(
|
||||
[]csi.ControllerServiceCapability_RPC_Type{
|
||||
csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME,
|
||||
csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME,
|
||||
csi.ControllerServiceCapability_RPC_GET_CAPACITY,
|
||||
csi.ControllerServiceCapability_RPC_LIST_VOLUMES,
|
||||
})
|
||||
|
||||
// Test controller service publish/unpublish is supported
|
||||
err = d.ValidateControllerServiceRequest(&v, csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test controller service create/delete is supported
|
||||
err = d.ValidateControllerServiceRequest(&v, csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test controller service list volumes is supported
|
||||
err = d.ValidateControllerServiceRequest(&v, csi.ControllerServiceCapability_RPC_LIST_VOLUMES)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test controller service get capacity is supported
|
||||
err = d.ValidateControllerServiceRequest(&v, csi.ControllerServiceCapability_RPC_GET_CAPACITY)
|
||||
assert.NoError(t, err)
|
||||
|
||||
}
|
58
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/identityserver-default.go
generated
vendored
Normal file
58
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/identityserver-default.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
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 (
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type DefaultIdentityServer struct {
|
||||
Driver *CSIDriver
|
||||
}
|
||||
|
||||
//GetSupportedVersions(context.Context, *GetSupportedVersionsRequest) (*GetSupportedVersionsResponse, error)
|
||||
//GetPluginInfo(context.Context, *GetPluginInfoRequest) (*GetPluginInfoResponse, error)
|
||||
func (ids *DefaultIdentityServer) GetSupportedVersions(ctx context.Context, req *csi.GetSupportedVersionsRequest) (*csi.GetSupportedVersionsResponse, error) {
|
||||
glog.V(5).Infof("Using default GetSupportedVersions")
|
||||
return &csi.GetSupportedVersionsResponse{
|
||||
SupportedVersions: ids.Driver.supVers,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ids *DefaultIdentityServer) GetPluginInfo(ctx context.Context, req *csi.GetPluginInfoRequest) (*csi.GetPluginInfoResponse, error) {
|
||||
glog.V(5).Infof("Using default GetPluginInnfo")
|
||||
|
||||
if ids.Driver.name == "" {
|
||||
return nil, status.Error(codes.Unavailable, "Driver name not configured")
|
||||
}
|
||||
|
||||
err := ids.Driver.CheckVersion(req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
version := GetVersionString(ids.Driver.version)
|
||||
|
||||
return &csi.GetPluginInfoResponse{
|
||||
Name: ids.Driver.name,
|
||||
VendorVersion: version,
|
||||
}, nil
|
||||
}
|
68
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/identityserver-default_test.go
generated
vendored
Normal file
68
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/identityserver-default_test.go
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
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"
|
||||
"testing"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func TestGetSupportedVersions(t *testing.T) {
|
||||
d := NewFakeDriver()
|
||||
|
||||
ids := NewDefaultIdentityServer(d)
|
||||
|
||||
req := csi.GetSupportedVersionsRequest{}
|
||||
|
||||
// Test Get supported versions are valid.
|
||||
resp, err := ids.GetSupportedVersions(context.Background(), &req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
for _, fv := range fakeVersionsSupported {
|
||||
found := false
|
||||
for _, rv := range resp.GetSupportedVersions() {
|
||||
if fv.GetMajor() == rv.GetMajor() && fv.GetMinor() == rv.GetMinor() && fv.GetPatch() == rv.GetPatch() {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
assert.True(t, found)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPluginInfo(t *testing.T) {
|
||||
d := NewFakeDriver()
|
||||
|
||||
ids := NewDefaultIdentityServer(d)
|
||||
|
||||
// Test invalid request
|
||||
req := csi.GetPluginInfoRequest{}
|
||||
resp, err := ids.GetPluginInfo(context.Background(), &req)
|
||||
s, ok := status.FromError(err)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, s.Code(), codes.InvalidArgument)
|
||||
|
||||
// Test valid request
|
||||
req.Version = &fakeVersion
|
||||
resp, err = ids.GetPluginInfo(context.Background(), &req)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, resp.GetName(), fakeDriverName)
|
||||
}
|
80
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/nodeserver-default.go
generated
vendored
Normal file
80
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/nodeserver-default.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
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 (
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type DefaultNodeServer struct {
|
||||
Driver *CSIDriver
|
||||
}
|
||||
|
||||
func (ns *DefaultNodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) {
|
||||
err := ns.Driver.CheckVersion(req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
}
|
||||
|
||||
func (ns *DefaultNodeServer) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpublishVolumeRequest) (*csi.NodeUnpublishVolumeResponse, error) {
|
||||
err := ns.Driver.CheckVersion(req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
}
|
||||
|
||||
func (ns *DefaultNodeServer) GetNodeID(ctx context.Context, req *csi.GetNodeIDRequest) (*csi.GetNodeIDResponse, error) {
|
||||
glog.V(5).Infof("Using default GetNodeID")
|
||||
|
||||
err := ns.Driver.CheckVersion(req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &csi.GetNodeIDResponse{
|
||||
NodeId: ns.Driver.nodeID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ns *DefaultNodeServer) NodeProbe(ctx context.Context, req *csi.NodeProbeRequest) (*csi.NodeProbeResponse, error) {
|
||||
glog.V(5).Infof("Using default NodeProbe")
|
||||
|
||||
err := ns.Driver.CheckVersion(req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &csi.NodeProbeResponse{}, nil
|
||||
}
|
||||
|
||||
func (ns *DefaultNodeServer) NodeGetCapabilities(ctx context.Context, req *csi.NodeGetCapabilitiesRequest) (*csi.NodeGetCapabilitiesResponse, error) {
|
||||
glog.V(5).Infof("Using default NodeGetCapabilities")
|
||||
|
||||
err := ns.Driver.CheckVersion(req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &csi.NodeGetCapabilitiesResponse{}, nil
|
||||
}
|
122
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/nodeserver-default_test.go
generated
vendored
Normal file
122
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/nodeserver-default_test.go
generated
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
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"
|
||||
"testing"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func TestGetNodeID(t *testing.T) {
|
||||
d := NewFakeDriver()
|
||||
|
||||
ns := NewDefaultNodeServer(d)
|
||||
|
||||
// Test invalid request
|
||||
req := csi.GetNodeIDRequest{}
|
||||
_, err := ns.GetNodeID(context.Background(), &req)
|
||||
s, ok := status.FromError(err)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, s.Code(), codes.InvalidArgument)
|
||||
|
||||
// Test valid request
|
||||
req.Version = &fakeVersion
|
||||
resp, err := ns.GetNodeID(context.Background(), &req)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, resp.GetNodeId(), fakeNodeID)
|
||||
}
|
||||
|
||||
func TestNodeGetCapabilities(t *testing.T) {
|
||||
d := NewFakeDriver()
|
||||
|
||||
ns := NewDefaultNodeServer(d)
|
||||
|
||||
// Test invalid request
|
||||
req := csi.NodeGetCapabilitiesRequest{}
|
||||
_, err := ns.NodeGetCapabilities(context.Background(), &req)
|
||||
s, ok := status.FromError(err)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, s.Code(), codes.InvalidArgument)
|
||||
|
||||
// Test valid request
|
||||
req.Version = &fakeVersion
|
||||
_, err = ns.NodeGetCapabilities(context.Background(), &req)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestNodeProbe(t *testing.T) {
|
||||
d := NewFakeDriver()
|
||||
|
||||
ns := NewDefaultNodeServer(d)
|
||||
|
||||
// Test invalid request
|
||||
req := csi.NodeProbeRequest{}
|
||||
_, err := ns.NodeProbe(context.Background(), &req)
|
||||
s, ok := status.FromError(err)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, s.Code(), codes.InvalidArgument)
|
||||
|
||||
// Test valid request
|
||||
req.Version = &fakeVersion
|
||||
_, err = ns.NodeProbe(context.Background(), &req)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestNodePublishVolume(t *testing.T) {
|
||||
d := NewFakeDriver()
|
||||
|
||||
ns := NewDefaultNodeServer(d)
|
||||
|
||||
// Test invalid request
|
||||
req := csi.NodePublishVolumeRequest{}
|
||||
_, err := ns.NodePublishVolume(context.Background(), &req)
|
||||
s, ok := status.FromError(err)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, s.Code(), codes.InvalidArgument)
|
||||
|
||||
// Test valid node publish request
|
||||
req.Version = &fakeVersion
|
||||
_, err = ns.NodePublishVolume(context.Background(), &req)
|
||||
s, ok = status.FromError(err)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, s.Code(), codes.Unimplemented)
|
||||
}
|
||||
|
||||
func TestNodeUnpublishVolume(t *testing.T) {
|
||||
d := NewFakeDriver()
|
||||
|
||||
ns := NewDefaultNodeServer(d)
|
||||
|
||||
// Test invalid request
|
||||
req := csi.NodeUnpublishVolumeRequest{}
|
||||
_, err := ns.NodeUnpublishVolume(context.Background(), &req)
|
||||
s, ok := status.FromError(err)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, s.Code(), codes.InvalidArgument)
|
||||
|
||||
// Test valid node publish request
|
||||
req.Version = &fakeVersion
|
||||
_, err = ns.NodeUnpublishVolume(context.Background(), &req)
|
||||
s, ok = status.FromError(err)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, s.Code(), codes.Unimplemented)
|
||||
}
|
112
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/server.go
generated
vendored
Normal file
112
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/server.go
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
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"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
)
|
||||
|
||||
// Defines Non blocking GRPC server interfaces
|
||||
type NonBlockingGRPCServer interface {
|
||||
// Start services at the endpoint
|
||||
Start(endpoint string, ids csi.IdentityServer, cs csi.ControllerServer, ns csi.NodeServer)
|
||||
// Waits for the service to stop
|
||||
Wait()
|
||||
// Stops the service gracefully
|
||||
Stop()
|
||||
// Stops the service forcefully
|
||||
ForceStop()
|
||||
}
|
||||
|
||||
func NewNonBlockingGRPCServer() NonBlockingGRPCServer {
|
||||
return &nonBlockingGRPCServer{}
|
||||
}
|
||||
|
||||
// NonBlocking server
|
||||
type nonBlockingGRPCServer struct {
|
||||
wg sync.WaitGroup
|
||||
server *grpc.Server
|
||||
}
|
||||
|
||||
func (s *nonBlockingGRPCServer) Start(endpoint string, ids csi.IdentityServer, cs csi.ControllerServer, ns csi.NodeServer) {
|
||||
|
||||
s.wg.Add(1)
|
||||
|
||||
go s.serve(endpoint, ids, cs, ns)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *nonBlockingGRPCServer) Wait() {
|
||||
s.wg.Wait()
|
||||
}
|
||||
|
||||
func (s *nonBlockingGRPCServer) Stop() {
|
||||
s.server.GracefulStop()
|
||||
}
|
||||
|
||||
func (s *nonBlockingGRPCServer) ForceStop() {
|
||||
s.server.Stop()
|
||||
}
|
||||
|
||||
func (s *nonBlockingGRPCServer) serve(endpoint string, ids csi.IdentityServer, cs csi.ControllerServer, ns csi.NodeServer) {
|
||||
|
||||
proto, addr, err := ParseEndpoint(endpoint)
|
||||
if err != nil {
|
||||
glog.Fatal(err.Error())
|
||||
}
|
||||
|
||||
if proto == "unix" {
|
||||
addr = "/" + addr
|
||||
if err := os.Remove(addr); err != nil && !os.IsNotExist(err) {
|
||||
glog.Fatalf("Failed to remove %s, error: %s", addr, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
listener, err := net.Listen(proto, addr)
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to listen: %v", err)
|
||||
}
|
||||
|
||||
opts := []grpc.ServerOption{
|
||||
grpc.UnaryInterceptor(logGRPC),
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
glog.Infof("Listening for connections on address: %#v", listener.Addr())
|
||||
|
||||
server.Serve(listener)
|
||||
|
||||
}
|
127
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/utils.go
generated
vendored
Normal file
127
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/utils.go
generated
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
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"
|
||||
"strings"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func GetVersionString(v *csi.Version) string {
|
||||
return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
|
||||
}
|
||||
|
||||
func GetVersionFromString(v string) (*csi.Version, error) {
|
||||
var major, minor, patch uint32
|
||||
|
||||
n, err := fmt.Sscanf(v, "%d.%d.%d", &major, &minor, &patch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n != 3 {
|
||||
return nil, fmt.Errorf("Invalid format. Specify version in x.y.z format")
|
||||
}
|
||||
|
||||
return &csi.Version{
|
||||
Major: major,
|
||||
Minor: minor,
|
||||
Patch: patch,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewVolumeCapabilityAccessMode(mode csi.VolumeCapability_AccessMode_Mode) *csi.VolumeCapability_AccessMode {
|
||||
return &csi.VolumeCapability_AccessMode{Mode: mode}
|
||||
}
|
||||
|
||||
func NewDefaultNodeServer(d *CSIDriver) *DefaultNodeServer {
|
||||
return &DefaultNodeServer{
|
||||
Driver: d,
|
||||
}
|
||||
}
|
||||
|
||||
func NewDefaultIdentityServer(d *CSIDriver) *DefaultIdentityServer {
|
||||
return &DefaultIdentityServer{
|
||||
Driver: d,
|
||||
}
|
||||
}
|
||||
|
||||
func NewDefaultControllerServer(d *CSIDriver) *DefaultControllerServer {
|
||||
return &DefaultControllerServer{
|
||||
Driver: d,
|
||||
}
|
||||
}
|
||||
|
||||
func NewControllerServiceCapability(cap csi.ControllerServiceCapability_RPC_Type) *csi.ControllerServiceCapability {
|
||||
return &csi.ControllerServiceCapability{
|
||||
Type: &csi.ControllerServiceCapability_Rpc{
|
||||
Rpc: &csi.ControllerServiceCapability_RPC{
|
||||
Type: cap,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func RunNodePublishServer(endpoint string, d *CSIDriver, ns csi.NodeServer) {
|
||||
ids := NewDefaultIdentityServer(d)
|
||||
|
||||
s := NewNonBlockingGRPCServer()
|
||||
s.Start(endpoint, ids, nil, ns)
|
||||
s.Wait()
|
||||
}
|
||||
|
||||
func RunControllerPublishServer(endpoint string, d *CSIDriver, cs csi.ControllerServer) {
|
||||
ids := NewDefaultIdentityServer(d)
|
||||
|
||||
s := NewNonBlockingGRPCServer()
|
||||
s.Start(endpoint, ids, cs, nil)
|
||||
s.Wait()
|
||||
}
|
||||
|
||||
func RunControllerandNodePublishServer(endpoint string, d *CSIDriver, cs csi.ControllerServer, ns csi.NodeServer) {
|
||||
ids := NewDefaultIdentityServer(d)
|
||||
|
||||
s := NewNonBlockingGRPCServer()
|
||||
s.Start(endpoint, ids, cs, ns)
|
||||
s.Wait()
|
||||
}
|
||||
|
||||
func logGRPC(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
glog.V(3).Infof("GRPC call: %s", info.FullMethod)
|
||||
glog.V(5).Infof("GRPC request: %+v", req)
|
||||
resp, err := handler(ctx, req)
|
||||
if err != nil {
|
||||
glog.Errorf("GRPC error: %v", err)
|
||||
} else {
|
||||
glog.V(5).Infof("GRPC response: %+v", resp)
|
||||
}
|
||||
return resp, err
|
||||
}
|
106
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/utils_test.go
generated
vendored
Normal file
106
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/utils_test.go
generated
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
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 (
|
||||
"testing"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetVersionFromString(t *testing.T) {
|
||||
|
||||
//Invalid version
|
||||
_, err := GetVersionFromString("")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
v, err := GetVersionFromString("1.2.3")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, v.GetMajor(), uint32(1))
|
||||
assert.Equal(t, v.GetMinor(), uint32(2))
|
||||
assert.Equal(t, v.GetPatch(), uint32(3))
|
||||
|
||||
// Invalid version
|
||||
_, err = GetVersionFromString("1.2")
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestGetVersionString(t *testing.T) {
|
||||
|
||||
v := &csi.Version{
|
||||
Major: 1,
|
||||
Minor: 0,
|
||||
Patch: 0,
|
||||
}
|
||||
//Invalid version
|
||||
vStr := GetVersionString(v)
|
||||
assert.Equal(t, vStr, "1.0.0")
|
||||
}
|
||||
|
||||
func TestParseEndpoint(t *testing.T) {
|
||||
|
||||
//Valid unix domain socket endpoint
|
||||
sockType, addr, err := ParseEndpoint("unix://fake.sock")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, sockType, "unix")
|
||||
assert.Equal(t, addr, "fake.sock")
|
||||
|
||||
sockType, addr, err = ParseEndpoint("unix:///fakedir/fakedir/fake.sock")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, sockType, "unix")
|
||||
assert.Equal(t, addr, "/fakedir/fakedir/fake.sock")
|
||||
|
||||
//Valid unix domain socket with uppercase
|
||||
sockType, addr, err = ParseEndpoint("UNIX://fake.sock")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, sockType, "UNIX")
|
||||
assert.Equal(t, addr, "fake.sock")
|
||||
|
||||
//Valid TCP endpoint with ip
|
||||
sockType, addr, err = ParseEndpoint("tcp://127.0.0.1:80")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, sockType, "tcp")
|
||||
assert.Equal(t, addr, "127.0.0.1:80")
|
||||
|
||||
//Valid TCP endpoint with uppercase
|
||||
sockType, addr, err = ParseEndpoint("TCP://127.0.0.1:80")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, sockType, "TCP")
|
||||
assert.Equal(t, addr, "127.0.0.1:80")
|
||||
|
||||
//Valid TCP endpoint with hostname
|
||||
sockType, addr, err = ParseEndpoint("tcp://fakehost:80")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, sockType, "tcp")
|
||||
assert.Equal(t, addr, "fakehost:80")
|
||||
|
||||
_, _, err = ParseEndpoint("unix:/fake.sock/")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
_, _, err = ParseEndpoint("fake.sock")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
_, _, err = ParseEndpoint("unix://")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
_, _, err = ParseEndpoint("://")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
_, _, err = ParseEndpoint("")
|
||||
assert.NotNil(t, err)
|
||||
}
|
42
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/README.md
generated
vendored
Normal file
42
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/README.md
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
# CSI to Flexvolume adapter
|
||||
|
||||
## Usage:
|
||||
|
||||
### Start Flexvolume adapter for simple nfs flexvolume driver
|
||||
```
|
||||
$ sudo ../_output/flexadapter --endpoint tcp://127.0.0.1:10000 --drivername simplenfs --driverpath ./examples/simple-nfs-flexdriver/nfs --nodeid CSINode
|
||||
```
|
||||
|
||||
### Test using csc
|
||||
Get ```csc``` tool from https://github.com/chakri-nelluri/gocsi/tree/master/csc
|
||||
|
||||
#### Get plugin info
|
||||
```
|
||||
$ csc identity plugininfo --endpoint tcp://127.0.0.1:10000
|
||||
"simplenfs" "0.1.0"
|
||||
```
|
||||
|
||||
### Get supported versions
|
||||
```
|
||||
$ csc identity supportedversions --endpoint tcp://127.0.0.1:10000
|
||||
0.1.0
|
||||
```
|
||||
|
||||
#### NodePublish a volume
|
||||
```
|
||||
$ csc node publishvolume --endpoint tcp://127.0.0.1:10000 --target-path /mnt/nfs --attrib server=a.b.c.d --attrib share=nfs_share nfstestvol
|
||||
nfstestvol
|
||||
```
|
||||
|
||||
#### NodeUnpublish a volume
|
||||
```
|
||||
$ csc node unpublishvolume --endpoint tcp://127.0.0.1:10000 --target-path /mnt/nfs nfstestvol
|
||||
nfstestvol
|
||||
```
|
||||
|
||||
#### Get NodeID
|
||||
```
|
||||
$ csc node getid --endpoint tcp://127.0.0.1:10000
|
||||
CSINode
|
||||
```
|
||||
|
95
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/controllerserver.go
generated
vendored
Normal file
95
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/controllerserver.go
generated
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
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 flexadapter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/kubernetes-csi/drivers/pkg/csi-common"
|
||||
)
|
||||
|
||||
const (
|
||||
deviceID = "deviceID"
|
||||
)
|
||||
|
||||
type controllerServer struct {
|
||||
*csicommon.DefaultControllerServer
|
||||
}
|
||||
|
||||
func GetVersionString(ver *csi.Version) string {
|
||||
return fmt.Sprintf("%d.%d.%d", ver.Major, ver.Minor, ver.Patch)
|
||||
}
|
||||
|
||||
func (cs *controllerServer) ControllerPublishVolume(ctx context.Context, req *csi.ControllerPublishVolumeRequest) (*csi.ControllerPublishVolumeResponse, error) {
|
||||
if err := cs.Driver.ValidateControllerServiceRequest(req.Version, csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
call := GetFlexAdapter().flexDriver.NewDriverCall(attachCmd)
|
||||
|
||||
fsType := req.GetVolumeCapability().GetMount().FsType
|
||||
call.AppendSpec(req.GetVolumeId(), fsType, req.GetReadonly(), req.GetVolumeAttributes())
|
||||
call.Append(req.GetNodeId())
|
||||
|
||||
callStatus, err := call.Run()
|
||||
if isCmdNotSupportedErr(err) {
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
} else if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
pvInfo := map[string]string{}
|
||||
|
||||
pvInfo[deviceID] = callStatus.DevicePath
|
||||
|
||||
return &csi.ControllerPublishVolumeResponse{
|
||||
PublishVolumeInfo: pvInfo,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (cs *controllerServer) ControllerUnpublishVolume(ctx context.Context, req *csi.ControllerUnpublishVolumeRequest) (*csi.ControllerUnpublishVolumeResponse, error) {
|
||||
if err := cs.Driver.ValidateControllerServiceRequest(req.Version, csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
call := GetFlexAdapter().flexDriver.NewDriverCall(detachCmd)
|
||||
call.Append(req.GetVolumeId())
|
||||
call.Append(req.GetNodeId())
|
||||
|
||||
_, err := call.Run()
|
||||
if isCmdNotSupportedErr(err) {
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
} else if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
return &csi.ControllerUnpublishVolumeResponse{}, nil
|
||||
}
|
||||
|
||||
func (cs *controllerServer) ValidateVolumeCapabilities(ctx context.Context, req *csi.ValidateVolumeCapabilitiesRequest) (*csi.ValidateVolumeCapabilitiesResponse, error) {
|
||||
for _, cap := range req.VolumeCapabilities {
|
||||
if cap.GetAccessMode().GetMode() != csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER {
|
||||
return &csi.ValidateVolumeCapabilitiesResponse{Supported: false, Message: ""}, nil
|
||||
}
|
||||
}
|
||||
return &csi.ValidateVolumeCapabilitiesResponse{Supported: true, Message: ""}, nil
|
||||
}
|
239
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/driver-call.go
generated
vendored
Normal file
239
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/driver-call.go
generated
vendored
Normal file
@ -0,0 +1,239 @@
|
||||
/*
|
||||
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 flexadapter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
const (
|
||||
// Driver calls
|
||||
initCmd = "init"
|
||||
getVolumeNameCmd = "getvolumename"
|
||||
|
||||
isAttached = "isattached"
|
||||
|
||||
attachCmd = "attach"
|
||||
waitForAttachCmd = "waitforattach"
|
||||
mountDeviceCmd = "mountdevice"
|
||||
|
||||
detachCmd = "detach"
|
||||
waitForDetachCmd = "waitfordetach"
|
||||
unmountDeviceCmd = "unmountdevice"
|
||||
|
||||
mountCmd = "mount"
|
||||
unmountCmd = "unmount"
|
||||
|
||||
// Option keys
|
||||
optionFSType = "kubernetes.io/fsType"
|
||||
optionReadWrite = "kubernetes.io/readwrite"
|
||||
optionKeySecret = "kubernetes.io/secret"
|
||||
optionFSGroup = "kubernetes.io/fsGroup"
|
||||
optionMountsDir = "kubernetes.io/mountsDir"
|
||||
optionPVorVolumeName = "kubernetes.io/pvOrVolumeName"
|
||||
|
||||
optionKeyPodName = "kubernetes.io/pod.name"
|
||||
optionKeyPodNamespace = "kubernetes.io/pod.namespace"
|
||||
optionKeyPodUID = "kubernetes.io/pod.uid"
|
||||
|
||||
optionKeyServiceAccountName = "kubernetes.io/serviceAccount.name"
|
||||
)
|
||||
|
||||
const (
|
||||
// StatusSuccess represents the successful completion of command.
|
||||
StatusSuccess = "Success"
|
||||
// StatusNotSupported represents that the command is not supported.
|
||||
StatusNotSupported = "Not supported"
|
||||
)
|
||||
|
||||
var (
|
||||
TimeoutError = fmt.Errorf("Timeout")
|
||||
)
|
||||
|
||||
// DriverCall implements the basic contract between FlexVolume and its driver.
|
||||
// The caller is responsible for providing the required args.
|
||||
type DriverCall struct {
|
||||
driver *flexVolumeDriver
|
||||
Command string
|
||||
Timeout time.Duration
|
||||
args []string
|
||||
}
|
||||
|
||||
func (d *flexVolumeDriver) NewDriverCall(command string) *DriverCall {
|
||||
return d.NewDriverCallWithTimeout(command, 0)
|
||||
}
|
||||
|
||||
func (d *flexVolumeDriver) NewDriverCallWithTimeout(command string, timeout time.Duration) *DriverCall {
|
||||
return &DriverCall{
|
||||
driver: d,
|
||||
Command: command,
|
||||
Timeout: timeout,
|
||||
args: []string{command},
|
||||
}
|
||||
}
|
||||
|
||||
func (dc *DriverCall) Append(arg string) {
|
||||
dc.args = append(dc.args, arg)
|
||||
}
|
||||
|
||||
func (dc *DriverCall) AppendSpec(volumeID, fsType string, readOnly bool, volumeAttributes map[string]string) error {
|
||||
optionsForDriver := NewOptionsForDriver(volumeID, fsType, readOnly, volumeAttributes)
|
||||
|
||||
jsonBytes, err := json.Marshal(optionsForDriver)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to marshal spec, error: %s", err.Error())
|
||||
}
|
||||
|
||||
dc.Append(string(jsonBytes))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dc *DriverCall) Run() (*DriverStatus, error) {
|
||||
if dc.driver.isUnsupported(dc.Command) {
|
||||
return nil, errors.New(StatusNotSupported)
|
||||
}
|
||||
execPath := dc.driver.getExecutable()
|
||||
|
||||
cmd := exec.Command(execPath, dc.args...)
|
||||
|
||||
timeout := false
|
||||
if dc.Timeout > 0 {
|
||||
timer := time.AfterFunc(dc.Timeout, func() {
|
||||
timeout = true
|
||||
//TODO: cmd.Stop()
|
||||
})
|
||||
defer timer.Stop()
|
||||
}
|
||||
|
||||
output, execErr := cmd.CombinedOutput()
|
||||
if execErr != nil {
|
||||
if timeout {
|
||||
return nil, TimeoutError
|
||||
}
|
||||
_, err := handleCmdResponse(dc.Command, output)
|
||||
if err == nil {
|
||||
glog.Errorf("FlexVolume: driver bug: %s: exec error (%s) but no error in response.", execPath, execErr)
|
||||
return nil, execErr
|
||||
}
|
||||
if isCmdNotSupportedErr(err) {
|
||||
dc.driver.unsupported(dc.Command)
|
||||
} else {
|
||||
glog.Warningf("FlexVolume: driver call failed: executable: %s, args: %s, error: %s, output: %q", execPath, dc.args, execErr.Error(), output)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
status, err := handleCmdResponse(dc.Command, output)
|
||||
if err != nil {
|
||||
if isCmdNotSupportedErr(err) {
|
||||
dc.driver.unsupported(dc.Command)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
|
||||
// OptionsForDriver represents the spec given to the driver.
|
||||
type OptionsForDriver map[string]string
|
||||
|
||||
func NewOptionsForDriver(volumeID, fsType string, readOnly bool, volumeAttributes map[string]string) OptionsForDriver {
|
||||
options := map[string]string{}
|
||||
|
||||
if readOnly {
|
||||
options[optionReadWrite] = "ro"
|
||||
} else {
|
||||
options[optionReadWrite] = "rw"
|
||||
}
|
||||
|
||||
options[optionFSType] = fsType
|
||||
options[optionPVorVolumeName] = volumeID
|
||||
|
||||
for key, value := range volumeAttributes {
|
||||
options[key] = value
|
||||
}
|
||||
|
||||
return OptionsForDriver(options)
|
||||
}
|
||||
|
||||
// DriverStatus represents the return value of the driver callout.
|
||||
type DriverStatus struct {
|
||||
// Status of the callout. One of "Success", "Failure" or "Not supported".
|
||||
Status string `json:"status"`
|
||||
// Reason for success/failure.
|
||||
Message string `json:"message,omitempty"`
|
||||
// Path to the device attached. This field is valid only for attach calls.
|
||||
// ie: /dev/sdx
|
||||
DevicePath string `json:"device,omitempty"`
|
||||
// Cluster wide unique name of the volume.
|
||||
VolumeName string `json:"volumeName,omitempty"`
|
||||
// Represents volume is attached on the node
|
||||
Attached bool `json:"attached,omitempty"`
|
||||
// Returns capabilities of the driver.
|
||||
// By default we assume all the capabilities are supported.
|
||||
// If the plugin does not support a capability, it can return false for that capability.
|
||||
Capabilities *DriverCapabilities `json:",omitempty"`
|
||||
}
|
||||
|
||||
type DriverCapabilities struct {
|
||||
Attach bool `json:"attach"`
|
||||
SELinuxRelabel bool `json:"selinuxRelabel"`
|
||||
}
|
||||
|
||||
func defaultCapabilities() *DriverCapabilities {
|
||||
return &DriverCapabilities{
|
||||
Attach: true,
|
||||
SELinuxRelabel: true,
|
||||
}
|
||||
}
|
||||
|
||||
// isCmdNotSupportedErr checks if the error corresponds to command not supported by
|
||||
// driver.
|
||||
func isCmdNotSupportedErr(err error) bool {
|
||||
if err != nil && err.Error() == StatusNotSupported {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// handleCmdResponse processes the command output and returns the appropriate
|
||||
// error code or message.
|
||||
func handleCmdResponse(cmd string, output []byte) (*DriverStatus, error) {
|
||||
status := DriverStatus{
|
||||
Capabilities: defaultCapabilities(),
|
||||
}
|
||||
if err := json.Unmarshal(output, &status); err != nil {
|
||||
glog.Errorf("Failed to unmarshal output for command: %s, output: %q, error: %s", cmd, string(output), err.Error())
|
||||
return nil, err
|
||||
} else if status.Status == StatusNotSupported {
|
||||
glog.V(5).Infof("%s command is not supported by the driver", cmd)
|
||||
return nil, errors.New(status.Status)
|
||||
} else if status.Status != StatusSuccess {
|
||||
errMsg := fmt.Sprintf("%s command failed, status: %s, reason: %s", cmd, status.Status, status.Message)
|
||||
glog.Errorf(errMsg)
|
||||
return nil, fmt.Errorf("%s", errMsg)
|
||||
}
|
||||
|
||||
return &status, nil
|
||||
}
|
25
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/examples/simplenfs-flexdriver/README.md
generated
vendored
Normal file
25
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/examples/simplenfs-flexdriver/README.md
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
# CSI-Flex NFS driver
|
||||
|
||||
# Requirements
|
||||
|
||||
The folllowing feature gates and runtime config have to be enabled to deploy the driver
|
||||
|
||||
```
|
||||
FEATURE_GATES=CSIPersistentVolume=true,MountPropagation=true
|
||||
RUNTIME_CONFIG="storage.k8s.io/v1alpha1=true"
|
||||
```
|
||||
|
||||
Mountprogpation requries support for privileged containers. So, make sure privileged containers are enabled in the cluster.
|
||||
|
||||
## Example local-up-cluster.sh
|
||||
|
||||
```ALLOW_PRIVILEGED=true FEATURE_GATES=CSIPersistentVolume=true,MountPropagation=true RUNTIME_CONFIG="storage.k8s.io/v1alpha1=true" LOG_LEVEL=5 hack/local-up-cluster.sh```
|
||||
|
||||
|
||||
# Deploy in Kubernetes
|
||||
|
||||
```kubectl -f deploy/kubernetes create```
|
||||
|
||||
# Example Nginx application
|
||||
|
||||
``` kubectl -f examples/kubernetes/nginx.yaml create```
|
@ -0,0 +1,107 @@
|
||||
# This YAML file contains all API objects that are necessary to run external
|
||||
# CSI attacher for nfs flex adapter
|
||||
#
|
||||
# In production, this needs to be in separate files, e.g. service account and
|
||||
# role and role binding needs to be created once, while stateful set may
|
||||
# require some tuning.
|
||||
#
|
||||
# In addition, mock CSI driver is hardcoded as the CSI driver.
|
||||
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: csi-attacher
|
||||
|
||||
---
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: external-attacher-runner
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["persistentvolumes"]
|
||||
verbs: ["get", "list", "watch", "update"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: ["storage.k8s.io"]
|
||||
resources: ["volumeattachments"]
|
||||
verbs: ["get", "list", "watch", "update"]
|
||||
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: csi-attacher-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: csi-attacher
|
||||
namespace: default
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: external-attacher-runner
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
---
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: csi-attacher-simplenfs-flexdriver
|
||||
labels:
|
||||
app: csi-attacher-simplenfs-flexdriver
|
||||
spec:
|
||||
selector:
|
||||
app: csi-attacher-simplenfs-flexdriver
|
||||
ports:
|
||||
- name: dummy
|
||||
port: 12345
|
||||
|
||||
---
|
||||
kind: StatefulSet
|
||||
apiVersion: apps/v1beta1
|
||||
metadata:
|
||||
name: csi-attacher-simplenfs-flexdriver
|
||||
spec:
|
||||
serviceName: "csi-attacher"
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: csi-attacher-simplenfs-flexdriver
|
||||
spec:
|
||||
serviceAccount: csi-attacher
|
||||
containers:
|
||||
- name: csi-attacher
|
||||
image: docker.io/k8scsi/csi-attacher
|
||||
args:
|
||||
- "--v=5"
|
||||
- "--csi-address=$(ADDRESS)"
|
||||
env:
|
||||
- name: ADDRESS
|
||||
value: /var/lib/csi/sockets/pluginproxy/csi.sock
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
volumeMounts:
|
||||
- name: socket-dir
|
||||
mountPath: /var/lib/csi/sockets/pluginproxy/
|
||||
|
||||
- name: simplenfs
|
||||
image: docker.io/k8scsi/csi-simplenfs-flexdriver:v0.1
|
||||
args :
|
||||
- "--nodeid=$(NODE_ID)"
|
||||
- "--drivername=csi-simplenfs-flexdriver"
|
||||
- "--endpoint=$(CSI_ENDPOINT)"
|
||||
env:
|
||||
- name: NODE_ID
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.nodeName
|
||||
- name: CSI_ENDPOINT
|
||||
value: unix://plugin/csi.sock
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
volumeMounts:
|
||||
- name: socket-dir
|
||||
mountPath: /plugin
|
||||
volumes:
|
||||
- name: socket-dir
|
||||
emptyDir:
|
||||
|
34
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/examples/simplenfs-flexdriver/deploy/kubernetes/csi-nodeplugin-rbac.yaml
generated
vendored
Normal file
34
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/examples/simplenfs-flexdriver/deploy/kubernetes/csi-nodeplugin-rbac.yaml
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
# This YAML defines all API objects to create RBAC roles for CSI node plugin
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: csi-nodeplugin
|
||||
|
||||
---
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: csi-nodeplugin
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["persistentvolumes"]
|
||||
verbs: ["get", "list", "watch", "update"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["get", "list", "watch", "update"]
|
||||
- apiGroups: ["storage.k8s.io"]
|
||||
resources: ["volumeattachments"]
|
||||
verbs: ["get", "list", "watch", "update"]
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: csi-nodeplugin
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: csi-nodeplugin
|
||||
namespace: default
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: csi-nodeplugin
|
||||
apiGroup: rbac.authorization.k8s.io
|
@ -0,0 +1,65 @@
|
||||
kind: DaemonSet
|
||||
apiVersion: apps/v1beta2
|
||||
metadata:
|
||||
name: csi-nodeplugin-simplenfs
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: csi-nodeplugin-simplenfs
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: csi-nodeplugin-simplenfs
|
||||
spec:
|
||||
serviceAccount: csi-nodeplugin
|
||||
hostNetwork: true
|
||||
containers:
|
||||
- name: driver-registrar
|
||||
image: docker.io/k8scsi/driver-registrar
|
||||
args:
|
||||
- "--v=5"
|
||||
- "--csi-address=$(ADDRESS)"
|
||||
env:
|
||||
- name: ADDRESS
|
||||
value: /plugin/csi.sock
|
||||
- name: KUBE_NODE_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.nodeName
|
||||
volumeMounts:
|
||||
- name: plugin-dir
|
||||
mountPath: /plugin
|
||||
- name: simplenfs
|
||||
securityContext:
|
||||
privileged: true
|
||||
capabilities:
|
||||
add: ["SYS_ADMIN"]
|
||||
allowPrivilegeEscalation: true
|
||||
image: docker.io/k8scsi/csi-simplenfs-flexdriver:v0.1
|
||||
args :
|
||||
- "--nodeid=$(NODE_ID)"
|
||||
- "--drivername=csi-simplenfs-flexdriver"
|
||||
- "--endpoint=$(CSI_ENDPOINT)"
|
||||
env:
|
||||
- name: NODE_ID
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.nodeName
|
||||
- name: CSI_ENDPOINT
|
||||
value: unix://plugin/csi.sock
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
volumeMounts:
|
||||
- name: plugin-dir
|
||||
mountPath: /plugin
|
||||
- name: pods-mount-dir
|
||||
mountPath: /var/lib/kubelet/pods
|
||||
mountPropagation: "Bidirectional"
|
||||
volumes:
|
||||
- name: plugin-dir
|
||||
hostPath:
|
||||
path: /var/lib/kubelet/plugins/csi-simplenfs-flexdriver
|
||||
type: Directory
|
||||
- name: pods-mount-dir
|
||||
hostPath:
|
||||
path: /var/lib/kubelet/pods
|
||||
type: Directory
|
10
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/examples/simplenfs-flexdriver/dockerfile/Dockerfile
generated
vendored
Normal file
10
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/examples/simplenfs-flexdriver/dockerfile/Dockerfile
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
FROM centos:7.4.1708
|
||||
|
||||
# Copy flexadapter from build _output directory
|
||||
COPY flexadapter /flexadapter
|
||||
# Copy nfs from driver directory
|
||||
COPY nfs /drivers/nfs
|
||||
|
||||
RUN yum -y install nfs-utils && yum -y install epel-release && yum -y install jq && yum clean all
|
||||
|
||||
ENTRYPOINT ["/flexadapter", "--driverpath=/drivers/nfs"]
|
113
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/examples/simplenfs-flexdriver/driver/nfs
generated
vendored
Executable file
113
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/examples/simplenfs-flexdriver/driver/nfs
generated
vendored
Executable file
@ -0,0 +1,113 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2015 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.
|
||||
|
||||
# Notes:
|
||||
# - Please install "jq" package before using this driver.
|
||||
usage() {
|
||||
err "Invalid usage. Usage: "
|
||||
err "\t$0 init"
|
||||
err "\t$0 mount <mount dir> <json params>"
|
||||
err "\t$0 unmount <mount dir>"
|
||||
exit 1
|
||||
}
|
||||
|
||||
err() {
|
||||
echo -ne $* 1>&2
|
||||
}
|
||||
|
||||
log() {
|
||||
echo -ne $* >&1
|
||||
}
|
||||
|
||||
ismounted() {
|
||||
MOUNT=`findmnt -n ${MNTPATH} 2>/dev/null | cut -d' ' -f1`
|
||||
if [ "${MOUNT}" == "${MNTPATH}" ]; then
|
||||
echo "1"
|
||||
else
|
||||
echo "0"
|
||||
fi
|
||||
}
|
||||
|
||||
domount() {
|
||||
MNTPATH=$1
|
||||
|
||||
NFS_SERVER=$(echo $2 | jq -r '.server')
|
||||
SHARE=$(echo $2 | jq -r '.share')
|
||||
|
||||
if [ $(ismounted) -eq 1 ] ; then
|
||||
log '{"status": "Success"}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mkdir -p ${MNTPATH} &> /dev/null
|
||||
|
||||
mount -t nfs ${NFS_SERVER}:/${SHARE} ${MNTPATH} &> /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
err "{ \"status\": \"Failure\", \"message\": \"Failed to mount ${NFS_SERVER}:${SHARE} at ${MNTPATH}\"}"
|
||||
exit 1
|
||||
fi
|
||||
log '{"status": "Success"}'
|
||||
exit 0
|
||||
}
|
||||
|
||||
unmount() {
|
||||
MNTPATH=$1
|
||||
if [ $(ismounted) -eq 0 ] ; then
|
||||
log '{"status": "Success"}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
umount ${MNTPATH} &> /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
err "{ \"status\": \"Failed\", \"message\": \"Failed to unmount volume at ${MNTPATH}\"}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log '{"status": "Success"}'
|
||||
exit 0
|
||||
}
|
||||
|
||||
op=$1
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
err "{ \"status\": \"Failure\", \"message\": \"'jq' binary not found. Please install jq package before using this driver\"}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$op" = "init" ]; then
|
||||
log '{"status": "Success", "capabilities": {"attach": false}}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ $# -lt 2 ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
shift
|
||||
|
||||
case "$op" in
|
||||
mount)
|
||||
domount $*
|
||||
;;
|
||||
unmount)
|
||||
unmount $*
|
||||
;;
|
||||
*)
|
||||
log '{"status": "Not supported"}'
|
||||
exit 0
|
||||
esac
|
||||
|
||||
exit 1
|
52
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/examples/simplenfs-flexdriver/examples/kubernetes/nginx.yaml
generated
vendored
Normal file
52
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/examples/simplenfs-flexdriver/examples/kubernetes/nginx.yaml
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: data
|
||||
labels:
|
||||
name: data
|
||||
annotations:
|
||||
csi.volume.kubernetes.io/volume-attributes: '{"server": "10.10.10.10", "share": "my_export"}'
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
capacity:
|
||||
storage: 100Gi
|
||||
csi:
|
||||
driver: csi-simplenfs-flexdriver
|
||||
volumeHandle: data-id
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: data
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 100Gi
|
||||
selector:
|
||||
matchExpressions:
|
||||
- key: name
|
||||
operator: In
|
||||
values: ["data"]
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: data
|
||||
spec:
|
||||
containers:
|
||||
- image: maersk/nginx
|
||||
imagePullPolicy: Always
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
protocol: TCP
|
||||
volumeMounts:
|
||||
- mountPath: /var/www
|
||||
name: data
|
||||
volumes:
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: data
|
97
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/flexadapter.go
generated
vendored
Normal file
97
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/flexadapter.go
generated
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
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 flexadapter
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/golang/glog"
|
||||
|
||||
"github.com/kubernetes-csi/drivers/pkg/csi-common"
|
||||
)
|
||||
|
||||
type flexAdapter struct {
|
||||
driver *csicommon.CSIDriver
|
||||
|
||||
flexDriver *flexVolumeDriver
|
||||
|
||||
ns *nodeServer
|
||||
cs *controllerServer
|
||||
|
||||
cap []*csi.VolumeCapability_AccessMode
|
||||
cscap []*csi.ControllerServiceCapability
|
||||
}
|
||||
|
||||
var (
|
||||
adapter *flexAdapter
|
||||
runOnce sync.Once
|
||||
version = csi.Version{
|
||||
Minor: 1,
|
||||
}
|
||||
)
|
||||
|
||||
func GetSupportedVersions() []*csi.Version {
|
||||
return []*csi.Version{&version}
|
||||
}
|
||||
|
||||
func GetFlexAdapter() *flexAdapter {
|
||||
runOnce.Do(func() {
|
||||
adapter = &flexAdapter{}
|
||||
})
|
||||
return adapter
|
||||
}
|
||||
|
||||
func NewControllerServer(d *csicommon.CSIDriver) *controllerServer {
|
||||
return &controllerServer{
|
||||
DefaultControllerServer: csicommon.NewDefaultControllerServer(d),
|
||||
}
|
||||
}
|
||||
|
||||
func NewNodeServer(d *csicommon.CSIDriver, f *flexVolumeDriver) *nodeServer {
|
||||
return &nodeServer{
|
||||
flexDriver: f,
|
||||
DefaultNodeServer: csicommon.NewDefaultNodeServer(d),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *flexAdapter) Run(driverName, driverPath, nodeID, endpoint string) {
|
||||
var err error
|
||||
|
||||
glog.Infof("Driver: %v version: %v", driverName, GetVersionString(&version))
|
||||
|
||||
// Create flex volume driver
|
||||
adapter.flexDriver, err = NewFlexVolumeDriver(driverName, driverPath)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to initialize flex volume driver, error: %v", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Initialize default library driver
|
||||
adapter.driver = csicommon.NewCSIDriver(driverName, &version, GetSupportedVersions(), nodeID)
|
||||
if adapter.flexDriver.capabilities.Attach {
|
||||
adapter.driver.AddControllerServiceCapabilities([]csi.ControllerServiceCapability_RPC_Type{csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME})
|
||||
}
|
||||
adapter.driver.AddVolumeCapabilityAccessModes([]csi.VolumeCapability_AccessMode_Mode{csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER})
|
||||
|
||||
// Create GRPC servers
|
||||
f.ns = NewNodeServer(adapter.driver, adapter.flexDriver)
|
||||
f.cs = NewControllerServer(adapter.driver)
|
||||
|
||||
csicommon.RunControllerandNodePublishServer(endpoint, adapter.driver, f.cs, f.ns)
|
||||
}
|
73
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/flexvolumedriver.go
generated
vendored
Normal file
73
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/flexvolumedriver.go
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
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 flexadapter
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type flexVolumeDriver struct {
|
||||
sync.Mutex
|
||||
driverName string
|
||||
execPath string
|
||||
unsupportedCommands []string
|
||||
capabilities DriverCapabilities
|
||||
}
|
||||
|
||||
// Returns true iff the given command is known to be unsupported.
|
||||
func (d *flexVolumeDriver) isUnsupported(command string) bool {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
for _, unsupportedCommand := range d.unsupportedCommands {
|
||||
if command == unsupportedCommand {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (d *flexVolumeDriver) getExecutable() string {
|
||||
return d.execPath
|
||||
}
|
||||
|
||||
// Mark the given commands as unsupported.
|
||||
func (d *flexVolumeDriver) unsupported(commands ...string) {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
d.unsupportedCommands = append(d.unsupportedCommands, commands...)
|
||||
}
|
||||
|
||||
func NewFlexVolumeDriver(driverName, driverPath string) (*flexVolumeDriver, error) {
|
||||
|
||||
flexDriver := &flexVolumeDriver{
|
||||
driverName: driverName,
|
||||
execPath: driverPath,
|
||||
}
|
||||
|
||||
adapter.flexDriver = flexDriver
|
||||
|
||||
// Initialize the plugin and probe the capabilities
|
||||
call := flexDriver.NewDriverCall(initCmd)
|
||||
ds, err := call.Run()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
flexDriver.capabilities = *ds.Capabilities
|
||||
|
||||
return flexDriver, nil
|
||||
}
|
113
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/nodeserver.go
generated
vendored
Normal file
113
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/nodeserver.go
generated
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
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 flexadapter
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
"k8s.io/kubernetes/pkg/volume/util"
|
||||
|
||||
"github.com/kubernetes-csi/drivers/pkg/csi-common"
|
||||
)
|
||||
|
||||
type nodeServer struct {
|
||||
flexDriver *flexVolumeDriver
|
||||
*csicommon.DefaultNodeServer
|
||||
}
|
||||
|
||||
func mountDevice(devicePath, targetPath, fsType string, readOnly bool, mountOptions []string) error {
|
||||
var options []string
|
||||
|
||||
if readOnly {
|
||||
options = append(options, "ro")
|
||||
} else {
|
||||
options = append(options, "rw")
|
||||
}
|
||||
options = append(options, mountOptions...)
|
||||
|
||||
diskMounter := &mount.SafeFormatAndMount{Interface: mount.New(""), Exec: mount.NewOsExec()}
|
||||
|
||||
return diskMounter.FormatAndMount(deviceID, targetPath, fsType, options)
|
||||
}
|
||||
|
||||
func (ns *nodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) {
|
||||
|
||||
targetPath := req.GetTargetPath()
|
||||
fsType := req.GetVolumeCapability().GetMount().GetFsType()
|
||||
|
||||
notMnt, err := mount.New("").IsLikelyNotMountPoint(targetPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if err := os.MkdirAll(targetPath, 0750); err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
notMnt = true
|
||||
} else {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if !notMnt {
|
||||
return &csi.NodePublishVolumeResponse{}, nil
|
||||
}
|
||||
|
||||
call := GetFlexAdapter().flexDriver.NewDriverCall(mountCmd)
|
||||
call.Append(req.GetTargetPath())
|
||||
|
||||
if req.GetPublishVolumeInfo() != nil {
|
||||
call.Append(req.GetPublishVolumeInfo()[deviceID])
|
||||
}
|
||||
|
||||
call.AppendSpec(req.GetVolumeId(), fsType, req.GetReadonly(), req.GetVolumeAttributes())
|
||||
_, err = call.Run()
|
||||
if isCmdNotSupportedErr(err) {
|
||||
mountFlags := req.GetVolumeCapability().GetMount().GetMountFlags()
|
||||
err := mountDevice(req.VolumeAttributes[deviceID], targetPath, fsType, req.GetReadonly(), mountFlags)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
} else if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
return &csi.NodePublishVolumeResponse{}, nil
|
||||
}
|
||||
|
||||
func unmountDevice(path string) error {
|
||||
return util.UnmountPath(path, mount.New(""))
|
||||
}
|
||||
|
||||
func (ns *nodeServer) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpublishVolumeRequest) (*csi.NodeUnpublishVolumeResponse, error) {
|
||||
|
||||
call := GetFlexAdapter().flexDriver.NewDriverCall(unmountCmd)
|
||||
call.Append(req.GetTargetPath())
|
||||
|
||||
_, err := call.Run()
|
||||
if isCmdNotSupportedErr(err) {
|
||||
err := unmountDevice(req.GetTargetPath())
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
} else if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
return &csi.NodeUnpublishVolumeResponse{}, nil
|
||||
}
|
84
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/controllerserver.go
generated
vendored
Normal file
84
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/controllerserver.go
generated
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
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 hostpath
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/pborman/uuid"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/kubernetes-csi/drivers/pkg/csi-common"
|
||||
)
|
||||
|
||||
const (
|
||||
deviceID = "deviceID"
|
||||
provisionRoot = "/tmp/"
|
||||
)
|
||||
|
||||
type controllerServer struct {
|
||||
*csicommon.DefaultControllerServer
|
||||
}
|
||||
|
||||
func GetVersionString(ver *csi.Version) string {
|
||||
return fmt.Sprintf("%d.%d.%d", ver.Major, ver.Minor, ver.Patch)
|
||||
}
|
||||
|
||||
func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) {
|
||||
if err := cs.Driver.ValidateControllerServiceRequest(req.Version, csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME); err != nil {
|
||||
glog.V(3).Infof("invalid create volume req: %v", req)
|
||||
return nil, err
|
||||
}
|
||||
volumeId := uuid.NewUUID().String()
|
||||
path := provisionRoot + volumeId
|
||||
err := os.MkdirAll(path, 0777)
|
||||
if err != nil {
|
||||
glog.V(3).Infof("failed to create volume: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
glog.V(4).Infof("create volume %s", path)
|
||||
return &csi.CreateVolumeResponse{
|
||||
VolumeInfo: &csi.VolumeInfo{
|
||||
Id: volumeId,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (cs *controllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest) (*csi.DeleteVolumeResponse, error) {
|
||||
if err := cs.Driver.ValidateControllerServiceRequest(req.Version, csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME); err != nil {
|
||||
glog.V(3).Infof("invalid delete volume req: %v", req)
|
||||
return nil, err
|
||||
}
|
||||
volumeId := req.VolumeId
|
||||
glog.V(4).Infof("deleting volume %s", volumeId)
|
||||
path := provisionRoot + volumeId
|
||||
os.RemoveAll(path)
|
||||
|
||||
return &csi.DeleteVolumeResponse{}, nil
|
||||
}
|
||||
|
||||
func (cs *controllerServer) ValidateVolumeCapabilities(ctx context.Context, req *csi.ValidateVolumeCapabilitiesRequest) (*csi.ValidateVolumeCapabilitiesResponse, error) {
|
||||
for _, cap := range req.VolumeCapabilities {
|
||||
if cap.GetAccessMode().GetMode() != csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER {
|
||||
return &csi.ValidateVolumeCapabilitiesResponse{Supported: false, Message: ""}, nil
|
||||
}
|
||||
}
|
||||
return &csi.ValidateVolumeCapabilitiesResponse{Supported: true, Message: ""}, nil
|
||||
}
|
6
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/extras/docker/Dockerfile
generated
vendored
Normal file
6
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/extras/docker/Dockerfile
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
FROM alpine
|
||||
LABEL maintainers="Kubernetes Authors"
|
||||
LABEL description="HostPath CSI Plugin"
|
||||
|
||||
COPY hostpathplugin /hostpathplugin
|
||||
ENTRYPOINT ["/hostpathplugin"]
|
13
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/extras/docker/Dockerfile.builder
generated
vendored
Normal file
13
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/extras/docker/Dockerfile.builder
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
FROM golang:alpine
|
||||
LABEL maintainers="Kubernetes Authors"
|
||||
LABEL description="HostPath CSI Plugin"
|
||||
|
||||
RUN apk add --no-cache git make wget
|
||||
RUN wget https://github.com/golang/dep/releases/download/v0.3.2/dep-linux-amd64 && \
|
||||
chmod +x dep-linux-amd64 && \
|
||||
mv dep-linux-amd64 /usr/bin/dep
|
||||
RUN go get -d github.com/kubernetes-csi/drivers/app/hostpathplugin
|
||||
RUN cd /go/src/github.com/kubernetes-csi/drivers && \
|
||||
dep ensure && \
|
||||
make hostpath && \
|
||||
cp _output/hostpathplugin /hostpathplugin
|
10
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/extras/docker/make.sh
generated
vendored
Executable file
10
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/extras/docker/make.sh
generated
vendored
Executable file
@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
|
||||
PROG=hostpathplugin
|
||||
|
||||
docker build --rm -f Dockerfile.builder -t ${PROG}:builder .
|
||||
docker run --rm --privileged -v $PWD:/host ${PROG}:builder cp /${PROG} /host/${PROG}
|
||||
sudo chown $USER ${PROG}
|
||||
docker build --rm -t docker.io/k8scsi/${PROG} .
|
||||
docker rmi ${PROG}:builder
|
||||
rm -f ${PROG}
|
86
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/hostpath.go
generated
vendored
Normal file
86
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/hostpath.go
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
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 hostpath
|
||||
|
||||
import (
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/golang/glog"
|
||||
|
||||
"github.com/kubernetes-csi/drivers/pkg/csi-common"
|
||||
)
|
||||
|
||||
type hostPath struct {
|
||||
driver *csicommon.CSIDriver
|
||||
|
||||
ids *identityServer
|
||||
ns *nodeServer
|
||||
cs *controllerServer
|
||||
|
||||
cap []*csi.VolumeCapability_AccessMode
|
||||
cscap []*csi.ControllerServiceCapability
|
||||
}
|
||||
|
||||
var (
|
||||
hostPathDriver *hostPath
|
||||
version = csi.Version{
|
||||
Minor: 1,
|
||||
}
|
||||
)
|
||||
|
||||
func GetSupportedVersions() []*csi.Version {
|
||||
return []*csi.Version{&version}
|
||||
}
|
||||
|
||||
func GetHostPathDriver() *hostPath {
|
||||
return &hostPath{}
|
||||
}
|
||||
|
||||
func NewIdentityServer(d *csicommon.CSIDriver) *identityServer {
|
||||
return &identityServer{
|
||||
DefaultIdentityServer: csicommon.NewDefaultIdentityServer(d),
|
||||
}
|
||||
}
|
||||
|
||||
func NewControllerServer(d *csicommon.CSIDriver) *controllerServer {
|
||||
return &controllerServer{
|
||||
DefaultControllerServer: csicommon.NewDefaultControllerServer(d),
|
||||
}
|
||||
}
|
||||
|
||||
func NewNodeServer(d *csicommon.CSIDriver) *nodeServer {
|
||||
return &nodeServer{
|
||||
DefaultNodeServer: csicommon.NewDefaultNodeServer(d),
|
||||
}
|
||||
}
|
||||
|
||||
func (hp *hostPath) Run(driverName, nodeID, endpoint string) {
|
||||
glog.Infof("Driver: %v version: %v", driverName, GetVersionString(&version))
|
||||
|
||||
// Initialize default library driver
|
||||
hp.driver = csicommon.NewCSIDriver(driverName, &version, GetSupportedVersions(), nodeID)
|
||||
hp.driver.AddControllerServiceCapabilities([]csi.ControllerServiceCapability_RPC_Type{csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME})
|
||||
hp.driver.AddVolumeCapabilityAccessModes([]csi.VolumeCapability_AccessMode_Mode{csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER})
|
||||
|
||||
// Create GRPC servers
|
||||
hp.ids = NewIdentityServer(hp.driver)
|
||||
hp.ns = NewNodeServer(hp.driver)
|
||||
hp.cs = NewControllerServer(hp.driver)
|
||||
|
||||
s := csicommon.NewNonBlockingGRPCServer()
|
||||
s.Start(endpoint, hp.ids, hp.cs, hp.ns)
|
||||
s.Wait()
|
||||
}
|
25
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/identityserver.go
generated
vendored
Normal file
25
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/identityserver.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
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 hostpath
|
||||
|
||||
import (
|
||||
"github.com/kubernetes-csi/drivers/pkg/csi-common"
|
||||
)
|
||||
|
||||
type identityServer struct {
|
||||
*csicommon.DefaultIdentityServer
|
||||
}
|
85
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/nodeserver.go
generated
vendored
Normal file
85
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/nodeserver.go
generated
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
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 hostpath
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
|
||||
"github.com/kubernetes-csi/drivers/pkg/csi-common"
|
||||
)
|
||||
|
||||
type nodeServer struct {
|
||||
*csicommon.DefaultNodeServer
|
||||
}
|
||||
|
||||
func (ns *nodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) {
|
||||
targetPath := req.GetTargetPath()
|
||||
notMnt, err := mount.New("").IsLikelyNotMountPoint(targetPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(targetPath, 0750); err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
notMnt = true
|
||||
} else {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if !notMnt {
|
||||
return &csi.NodePublishVolumeResponse{}, nil
|
||||
}
|
||||
|
||||
fsType := req.GetVolumeCapability().GetMount().GetFsType()
|
||||
|
||||
deviceId := ""
|
||||
if req.GetPublishVolumeInfo() != nil {
|
||||
deviceId = req.GetPublishVolumeInfo()[deviceID]
|
||||
}
|
||||
|
||||
readOnly := req.GetReadonly()
|
||||
volumeId := req.GetVolumeId()
|
||||
attrib := req.GetVolumeAttributes()
|
||||
mountFlags := req.GetVolumeCapability().GetMount().GetMountFlags()
|
||||
|
||||
glog.V(4).Infof("target %v\nfstype %v\ndevice %v\nreadonly %v\nattributes %v\n mountflags %v\n",
|
||||
targetPath, fsType, deviceId, readOnly, volumeId, attrib, mountFlags)
|
||||
|
||||
options := []string{"bind"}
|
||||
if readOnly {
|
||||
options = append(options, "ro")
|
||||
}
|
||||
mounter := mount.New("")
|
||||
path := provisionRoot + volumeId
|
||||
if err := mounter.Mount(path, targetPath, "", options); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &csi.NodePublishVolumeResponse{}, nil
|
||||
}
|
||||
|
||||
func (ns *nodeServer) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpublishVolumeRequest) (*csi.NodeUnpublishVolumeResponse, error) {
|
||||
return &csi.NodeUnpublishVolumeResponse{}, nil
|
||||
}
|
43
vendor/github.com/kubernetes-csi/drivers/pkg/iscsi/README.md
generated
vendored
Normal file
43
vendor/github.com/kubernetes-csi/drivers/pkg/iscsi/README.md
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
# CSI ISCSI driver
|
||||
|
||||
## Usage:
|
||||
|
||||
### Start ISCSI driver
|
||||
```
|
||||
$ sudo ../_output/iscsidriver --endpoint tcp://127.0.0.1:10000 --nodeid CSINode
|
||||
```
|
||||
|
||||
### Test using csc
|
||||
Get ```csc``` tool from https://github.com/chakri-nelluri/gocsi/tree/master/csc
|
||||
|
||||
#### Get plugin info
|
||||
```
|
||||
$ csc identity plugin-info --endpoint tcp://127.0.0.1:10000
|
||||
"ISCSI" "0.1.0"
|
||||
```
|
||||
|
||||
### Get supported versions
|
||||
```
|
||||
$ csc identity supported-versions --endpoint tcp://127.0.0.1:10000
|
||||
0.1.0
|
||||
```
|
||||
|
||||
#### NodePublish a volume
|
||||
```
|
||||
$ export ISCSI_TARGET="iSCSI Target Server IP (Ex: 10.10.10.10)"
|
||||
$ export IQN="Target IQN"
|
||||
$ csc node publish --endpoint tcp://127.0.0.1:10000 --target-path /mnt/iscsi --attrib targetPortal=$ISCSI_TARGET --attrib iqn=$IQN --attrib lun=<lun-id> iscsitestvol
|
||||
iscsitestvol
|
||||
```
|
||||
|
||||
#### NodeUnpublish a volume
|
||||
```
|
||||
$ csc node unpublish --endpoint tcp://127.0.0.1:10000 --target-path /mnt/iscsi iscsitestvol
|
||||
iscsitestvol
|
||||
```
|
||||
|
||||
#### Get NodeID
|
||||
```
|
||||
$ csc node get-id --endpoint tcp://127.0.0.1:10000
|
||||
CSINode
|
||||
```
|
74
vendor/github.com/kubernetes-csi/drivers/pkg/iscsi/driver.go
generated
vendored
Normal file
74
vendor/github.com/kubernetes-csi/drivers/pkg/iscsi/driver.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
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 iscsi
|
||||
|
||||
import (
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/golang/glog"
|
||||
|
||||
"github.com/kubernetes-csi/drivers/pkg/csi-common"
|
||||
)
|
||||
|
||||
type driver struct {
|
||||
csiDriver *csicommon.CSIDriver
|
||||
endpoint string
|
||||
|
||||
ids *csicommon.DefaultIdentityServer
|
||||
ns *nodeServer
|
||||
|
||||
cap []*csi.VolumeCapability_AccessMode
|
||||
cscap []*csi.ControllerServiceCapability
|
||||
}
|
||||
|
||||
const (
|
||||
driverName = "ISCSI"
|
||||
)
|
||||
|
||||
var (
|
||||
version = csi.Version{
|
||||
Minor: 1,
|
||||
}
|
||||
)
|
||||
|
||||
func GetSupportedVersions() []*csi.Version {
|
||||
return []*csi.Version{&version}
|
||||
}
|
||||
|
||||
func NewDriver(nodeID, endpoint string) *driver {
|
||||
glog.Infof("Driver: %v version: %v", driverName, csicommon.GetVersionString(&version))
|
||||
|
||||
d := &driver{}
|
||||
|
||||
d.endpoint = endpoint
|
||||
|
||||
csiDriver := csicommon.NewCSIDriver(driverName, &version, GetSupportedVersions(), nodeID)
|
||||
csiDriver.AddVolumeCapabilityAccessModes([]csi.VolumeCapability_AccessMode_Mode{csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER})
|
||||
|
||||
d.csiDriver = csiDriver
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
func NewNodeServer(d *driver) *nodeServer {
|
||||
return &nodeServer{
|
||||
DefaultNodeServer: csicommon.NewDefaultNodeServer(d.csiDriver),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *driver) Run() {
|
||||
csicommon.RunNodePublishServer(d.endpoint, d.csiDriver, NewNodeServer(d))
|
||||
}
|
148
vendor/github.com/kubernetes-csi/drivers/pkg/iscsi/iscsi.go
generated
vendored
Normal file
148
vendor/github.com/kubernetes-csi/drivers/pkg/iscsi/iscsi.go
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
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 iscsi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
"k8s.io/kubernetes/pkg/volume/util"
|
||||
)
|
||||
|
||||
func getISCSIInfo(req *csi.NodePublishVolumeRequest) (*iscsiDisk, error) {
|
||||
volName := req.GetVolumeId()
|
||||
tp := req.GetVolumeAttributes()["targetPortal"]
|
||||
iqn := req.GetVolumeAttributes()["iqn"]
|
||||
lun := req.GetVolumeAttributes()["lun"]
|
||||
if tp == "" || iqn == "" || lun == "" {
|
||||
return nil, fmt.Errorf("iSCSI target information is missing")
|
||||
}
|
||||
|
||||
portalList := req.GetVolumeAttributes()["portals"]
|
||||
secretParams := req.GetVolumeAttributes()["secret"]
|
||||
secret := parseSecret(secretParams)
|
||||
|
||||
portal := portalMounter(tp)
|
||||
var bkportal []string
|
||||
bkportal = append(bkportal, portal)
|
||||
|
||||
portals := []string{}
|
||||
if err := json.Unmarshal([]byte(portalList), &portals); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, portal := range portals {
|
||||
bkportal = append(bkportal, portalMounter(string(portal)))
|
||||
}
|
||||
|
||||
iface := req.GetVolumeAttributes()["iscsiInterface"]
|
||||
initiatorName := req.GetVolumeAttributes()["initiatorName"]
|
||||
chapDiscovery := false
|
||||
if req.GetVolumeAttributes()["discoveryCHAPAuth"] == "true" {
|
||||
chapDiscovery = true
|
||||
}
|
||||
|
||||
chapSession := false
|
||||
if req.GetVolumeAttributes()["sessionCHAPAuth"] == "true" {
|
||||
chapSession = true
|
||||
}
|
||||
|
||||
return &iscsiDisk{
|
||||
VolName: volName,
|
||||
Portals: bkportal,
|
||||
Iqn: iqn,
|
||||
lun: lun,
|
||||
Iface: iface,
|
||||
chap_discovery: chapDiscovery,
|
||||
chap_session: chapSession,
|
||||
secret: secret,
|
||||
InitiatorName: initiatorName}, nil
|
||||
}
|
||||
|
||||
func getISCSIDiskMounter(iscsiInfo *iscsiDisk, req *csi.NodePublishVolumeRequest) *iscsiDiskMounter {
|
||||
readOnly := req.GetReadonly()
|
||||
fsType := req.GetVolumeCapability().GetMount().GetFsType()
|
||||
mountOptions := req.GetVolumeCapability().GetMount().GetMountFlags()
|
||||
|
||||
return &iscsiDiskMounter{
|
||||
iscsiDisk: iscsiInfo,
|
||||
fsType: fsType,
|
||||
readOnly: readOnly,
|
||||
mountOptions: mountOptions,
|
||||
mounter: &mount.SafeFormatAndMount{Interface: mount.New(""), Exec: mount.NewOsExec()},
|
||||
exec: mount.NewOsExec(),
|
||||
targetPath: req.GetTargetPath(),
|
||||
deviceUtil: util.NewDeviceHandler(util.NewIOHandler()),
|
||||
}
|
||||
}
|
||||
|
||||
func getISCSIDiskUnmounter(req *csi.NodeUnpublishVolumeRequest) *iscsiDiskUnmounter {
|
||||
return &iscsiDiskUnmounter{
|
||||
iscsiDisk: &iscsiDisk{
|
||||
VolName: req.GetVolumeId(),
|
||||
},
|
||||
mounter: mount.New(""),
|
||||
exec: mount.NewOsExec(),
|
||||
}
|
||||
}
|
||||
|
||||
func portalMounter(portal string) string {
|
||||
if !strings.Contains(portal, ":") {
|
||||
portal = portal + ":3260"
|
||||
}
|
||||
return portal
|
||||
}
|
||||
|
||||
func parseSecret(secretParams string) map[string]string {
|
||||
var secret map[string]string
|
||||
if err := json.Unmarshal([]byte(secretParams), &secret); err != nil {
|
||||
return nil
|
||||
}
|
||||
return secret
|
||||
}
|
||||
|
||||
type iscsiDisk struct {
|
||||
Portals []string
|
||||
Iqn string
|
||||
lun string
|
||||
Iface string
|
||||
chap_discovery bool
|
||||
chap_session bool
|
||||
secret map[string]string
|
||||
InitiatorName string
|
||||
VolName string
|
||||
}
|
||||
|
||||
type iscsiDiskMounter struct {
|
||||
*iscsiDisk
|
||||
readOnly bool
|
||||
fsType string
|
||||
mountOptions []string
|
||||
mounter *mount.SafeFormatAndMount
|
||||
exec mount.Exec
|
||||
deviceUtil util.DeviceUtil
|
||||
targetPath string
|
||||
}
|
||||
|
||||
type iscsiDiskUnmounter struct {
|
||||
*iscsiDisk
|
||||
mounter mount.Interface
|
||||
exec mount.Exec
|
||||
}
|
478
vendor/github.com/kubernetes-csi/drivers/pkg/iscsi/iscsi_util.go
generated
vendored
Normal file
478
vendor/github.com/kubernetes-csi/drivers/pkg/iscsi/iscsi_util.go
generated
vendored
Normal file
@ -0,0 +1,478 @@
|
||||
/*
|
||||
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 iscsi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
volumeutil "k8s.io/kubernetes/pkg/volume/util"
|
||||
)
|
||||
|
||||
var (
|
||||
chap_st = []string{
|
||||
"discovery.sendtargets.auth.username",
|
||||
"discovery.sendtargets.auth.password",
|
||||
"discovery.sendtargets.auth.username_in",
|
||||
"discovery.sendtargets.auth.password_in"}
|
||||
chap_sess = []string{
|
||||
"node.session.auth.username",
|
||||
"node.session.auth.password",
|
||||
"node.session.auth.username_in",
|
||||
"node.session.auth.password_in"}
|
||||
ifaceTransportNameRe = regexp.MustCompile(`iface.transport_name = (.*)\n`)
|
||||
)
|
||||
|
||||
func updateISCSIDiscoverydb(b iscsiDiskMounter, tp string) error {
|
||||
if !b.chap_discovery {
|
||||
return nil
|
||||
}
|
||||
out, err := b.exec.Run("iscsiadm", "-m", "discoverydb", "-t", "sendtargets", "-p", tp, "-I", b.Iface, "-o", "update", "-n", "discovery.sendtargets.auth.authmethod", "-v", "CHAP")
|
||||
if err != nil {
|
||||
return fmt.Errorf("iscsi: failed to update discoverydb with CHAP, output: %v", string(out))
|
||||
}
|
||||
|
||||
for _, k := range chap_st {
|
||||
v := b.secret[k]
|
||||
if len(v) > 0 {
|
||||
out, err := b.exec.Run("iscsiadm", "-m", "discoverydb", "-t", "sendtargets", "-p", tp, "-I", b.Iface, "-o", "update", "-n", k, "-v", v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("iscsi: failed to update discoverydb key %q with value %q error: %v", k, v, string(out))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateISCSINode(b iscsiDiskMounter, tp string) error {
|
||||
if !b.chap_session {
|
||||
return nil
|
||||
}
|
||||
|
||||
out, err := b.exec.Run("iscsiadm", "-m", "node", "-p", tp, "-T", b.Iqn, "-I", b.Iface, "-o", "update", "-n", "node.session.auth.authmethod", "-v", "CHAP")
|
||||
if err != nil {
|
||||
return fmt.Errorf("iscsi: failed to update node with CHAP, output: %v", string(out))
|
||||
}
|
||||
|
||||
for _, k := range chap_sess {
|
||||
v := b.secret[k]
|
||||
if len(v) > 0 {
|
||||
out, err := b.exec.Run("iscsiadm", "-m", "node", "-p", tp, "-T", b.Iqn, "-I", b.Iface, "-o", "update", "-n", k, "-v", v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("iscsi: failed to update node session key %q with value %q error: %v", k, v, string(out))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// stat a path, if not exists, retry maxRetries times
|
||||
// when iscsi transports other than default are used, use glob instead as pci id of device is unknown
|
||||
type StatFunc func(string) (os.FileInfo, error)
|
||||
type GlobFunc func(string) ([]string, error)
|
||||
|
||||
func waitForPathToExist(devicePath *string, maxRetries int, deviceTransport string) bool {
|
||||
// This makes unit testing a lot easier
|
||||
return waitForPathToExistInternal(devicePath, maxRetries, deviceTransport, os.Stat, filepath.Glob)
|
||||
}
|
||||
|
||||
func waitForPathToExistInternal(devicePath *string, maxRetries int, deviceTransport string, osStat StatFunc, filepathGlob GlobFunc) bool {
|
||||
if devicePath == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < maxRetries; i++ {
|
||||
var err error
|
||||
if deviceTransport == "tcp" {
|
||||
_, err = osStat(*devicePath)
|
||||
} else {
|
||||
fpath, _ := filepathGlob(*devicePath)
|
||||
if fpath == nil {
|
||||
err = os.ErrNotExist
|
||||
} else {
|
||||
// There might be a case that fpath contains multiple device paths if
|
||||
// multiple PCI devices connect to same iscsi target. We handle this
|
||||
// case at subsequent logic. Pick up only first path here.
|
||||
*devicePath = fpath[0]
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
if !os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
if i == maxRetries-1 {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type ISCSIUtil struct{}
|
||||
|
||||
func (util *ISCSIUtil) persistISCSI(conf iscsiDisk, mnt string) error {
|
||||
file := path.Join(mnt, conf.VolName+".json")
|
||||
fp, err := os.Create(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("iscsi: create %s err %s", file, err)
|
||||
}
|
||||
defer fp.Close()
|
||||
encoder := json.NewEncoder(fp)
|
||||
if err = encoder.Encode(conf); err != nil {
|
||||
return fmt.Errorf("iscsi: encode err: %v.", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (util *ISCSIUtil) loadISCSI(conf *iscsiDisk, mnt string) error {
|
||||
// NOTE: The iscsi config json is not deleted after logging out from target portals.
|
||||
file := path.Join(mnt, conf.VolName+".json")
|
||||
fp, err := os.Open(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("iscsi: open %s err %s", file, err)
|
||||
}
|
||||
defer fp.Close()
|
||||
decoder := json.NewDecoder(fp)
|
||||
if err = decoder.Decode(conf); err != nil {
|
||||
return fmt.Errorf("iscsi: decode err: %v.", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (util *ISCSIUtil) AttachDisk(b iscsiDiskMounter) (string, error) {
|
||||
var devicePath string
|
||||
var devicePaths []string
|
||||
var iscsiTransport string
|
||||
var lastErr error
|
||||
|
||||
out, err := b.exec.Run("iscsiadm", "-m", "iface", "-I", b.Iface, "-o", "show")
|
||||
if err != nil {
|
||||
glog.Errorf("iscsi: could not read iface %s error: %s", b.Iface, string(out))
|
||||
return "", err
|
||||
}
|
||||
|
||||
iscsiTransport = extractTransportname(string(out))
|
||||
|
||||
bkpPortal := b.Portals
|
||||
|
||||
// create new iface and copy parameters from pre-configured iface to the created iface
|
||||
if b.InitiatorName != "" {
|
||||
// new iface name is <target portal>:<volume name>
|
||||
newIface := bkpPortal[0] + ":" + b.VolName
|
||||
err = cloneIface(b, newIface)
|
||||
if err != nil {
|
||||
glog.Errorf("iscsi: failed to clone iface: %s error: %v", b.Iface, err)
|
||||
return "", err
|
||||
}
|
||||
// update iface name
|
||||
b.Iface = newIface
|
||||
}
|
||||
|
||||
for _, tp := range bkpPortal {
|
||||
// Rescan sessions to discover newly mapped LUNs. Do not specify the interface when rescanning
|
||||
// to avoid establishing additional sessions to the same target.
|
||||
out, err := b.exec.Run("iscsiadm", "-m", "node", "-p", tp, "-T", b.Iqn, "-R")
|
||||
if err != nil {
|
||||
glog.Errorf("iscsi: failed to rescan session with error: %s (%v)", string(out), err)
|
||||
}
|
||||
|
||||
if iscsiTransport == "" {
|
||||
glog.Errorf("iscsi: could not find transport name in iface %s", b.Iface)
|
||||
return "", fmt.Errorf("Could not parse iface file for %s", b.Iface)
|
||||
}
|
||||
if iscsiTransport == "tcp" {
|
||||
devicePath = strings.Join([]string{"/dev/disk/by-path/ip", tp, "iscsi", b.Iqn, "lun", b.lun}, "-")
|
||||
} else {
|
||||
devicePath = strings.Join([]string{"/dev/disk/by-path/pci", "*", "ip", tp, "iscsi", b.Iqn, "lun", b.lun}, "-")
|
||||
}
|
||||
|
||||
if exist := waitForPathToExist(&devicePath, 1, iscsiTransport); exist {
|
||||
glog.V(4).Infof("iscsi: devicepath (%s) exists", devicePath)
|
||||
devicePaths = append(devicePaths, devicePath)
|
||||
continue
|
||||
}
|
||||
// build discoverydb and discover iscsi target
|
||||
b.exec.Run("iscsiadm", "-m", "discoverydb", "-t", "sendtargets", "-p", tp, "-I", b.Iface, "-o", "new")
|
||||
// update discoverydb with CHAP secret
|
||||
err = updateISCSIDiscoverydb(b, tp)
|
||||
if err != nil {
|
||||
lastErr = fmt.Errorf("iscsi: failed to update discoverydb to portal %s error: %v", tp, err)
|
||||
continue
|
||||
}
|
||||
out, err = b.exec.Run("iscsiadm", "-m", "discoverydb", "-t", "sendtargets", "-p", tp, "-I", b.Iface, "--discover")
|
||||
if err != nil {
|
||||
// delete discoverydb record
|
||||
b.exec.Run("iscsiadm", "-m", "discoverydb", "-t", "sendtargets", "-p", tp, "-I", b.Iface, "-o", "delete")
|
||||
lastErr = fmt.Errorf("iscsi: failed to sendtargets to portal %s output: %s, err %v", tp, string(out), err)
|
||||
continue
|
||||
}
|
||||
err = updateISCSINode(b, tp)
|
||||
if err != nil {
|
||||
// failure to update node db is rare. But deleting record will likely impact those who already start using it.
|
||||
lastErr = fmt.Errorf("iscsi: failed to update iscsi node to portal %s error: %v", tp, err)
|
||||
continue
|
||||
}
|
||||
// login to iscsi target
|
||||
out, err = b.exec.Run("iscsiadm", "-m", "node", "-p", tp, "-T", b.Iqn, "-I", b.Iface, "--login")
|
||||
if err != nil {
|
||||
// delete the node record from database
|
||||
b.exec.Run("iscsiadm", "-m", "node", "-p", tp, "-I", b.Iface, "-T", b.Iqn, "-o", "delete")
|
||||
lastErr = fmt.Errorf("iscsi: failed to attach disk: Error: %s (%v)", string(out), err)
|
||||
continue
|
||||
}
|
||||
if exist := waitForPathToExist(&devicePath, 10, iscsiTransport); !exist {
|
||||
glog.Errorf("Could not attach disk: Timeout after 10s")
|
||||
// update last error
|
||||
lastErr = fmt.Errorf("Could not attach disk: Timeout after 10s")
|
||||
continue
|
||||
} else {
|
||||
devicePaths = append(devicePaths, devicePath)
|
||||
}
|
||||
}
|
||||
|
||||
if len(devicePaths) == 0 {
|
||||
// delete cloned iface
|
||||
b.exec.Run("iscsiadm", "-m", "iface", "-I", b.Iface, "-o", "delete")
|
||||
glog.Errorf("iscsi: failed to get any path for iscsi disk, last err seen:\n%v", lastErr)
|
||||
return "", fmt.Errorf("failed to get any path for iscsi disk, last err seen:\n%v", lastErr)
|
||||
}
|
||||
if lastErr != nil {
|
||||
glog.Errorf("iscsi: last error occurred during iscsi init:\n%v", lastErr)
|
||||
}
|
||||
|
||||
// Make sure we use a valid devicepath to find mpio device.
|
||||
devicePath = devicePaths[0]
|
||||
|
||||
// Mount device
|
||||
mntPath := path.Join(b.targetPath, b.VolName)
|
||||
notMnt, err := b.mounter.IsLikelyNotMountPoint(mntPath)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return "", fmt.Errorf("Heuristic determination of mount point failed:%v", err)
|
||||
}
|
||||
if !notMnt {
|
||||
glog.Infof("iscsi: %s already mounted", mntPath)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(mntPath, 0750); err != nil {
|
||||
glog.Errorf("iscsi: failed to mkdir %s, error", mntPath)
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Persist iscsi disk config to json file for DetachDisk path
|
||||
if err := util.persistISCSI(*(b.iscsiDisk), b.targetPath); err != nil {
|
||||
glog.Errorf("iscsi: failed to save iscsi config with error: %v", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, path := range devicePaths {
|
||||
// There shouldnt be any empty device paths. However adding this check
|
||||
// for safer side to avoid the possibility of an empty entry.
|
||||
if path == "" {
|
||||
continue
|
||||
}
|
||||
// check if the dev is using mpio and if so mount it via the dm-XX device
|
||||
if mappedDevicePath := b.deviceUtil.FindMultipathDeviceForDevice(path); mappedDevicePath != "" {
|
||||
devicePath = mappedDevicePath
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var options []string
|
||||
|
||||
if b.readOnly {
|
||||
options = append(options, "ro")
|
||||
} else {
|
||||
options = append(options, "rw")
|
||||
}
|
||||
options = append(options, b.mountOptions...)
|
||||
|
||||
err = b.mounter.FormatAndMount(devicePath, mntPath, b.fsType, options)
|
||||
if err != nil {
|
||||
glog.Errorf("iscsi: failed to mount iscsi volume %s [%s] to %s, error %v", devicePath, b.fsType, mntPath, err)
|
||||
}
|
||||
|
||||
return devicePath, err
|
||||
}
|
||||
|
||||
func (util *ISCSIUtil) DetachDisk(c iscsiDiskUnmounter, targetPath string) error {
|
||||
mntPath := path.Join(targetPath, c.VolName)
|
||||
_, cnt, err := mount.GetDeviceNameFromMount(c.mounter, mntPath)
|
||||
if err != nil {
|
||||
glog.Errorf("iscsi detach disk: failed to get device from mnt: %s\nError: %v", mntPath, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if pathExists, pathErr := volumeutil.PathExists(mntPath); pathErr != nil {
|
||||
return fmt.Errorf("Error checking if path exists: %v", pathErr)
|
||||
} else if !pathExists {
|
||||
glog.Warningf("Warning: Unmount skipped because path does not exist: %v", mntPath)
|
||||
return nil
|
||||
}
|
||||
if err = c.mounter.Unmount(mntPath); err != nil {
|
||||
glog.Errorf("iscsi detach disk: failed to unmount: %s\nError: %v", mntPath, err)
|
||||
return err
|
||||
}
|
||||
cnt--
|
||||
if cnt != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var bkpPortal []string
|
||||
var volName, iqn, iface, initiatorName string
|
||||
found := true
|
||||
|
||||
// load iscsi disk config from json file
|
||||
if err := util.loadISCSI(c.iscsiDisk, targetPath); err == nil {
|
||||
bkpPortal, iqn, iface, volName = c.iscsiDisk.Portals, c.iscsiDisk.Iqn, c.iscsiDisk.Iface, c.iscsiDisk.VolName
|
||||
initiatorName = c.iscsiDisk.InitiatorName
|
||||
} else {
|
||||
glog.Errorf("iscsi detach disk: failed to get iscsi config from path %s Error: %v", targetPath, err)
|
||||
return err
|
||||
}
|
||||
portals := removeDuplicate(bkpPortal)
|
||||
if len(portals) == 0 {
|
||||
return fmt.Errorf("iscsi detach disk: failed to detach iscsi disk. Couldn't get connected portals from configurations.")
|
||||
}
|
||||
|
||||
for _, portal := range portals {
|
||||
logoutArgs := []string{"-m", "node", "-p", portal, "-T", iqn, "--logout"}
|
||||
deleteArgs := []string{"-m", "node", "-p", portal, "-T", iqn, "-o", "delete"}
|
||||
if found {
|
||||
logoutArgs = append(logoutArgs, []string{"-I", iface}...)
|
||||
deleteArgs = append(deleteArgs, []string{"-I", iface}...)
|
||||
}
|
||||
glog.Infof("iscsi: log out target %s iqn %s iface %s", portal, iqn, iface)
|
||||
out, err := c.exec.Run("iscsiadm", logoutArgs...)
|
||||
if err != nil {
|
||||
glog.Errorf("iscsi: failed to detach disk Error: %s", string(out))
|
||||
}
|
||||
// Delete the node record
|
||||
glog.Infof("iscsi: delete node record target %s iqn %s", portal, iqn)
|
||||
out, err = c.exec.Run("iscsiadm", deleteArgs...)
|
||||
if err != nil {
|
||||
glog.Errorf("iscsi: failed to delete node record Error: %s", string(out))
|
||||
}
|
||||
}
|
||||
// Delete the iface after all sessions have logged out
|
||||
// If the iface is not created via iscsi plugin, skip to delete
|
||||
if initiatorName != "" && found && iface == (portals[0]+":"+volName) {
|
||||
deleteArgs := []string{"-m", "iface", "-I", iface, "-o", "delete"}
|
||||
out, err := c.exec.Run("iscsiadm", deleteArgs...)
|
||||
if err != nil {
|
||||
glog.Errorf("iscsi: failed to delete iface Error: %s", string(out))
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.RemoveAll(targetPath); err != nil {
|
||||
glog.Errorf("iscsi: failed to remove mount path Error: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func extractTransportname(ifaceOutput string) (iscsiTransport string) {
|
||||
rexOutput := ifaceTransportNameRe.FindStringSubmatch(ifaceOutput)
|
||||
if rexOutput == nil {
|
||||
return ""
|
||||
}
|
||||
iscsiTransport = rexOutput[1]
|
||||
|
||||
// While iface.transport_name is a required parameter, handle it being unspecified anyways
|
||||
if iscsiTransport == "<empty>" {
|
||||
iscsiTransport = "tcp"
|
||||
}
|
||||
return iscsiTransport
|
||||
}
|
||||
|
||||
// Remove duplicates or string
|
||||
func removeDuplicate(s []string) []string {
|
||||
m := map[string]bool{}
|
||||
for _, v := range s {
|
||||
if v != "" && !m[v] {
|
||||
s[len(m)] = v
|
||||
m[v] = true
|
||||
}
|
||||
}
|
||||
s = s[:len(m)]
|
||||
return s
|
||||
}
|
||||
|
||||
func parseIscsiadmShow(output string) (map[string]string, error) {
|
||||
params := make(map[string]string)
|
||||
slice := strings.Split(output, "\n")
|
||||
for _, line := range slice {
|
||||
if !strings.HasPrefix(line, "iface.") || strings.Contains(line, "<empty>") {
|
||||
continue
|
||||
}
|
||||
iface := strings.Fields(line)
|
||||
if len(iface) != 3 || iface[1] != "=" {
|
||||
return nil, fmt.Errorf("Error: invalid iface setting: %v", iface)
|
||||
}
|
||||
// iscsi_ifacename is immutable once the iface is created
|
||||
if iface[0] == "iface.iscsi_ifacename" {
|
||||
continue
|
||||
}
|
||||
params[iface[0]] = iface[2]
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func cloneIface(b iscsiDiskMounter, newIface string) error {
|
||||
var lastErr error
|
||||
// get pre-configured iface records
|
||||
out, err := b.exec.Run("iscsiadm", "-m", "iface", "-I", b.Iface, "-o", "show")
|
||||
if err != nil {
|
||||
lastErr = fmt.Errorf("iscsi: failed to show iface records: %s (%v)", string(out), err)
|
||||
return lastErr
|
||||
}
|
||||
// parse obtained records
|
||||
params, err := parseIscsiadmShow(string(out))
|
||||
if err != nil {
|
||||
lastErr = fmt.Errorf("iscsi: failed to parse iface records: %s (%v)", string(out), err)
|
||||
return lastErr
|
||||
}
|
||||
// update initiatorname
|
||||
params["iface.initiatorname"] = b.InitiatorName
|
||||
// create new iface
|
||||
out, err = b.exec.Run("iscsiadm", "-m", "iface", "-I", newIface, "-o", "new")
|
||||
if err != nil {
|
||||
lastErr = fmt.Errorf("iscsi: failed to create new iface: %s (%v)", string(out), err)
|
||||
return lastErr
|
||||
}
|
||||
// update new iface records
|
||||
for key, val := range params {
|
||||
_, err = b.exec.Run("iscsiadm", "-m", "iface", "-I", newIface, "-o", "update", "-n", key, "-v", val)
|
||||
if err != nil {
|
||||
b.exec.Run("iscsiadm", "-m", "iface", "-I", newIface, "-o", "delete")
|
||||
lastErr = fmt.Errorf("iscsi: failed to update iface records: %s (%v). iface(%s) will be used", string(out), err, b.Iface)
|
||||
break
|
||||
}
|
||||
}
|
||||
return lastErr
|
||||
}
|
59
vendor/github.com/kubernetes-csi/drivers/pkg/iscsi/nodeserver.go
generated
vendored
Normal file
59
vendor/github.com/kubernetes-csi/drivers/pkg/iscsi/nodeserver.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
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 iscsi
|
||||
|
||||
import (
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/kubernetes-csi/drivers/pkg/csi-common"
|
||||
)
|
||||
|
||||
type nodeServer struct {
|
||||
*csicommon.DefaultNodeServer
|
||||
}
|
||||
|
||||
func (ns *nodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) {
|
||||
iscsiInfo, err := getISCSIInfo(req)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
diskMounter := getISCSIDiskMounter(iscsiInfo, req)
|
||||
|
||||
util := &ISCSIUtil{}
|
||||
_, err = util.AttachDisk(*diskMounter)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
return &csi.NodePublishVolumeResponse{}, nil
|
||||
}
|
||||
|
||||
func (ns *nodeServer) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpublishVolumeRequest) (*csi.NodeUnpublishVolumeResponse, error) {
|
||||
diskUnmounter := getISCSIDiskUnmounter(req)
|
||||
targetPath := req.GetTargetPath()
|
||||
|
||||
iscsiutil := &ISCSIUtil{}
|
||||
err := iscsiutil.DetachDisk(*diskUnmounter, targetPath)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
return &csi.NodeUnpublishVolumeResponse{}, nil
|
||||
}
|
70
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/README.md
generated
vendored
Normal file
70
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/README.md
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
# CSI NFS driver
|
||||
|
||||
|
||||
## Kubernetes
|
||||
### Requirements
|
||||
|
||||
The folllowing feature gates and runtime config have to be enabled to deploy the driver
|
||||
|
||||
```
|
||||
FEATURE_GATES=CSIPersistentVolume=true,MountPropagation=true
|
||||
RUNTIME_CONFIG="storage.k8s.io/v1alpha1=true"
|
||||
```
|
||||
|
||||
Mountprogpation requries support for privileged containers. So, make sure privileged containers are enabled in the cluster.
|
||||
|
||||
### Example local-up-cluster.sh
|
||||
|
||||
```ALLOW_PRIVILEGED=true FEATURE_GATES=CSIPersistentVolume=true,MountPropagation=true RUNTIME_CONFIG="storage.k8s.io/v1alpha1=true" LOG_LEVEL=5 hack/local-up-cluster.sh```
|
||||
|
||||
### Deploy
|
||||
|
||||
```kubectl -f deploy/kubernetes create```
|
||||
|
||||
### Example Nginx application
|
||||
Please update the NFS Server & share information in nginx.yaml file.
|
||||
|
||||
```kubectl -f examples/kubernetes/nginx.yaml create```
|
||||
|
||||
## Using CSC tool
|
||||
|
||||
### Start NFS driver
|
||||
```
|
||||
$ sudo ../../_output/flexadapter --endpoint tcp://127.0.0.1:10000 --drivername simplenfs --driverpath ./examples/simplenfs-flexdriver/driver/nfs --nodeid CSINode -v=3
|
||||
```
|
||||
|
||||
## Test
|
||||
Get ```csc``` tool from https://github.com/thecodeteam/gocsi/tree/master/csc
|
||||
|
||||
#### Get plugin info
|
||||
```
|
||||
$ csc identity plugininfo --endpoint tcp://127.0.0.1:10000
|
||||
"NFS" "0.1.0"
|
||||
```
|
||||
|
||||
### Get supported versions
|
||||
```
|
||||
$ csc identity supportedversions --endpoint tcp://127.0.0.1:10000
|
||||
0.1.0
|
||||
```
|
||||
|
||||
#### NodePublish a volume
|
||||
```
|
||||
$ export NFS_SERVER="Your Server IP (Ex: 10.10.10.10)"
|
||||
$ export NFS_SHARE="Your NFS share"
|
||||
$ csc node publishvolume --endpoint tcp://127.0.0.1:10000 --target-path /mnt/nfs --attrib server=$NFS_SERVER --attrib share=$NFS_SHARE nfstestvol
|
||||
nfstestvol
|
||||
```
|
||||
|
||||
#### NodeUnpublish a volume
|
||||
```
|
||||
$ csc node unpublishvolume --endpoint tcp://127.0.0.1:10000 --target-path /mnt/nfs nfstestvol
|
||||
nfstestvol
|
||||
```
|
||||
|
||||
#### Get NodeID
|
||||
```
|
||||
$ csc node getid --endpoint tcp://127.0.0.1:10000
|
||||
CSINode
|
||||
```
|
||||
|
64
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/deploy/kubernetes/csi-attacher-nfsplugin.yaml
generated
vendored
Normal file
64
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/deploy/kubernetes/csi-attacher-nfsplugin.yaml
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
# This YAML file contains attacher & csi driver API objects that are necessary
|
||||
# to run external CSI attacher for nfs
|
||||
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: csi-attacher-nfsplugin
|
||||
labels:
|
||||
app: csi-attacher-nfsplugin
|
||||
spec:
|
||||
selector:
|
||||
app: csi-attacher-nfsplugin
|
||||
ports:
|
||||
- name: dummy
|
||||
port: 12345
|
||||
|
||||
---
|
||||
kind: StatefulSet
|
||||
apiVersion: apps/v1beta1
|
||||
metadata:
|
||||
name: csi-attacher-nfsplugin
|
||||
spec:
|
||||
serviceName: "csi-attacher"
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: csi-attacher-nfsplugin
|
||||
spec:
|
||||
serviceAccount: csi-attacher
|
||||
containers:
|
||||
- name: csi-attacher
|
||||
image: docker.io/k8scsi/csi-attacher
|
||||
args:
|
||||
- "--v=5"
|
||||
- "--csi-address=$(ADDRESS)"
|
||||
env:
|
||||
- name: ADDRESS
|
||||
value: /var/lib/csi/sockets/pluginproxy/csi.sock
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
volumeMounts:
|
||||
- name: socket-dir
|
||||
mountPath: /var/lib/csi/sockets/pluginproxy/
|
||||
|
||||
- name: nfs
|
||||
image: docker.io/k8s/nfsplugin:v0.1
|
||||
args :
|
||||
- "--nodeid=$(NODE_ID)"
|
||||
- "--endpoint=$(CSI_ENDPOINT)"
|
||||
env:
|
||||
- name: NODE_ID
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.nodeName
|
||||
- name: CSI_ENDPOINT
|
||||
value: unix://plugin/csi.sock
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
volumeMounts:
|
||||
- name: socket-dir
|
||||
mountPath: /plugin
|
||||
volumes:
|
||||
- name: socket-dir
|
||||
emptyDir:
|
||||
|
37
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/deploy/kubernetes/csi-attacher-rbac.yaml
generated
vendored
Normal file
37
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/deploy/kubernetes/csi-attacher-rbac.yaml
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
# This YAML file contains RBAC API objects that are necessary to run external
|
||||
# CSI attacher for nfs flex adapter
|
||||
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: csi-attacher
|
||||
|
||||
---
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: external-attacher-runner
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["persistentvolumes"]
|
||||
verbs: ["get", "list", "watch", "update"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: ["storage.k8s.io"]
|
||||
resources: ["volumeattachments"]
|
||||
verbs: ["get", "list", "watch", "update"]
|
||||
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: csi-attacher-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: csi-attacher
|
||||
namespace: default
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: external-attacher-runner
|
||||
apiGroup: rbac.authorization.k8s.io
|
66
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/deploy/kubernetes/csi-nodeplugin-nfsplugin.yaml
generated
vendored
Normal file
66
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/deploy/kubernetes/csi-nodeplugin-nfsplugin.yaml
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
# This YAML file contains driver-registrar & csi driver nodeplugin API objects
|
||||
# that are necessary to run CSI nodeplugin for nfs
|
||||
kind: DaemonSet
|
||||
apiVersion: apps/v1beta2
|
||||
metadata:
|
||||
name: csi-nodeplugin-nfsplugin
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: csi-nodeplugin-nfsplugin
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: csi-nodeplugin-nfsplugin
|
||||
spec:
|
||||
serviceAccount: csi-nodeplugin
|
||||
hostNetwork: true
|
||||
containers:
|
||||
- name: driver-registrar
|
||||
image: docker.io/k8scsi/driver-registrar
|
||||
args:
|
||||
- "--v=5"
|
||||
- "--csi-address=$(ADDRESS)"
|
||||
env:
|
||||
- name: ADDRESS
|
||||
value: /plugin/csi.sock
|
||||
- name: KUBE_NODE_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.nodeName
|
||||
volumeMounts:
|
||||
- name: plugin-dir
|
||||
mountPath: /plugin
|
||||
- name: nfs
|
||||
securityContext:
|
||||
privileged: true
|
||||
capabilities:
|
||||
add: ["SYS_ADMIN"]
|
||||
allowPrivilegeEscalation: true
|
||||
image: docker.io/k8s/nfsplugin:v0.1
|
||||
args :
|
||||
- "--nodeid=$(NODE_ID)"
|
||||
- "--endpoint=$(CSI_ENDPOINT)"
|
||||
env:
|
||||
- name: NODE_ID
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.nodeName
|
||||
- name: CSI_ENDPOINT
|
||||
value: unix://plugin/csi.sock
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
volumeMounts:
|
||||
- name: plugin-dir
|
||||
mountPath: /plugin
|
||||
- name: pods-mount-dir
|
||||
mountPath: /var/lib/kubelet/pods
|
||||
mountPropagation: "Bidirectional"
|
||||
volumes:
|
||||
- name: plugin-dir
|
||||
hostPath:
|
||||
path: /var/lib/kubelet/plugins/csi-nfsplugin
|
||||
type: DirectoryOrCreate
|
||||
- name: pods-mount-dir
|
||||
hostPath:
|
||||
path: /var/lib/kubelet/pods
|
||||
type: Directory
|
34
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/deploy/kubernetes/csi-nodeplugin-rbac.yaml
generated
vendored
Normal file
34
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/deploy/kubernetes/csi-nodeplugin-rbac.yaml
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
# This YAML defines all API objects to create RBAC roles for CSI node plugin
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: csi-nodeplugin
|
||||
|
||||
---
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: csi-nodeplugin
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["persistentvolumes"]
|
||||
verbs: ["get", "list", "watch", "update"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["get", "list", "watch", "update"]
|
||||
- apiGroups: ["storage.k8s.io"]
|
||||
resources: ["volumeattachments"]
|
||||
verbs: ["get", "list", "watch", "update"]
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: csi-nodeplugin
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: csi-nodeplugin
|
||||
namespace: default
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: csi-nodeplugin
|
||||
apiGroup: rbac.authorization.k8s.io
|
8
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/dockerfile/Dockerfile
generated
vendored
Normal file
8
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/dockerfile/Dockerfile
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
FROM centos:7.4.1708
|
||||
|
||||
# Copy nfsplugin from build _output directory
|
||||
COPY nfsplugin /nfsplugin
|
||||
|
||||
RUN yum -y install nfs-utils && yum -y install epel-release && yum -y install jq && yum clean all
|
||||
|
||||
ENTRYPOINT ["/nfsplugin"]
|
79
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/driver.go
generated
vendored
Normal file
79
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/driver.go
generated
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
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 nfs
|
||||
|
||||
import (
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/golang/glog"
|
||||
|
||||
"github.com/kubernetes-csi/drivers/pkg/csi-common"
|
||||
)
|
||||
|
||||
type driver struct {
|
||||
csiDriver *csicommon.CSIDriver
|
||||
endpoint string
|
||||
|
||||
ids *csicommon.DefaultIdentityServer
|
||||
ns *nodeServer
|
||||
|
||||
cap []*csi.VolumeCapability_AccessMode
|
||||
cscap []*csi.ControllerServiceCapability
|
||||
}
|
||||
|
||||
const (
|
||||
driverName = "csi-nfsplugin"
|
||||
)
|
||||
|
||||
var (
|
||||
version = csi.Version{
|
||||
Minor: 1,
|
||||
}
|
||||
)
|
||||
|
||||
func GetSupportedVersions() []*csi.Version {
|
||||
return []*csi.Version{&version}
|
||||
}
|
||||
|
||||
func NewDriver(nodeID, endpoint string) *driver {
|
||||
glog.Infof("Driver: %v version: %v", driverName, csicommon.GetVersionString(&version))
|
||||
|
||||
d := &driver{}
|
||||
|
||||
d.endpoint = endpoint
|
||||
|
||||
csiDriver := csicommon.NewCSIDriver(driverName, &version, GetSupportedVersions(), nodeID)
|
||||
csiDriver.AddVolumeCapabilityAccessModes([]csi.VolumeCapability_AccessMode_Mode{csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER})
|
||||
|
||||
d.csiDriver = csiDriver
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
func NewNodeServer(d *driver) *nodeServer {
|
||||
return &nodeServer{
|
||||
DefaultNodeServer: csicommon.NewDefaultNodeServer(d.csiDriver),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *driver) Run() {
|
||||
s := csicommon.NewNonBlockingGRPCServer()
|
||||
s.Start(d.endpoint,
|
||||
csicommon.NewDefaultIdentityServer(d.csiDriver),
|
||||
csicommon.NewDefaultControllerServer(d.csiDriver),
|
||||
NewNodeServer(d))
|
||||
s.Wait()
|
||||
}
|
52
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/examples/kubernetes/nginx.yaml
generated
vendored
Normal file
52
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/examples/kubernetes/nginx.yaml
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: data-nfsplugin
|
||||
labels:
|
||||
name: data-nfsplugin
|
||||
annotations:
|
||||
csi.volume.kubernetes.io/volume-attributes: '{"server": "10.10.10.10", "share": "share"}'
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
capacity:
|
||||
storage: 100Gi
|
||||
csi:
|
||||
driver: csi-nfsplugin
|
||||
volumeHandle: data-id
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: data-nfsplugin
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 100Gi
|
||||
selector:
|
||||
matchExpressions:
|
||||
- key: name
|
||||
operator: In
|
||||
values: ["data-nfsplugin"]
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- image: maersk/nginx
|
||||
imagePullPolicy: Always
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
protocol: TCP
|
||||
volumeMounts:
|
||||
- mountPath: /var/www
|
||||
name: data-nfsplugin
|
||||
volumes:
|
||||
- name: data-nfsplugin
|
||||
persistentVolumeClaim:
|
||||
claimName: data-nfsplugin
|
101
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/nodeserver.go
generated
vendored
Normal file
101
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/nodeserver.go
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
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 nfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
"k8s.io/kubernetes/pkg/volume/util"
|
||||
|
||||
"github.com/kubernetes-csi/drivers/pkg/csi-common"
|
||||
)
|
||||
|
||||
type nodeServer struct {
|
||||
*csicommon.DefaultNodeServer
|
||||
}
|
||||
|
||||
func (ns *nodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) {
|
||||
targetPath := req.GetTargetPath()
|
||||
notMnt, err := mount.New("").IsLikelyNotMountPoint(targetPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if err := os.MkdirAll(targetPath, 0750); err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
notMnt = true
|
||||
} else {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if !notMnt {
|
||||
return &csi.NodePublishVolumeResponse{}, nil
|
||||
}
|
||||
|
||||
mo := req.GetVolumeCapability().GetMount().GetMountFlags()
|
||||
if req.GetReadonly() {
|
||||
mo = append(mo, "ro")
|
||||
}
|
||||
|
||||
s := req.GetVolumeAttributes()["server"]
|
||||
ep := req.GetVolumeAttributes()["exportPath"]
|
||||
source := fmt.Sprintf("%s:%s", s, ep)
|
||||
|
||||
mounter := mount.New("")
|
||||
err = mounter.Mount(source, targetPath, "nfs", mo)
|
||||
if err != nil {
|
||||
if os.IsPermission(err) {
|
||||
return nil, status.Error(codes.PermissionDenied, err.Error())
|
||||
}
|
||||
if strings.Contains(err.Error(), "invalid argument") {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
return &csi.NodePublishVolumeResponse{}, nil
|
||||
}
|
||||
|
||||
func (ns *nodeServer) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpublishVolumeRequest) (*csi.NodeUnpublishVolumeResponse, error) {
|
||||
targetPath := req.GetTargetPath()
|
||||
notMnt, err := mount.New("").IsLikelyNotMountPoint(targetPath)
|
||||
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, status.Error(codes.NotFound, "Targetpath not found")
|
||||
} else {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
}
|
||||
if notMnt {
|
||||
return nil, status.Error(codes.NotFound, "Volume not mounted")
|
||||
}
|
||||
|
||||
err = util.UnmountPath(req.GetTargetPath(), mount.New(""))
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
return &csi.NodeUnpublishVolumeResponse{}, nil
|
||||
}
|
Reference in New Issue
Block a user