From 71302d11638295d19c1beb9ae5dfe9b2ce793e78 Mon Sep 17 00:00:00 2001 From: saadali Date: Fri, 22 Apr 2016 16:21:42 -0700 Subject: [PATCH] Add data structure for storing attach detach controller state. --- .../volume/attach_detach_controller.go | 19 + .../cache/attach_detach_volume_cache.go | 412 +++++++++++++ .../cache/attach_detach_volume_cache_test.go | 579 ++++++++++++++++++ 3 files changed, 1010 insertions(+) create mode 100644 pkg/controller/volume/attach_detach_controller.go create mode 100644 pkg/controller/volume/cache/attach_detach_volume_cache.go create mode 100644 pkg/controller/volume/cache/attach_detach_volume_cache_test.go diff --git a/pkg/controller/volume/attach_detach_controller.go b/pkg/controller/volume/attach_detach_controller.go new file mode 100644 index 0000000000..683a95e830 --- /dev/null +++ b/pkg/controller/volume/attach_detach_controller.go @@ -0,0 +1,19 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 volume implements a controller to manage volume attach and detach +// operations. +package volume diff --git a/pkg/controller/volume/cache/attach_detach_volume_cache.go b/pkg/controller/volume/cache/attach_detach_volume_cache.go new file mode 100644 index 0000000000..573e095a07 --- /dev/null +++ b/pkg/controller/volume/cache/attach_detach_volume_cache.go @@ -0,0 +1,412 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 cache implements a data structure used by the attach/detach controller +to keep track of volumes, the nodes they are attached to, and the pods that +reference them. It is thread-safe. +*/ +package cache + +import ( + "fmt" + "sync" +) + +// AttachDetachVolumeCache defines the set of operations the volume cache +// supports. +type AttachDetachVolumeCache interface { + // AddVolume adds the given volume to the list of volumes managed by the + // attach detach controller. + // If the volume already exists, this is a no-op. + AddVolume(volumeName string) + + // AddNode adds the given node to the list of nodes the specified volume is + // attached to. + // If no volume with the name volumeName exists in the list of managed + // volumes, an error is returned. + // If the node already exists for the specified volume, this is a no-op. + AddNode(nodeName, volumeName string) error + + // AddPod adds the given pod to the list of pods that are scheduled to + // the specified node and referencing the specified volume. + // If no node with the name nodeName exists in the list of attached nodes, + // an error is returned. + // If no volume with the name volumeName exists in the list of managed + // volumes, an error is returned. + // If the pod already exists for the specified volume, this is a no-op. + AddPod(podName, nodeName, volumeName string) error + + // DeleteVolume removes the given volume from the list of volumes managed + // by the attach detach controller. + // If no volume with the name volumeName exists in the list of managed + // volumes, an error is returned. + // All attachedNodes must be deleted from the volume before it is deleted. + // If the specified volume contains 1 or more attachedNodes, an error is + // returned. + DeleteVolume(volumeName string) error + + // DeleteNode removes the given node from the list of nodes the specified + // volume is attached to. + // If no node with the name nodeName exists in the list of attached nodes, + // an error is returned. + // If no volume with the name volumeName exists in the list of managed + // volumes, an error is returned. + // All scheduledPods must be deleted from the node before it is deleted. + // If the specified node contains 1 or more scheduledPods, an error is + // returned. + DeleteNode(nodeName, volumeName string) error + + // DeletePod removes the given pod from the list of pods that are scheduled + // to the specified node and referencing the specified volume. + // If no pod with the name podName exists for the specified volume/node, an + // error is returned. + // If no node with the name nodeName exists in the list of attached nodes, + // an error is returned. + // If no volume with the name volumeName exists in the list of managed + // volumes, an error is returned. + DeletePod(podName, nodeName, volumeName string) error + + // VolumeExists returns true if the volume with the specified name exists + // in the list of volumes managed by the attach detach controller. + VolumeExists(volumeName string) bool + + // NodeExists returns true if the node with the specified name exists in + // the list of nodes the specified volume is attached to. + // If no volume with the name volumeName exists in the list of managed + // volumes, an error is returned. + NodeExists(nodeName, volumeName string) (bool, error) + + // PodExists returns true if the pod with the specified name exists in the + // list of pods that are scheduled to the specified node and referencing + // the specified volume. + // If no node with the name nodeName exists in the list of attached nodes, + // an error is returned. + // If no volume with the name volumeName exists in the list of managed + // volumes, an error is returned. + PodExists(podName, nodeName, volumeName string) (bool, error) +} + +// NewAttachDetachVolumeCache returns a new instance of the +// AttachDetachVolumeCache. +func NewAttachDetachVolumeCache() AttachDetachVolumeCache { + return &attachDetachVolumeCache{ + volumesManaged: make(map[string]volume), + } +} + +type attachDetachVolumeCache struct { + // volumesManaged is a map containing the set of volumes managed by the + // attach/detach controller. The key in this map is the name of the unique + // volume identifier and the value is a volume object containing more + // information about the volume. + volumesManaged map[string]volume + sync.RWMutex +} + +// The volume object represents a volume that is being tracked by the attach +// detach controller. +type volume struct { + // name contains the unique identifer for this volume. + name string + + // attachedNodes is a map containing the set of nodes this volume has + // succesfully been attached to. The key in this map is the name of the + // node and the value is a node object containing more information about + // the node. + attachedNodes map[string]node +} + +// The node object represents a node that a volume is attached to. +type node struct { + // name contains the name of this node. + name string + + // scheduledPods is a map containing the set of pods that are scheduled to + // this node and referencing the underlying volume. The key in the map is + // the name of the pod and the value is a pod object containing more + // information about the pod. + scheduledPods map[string]pod +} + +// The pod object represents a pod that is scheduled to a node and referncing +// the underlying volume. +type pod struct { + // name contains the name of this pod. + name string +} + +// AddVolume adds the given volume to the list of volumes managed by the attach +// detach controller. +// If the volume already exists, this is a no-op. +func (vc *attachDetachVolumeCache) AddVolume(volumeName string) { + vc.Lock() + defer vc.Unlock() + if _, exists := vc.volumesManaged[volumeName]; !exists { + vc.volumesManaged[volumeName] = volume{ + name: volumeName, + attachedNodes: make(map[string]node), + } + } +} + +// AddNode adds the given node to the list of nodes the specified volume is +// attached to. +// If no volume with the name volumeName exists in the list of managed volumes, +// an error is returned. +// If the node already exists for the specified volume, this is a no-op. +func (vc *attachDetachVolumeCache) AddNode(nodeName, volumeName string) error { + vc.Lock() + defer vc.Unlock() + + vol, volExists := vc.volumesManaged[volumeName] + if !volExists { + return fmt.Errorf( + "failed to add node %q to volume %q--no volume with that name exists in the list of managed volumes", + nodeName, + volumeName) + } + + if _, nodeExists := vol.attachedNodes[nodeName]; !nodeExists { + vc.volumesManaged[volumeName].attachedNodes[nodeName] = node{ + name: nodeName, + scheduledPods: make(map[string]pod), + } + } + + return nil +} + +// AddPod adds the given pod to the list of pods that are scheduled to the +// specified node and referencing the specified volume. +// If no node with the name nodeName exists in the list of attached nodes, +// an error is returned. +// If no volume with the name volumeName exists in the list of managed +// volumes, an error is returned. +// If the pod already exists for the specified volume, this is a no-op. +func (vc *attachDetachVolumeCache) AddPod(podName, nodeName, volumeName string) error { + vc.Lock() + defer vc.Unlock() + + volObj, volExists := vc.volumesManaged[volumeName] + if !volExists { + return fmt.Errorf( + "failed to add pod %q to node %q volume %q--no volume with that name exists in the list of managed volumes", + podName, + nodeName, + volumeName) + } + + nodeObj, nodeExists := volObj.attachedNodes[nodeName] + if !nodeExists { + return fmt.Errorf( + "failed to add pod %q to node %q volume %q--no node with that name exists in the list of attached nodes for that volume", + podName, + nodeName, + volumeName) + } + + if _, podExists := nodeObj.scheduledPods[podName]; !podExists { + vc.volumesManaged[volumeName].attachedNodes[nodeName].scheduledPods[podName] = + pod{ + name: podName, + } + } + + return nil +} + +// DeleteVolume removes the given volume from the list of volumes managed by +// the attach detach controller. +// If no volume with the name volumeName exists in the list of managed volumes, +// an error is returned. +// All attachedNodes must be deleted from the volume before it is deleted. +// If the specified volume contains 1 or more attachedNodes, an error is +// returned. +func (vc *attachDetachVolumeCache) DeleteVolume(volumeName string) error { + vc.Lock() + defer vc.Unlock() + + volObj, volExists := vc.volumesManaged[volumeName] + if !volExists { + return fmt.Errorf( + "failed to delete volume %q--no volume with that name exists in the list of managed volumes", + volumeName) + } + + if len(volObj.attachedNodes) > 0 { + return fmt.Errorf( + "failed to remove volume %q from list of managed volumes--the volume still contains %v nodes in its list of attached nodes", + volumeName, + len(volObj.attachedNodes)) + } + + delete( + vc.volumesManaged, + volumeName) + return nil +} + +// DeleteNode removes the given node from the list of nodes the specified +// volume is attached to. +// If no node with the name nodeName exists in the list of attached nodes, an +// error is returned. +// If no volume with the name volumeName exists in the list of managed +// volumes, an error is returned. +// All scheduledPods must be deleted from the node before it is deleted. +// If the specified node contains 1 or more scheduledPods, an error is +// returned. +func (vc *attachDetachVolumeCache) DeleteNode(nodeName, volumeName string) error { + vc.Lock() + defer vc.Unlock() + + volObj, volExists := vc.volumesManaged[volumeName] + if !volExists { + return fmt.Errorf( + "failed to delete node %q from volume %q--no volume with that name exists in the list of managed volumes", + nodeName, + volumeName) + } + + nodeObj, nodeExists := volObj.attachedNodes[nodeName] + if !nodeExists { + return fmt.Errorf( + "failed to delete node %q from volume %q--no node with the that name exists in the list of attached nodes for that volume", + nodeName, + volumeName) + } + + if len(nodeObj.scheduledPods) > 0 { + return fmt.Errorf( + "failed to remove node %q from volume %q--the node still contains %v pods in its list of scheduled pods", + nodeName, + volumeName, + len(nodeObj.scheduledPods)) + } + + delete( + vc.volumesManaged[volumeName].attachedNodes, + nodeName) + return nil +} + +// DeletePod removes the given pod from the list of pods that are scheduled +// to the specified node and referencing the specified volume. +// If no pod with the name podName exists for the specified volume/node, an +// error is returned. +// If no node with the name nodeName exists in the list of attached nodes, +// an error is returned. +// If no volume with the name volumeName exists in the list of managed +// volumes, an error is returned. +func (vc *attachDetachVolumeCache) DeletePod(podName, nodeName, volumeName string) error { + vc.Lock() + defer vc.Unlock() + + volObj, volExists := vc.volumesManaged[volumeName] + if !volExists { + return fmt.Errorf( + "failed to delete pod %q from node %q volume %q--no volume with that name exists in the list of managed volumes", + podName, + nodeName, + volumeName) + } + + nodeObj, nodeExists := volObj.attachedNodes[nodeName] + if !nodeExists { + return fmt.Errorf( + "failed to delete pod %q from node %q volume %q--no node with that name exists in the list of attached nodes for that volume", + podName, + nodeName, + volumeName) + } + + if _, podExists := nodeObj.scheduledPods[podName]; !podExists { + return fmt.Errorf( + "failed to delete pod %q from node %q volume %q--no pod with that name exists in the list of scheduled pods under that node/volume", + podName, + nodeName, + volumeName) + } + + delete( + vc.volumesManaged[volumeName].attachedNodes[nodeName].scheduledPods, + podName) + return nil +} + +// VolumeExists returns true if the volume with the specified name exists in +// the list of volumes managed by the attach detach controller. +func (vc *attachDetachVolumeCache) VolumeExists(volumeName string) bool { + vc.RLock() + defer vc.RUnlock() + + _, volExists := vc.volumesManaged[volumeName] + return volExists +} + +// NodeExists returns true if the node with the specified name exists in the +// list of nodes the specified volume is attached to. +// If no volume with the name volumeName exists in the list of managed +// volumes, an error is returned. +func (vc *attachDetachVolumeCache) NodeExists(nodeName, volumeName string) (bool, error) { + vc.RLock() + defer vc.RUnlock() + + volObj, volExists := vc.volumesManaged[volumeName] + if !volExists { + return false, + fmt.Errorf( + "failed to check if node %q exists under volume %q--no volume with that name exists in the list of managed volumes", + nodeName, + volumeName) + } + + _, nodeExists := volObj.attachedNodes[nodeName] + return nodeExists, nil +} + +// PodExists returns true if the pod with the specified name exists in the list +// of pods that are scheduled to the specified node and referencing the +// specified volume. +// If no node with the name nodeName exists in the list of attached nodes, an +// error is returned. +// If no volume with the name volumeName exists in the list of managed volumes, +// an error is returned. +func (vc *attachDetachVolumeCache) PodExists(podName, nodeName, volumeName string) (bool, error) { + vc.RLock() + defer vc.RUnlock() + + volObj, volExists := vc.volumesManaged[volumeName] + if !volExists { + return false, + fmt.Errorf( + "failed to check if node %q exists under volume %q--no volume with that name exists in the list of managed volumes", + nodeName, + volumeName) + } + + nodeObj, nodeExists := volObj.attachedNodes[nodeName] + if !nodeExists { + return false, fmt.Errorf( + "failed to check if pod %q exists under node %q volume %q--no node with that name exists in the list of attached nodes for that volume", + podName, + nodeName, + volumeName) + } + + _, podExists := nodeObj.scheduledPods[podName] + return podExists, nil +} diff --git a/pkg/controller/volume/cache/attach_detach_volume_cache_test.go b/pkg/controller/volume/cache/attach_detach_volume_cache_test.go new file mode 100644 index 0000000000..cc8a064988 --- /dev/null +++ b/pkg/controller/volume/cache/attach_detach_volume_cache_test.go @@ -0,0 +1,579 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 cache + +import "testing" + +func Test_AddVolume_Positive_NewVolume(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + volumeName := "volume-name" + + // Act + vc.AddVolume(volumeName) + + // Assert + volumeExists := vc.VolumeExists(volumeName) + if !volumeExists { + t.Fatalf("Added volume %q does not exist, it should.", volumeName) + } +} + +func Test_AddVolume_Positive_ExistingVolume(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + volumeName := "volume-name" + vc.AddVolume(volumeName) + + // Act + vc.AddVolume(volumeName) + + // Assert + volumeExists := vc.VolumeExists(volumeName) + if !volumeExists { + t.Fatalf("Added volume %q does not exist, it should.", volumeName) + } +} + +func Test_AddNode_Positive_NewNodeVolumeExists(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + volumeName := "volume-name" + nodeName := "node-name" + vc.AddVolume(volumeName) + + // Act + nodeErr := vc.AddNode(nodeName, volumeName) + + // Assert + if nodeErr != nil { + t.Fatalf("AddNode failed. Expected: Actual: <%v>", nodeErr) + } + + nodeExists, nodeExistsErr := vc.NodeExists(nodeName, volumeName) + if nodeExistsErr != nil { + t.Fatalf("NodeExists failed. Expected: Actual: <%v>", nodeExistsErr) + } + if !nodeExists { + t.Fatalf("Added node %q does not exist, it should.", nodeName) + } +} + +func Test_AddNode_Positive_NodeExistsVolumeExists(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + volumeName := "volume-name" + nodeName := "node-name" + vc.AddVolume(volumeName) + nodeErr1 := vc.AddNode(nodeName, volumeName) + if nodeErr1 != nil { + t.Fatalf("First call to AddNode failed. Expected: Actual: <%v>", nodeErr1) + } + + // Act + nodeErr2 := vc.AddNode(nodeName, volumeName) + + // Assert + if nodeErr2 != nil { + t.Fatalf("Second call to AddNode failed. Expected: Actual: <%v>", nodeErr2) + } + + nodeExists, nodeExistsErr := vc.NodeExists(nodeName, volumeName) + if nodeExistsErr != nil { + t.Fatalf("NodeExists failed. Expected: Actual: <%v>", nodeExistsErr) + } + if !nodeExists { + t.Fatalf("Added node %q does not exist, it should.", nodeName) + } +} + +func Test_AddNode_Negative_NewNodeVolumeDoesntExists(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + volumeName := "volume-name" + nodeName := "node-name" + + // Act + nodeErr := vc.AddNode(nodeName, volumeName) + + // Assert + if nodeErr == nil { + t.Fatalf("AddNode did not fail. Expected: <\"failed to add node...no volume with that name exists in the list of managed volumes\"> Actual: ") + } + + nodeExists, nodeExistsErr := vc.NodeExists(nodeName, volumeName) + if nodeExistsErr == nil { + t.Fatalf("NodeExists did not fail. Expected: Actual: ") + } + if nodeExists { + t.Fatalf("Added node %q exists, it should not.", nodeName) + } +} + +func Test_AddPod_Positive_NewPodNodeExistsVolumeExists(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + volumeName := "volume-name" + nodeName := "node-name" + podName := "pod-name" + vc.AddVolume(volumeName) + nodeErr := vc.AddNode(nodeName, volumeName) + if nodeErr != nil { + t.Fatalf("AddNode failed. Expected: Actual: <%v>", nodeErr) + } + + // Act + podErr := vc.AddPod(podName, nodeName, volumeName) + + // Assert + if podErr != nil { + t.Fatalf("AddPod failed. Expected: Actual: <%v>", podErr) + } + + podExists, podExistsErr := vc.PodExists(podName, nodeName, volumeName) + if podExistsErr != nil { + t.Fatalf("PodExists failed. Expected: Actual: <%v>", podExistsErr) + } + if !podExists { + t.Fatalf("Added pod %q does not exist, it should.", podName) + } +} + +func Test_AddPod_Positive_PodExistsNodeExistsVolumeExists(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + volumeName := "volume-name" + nodeName := "node-name" + podName := "pod-name" + vc.AddVolume(volumeName) + nodeErr := vc.AddNode(nodeName, volumeName) + if nodeErr != nil { + t.Fatalf("AddNode failed. Expected: Actual: <%v>", nodeErr) + } + podErr1 := vc.AddPod(podName, nodeName, volumeName) + if podErr1 != nil { + t.Fatalf("AddPod failed. Expected: Actual: <%v>", podErr1) + } + + // Act + podErr2 := vc.AddPod(podName, nodeName, volumeName) + + // Assert + if podErr2 != nil { + t.Fatalf("AddPod failed. Expected: Actual: <%v>", podErr2) + } + + podExists, podExistsErr := vc.PodExists(podName, nodeName, volumeName) + if podExistsErr != nil { + t.Fatalf("PodExists failed. Expected: Actual: <%v>", podExistsErr) + } + if !podExists { + t.Fatalf("Added pod %q does not exist, it should.", podName) + } +} + +func Test_AddPod_Negative_NewPodNodeDoesntExistsVolumeExists(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + volumeName := "volume-name" + nodeName := "node-name" + podName := "pod-name" + vc.AddVolume(volumeName) + + // Act + podErr := vc.AddPod(podName, nodeName, volumeName) + + // Assert + if podErr == nil { + t.Fatalf("AddPod did not fail. Expected: <\"failed to add pod...no node with that name exists in the list of attached nodes for that volume\"> Actual: ") + } + + podExists, podExistsErr := vc.PodExists(podName, nodeName, volumeName) + if podExistsErr == nil { + t.Fatalf("PodExists did not fail. Expected: <\"failed to check if pod exists...no node with that name exists in the list of attached nodes for that volume\"> Actual: ") + } + if podExists { + t.Fatalf("Added pod %q exists, it should not.", podName) + } +} + +func Test_AddPod_Negative_NewPodNodeDoesntExistsVolumeDoesntExists(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + volumeName := "volume-name" + nodeName := "node-name" + podName := "pod-name" + + // Act + podErr := vc.AddPod(podName, nodeName, volumeName) + + // Assert + if podErr == nil { + t.Fatalf("AddPod did not fail. Expected: <\"failed to add pod...no volume with that name exists in the list of managed volumes\"> Actual: ") + } + + podExists, podExistsErr := vc.PodExists(podName, nodeName, volumeName) + if podExistsErr == nil { + t.Fatalf("PodExists did not fail. Expected: <\"failed to check if node...no volume with that name exists in the list of managed volumes\"> Actual: ") + } + if podExists { + t.Fatalf("Added pod %q exists, it should not.", podName) + } +} + +func Test_VolumeExists_Positive_NonExistantVolume(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + notAddedVolumeName := "volume-not-added-name" + + // Act + notAddedVolumeExists := vc.VolumeExists(notAddedVolumeName) + + // Assert + if notAddedVolumeExists { + t.Fatalf("Not added volume %q exists, it should not.", notAddedVolumeName) + } +} + +func Test_NodeExists_Positive_NonExistantNodeVolumeExists(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + volumeName := "volume-name" + notAddedNodeName := "node-not-added-name" + vc.AddVolume(volumeName) + + // Act + notAddedNodeExists, notAddedNodeExistsErr := vc.NodeExists(notAddedNodeName, volumeName) + + // Assert + if notAddedNodeExistsErr != nil { + t.Fatalf("NodeExists failed. Expected: Actual: <%v>", notAddedNodeExistsErr) + } + if notAddedNodeExists { + t.Fatalf("Not added node %q exists, it should not.", notAddedNodeName) + } +} + +func Test_NodeExists_Negative_NonExistantNodeVolumeDoesntExist(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + volumeName := "volume-name" + notAddedNodeName := "node-not-added-name" + + // Act + notAddedNodeExists, notAddedNodeExistsErr := vc.NodeExists(notAddedNodeName, volumeName) + + // Assert + if notAddedNodeExistsErr == nil { + t.Fatalf("NodeExists did not fail. Expected: Actual: ") + } + if notAddedNodeExists { + t.Fatalf("Added node %q exists, it should not.", notAddedNodeName) + } +} + +func Test_PodExists_Positive_NonExistantPodNodeExistsVolumeExists(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + volumeName := "volume-name" + nodeName := "node-name" + notAddedPodName := "pod-not-added-name" + vc.AddVolume(volumeName) + addNodeErr := vc.AddNode(nodeName, volumeName) + if addNodeErr != nil { + t.Fatalf("AddNode for node %q failed. Expected: Actual: <%v>", nodeName, addNodeErr) + } + + // Act + notAddedPodExists, notAddedPodExistsErr := vc.PodExists(notAddedPodName, nodeName, volumeName) + + // Assert + if notAddedPodExistsErr != nil { + t.Fatalf("PodExists failed. Expected: Actual: <%v>", notAddedPodExistsErr) + } + if notAddedPodExists { + t.Fatalf("Not added pod %q exists, it should not.", notAddedPodName) + } +} + +func Test_PodExists_Negative_NonExistantPodNodeDoesntExistVolumeExists(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + volumeName := "volume-name" + nodeName := "node-name" + notAddedPodName := "pod-not-added-name" + vc.AddVolume(volumeName) + + // Act + notAddedPodExists, notAddedPodExistsErr := vc.PodExists(notAddedPodName, nodeName, volumeName) + + // Assert + if notAddedPodExistsErr == nil { + t.Fatalf("PodExists did not fail. Expected: <\"failed to check if pod exists...no node with that name exists in the list of attached nodes for that volume\"> Actual: ") + } + if notAddedPodExists { + t.Fatalf("Added pod %q exists, it should not.", notAddedPodName) + } +} + +func Test_PodExists_Negative_NonExistantPodNodeDoesntExistVolumeDoesntExist(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + volumeName := "volume-name" + nodeName := "node-name" + notAddedPodName := "pod-not-added-name" + + // Act + notAddedPodExists, notAddedPodExistsErr := vc.PodExists(notAddedPodName, nodeName, volumeName) + + // Assert + if notAddedPodExistsErr == nil { + t.Fatalf("PodExists did not fail. Expected: <\"failed to check if node...no volume with that name exists in the list of managed volumes\"> Actual: ") + } + if notAddedPodExists { + t.Fatalf("Added pod %q exists, it should not.", notAddedPodName) + } +} + +func Test_DeleteVolume_Positive_VolumeExists(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + volumeName := "volume-name" + vc.AddVolume(volumeName) + + // Act + deleteVolumeErr := vc.DeleteVolume(volumeName) + + // Assert + if deleteVolumeErr != nil { + t.Fatalf("DeleteVolume failed. Expected: Actual: <%v>", deleteVolumeErr) + } + + volumeExists := vc.VolumeExists(volumeName) + if volumeExists { + t.Fatalf("Deleted volume %q still exists, it should not.", volumeName) + } +} + +func Test_DeleteVolume_Negative_VolumeDoesntExists(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + notAddedVolumeName := "volume-not-added-name" + + // Act + deleteVolumeErr := vc.DeleteVolume(notAddedVolumeName) + + // Assert + if deleteVolumeErr == nil { + t.Fatalf("DeleteVolume did not fail. Expected: <\"failed to delete volume...no volume with that name exists in the list of managed volumes\"> Actual: ") + } + + notAddedVolumeExists := vc.VolumeExists(notAddedVolumeName) + if notAddedVolumeExists { + t.Fatalf("Not added volume %q exists, it should not.", notAddedVolumeName) + } +} + +func Test_DeleteNode_Positive_NodeExistsVolumeExists(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + volumeName := "volume-name" + nodeName := "node-name" + vc.AddVolume(volumeName) + nodeErr := vc.AddNode(nodeName, volumeName) + if nodeErr != nil { + t.Fatalf("AddNode failed. Expected: Actual: <%v>", nodeErr) + } + + // Act + deleteNodeErr := vc.DeleteNode(nodeName, volumeName) + + // Assert + if deleteNodeErr != nil { + t.Fatalf("DeleteNode failed. Expected: Actual: <%v>", deleteNodeErr) + } + + nodeExists, nodeExistsErr := vc.NodeExists(nodeName, volumeName) + if nodeExistsErr != nil { + t.Fatalf("NodeExists failed. Expected: Actual: <%v>", nodeExistsErr) + } + if nodeExists { + t.Fatalf("Deleted node %q still exists, it should not.", nodeName) + } +} + +func Test_DeleteNode_Negative_NodeDoesntExistVolumeExists(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + volumeName := "volume-name" + notAddedNodeName := "node-not-added-name" + vc.AddVolume(volumeName) + + // Act + deleteNodeErr := vc.DeleteNode(notAddedNodeName, volumeName) + + // Assert + if deleteNodeErr == nil { + t.Fatalf("DeleteNode did not fail. Expected: <\"failed to delete node...no node with the that name exists in the list of attached nodes for that volume\"> Actual: ") + } + + notAddedNodeExists, notAddedNodeExistsErr := vc.NodeExists(notAddedNodeName, volumeName) + if notAddedNodeExistsErr != nil { + t.Fatalf("NodeExists failed. Expected: Actual: <%v>", notAddedNodeExistsErr) + } + if notAddedNodeExists { + t.Fatalf("Not added node %q exists, it should not.", notAddedNodeName) + } +} + +func Test_DeleteNode_Negative_NodeDoesntExistVolumeDoesntExist(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + volumeName := "volume-name" + notAddedNodeName := "node-not-added-name" + + // Act + deleteNodeErr := vc.DeleteNode(notAddedNodeName, volumeName) + + // Assert + if deleteNodeErr == nil { + t.Fatalf("DeleteNode did not fail. Expected: <\"failed to delete node...no volume with that name exists in the list of managed volumes\"> Actual: ") + } + + notAddedNodeExists, notAddedNodeExistsErr := vc.NodeExists(notAddedNodeName, volumeName) + if notAddedNodeExistsErr == nil { + t.Fatalf("NodeExists did not fail. Expected: <\failed to check if node...no volume with that name exists in the list of managed volumes\"> Actual: ") + } + if notAddedNodeExists { + t.Fatalf("Not added node %q exists, it should not.", notAddedNodeName) + } +} + +func Test_DeletePod_Positive_PodExistsNodeExistsVolumeExists(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + volumeName := "volume-name" + nodeName := "node-name" + podName := "pod-name" + vc.AddVolume(volumeName) + nodeErr := vc.AddNode(nodeName, volumeName) + if nodeErr != nil { + t.Fatalf("AddNode failed. Expected: Actual: <%v>", nodeErr) + } + podErr := vc.AddPod(podName, nodeName, volumeName) + if podErr != nil { + t.Fatalf("AddPod failed. Expected: Actual: <%v>", podErr) + } + + // Act + deletePodErr := vc.DeletePod(podName, nodeName, volumeName) + + // Assert + if deletePodErr != nil { + t.Fatalf("DeletePod failed. Expected: Actual: <%v>", podName) + } + + podExists, podExistsErr := vc.PodExists(podName, nodeName, volumeName) + if podExistsErr != nil { + t.Fatalf("PodExists failed. Expected: Actual: <%v>", podExistsErr) + } + if podExists { + t.Fatalf("Deleted pod %q still exists, it should not.", podName) + } +} + +func Test_DeletePod_Positive_PodDoesntExistNodeExistsVolumeExists(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + volumeName := "volume-name" + nodeName := "node-name" + podName := "pod-name" + vc.AddVolume(volumeName) + nodeErr := vc.AddNode(nodeName, volumeName) + if nodeErr != nil { + t.Fatalf("AddNode failed. Expected: Actual: <%v>", nodeErr) + } + + // Act + deletePodErr := vc.DeletePod(podName, nodeName, volumeName) + + // Assert + if deletePodErr == nil { + t.Fatalf("DeletePod did not fail. Expected: <\"failed to delete pod...no pod with that name exists in the list of scheduled pods under that node/volume\"> Actual: ") + } + + podExists, podExistsErr := vc.PodExists(podName, nodeName, volumeName) + if podExistsErr != nil { + t.Fatalf("PodExists failed. Expected: Actual: <%v>", podExistsErr) + } + if podExists { + t.Fatalf("Deleted pod %q still exists, it should not.", podName) + } +} + +func Test_DeletePod_Positive_PodDoesntExistNodeDoesntExistVolumeExists(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + volumeName := "volume-name" + nodeName := "node-name" + podName := "pod-name" + vc.AddVolume(volumeName) + + // Act + deletePodErr := vc.DeletePod(podName, nodeName, volumeName) + + // Assert + if deletePodErr == nil { + t.Fatalf("DeletePod did not fail. Expected: <\"failed to delete pod...no node with that name exists in the list of attached nodes for that volume\"> Actual: ") + } + + podExists, podExistsErr := vc.PodExists(podName, nodeName, volumeName) + if podExistsErr == nil { + t.Fatalf("PodExists did not fail. Expected: <\failed to check if pod...no node with that name exists in the list of attached nodes for that volume\"> Actual: ") + } + if podExists { + t.Fatalf("Deleted pod %q still exists, it should not.", podName) + } +} + +func Test_DeletePod_Positive_PodDoesntExistNodeDoesntExistVolumeDoesntExist(t *testing.T) { + // Arrange + vc := NewAttachDetachVolumeCache() + volumeName := "volume-name" + nodeName := "node-name" + podName := "pod-name" + + // Act + deletePodErr := vc.DeletePod(podName, nodeName, volumeName) + + // Assert + if deletePodErr == nil { + t.Fatalf("DeletePod did not fail. Expected: <\"failed to delete pod...no volume with that name exists in the list of managed volumes\"> Actual: ") + } + + podExists, podExistsErr := vc.PodExists(podName, nodeName, volumeName) + if podExistsErr == nil { + t.Fatalf("PodExists did not fail. Expected: <\failed to check if pod...no volume with that name exists in the list of managed volumes\"> Actual: ") + } + if podExists { + t.Fatalf("Deleted pod %q still exists, it should not.", podName) + } +} + +/* + t.Fatalf("%q", notAddedNodeExistsErr) +*/