commit f481dddf44391425ccef6e263f4b33a381d4d1d0 Author: Chakravarthy Nelluri Date: Mon Dec 4 15:01:40 2017 -0700 Refactor to app & pkg directories diff --git a/app/nfsplugin/main.go b/app/nfsplugin/main.go new file mode 100644 index 00000000..e2af08d8 --- /dev/null +++ b/app/nfsplugin/main.go @@ -0,0 +1,59 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + + "github.com/kubernetes-csi/drivers/pkg/nfs" +) + +var ( + endpoint string + nodeID string +) + +func main() { + cmd := &cobra.Command{ + Use: "NFS", + Short: "CSI based NFS driver", + Run: func(cmd *cobra.Command, args []string) { + handle() + }, + } + + cmd.PersistentFlags().StringVar(&nodeID, "nodeid", "", "node id") + cmd.MarkPersistentFlagRequired("nodeid") + + cmd.PersistentFlags().StringVar(&endpoint, "endpoint", "", "CSI endpoint") + cmd.MarkPersistentFlagRequired("endpoint") + + if err := cmd.Execute(); err != nil { + fmt.Fprintf(os.Stderr, "%s", err.Error()) + os.Exit(1) + } + + os.Exit(0) +} + +func handle() { + d := nfs.NewDriver(nodeID, endpoint) + d.Run() +} diff --git a/pkg/nfs/README.md b/pkg/nfs/README.md new file mode 100644 index 00000000..1b878a7a --- /dev/null +++ b/pkg/nfs/README.md @@ -0,0 +1,44 @@ +# CSI NFS driver + +## Usage: + +### Start NFS driver +``` +$ sudo ../_output/nfsdriver --endpoint tcp://127.0.0.1:10000 --nodeid CSINode +``` + +### Test using csc +Get ```csc``` tool from https://github.com/chakri-nelluri/gocsi/tree/master/csc + +#### Get plugin info +``` +$ csc identity plugininfo --endpoint tcp://127.0.0.1:10000 +"NFS" "0.1.0" +``` + +### Get supported versions +``` +$ csc identity supportedversions --endpoint tcp://127.0.0.1:10000 +0.1.0 +``` + +#### NodePublish a volume +``` +$ export NFS_SERVER="Your Server IP (Ex: 10.10.10.10)" +$ export NFS_SHARE="Your NFS share" +$ csc node publishvolume --endpoint tcp://127.0.0.1:10000 --target-path /mnt/nfs --attrib server=$NFS_SERVER --attrib exportPath=$NFS_SHARE nfstestvol +nfstestvol +``` + +#### NodeUnpublish a volume +``` +$ csc node unpublishvolume --endpoint tcp://127.0.0.1:10000 --target-path /mnt/nfs nfstestvol +nfstestvol +``` + +#### Get NodeID +``` +$ csc node getid --endpoint tcp://127.0.0.1:10000 +CSINode +``` + diff --git a/pkg/nfs/driver.go b/pkg/nfs/driver.go new file mode 100644 index 00000000..b31eff35 --- /dev/null +++ b/pkg/nfs/driver.go @@ -0,0 +1,74 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package 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 = "NFS" +) + +var ( + version = csi.Version{ + Minor: 1, + } +) + +func GetSupportedVersions() []*csi.Version { + return []*csi.Version{&version} +} + +func NewDriver(nodeID, endpoint string) *driver { + glog.Infof("Driver: %v version: %v", driverName, csicommon.GetVersionString(&version)) + + d := &driver{} + + d.endpoint = endpoint + + csiDriver := csicommon.NewCSIDriver(driverName, &version, GetSupportedVersions(), nodeID) + csiDriver.AddVolumeCapabilityAccessModes([]csi.VolumeCapability_AccessMode_Mode{csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER}) + + d.csiDriver = csiDriver + + return d +} + +func NewNodeServer(d *driver) *nodeServer { + return &nodeServer{ + DefaultNodeServer: csicommon.NewDefaultNodeServer(d.csiDriver), + } +} + +func (d *driver) Run() { + csicommon.RunNodePublishServer(d.endpoint, d.csiDriver, NewNodeServer(d)) +} diff --git a/pkg/nfs/nodeserver.go b/pkg/nfs/nodeserver.go new file mode 100644 index 00000000..a2398662 --- /dev/null +++ b/pkg/nfs/nodeserver.go @@ -0,0 +1,101 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package nfs + +import ( + "fmt" + "os" + "strings" + + "github.com/container-storage-interface/spec/lib/go/csi" + "golang.org/x/net/context" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "k8s.io/kubernetes/pkg/util/mount" + "k8s.io/kubernetes/pkg/volume/util" + + "github.com/kubernetes-csi/drivers/pkg/csi-common" +) + +type nodeServer struct { + *csicommon.DefaultNodeServer +} + +func (ns *nodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) { + targetPath := req.GetTargetPath() + notMnt, err := mount.New("").IsLikelyNotMountPoint(targetPath) + if err != nil { + if os.IsNotExist(err) { + if err := os.MkdirAll(targetPath, 0750); err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + notMnt = true + } else { + return nil, status.Error(codes.Internal, err.Error()) + } + } + + if !notMnt { + return &csi.NodePublishVolumeResponse{}, nil + } + + mo := req.GetVolumeCapability().GetMount().GetMountFlags() + if req.GetReadonly() { + mo = append(mo, "ro") + } + + s := req.GetVolumeAttributes()["server"] + ep := req.GetVolumeAttributes()["exportPath"] + source := fmt.Sprintf("%s:%s", s, ep) + + mounter := mount.New("") + err = mounter.Mount(source, targetPath, "nfs", mo) + if err != nil { + if os.IsPermission(err) { + return nil, status.Error(codes.PermissionDenied, err.Error()) + } + if strings.Contains(err.Error(), "invalid argument") { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + return nil, status.Error(codes.Internal, err.Error()) + } + + return &csi.NodePublishVolumeResponse{}, nil +} + +func (ns *nodeServer) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpublishVolumeRequest) (*csi.NodeUnpublishVolumeResponse, error) { + targetPath := req.GetTargetPath() + notMnt, err := mount.New("").IsLikelyNotMountPoint(targetPath) + + if err != nil { + if os.IsNotExist(err) { + return nil, status.Error(codes.NotFound, "Targetpath not found") + } else { + return nil, status.Error(codes.Internal, err.Error()) + } + } + if notMnt { + return nil, status.Error(codes.NotFound, "Volume not mounted") + } + + err = util.UnmountPath(req.GetTargetPath(), mount.New("")) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &csi.NodeUnpublishVolumeResponse{}, nil +}