mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 02:43:36 +00:00
104
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/driver_test.go
generated
vendored
104
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/driver_test.go
generated
vendored
@ -1,104 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
vendorVersion = "1.0.0-rc2"
|
||||
)
|
||||
|
||||
func NewFakeDriver() *CSIDriver {
|
||||
|
||||
driver := NewCSIDriver(fakeDriverName, vendorVersion, fakeNodeID)
|
||||
|
||||
return driver
|
||||
}
|
||||
|
||||
func TestNewFakeDriver(t *testing.T) {
|
||||
// Test New fake driver with invalid arguments.
|
||||
d := NewCSIDriver("", vendorVersion, fakeNodeID)
|
||||
assert.Nil(t, d)
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
// Valid requests which require no capabilities
|
||||
err := d.ValidateControllerServiceRequest(csi.ControllerServiceCapability_RPC_UNKNOWN)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test controller service publish/unpublish not supported
|
||||
err = d.ValidateControllerServiceRequest(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(csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test controller service create/delete is supported
|
||||
err = d.ValidateControllerServiceRequest(csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test controller service list volumes is supported
|
||||
err = d.ValidateControllerServiceRequest(csi.ControllerServiceCapability_RPC_LIST_VOLUMES)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test controller service get capacity is supported
|
||||
err = d.ValidateControllerServiceRequest(csi.ControllerServiceCapability_RPC_GET_CAPACITY)
|
||||
assert.NoError(t, err)
|
||||
|
||||
}
|
37
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/identityserver-default_test.go
generated
vendored
37
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/identityserver-default_test.go
generated
vendored
@ -1,37 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
)
|
||||
|
||||
func TestGetPluginInfo(t *testing.T) {
|
||||
d := NewFakeDriver()
|
||||
|
||||
ids := NewDefaultIdentityServer(d)
|
||||
|
||||
req := csi.GetPluginInfoRequest{}
|
||||
resp, err := ids.GetPluginInfo(context.Background(), &req)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, resp.GetName(), fakeDriverName)
|
||||
assert.Equal(t, resp.GetVendorVersion(), vendorVersion)
|
||||
}
|
76
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/nodeserver-default_test.go
generated
vendored
76
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/nodeserver-default_test.go
generated
vendored
@ -1,76 +0,0 @@
|
||||
/*
|
||||
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 TestNodeGetInfo(t *testing.T) {
|
||||
d := NewFakeDriver()
|
||||
|
||||
ns := NewDefaultNodeServer(d)
|
||||
|
||||
// Test valid request
|
||||
req := csi.NodeGetInfoRequest{}
|
||||
resp, err := ns.NodeGetInfo(context.Background(), &req)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, resp.GetNodeId(), fakeNodeID)
|
||||
}
|
||||
|
||||
func TestNodeGetCapabilities(t *testing.T) {
|
||||
d := NewFakeDriver()
|
||||
|
||||
ns := NewDefaultNodeServer(d)
|
||||
|
||||
// Test valid request
|
||||
req := csi.NodeGetCapabilitiesRequest{}
|
||||
_, err := ns.NodeGetCapabilities(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.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.Unimplemented)
|
||||
}
|
76
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/utils_test.go
generated
vendored
76
vendor/github.com/kubernetes-csi/drivers/pkg/csi-common/utils_test.go
generated
vendored
@ -1,76 +0,0 @@
|
||||
/*
|
||||
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/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
36
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/README.md
generated
vendored
36
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/README.md
generated
vendored
@ -1,36 +0,0 @@
|
||||
# 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 ./pkg/flexadapter/examples/simplenfs-flexdriver/driver/nfs --nodeid CSINode -v=5
|
||||
```
|
||||
|
||||
### Test using csc
|
||||
Get ```csc``` tool from https://github.com/rexray/gocsi/tree/master/csc
|
||||
|
||||
#### Get plugin info
|
||||
```
|
||||
$ csc identity plugin-info --endpoint tcp://127.0.0.1:10000
|
||||
"simplenfs" "0.1.0"
|
||||
```
|
||||
|
||||
#### NodePublish a volume
|
||||
```
|
||||
$ csc node publish --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 unpublish --endpoint tcp://127.0.0.1:10000 --target-path /mnt/nfs nfstestvol
|
||||
nfstestvol
|
||||
```
|
||||
|
||||
#### Get NodeID
|
||||
```
|
||||
$ csc node get-id --endpoint tcp://127.0.0.1:10000
|
||||
CSINode
|
||||
```
|
||||
|
90
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/controllerserver.go
generated
vendored
90
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/controllerserver.go
generated
vendored
@ -1,90 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"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 {
|
||||
flexDriver *flexVolumeDriver
|
||||
*csicommon.DefaultControllerServer
|
||||
}
|
||||
|
||||
func (cs *controllerServer) ControllerPublishVolume(ctx context.Context, req *csi.ControllerPublishVolumeRequest) (*csi.ControllerPublishVolumeResponse, error) {
|
||||
if err := cs.Driver.ValidateControllerServiceRequest(csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cap := req.GetVolumeCapability()
|
||||
fsType := "ext4"
|
||||
if cap != nil {
|
||||
mount := req.GetVolumeCapability().GetMount()
|
||||
fsType = mount.FsType
|
||||
}
|
||||
|
||||
call := cs.flexDriver.NewDriverCall(attachCmd)
|
||||
call.AppendSpec(req.GetVolumeId(), fsType, req.GetReadonly(), req.GetVolumeContext())
|
||||
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())
|
||||
}
|
||||
|
||||
publishContext := map[string]string{}
|
||||
|
||||
publishContext[deviceID] = callStatus.DevicePath
|
||||
|
||||
return &csi.ControllerPublishVolumeResponse{
|
||||
PublishContext: publishContext,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (cs *controllerServer) ControllerUnpublishVolume(ctx context.Context, req *csi.ControllerUnpublishVolumeRequest) (*csi.ControllerUnpublishVolumeResponse, error) {
|
||||
if err := cs.Driver.ValidateControllerServiceRequest(csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
call := cs.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) {
|
||||
return cs.DefaultControllerServer.ValidateVolumeCapabilities(ctx, req)
|
||||
}
|
239
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/driver-call.go
generated
vendored
239
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/driver-call.go
generated
vendored
@ -1,239 +0,0 @@
|
||||
/*
|
||||
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
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
# 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```
|
@ -1,107 +0,0 @@
|
||||
# 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: quay.io/k8scsi/csi-attacher:v0.3.0
|
||||
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:
|
||||
|
@ -1,34 +0,0 @@
|
||||
# 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
|
@ -1,65 +0,0 @@
|
||||
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: quay.io/k8scsi/driver-registrar:v0.3.0
|
||||
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
|
@ -1,10 +0,0 @@
|
||||
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"]
|
@ -1,113 +0,0 @@
|
||||
#!/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
|
@ -1,52 +0,0 @@
|
||||
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
|
86
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/flexadapter.go
generated
vendored
86
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/flexadapter.go
generated
vendored
@ -1,86 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
"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 (
|
||||
version = "1.0.0-rc2"
|
||||
)
|
||||
|
||||
func New() *flexAdapter {
|
||||
return &flexAdapter{}
|
||||
}
|
||||
|
||||
func NewControllerServer(d *csicommon.CSIDriver, f *flexVolumeDriver) *controllerServer {
|
||||
return &controllerServer{
|
||||
flexDriver: f,
|
||||
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, version)
|
||||
|
||||
// Create flex volume driver
|
||||
f.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
|
||||
f.driver = csicommon.NewCSIDriver(driverName, version, nodeID)
|
||||
if f.flexDriver.capabilities.Attach {
|
||||
f.driver.AddControllerServiceCapabilities([]csi.ControllerServiceCapability_RPC_Type{csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME})
|
||||
}
|
||||
f.driver.AddVolumeCapabilityAccessModes([]csi.VolumeCapability_AccessMode_Mode{csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER})
|
||||
|
||||
// Create GRPC servers
|
||||
f.ns = NewNodeServer(f.driver, f.flexDriver)
|
||||
f.cs = NewControllerServer(f.driver, f.flexDriver)
|
||||
|
||||
csicommon.RunControllerandNodePublishServer(endpoint, f.driver, f.cs, f.ns)
|
||||
}
|
71
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/flexvolumedriver.go
generated
vendored
71
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/flexvolumedriver.go
generated
vendored
@ -1,71 +0,0 @@
|
||||
/*
|
||||
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,
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
170
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/nodeserver.go
generated
vendored
170
vendor/github.com/kubernetes-csi/drivers/pkg/flexadapter/nodeserver.go
generated
vendored
@ -1,170 +0,0 @@
|
||||
/*
|
||||
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(devicePath, targetPath, fsType, options)
|
||||
}
|
||||
|
||||
func (ns *nodeServer) waitForAttach(req *csi.NodePublishVolumeRequest, fsType string) error {
|
||||
|
||||
var dID string
|
||||
|
||||
if req.GetPublishContext() != nil {
|
||||
var ok bool
|
||||
dID, ok = req.GetPublishContext()[deviceID]
|
||||
if !ok {
|
||||
return status.Error(codes.InvalidArgument, "Missing device ID")
|
||||
}
|
||||
} else {
|
||||
return status.Error(codes.InvalidArgument, "Missing publish info and device ID")
|
||||
}
|
||||
|
||||
call := ns.flexDriver.NewDriverCall(waitForAttachCmd)
|
||||
call.Append(dID)
|
||||
call.AppendSpec(req.GetVolumeId(), fsType, req.GetReadonly(), req.GetVolumeContext())
|
||||
|
||||
_, err := call.Run()
|
||||
if isCmdNotSupportedErr(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
var call *DriverCall
|
||||
|
||||
// Attachable driver.
|
||||
if ns.flexDriver.capabilities.Attach {
|
||||
err = ns.waitForAttach(req, fsType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
call = ns.flexDriver.NewDriverCall(mountDeviceCmd)
|
||||
} else {
|
||||
call = ns.flexDriver.NewDriverCall(mountCmd)
|
||||
}
|
||||
|
||||
call.Append(req.GetTargetPath())
|
||||
|
||||
if req.GetPublishContext() != nil {
|
||||
call.Append(req.GetPublishContext()[deviceID])
|
||||
}
|
||||
|
||||
call.AppendSpec(req.GetVolumeId(), fsType, req.GetReadonly(), req.GetVolumeContext())
|
||||
_, err = call.Run()
|
||||
if isCmdNotSupportedErr(err) {
|
||||
mountFlags := req.GetVolumeCapability().GetMount().GetMountFlags()
|
||||
err := mountDevice(req.VolumeContext[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) {
|
||||
|
||||
var call *DriverCall
|
||||
if ns.flexDriver.capabilities.Attach {
|
||||
call = ns.flexDriver.NewDriverCall(unmountDeviceCmd)
|
||||
} else {
|
||||
call = ns.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())
|
||||
}
|
||||
|
||||
// WaitForDetach is ignored in current K8S plugins
|
||||
return &csi.NodeUnpublishVolumeResponse{}, nil
|
||||
}
|
||||
|
||||
func (ns *nodeServer) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstageVolumeRequest) (*csi.NodeUnstageVolumeResponse, error) {
|
||||
return &csi.NodeUnstageVolumeResponse{}, nil
|
||||
}
|
||||
|
||||
func (ns *nodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) {
|
||||
return &csi.NodeStageVolumeResponse{}, nil
|
||||
}
|
58
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/README.md
generated
vendored
58
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/README.md
generated
vendored
@ -1,58 +0,0 @@
|
||||
# CSI Hostpath driver
|
||||
|
||||
## Usage:
|
||||
|
||||
### Build hostpathplugin
|
||||
```
|
||||
$ make hostpath
|
||||
```
|
||||
|
||||
### Start Hostpath driver
|
||||
```
|
||||
$ sudo ./_output/hostpathplugin --endpoint tcp://127.0.0.1:10000 --nodeid CSINode -v=5
|
||||
```
|
||||
|
||||
### Test using csc
|
||||
Get ```csc``` tool from https://github.com/rexray/gocsi/tree/master/csc
|
||||
|
||||
#### Get plugin info
|
||||
```
|
||||
$ csc identity plugin-info --endpoint tcp://127.0.0.1:10000
|
||||
"csi-hostpath" "0.1.0"
|
||||
```
|
||||
|
||||
#### Create a volume
|
||||
```
|
||||
$ csc controller new --endpoint tcp://127.0.0.1:10000 --cap 1,block CSIVolumeName
|
||||
CSIVolumeID
|
||||
```
|
||||
|
||||
#### Delete a volume
|
||||
```
|
||||
$ csc controller del --endpoint tcp://127.0.0.1:10000 CSIVolumeID
|
||||
CSIVolumeID
|
||||
```
|
||||
|
||||
#### Validate volume capabilities
|
||||
```
|
||||
$ csc controller validate-volume-capabilities --endpoint tcp://127.0.0.1:10000 --cap 1,block CSIVolumeID
|
||||
CSIVolumeID true
|
||||
```
|
||||
|
||||
#### NodePublish a volume
|
||||
```
|
||||
$ csc node publish --endpoint tcp://127.0.0.1:10000 --cap 1,block --target-path /mnt/hostpath CSIVolumeID
|
||||
CSIVolumeID
|
||||
```
|
||||
|
||||
#### NodeUnpublish a volume
|
||||
```
|
||||
$ csc node unpublish --endpoint tcp://127.0.0.1:10000 --target-path /mnt/hostpath CSIVolumeID
|
||||
CSIVolumeID
|
||||
```
|
||||
|
||||
#### Get NodeID
|
||||
```
|
||||
$ csc node get-id --endpoint tcp://127.0.0.1:10000
|
||||
CSINode
|
||||
```
|
379
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/controllerserver.go
generated
vendored
379
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/controllerserver.go
generated
vendored
@ -1,379 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
"math"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/pborman/uuid"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/kubernetes-csi/drivers/pkg/csi-common"
|
||||
utilexec "k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
const (
|
||||
deviceID = "deviceID"
|
||||
provisionRoot = "/tmp/"
|
||||
snapshotRoot = "/tmp/"
|
||||
maxStorageCapacity = tib
|
||||
)
|
||||
|
||||
type controllerServer struct {
|
||||
*csicommon.DefaultControllerServer
|
||||
}
|
||||
|
||||
func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) {
|
||||
if err := cs.Driver.ValidateControllerServiceRequest(csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME); err != nil {
|
||||
glog.V(3).Infof("invalid create volume req: %v", req)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check arguments
|
||||
if len(req.GetName()) == 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "Name missing in request")
|
||||
}
|
||||
caps := req.GetVolumeCapabilities()
|
||||
if caps == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "Volume Capabilities missing in request")
|
||||
}
|
||||
for _, cap := range caps {
|
||||
if cap.GetBlock() != nil {
|
||||
return nil, status.Error(codes.Unimplemented, "Block Volume not supported")
|
||||
}
|
||||
}
|
||||
// A real driver would also need to check that the other
|
||||
// fields in VolumeCapabilities are sane. The check above is
|
||||
// just enough to pass the "[Testpattern: Dynamic PV (block
|
||||
// volmode)] volumeMode should fail in binding dynamic
|
||||
// provisioned PV to PVC" storage E2E test.
|
||||
|
||||
// Need to check for already existing volume name, and if found
|
||||
// check for the requested capacity and already allocated capacity
|
||||
if exVol, err := getVolumeByName(req.GetName()); err == nil {
|
||||
// Since err is nil, it means the volume with the same name already exists
|
||||
// need to check if the size of exisiting volume is the same as in new
|
||||
// request
|
||||
if exVol.VolSize >= int64(req.GetCapacityRange().GetRequiredBytes()) {
|
||||
// exisiting volume is compatible with new request and should be reused.
|
||||
// TODO (sbezverk) Do I need to make sure that RBD volume still exists?
|
||||
return &csi.CreateVolumeResponse{
|
||||
Volume: &csi.Volume{
|
||||
VolumeId: exVol.VolID,
|
||||
CapacityBytes: int64(exVol.VolSize),
|
||||
VolumeContext: req.GetParameters(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return nil, status.Error(codes.AlreadyExists, fmt.Sprintf("Volume with the same name: %s but with different size already exist", req.GetName()))
|
||||
}
|
||||
// Check for maximum available capacity
|
||||
capacity := int64(req.GetCapacityRange().GetRequiredBytes())
|
||||
if capacity >= maxStorageCapacity {
|
||||
return nil, status.Errorf(codes.OutOfRange, "Requested capacity %d exceeds maximum allowed %d", capacity, maxStorageCapacity)
|
||||
}
|
||||
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
|
||||
}
|
||||
if req.GetVolumeContentSource() != nil {
|
||||
contentSource := req.GetVolumeContentSource()
|
||||
if contentSource.GetSnapshot() != nil {
|
||||
snapshotId := contentSource.GetSnapshot().GetSnapshotId()
|
||||
snapshot, ok := hostPathVolumeSnapshots[snapshotId]
|
||||
if !ok {
|
||||
return nil, status.Errorf(codes.NotFound, "cannot find snapshot %v", snapshotId)
|
||||
}
|
||||
if snapshot.ReadyToUse != true {
|
||||
return nil, status.Errorf(codes.Internal, "Snapshot %v is not yet ready to use.", snapshotId)
|
||||
}
|
||||
snapshotPath := snapshot.Path
|
||||
args := []string{"zxvf", snapshotPath, "-C", path}
|
||||
executor := utilexec.New()
|
||||
out, err := executor.Command("tar", args...).CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, fmt.Sprintf("failed pre-populate data for volume: %v: %s", err, out))
|
||||
}
|
||||
}
|
||||
}
|
||||
glog.V(4).Infof("create volume %s", path)
|
||||
hostPathVol := hostPathVolume{}
|
||||
hostPathVol.VolName = req.GetName()
|
||||
hostPathVol.VolID = volumeID
|
||||
hostPathVol.VolSize = capacity
|
||||
hostPathVol.VolPath = path
|
||||
hostPathVolumes[volumeID] = hostPathVol
|
||||
return &csi.CreateVolumeResponse{
|
||||
Volume: &csi.Volume{
|
||||
VolumeId: volumeID,
|
||||
CapacityBytes: req.GetCapacityRange().GetRequiredBytes(),
|
||||
VolumeContext: req.GetParameters(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (cs *controllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest) (*csi.DeleteVolumeResponse, error) {
|
||||
|
||||
// Check arguments
|
||||
if len(req.GetVolumeId()) == 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "Volume ID missing in request")
|
||||
}
|
||||
|
||||
if err := cs.Driver.ValidateControllerServiceRequest(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)
|
||||
delete(hostPathVolumes, volumeID)
|
||||
return &csi.DeleteVolumeResponse{}, nil
|
||||
}
|
||||
|
||||
func (cs *controllerServer) ValidateVolumeCapabilities(ctx context.Context, req *csi.ValidateVolumeCapabilitiesRequest) (*csi.ValidateVolumeCapabilitiesResponse, error) {
|
||||
return cs.DefaultControllerServer.ValidateVolumeCapabilities(ctx, req)
|
||||
}
|
||||
|
||||
// CreateSnapshot uses tar command to create snapshot for hostpath volume. The tar command can quickly create
|
||||
// archives of entire directories. The host image must have "tar" binaries in /bin, /usr/sbin, or /usr/bin.
|
||||
func (cs *controllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateSnapshotRequest) (*csi.CreateSnapshotResponse, error) {
|
||||
if err := cs.Driver.ValidateControllerServiceRequest(csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT); err != nil {
|
||||
glog.V(3).Infof("invalid create snapshot req: %v", req)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(req.GetName()) == 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "Name missing in request")
|
||||
}
|
||||
// Check arguments
|
||||
if len(req.GetSourceVolumeId()) == 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "SourceVolumeId missing in request")
|
||||
}
|
||||
|
||||
// Need to check for already existing snapshot name, and if found check for the
|
||||
// requested sourceVolumeId and sourceVolumeId of snapshot that has been created.
|
||||
if exSnap, err := getSnapshotByName(req.GetName()); err == nil {
|
||||
// Since err is nil, it means the snapshot with the same name already exists need
|
||||
// to check if the sourceVolumeId of existing snapshot is the same as in new request.
|
||||
if exSnap.VolID == req.GetSourceVolumeId() {
|
||||
// same snapshot has been created.
|
||||
return &csi.CreateSnapshotResponse{
|
||||
Snapshot: &csi.Snapshot{
|
||||
SnapshotId: exSnap.Id,
|
||||
SourceVolumeId: exSnap.VolID,
|
||||
CreationTime: &exSnap.CreationTime,
|
||||
SizeBytes: exSnap.SizeBytes,
|
||||
ReadyToUse: exSnap.ReadyToUse,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return nil, status.Error(codes.AlreadyExists, fmt.Sprintf("snapshot with the same name: %s but with different SourceVolumeId already exist", req.GetName()))
|
||||
}
|
||||
|
||||
volumeID := req.GetSourceVolumeId()
|
||||
hostPathVolume, ok := hostPathVolumes[volumeID]
|
||||
if !ok {
|
||||
return nil, status.Error(codes.Internal, "volumeID is not exist")
|
||||
}
|
||||
|
||||
snapshotID := uuid.NewUUID().String()
|
||||
creationTime := ptypes.TimestampNow()
|
||||
volPath := hostPathVolume.VolPath
|
||||
file := snapshotRoot + snapshotID + ".tgz"
|
||||
args := []string{"czf", file, "-C", volPath, "."}
|
||||
executor := utilexec.New()
|
||||
out, err := executor.Command("tar", args...).CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, fmt.Sprintf("failed create snapshot: %v: %s", err, out))
|
||||
}
|
||||
|
||||
glog.V(4).Infof("create volume snapshot %s", file)
|
||||
snapshot := hostPathSnapshot{}
|
||||
snapshot.Name = req.GetName()
|
||||
snapshot.Id = snapshotID
|
||||
snapshot.VolID = volumeID
|
||||
snapshot.Path = file
|
||||
snapshot.CreationTime = *creationTime
|
||||
snapshot.SizeBytes = hostPathVolume.VolSize
|
||||
snapshot.ReadyToUse = true
|
||||
|
||||
hostPathVolumeSnapshots[snapshotID] = snapshot
|
||||
|
||||
return &csi.CreateSnapshotResponse{
|
||||
Snapshot: &csi.Snapshot{
|
||||
SnapshotId: snapshot.Id,
|
||||
SourceVolumeId: snapshot.VolID,
|
||||
CreationTime: &snapshot.CreationTime,
|
||||
SizeBytes: snapshot.SizeBytes,
|
||||
ReadyToUse: snapshot.ReadyToUse,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (cs *controllerServer) DeleteSnapshot(ctx context.Context, req *csi.DeleteSnapshotRequest) (*csi.DeleteSnapshotResponse, error) {
|
||||
// Check arguments
|
||||
if len(req.GetSnapshotId()) == 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "Snapshot ID missing in request")
|
||||
}
|
||||
|
||||
if err := cs.Driver.ValidateControllerServiceRequest(csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT); err != nil {
|
||||
glog.V(3).Infof("invalid delete snapshot req: %v", req)
|
||||
return nil, err
|
||||
}
|
||||
snapshotID := req.GetSnapshotId()
|
||||
glog.V(4).Infof("deleting volume %s", snapshotID)
|
||||
path := snapshotRoot + snapshotID + ".tgz"
|
||||
os.RemoveAll(path)
|
||||
delete(hostPathVolumeSnapshots, snapshotID)
|
||||
return &csi.DeleteSnapshotResponse{}, nil
|
||||
}
|
||||
|
||||
func (cs *controllerServer) ListSnapshots(ctx context.Context, req *csi.ListSnapshotsRequest) (*csi.ListSnapshotsResponse, error) {
|
||||
if err := cs.Driver.ValidateControllerServiceRequest(csi.ControllerServiceCapability_RPC_LIST_SNAPSHOTS); err != nil {
|
||||
glog.V(3).Infof("invalid list snapshot req: %v", req)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// case 1: SnapshotId is not empty, return snapshots that match the snapshot id.
|
||||
if len(req.GetSnapshotId()) != 0 {
|
||||
snapshotID := req.SnapshotId
|
||||
if snapshot, ok := hostPathVolumeSnapshots[snapshotID]; ok {
|
||||
return convertSnapshot(snapshot), nil
|
||||
}
|
||||
}
|
||||
|
||||
// case 2: SourceVolumeId is not empty, return snapshots that match the source volume id.
|
||||
if len(req.GetSourceVolumeId()) != 0 {
|
||||
for _, snapshot := range hostPathVolumeSnapshots {
|
||||
if snapshot.VolID == req.SourceVolumeId {
|
||||
return convertSnapshot(snapshot), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var snapshots []csi.Snapshot
|
||||
// case 3: no parameter is set, so we return all the snapshots.
|
||||
sortedKeys := make([]string, 0)
|
||||
for k := range hostPathVolumeSnapshots {
|
||||
sortedKeys = append(sortedKeys, k)
|
||||
}
|
||||
sort.Strings(sortedKeys)
|
||||
|
||||
for _, key := range sortedKeys {
|
||||
snap := hostPathVolumeSnapshots[key]
|
||||
snapshot := csi.Snapshot{
|
||||
SnapshotId: snap.Id,
|
||||
SourceVolumeId: snap.VolID,
|
||||
CreationTime: &snap.CreationTime,
|
||||
SizeBytes: snap.SizeBytes,
|
||||
ReadyToUse: snap.ReadyToUse,
|
||||
}
|
||||
snapshots = append(snapshots, snapshot)
|
||||
}
|
||||
|
||||
var (
|
||||
ulenSnapshots = int32(len(snapshots))
|
||||
maxEntries = req.MaxEntries
|
||||
startingToken int32
|
||||
)
|
||||
|
||||
if v := req.StartingToken; v != "" {
|
||||
i, err := strconv.ParseUint(v, 10, 32)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(
|
||||
codes.Aborted,
|
||||
"startingToken=%d !< int32=%d",
|
||||
startingToken, math.MaxUint32)
|
||||
}
|
||||
startingToken = int32(i)
|
||||
}
|
||||
|
||||
if startingToken > ulenSnapshots {
|
||||
return nil, status.Errorf(
|
||||
codes.Aborted,
|
||||
"startingToken=%d > len(snapshots)=%d",
|
||||
startingToken, ulenSnapshots)
|
||||
}
|
||||
|
||||
// Discern the number of remaining entries.
|
||||
rem := ulenSnapshots - startingToken
|
||||
|
||||
// If maxEntries is 0 or greater than the number of remaining entries then
|
||||
// set maxEntries to the number of remaining entries.
|
||||
if maxEntries == 0 || maxEntries > rem {
|
||||
maxEntries = rem
|
||||
}
|
||||
|
||||
var (
|
||||
i int
|
||||
j = startingToken
|
||||
entries = make(
|
||||
[]*csi.ListSnapshotsResponse_Entry,
|
||||
maxEntries)
|
||||
)
|
||||
|
||||
for i = 0; i < len(entries); i++ {
|
||||
entries[i] = &csi.ListSnapshotsResponse_Entry{
|
||||
Snapshot: &snapshots[j],
|
||||
}
|
||||
j++
|
||||
}
|
||||
|
||||
var nextToken string
|
||||
if j < ulenSnapshots {
|
||||
nextToken = fmt.Sprintf("%d", j)
|
||||
}
|
||||
|
||||
return &csi.ListSnapshotsResponse{
|
||||
Entries: entries,
|
||||
NextToken: nextToken,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func convertSnapshot(snap hostPathSnapshot) *csi.ListSnapshotsResponse {
|
||||
entries := []*csi.ListSnapshotsResponse_Entry{
|
||||
{
|
||||
Snapshot: &csi.Snapshot{
|
||||
SnapshotId: snap.Id,
|
||||
SourceVolumeId: snap.VolID,
|
||||
CreationTime: &snap.CreationTime,
|
||||
SizeBytes: snap.SizeBytes,
|
||||
ReadyToUse: snap.ReadyToUse,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
rsp := &csi.ListSnapshotsResponse{
|
||||
Entries: entries,
|
||||
}
|
||||
|
||||
return rsp
|
||||
}
|
151
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/hostpath.go
generated
vendored
151
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/hostpath.go
generated
vendored
@ -1,151 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/golang/glog"
|
||||
|
||||
timestamp "github.com/golang/protobuf/ptypes/timestamp"
|
||||
"github.com/kubernetes-csi/drivers/pkg/csi-common"
|
||||
)
|
||||
|
||||
const (
|
||||
kib int64 = 1024
|
||||
mib int64 = kib * 1024
|
||||
gib int64 = mib * 1024
|
||||
gib100 int64 = gib * 100
|
||||
tib int64 = gib * 1024
|
||||
tib100 int64 = tib * 100
|
||||
)
|
||||
|
||||
type hostPath struct {
|
||||
driver *csicommon.CSIDriver
|
||||
|
||||
ids *identityServer
|
||||
ns *nodeServer
|
||||
cs *controllerServer
|
||||
|
||||
cap []*csi.VolumeCapability_AccessMode
|
||||
cscap []*csi.ControllerServiceCapability
|
||||
}
|
||||
|
||||
type hostPathVolume struct {
|
||||
VolName string `json:"volName"`
|
||||
VolID string `json:"volID"`
|
||||
VolSize int64 `json:"volSize"`
|
||||
VolPath string `json:"volPath"`
|
||||
}
|
||||
|
||||
type hostPathSnapshot struct {
|
||||
Name string `json:"name"`
|
||||
Id string `json:"id"`
|
||||
VolID string `json:"volID"`
|
||||
Path string `json:"path"`
|
||||
CreationTime timestamp.Timestamp `json:"creationTime"`
|
||||
SizeBytes int64 `json:"sizeBytes"`
|
||||
ReadyToUse bool `json:"readyToUse"`
|
||||
}
|
||||
|
||||
var hostPathVolumes map[string]hostPathVolume
|
||||
var hostPathVolumeSnapshots map[string]hostPathSnapshot
|
||||
|
||||
var (
|
||||
hostPathDriver *hostPath
|
||||
vendorVersion = "dev"
|
||||
)
|
||||
|
||||
func init() {
|
||||
hostPathVolumes = map[string]hostPathVolume{}
|
||||
hostPathVolumeSnapshots = map[string]hostPathSnapshot{}
|
||||
}
|
||||
|
||||
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 ", driverName)
|
||||
glog.Infof("Version: %s", vendorVersion)
|
||||
|
||||
// Initialize default library driver
|
||||
hp.driver = csicommon.NewCSIDriver(driverName, vendorVersion, nodeID)
|
||||
if hp.driver == nil {
|
||||
glog.Fatalln("Failed to initialize CSI Driver.")
|
||||
}
|
||||
hp.driver.AddControllerServiceCapabilities(
|
||||
[]csi.ControllerServiceCapability_RPC_Type{
|
||||
csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME,
|
||||
csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT,
|
||||
csi.ControllerServiceCapability_RPC_LIST_SNAPSHOTS,
|
||||
})
|
||||
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()
|
||||
}
|
||||
|
||||
func getVolumeByID(volumeID string) (hostPathVolume, error) {
|
||||
if hostPathVol, ok := hostPathVolumes[volumeID]; ok {
|
||||
return hostPathVol, nil
|
||||
}
|
||||
return hostPathVolume{}, fmt.Errorf("volume id %s does not exit in the volumes list", volumeID)
|
||||
}
|
||||
|
||||
func getVolumeByName(volName string) (hostPathVolume, error) {
|
||||
for _, hostPathVol := range hostPathVolumes {
|
||||
if hostPathVol.VolName == volName {
|
||||
return hostPathVol, nil
|
||||
}
|
||||
}
|
||||
return hostPathVolume{}, fmt.Errorf("volume name %s does not exit in the volumes list", volName)
|
||||
}
|
||||
|
||||
func getSnapshotByName(name string) (hostPathSnapshot, error) {
|
||||
for _, snapshot := range hostPathVolumeSnapshots {
|
||||
if snapshot.Name == name {
|
||||
return snapshot, nil
|
||||
}
|
||||
}
|
||||
return hostPathSnapshot{}, fmt.Errorf("snapshot name %s does not exit in the snapshots list", name)
|
||||
}
|
25
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/identityserver.go
generated
vendored
25
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/identityserver.go
generated
vendored
@ -1,25 +0,0 @@
|
||||
/*
|
||||
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
|
||||
}
|
141
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/nodeserver.go
generated
vendored
141
vendor/github.com/kubernetes-csi/drivers/pkg/hostpath/nodeserver.go
generated
vendored
@ -1,141 +0,0 @@
|
||||
/*
|
||||
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) {
|
||||
|
||||
// Check arguments
|
||||
if req.GetVolumeCapability() == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "Volume capability missing in request")
|
||||
}
|
||||
if len(req.GetVolumeId()) == 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "Volume ID missing in request")
|
||||
}
|
||||
if len(req.GetTargetPath()) == 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "Target path missing in request")
|
||||
}
|
||||
|
||||
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.GetPublishContext() != nil {
|
||||
deviceId = req.GetPublishContext()[deviceID]
|
||||
}
|
||||
|
||||
readOnly := req.GetReadonly()
|
||||
volumeId := req.GetVolumeId()
|
||||
attrib := req.GetVolumeContext()
|
||||
mountFlags := req.GetVolumeCapability().GetMount().GetMountFlags()
|
||||
|
||||
glog.V(4).Infof("target %v\nfstype %v\ndevice %v\nreadonly %v\nvolumeId %v\nattributes %v\nmountflags %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) {
|
||||
|
||||
// Check arguments
|
||||
if len(req.GetVolumeId()) == 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "Volume ID missing in request")
|
||||
}
|
||||
if len(req.GetTargetPath()) == 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "Target path missing in request")
|
||||
}
|
||||
targetPath := req.GetTargetPath()
|
||||
volumeID := req.GetVolumeId()
|
||||
|
||||
// Unmounting the image
|
||||
err := mount.New("").Unmount(req.GetTargetPath())
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
glog.V(4).Infof("hostpath: volume %s/%s has been unmounted.", targetPath, volumeID)
|
||||
|
||||
return &csi.NodeUnpublishVolumeResponse{}, nil
|
||||
}
|
||||
|
||||
func (ns *nodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) {
|
||||
|
||||
// Check arguments
|
||||
if len(req.GetVolumeId()) == 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "Volume ID missing in request")
|
||||
}
|
||||
if len(req.GetStagingTargetPath()) == 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "Target path missing in request")
|
||||
}
|
||||
|
||||
return &csi.NodeStageVolumeResponse{}, nil
|
||||
}
|
||||
|
||||
func (ns *nodeServer) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstageVolumeRequest) (*csi.NodeUnstageVolumeResponse, error) {
|
||||
|
||||
// Check arguments
|
||||
if len(req.GetVolumeId()) == 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "Volume ID missing in request")
|
||||
}
|
||||
if len(req.GetStagingTargetPath()) == 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "Target path missing in request")
|
||||
}
|
||||
|
||||
return &csi.NodeUnstageVolumeResponse{}, nil
|
||||
}
|
37
vendor/github.com/kubernetes-csi/drivers/pkg/iscsi/README.md
generated
vendored
37
vendor/github.com/kubernetes-csi/drivers/pkg/iscsi/README.md
generated
vendored
@ -1,37 +0,0 @@
|
||||
# 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/rexray/gocsi/tree/master/csc
|
||||
|
||||
#### Get plugin info
|
||||
```
|
||||
$ csc identity plugin-info --endpoint tcp://127.0.0.1:10000
|
||||
"ISCSI" "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
|
||||
```
|
68
vendor/github.com/kubernetes-csi/drivers/pkg/iscsi/driver.go
generated
vendored
68
vendor/github.com/kubernetes-csi/drivers/pkg/iscsi/driver.go
generated
vendored
@ -1,68 +0,0 @@
|
||||
/*
|
||||
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 = "1.0.0-rc2"
|
||||
)
|
||||
|
||||
func NewDriver(nodeID, endpoint string) *driver {
|
||||
glog.Infof("Driver: %v version: %v", driverName, version)
|
||||
|
||||
d := &driver{}
|
||||
|
||||
d.endpoint = endpoint
|
||||
|
||||
csiDriver := csicommon.NewCSIDriver(driverName, version, 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
148
vendor/github.com/kubernetes-csi/drivers/pkg/iscsi/iscsi.go
generated
vendored
@ -1,148 +0,0 @@
|
||||
/*
|
||||
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.GetVolumeContext()["targetPortal"]
|
||||
iqn := req.GetVolumeContext()["iqn"]
|
||||
lun := req.GetVolumeContext()["lun"]
|
||||
if tp == "" || iqn == "" || lun == "" {
|
||||
return nil, fmt.Errorf("iSCSI target information is missing")
|
||||
}
|
||||
|
||||
portalList := req.GetVolumeContext()["portals"]
|
||||
secretParams := req.GetVolumeContext()["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.GetVolumeContext()["iscsiInterface"]
|
||||
initiatorName := req.GetVolumeContext()["initiatorName"]
|
||||
chapDiscovery := false
|
||||
if req.GetVolumeContext()["discoveryCHAPAuth"] == "true" {
|
||||
chapDiscovery = true
|
||||
}
|
||||
|
||||
chapSession := false
|
||||
if req.GetVolumeContext()["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
|
||||
}
|
477
vendor/github.com/kubernetes-csi/drivers/pkg/iscsi/iscsi_util.go
generated
vendored
477
vendor/github.com/kubernetes-csi/drivers/pkg/iscsi/iscsi_util.go
generated
vendored
@ -1,477 +0,0 @@
|
||||
/*
|
||||
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 := b.targetPath
|
||||
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 {
|
||||
_, cnt, err := mount.GetDeviceNameFromMount(c.mounter, targetPath)
|
||||
if err != nil {
|
||||
glog.Errorf("iscsi detach disk: failed to get device from mnt: %s\nError: %v", targetPath, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if pathExists, pathErr := volumeutil.PathExists(targetPath); 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", targetPath)
|
||||
return nil
|
||||
}
|
||||
if err = c.mounter.Unmount(targetPath); err != nil {
|
||||
glog.Errorf("iscsi detach disk: failed to unmount: %s\nError: %v", targetPath, 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
|
||||
}
|
67
vendor/github.com/kubernetes-csi/drivers/pkg/iscsi/nodeserver.go
generated
vendored
67
vendor/github.com/kubernetes-csi/drivers/pkg/iscsi/nodeserver.go
generated
vendored
@ -1,67 +0,0 @@
|
||||
/*
|
||||
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
|
||||
}
|
||||
|
||||
func (ns *nodeServer) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstageVolumeRequest) (*csi.NodeUnstageVolumeResponse, error) {
|
||||
return &csi.NodeUnstageVolumeResponse{}, nil
|
||||
}
|
||||
|
||||
func (ns *nodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) {
|
||||
return &csi.NodeStageVolumeResponse{}, nil
|
||||
}
|
69
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/README.md
generated
vendored
69
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/README.md
generated
vendored
@ -1,69 +0,0 @@
|
||||
# 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
|
||||
|
||||
### Build nfsplugin
|
||||
```
|
||||
$ make nfs
|
||||
```
|
||||
|
||||
### Start NFS driver
|
||||
```
|
||||
$ sudo ./_output/nfsplugin --endpoint tcp://127.0.0.1:10000 --nodeid CSINode -v=5
|
||||
```
|
||||
|
||||
## Test
|
||||
Get ```csc``` tool from https://github.com/rexray/gocsi/tree/master/csc
|
||||
|
||||
#### Get plugin info
|
||||
```
|
||||
$ csc identity plugin-info --endpoint tcp://127.0.0.1:10000
|
||||
"NFS" "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 publish --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 unpublish --endpoint tcp://127.0.0.1:10000 --target-path /mnt/nfs nfstestvol
|
||||
nfstestvol
|
||||
```
|
||||
|
||||
#### Get NodeID
|
||||
```
|
||||
$ csc node get-id --endpoint tcp://127.0.0.1:10000
|
||||
CSINode
|
||||
```
|
||||
|
@ -1,64 +0,0 @@
|
||||
# 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: quay.io/k8scsi/csi-attacher:v0.3.0
|
||||
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: quay.io/k8scsi/nfsplugin:v0.3.0
|
||||
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:
|
||||
|
@ -1,37 +0,0 @@
|
||||
# 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
|
@ -1,66 +0,0 @@
|
||||
# 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: quay.io/k8scsi/driver-registrar:v0.3.0
|
||||
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: quay.io/k8scsi/nfsplugin:v0.3.0
|
||||
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
|
@ -1,34 +0,0 @@
|
||||
# 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
8
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/dockerfile/Dockerfile
generated
vendored
@ -1,8 +0,0 @@
|
||||
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"]
|
78
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/driver.go
generated
vendored
78
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/driver.go
generated
vendored
@ -1,78 +0,0 @@
|
||||
/*
|
||||
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 = "1.0.0-rc2"
|
||||
)
|
||||
|
||||
func NewDriver(nodeID, endpoint string) *driver {
|
||||
glog.Infof("Driver: %v version: %v", driverName, version)
|
||||
|
||||
d := &driver{}
|
||||
|
||||
d.endpoint = endpoint
|
||||
|
||||
csiDriver := csicommon.NewCSIDriver(driverName, version, nodeID)
|
||||
csiDriver.AddVolumeCapabilityAccessModes([]csi.VolumeCapability_AccessMode_Mode{csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER})
|
||||
// NFS plugin does not support ControllerServiceCapability now.
|
||||
// If support is added, it should set to appropriate
|
||||
// ControllerServiceCapability RPC types.
|
||||
csiDriver.AddControllerServiceCapabilities([]csi.ControllerServiceCapability_RPC_Type{csi.ControllerServiceCapability_RPC_UNKNOWN})
|
||||
|
||||
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),
|
||||
// NFS plugin has not implemented ControllerServer.
|
||||
nil,
|
||||
NewNodeServer(d))
|
||||
s.Wait()
|
||||
}
|
53
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/examples/kubernetes/nginx.yaml
generated
vendored
53
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/examples/kubernetes/nginx.yaml
generated
vendored
@ -1,53 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: data-nfsplugin
|
||||
labels:
|
||||
name: data-nfsplugin
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
capacity:
|
||||
storage: 100Gi
|
||||
csi:
|
||||
driver: csi-nfsplugin
|
||||
volumeHandle: data-id
|
||||
volumeAttributes:
|
||||
server: 127.0.0.1
|
||||
share: /export
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: data-nfsplugin
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
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
|
109
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/nodeserver.go
generated
vendored
109
vendor/github.com/kubernetes-csi/drivers/pkg/nfs/nodeserver.go
generated
vendored
@ -1,109 +0,0 @@
|
||||
/*
|
||||
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.GetVolumeContext()["server"]
|
||||
ep := req.GetVolumeContext()["share"]
|
||||
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
|
||||
}
|
||||
|
||||
func (ns *nodeServer) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstageVolumeRequest) (*csi.NodeUnstageVolumeResponse, error) {
|
||||
return &csi.NodeUnstageVolumeResponse{}, nil
|
||||
}
|
||||
|
||||
func (ns *nodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) {
|
||||
return &csi.NodeStageVolumeResponse{}, nil
|
||||
}
|
Reference in New Issue
Block a user