Skip to content

Commit

Permalink
Nicer Happy Path Test
Browse files Browse the repository at this point in the history
The Makefile target `test` now executes its happy path workflow with
friendlier output.
  • Loading branch information
akutz committed Feb 18, 2018
1 parent 8fddfa3 commit 270c6bf
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 54 deletions.
3 changes: 1 addition & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,4 @@ jobs:
# the sudo requirement related to bind mounts.
- stage: test
sudo: true
install: make build csc etcd
script: sudo make test
script: make docker-test
145 changes: 108 additions & 37 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -51,77 +51,148 @@ CSC := ./csc
$(CSC):
go build -o $@ ./vendor/github.com/thecodeteam/gocsi/csc

test: build | $(ETCD) $(CSC)
@umount /tmp/vol-00 "$(HOME)/.csi-vfs/*/vol-00" 2> /dev/null || true
@rm -fr /tmp/vol-00
test ! -e "$(HOME)/.csi-vfs/*/vol-00" -a ! -e /tmp/vol-00
@echo
VOL_ID := vol-00
VOL_JSN := .info.json
TGT_DIR := /tmp/$(VOL_ID)
VFS_DIR := $(HOME)/.csi-vfs
VOL_DIR := $(VFS_DIR)/vol/$(VOL_ID)
DEV_DIR := $(VFS_DIR)/dev/$(VOL_ID)
MNT_DIR := $(VFS_DIR)/mnt/$(VOL_ID)

test-clean:
@echo "CLEANUP"
@umount $(TGT_DIR) $(VOL_DIR) $(DEV_DIR) $(MNT_DIR) 2> /dev/null || true
@test ! "$$(mount | grep $(VOL_ID))"
@echo "- verified volume not mounted"
@rm -fr "$(VFS_DIR)/*/$(VOL_ID)" "$(TGT_DIR)"
@test ! -e "$(VFS_DIR)/*/$(VOL_ID)" -a ! -e $(TGT_DIR)
@echo "- verified volume paths do not exist"
@pkill $(notdir $(ETCD)) || true
@echo "- killed etcd"
@rm -fr etcd.log default.etcd
@echo "- removed etcd log file & data directory"
@pkill -2 $(notdir $(CSI_VFS)) || true
@rm -fr $(X_CSI_LOG) $(CSI_ENDPOINT) etcd.log default.etcd
$(ETCD) > etcd.log 2>&1 &
@echo
$(CSI_VFS) > $(X_CSI_LOG) 2>&1 &
@echo
@echo "- killed csi-vfs"
@rm -fr $(X_CSI_LOG) $(CSI_ENDPOINT)
@echo "- removed csi-vfs log file & unix socket"
@echo "- test environment ready"

test-up:
@$(MAKE) --no-print-directory test-clean
@echo
@echo "INITIALIZATION"
@$(ETCD) > etcd.log 2>&1 &
@echo "- started etcd"
@$(CSI_VFS) > $(X_CSI_LOG) 2>&1 &
@echo "- started csi-vfs"
@for i in 1 2 3 4 5 6 7 8 9 10; do \
if grep -q "msg=serving" $(X_CSI_LOG); then break; \
else sleep 0.1; fi \
done
@echo "- csi-vfs ready"
@echo
@echo "GET SUPPORTED VERSIONS"
$(CSC) -v $(X_CSI_VERSION) i version
@echo
@echo "GET PLUG-IN INFO"
$(CSC) -v $(X_CSI_VERSION) i info
@echo
((cat /proc/self/mountinfo 2> /dev/null || mount) | grep vol-00) || true
@echo
@echo "CREATE NEW VOLUME"
$(CSC) -v $(X_CSI_VERSION) c new \
--cap SINGLE_NODE_WRITER,mount,vfs \
vol-00
--cap SINGLE_NODE_WRITER,mount,vfs \
$(VOL_ID)
@echo
test -e "$(HOME)/.csi-vfs/vol/vol-00"
@echo "VERIFY VOLUME DIR"
test -e "$(VOL_DIR)" -a -e "$(VOL_DIR)/$(VOL_JSN)"
@echo
@echo "CONTROLLER PUBLISH VOLUME"
$(CSC) -v $(X_CSI_VERSION) c publish \
--cap SINGLE_NODE_WRITER,mount,vfs \
--node-id localhost \
vol-00
--cap SINGLE_NODE_WRITER,mount,vfs \
--node-id localhost \
$(VOL_ID)
@echo
@echo "VERIFY DEVICE DIR"
test -e "$(DEV_DIR)" -a -e "$(DEV_DIR)/$(VOL_JSN)"
@echo
test -e "$(HOME)/.csi-vfs/dev/vol-00"
@echo "VERIFY VOLUME->DEVICE BIND MOUNT"
test "$$(mount | grep $(DEV_DIR))"
@echo
mkdir -p /tmp/vol-00
@echo "CREATE TARGET PATH"
mkdir -p $(TGT_DIR)
@echo
@echo "NODE PUBLISH VOLUME"
$(CSC) -v $(X_CSI_VERSION) n publish \
--cap SINGLE_NODE_WRITER,mount,vfs \
--target-path /tmp/vol-00 \
--pub-info devPath=$(HOME)/.csi-vfs/dev/vol-00 \
vol-00
--cap SINGLE_NODE_WRITER,mount,vfs \
--target-path $(TGT_DIR) \
--pub-info devPath=$(VFS_DIR)/dev/$(VOL_ID) \
$(VOL_ID)
@echo
test -e "$(HOME)/.csi-vfs/mnt/vol-00"
@echo "VERIFY MOUNT DIR"
test -e "$(MNT_DIR)" -a -e "$(MNT_DIR)/$(VOL_JSN)"
@echo
(cat /proc/self/mountinfo 2> /dev/null || mount) | grep vol-00
@echo "VERIFY DEVICE->MOUNT BIND MOUNT"
test "$$(mount | grep $(MNT_DIR))"
@echo
$(CSC) -v $(X_CSI_VERSION) n unpublish --target-path /tmp/vol-00 vol-00
@echo "VERIFY MOUNT->TARGET BIND MOUNT"
test "$$(mount | grep $(TGT_DIR))"
@echo
test ! -e "$(HOME)/.csi-vfs/mnt/vol-00"
@echo "NODE PUBLISH VOLUME (IDEMPONTENT)"
$(CSC) -v $(X_CSI_VERSION) n publish \
--cap SINGLE_NODE_WRITER,mount,vfs \
--target-path $(TGT_DIR) \
--pub-info devPath=$(VFS_DIR)/dev/$(VOL_ID) \
$(VOL_ID)
@echo
@echo "VERIFY SINGLE MOUNT->TARGET BIND MOUNT"
test "1" -eq "$$(mount | grep $(TGT_DIR) | wc -l | awk '{print $$1}')"

test-down:
@echo "NODE UNPUBLISH VOLUME"
$(CSC) -v $(X_CSI_VERSION) n unpublish --target-path $(TGT_DIR) $(VOL_ID)
@echo
$(CSC) -v $(X_CSI_VERSION) c unpublish --node-id localhost vol-00
@echo "VERIFY ! MOUNT->TARGET BIND MOUNT"
test ! "$$(mount | grep $(TGT_DIR))"
@echo
test ! -e "$(HOME)/.csi-vfs/dev/vol-00"
@echo "VERIFY ! DEVICE->MOUNT BIND MOUNT"
test ! "$$(mount | grep $(MNT_DIR))"
@echo
$(CSC) -v $(X_CSI_VERSION) c delete vol-00
@echo "VERIFY ! MOUNT DIR"
test ! -e "$(MNT_DIR)"
@echo
test ! -e "$(HOME)/.csi-vfs/vol/vol-00"
@echo "CONTROLLER UNPUBLISH VOLUME"
$(CSC) -v $(X_CSI_VERSION) c unpublish --node-id localhost $(VOL_ID)
@echo
pkill -2 $(notdir $(CSI_VFS))
@echo "VERIFY ! VOLUME->DEVICE BIND MOUNT"
test ! "$$(mount | grep $(DEV_DIR))"
@echo
pkill $(notdir $(ETCD))
@echo "VERIFY ! DEVICE DIR"
test ! -e "$(DEV_DIR)"
@echo
((cat /proc/self/mountinfo 2> /dev/null || mount) | grep vol-00) || true
@echo "CONTROLLER DELETE VOLUME"
$(CSC) -v $(X_CSI_VERSION) c delete $(VOL_ID)
@echo
cat $(X_CSI_LOG)
@echo "VERIFY ! VOLUME DIR"
test ! -e "$(VOL_DIR)"
@echo
@$(MAKE) --no-print-directory test-clean 1> /dev/null

test: build | $(ETCD) $(CSC)
@$(MAKE) --no-print-directory test-up
@echo
@$(MAKE) --no-print-directory test-down

docker-test:
docker run --privileged --rm -it \
-v $(shell pwd):/go/src/github.com/thecodeteam/csi-vfs golang:1.9.4 \
make -C /go/src/github.com/thecodeteam/csi-vfs test


################################################################################
## BUILD ##
################################################################################
build: $(CSI_VFS)

.PHONY: build test
clean:
rm -fr $(CSI_VFS) $(ETCD) $(CSC)

.PHONY: build clean test test-clean docker-test
4 changes: 2 additions & 2 deletions service/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ func (s *service) ControllerPublishVolume(

// Get the mount info to determine if the volume dir is already
// bind mounted to the device dir.
minfo, err := gofsutil.GetMounts(ctx)
minfo, err := getMounts(ctx)
if err != nil {
return nil, status.Errorf(
codes.Internal, "failed to get mount info: %v", err)
Expand Down Expand Up @@ -228,7 +228,7 @@ func (s *service) ControllerUnpublishVolume(
devPath := path.Join(s.dev, req.VolumeId)

// Get the node's mount information.
minfo, err := gofsutil.GetMounts(ctx)
minfo, err := getMounts(ctx)
if err != nil {
return nil, status.Errorf(
codes.Internal, "failed to get mount info: %v", err)
Expand Down
25 changes: 13 additions & 12 deletions service/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,19 +78,19 @@ func (s *service) NodePublishVolume(

// Get the mount info to determine if the device is already mounted
// into the private mount directory.
minfo, err := gofsutil.GetMounts(ctx)
minfo, err := getMounts(ctx)
if err != nil {
return nil, status.Errorf(
codes.Internal, "failed to get mount info: %v", err)
}
isPrivMounted := false
for _, i := range minfo {
if i.Source == devPath && i.Path == mntPath {
isPrivMounted = true
}
if i.Source == mntPath && i.Path == tgtPath {
if i.Source == vol.path && i.Path == tgtPath {
return &csi.NodePublishVolumeResponse{}, nil
}
if i.Source == vol.path && i.Path == mntPath {
isPrivMounted = true
}
}

// If the devie is not already mounted into the private mount
Expand Down Expand Up @@ -137,6 +137,7 @@ func (s *service) NodeUnpublishVolume(
}

// Get the path of the volume.
devPath := path.Join(s.dev, req.VolumeId)
mntPath := path.Join(s.mnt, req.VolumeId)
tgtPath := req.TargetPath
if err := gofsutil.EvalSymlinks(ctx, &tgtPath); err != nil {
Expand All @@ -145,7 +146,7 @@ func (s *service) NodeUnpublishVolume(
}

// Get the node's mount information.
minfo, err := gofsutil.GetMounts(ctx)
minfo, err := getMounts(ctx)
if err != nil {
return nil, status.Errorf(
codes.Internal, "failed to get mount info: %v", err)
Expand All @@ -158,17 +159,17 @@ func (s *service) NodeUnpublishVolume(
mountCount := 0
for _, i := range minfo {

// If there is a device that matches the mntPath value then
// increment the number of times this volume is mounted on
// this node.
if i.Source == mntPath {
// If there is an entry that matches the volPath value that
// isn't the dev or mnt paths then increment the number of
// times this volume is mounted on this node.
if i.Source == volPath && (i.Path != devPath && i.Path != mntPath) {
mountCount++
}

// If there is a device that matches the mntPath value and
// If there is an entry that matches the volPath value and
// a path that matches the tgtPath value then unmount it as
// it is the subject of this request.
if i.Source == mntPath && i.Path == tgtPath {
if i.Source == volPath && i.Path == tgtPath {
if err := gofsutil.Unmount(ctx, tgtPath); err != nil {
return nil, status.Errorf(
codes.Internal, "unmount failed: %s: %v", tgtPath, err)
Expand Down
49 changes: 48 additions & 1 deletion service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"net"
"os"
"path"
"regexp"
"strings"

"github.com/golang/protobuf/jsonpb"
log "github.com/sirupsen/logrus"
Expand Down Expand Up @@ -281,7 +283,7 @@ func getVolumeMountPaths(

mntPath := path.Join(mntDir, volumeID)

minfo, err := gofsutil.GetMounts(ctx)
minfo, err := getMounts(ctx)
if err != nil {
return nil, err
}
Expand All @@ -296,3 +298,48 @@ func getVolumeMountPaths(

return mountPaths, nil
}

func getMounts(ctx context.Context) ([]gofsutil.Info, error) {
return getMountsObj.GetMounts(ctx)
}

var getMountsObj = &gofsutil.FS{
ScanEntry: func(
ctx context.Context,
entry gofsutil.Entry,
cache map[string]gofsutil.Entry) (
info gofsutil.Info, valid bool, failed error) {

// Validate the mount table entry.
validFSType, _ := regexp.MatchString(
`(?i)^devtmpfs|(?:fuse\..*)|(?:nfs\d?)|overlay$`, entry.FSType)
sourceHasSlashPrefix := strings.HasPrefix(entry.MountSource, "/")
if valid = validFSType || sourceHasSlashPrefix; !valid {
return
}

// Copy the Entry object's fields to the Info object.
info.Device = entry.MountSource
info.Opts = make([]string, len(entry.MountOpts))
copy(info.Opts, entry.MountOpts)
info.Path = entry.MountPoint
info.Type = entry.FSType
info.Source = entry.MountSource

// If this is the first time a source is encountered in the
// output then cache its mountPoint field as the filesystem path
// to which the source is mounted as a non-bind mount.
//
// Subsequent encounters with the source will resolve it
// to the cached root value in order to set the mount info's
// Source field to the the cached mountPont field value + the
// value of the current line's root field.
if cachedEntry, ok := cache[entry.MountSource]; ok {
info.Source = path.Join(cachedEntry.MountPoint, entry.Root)
} else {
cache[entry.MountSource] = entry
}

return
},
}

0 comments on commit 270c6bf

Please sign in to comment.