diff --git a/pkg/util/mount/fake.go b/pkg/util/mount/fake.go index f7d816b79bd..e6c48bec93c 100644 --- a/pkg/util/mount/fake.go +++ b/pkg/util/mount/fake.go @@ -22,6 +22,8 @@ type FakeMounter struct { Log []FakeAction } +var _ Interface = &FakeMounter{} + // Values for FakeAction.Action const FakeActionMount = "mount" const FakeActionUnmount = "unmount" diff --git a/pkg/volume/secret/secret_test.go b/pkg/volume/secret/secret_test.go index 2810a684920..a29e4850a37 100644 --- a/pkg/volume/secret/secret_test.go +++ b/pkg/volume/secret/secret_test.go @@ -31,20 +31,22 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/util/mount" "github.com/GoogleCloudPlatform/kubernetes/pkg/volume" "github.com/GoogleCloudPlatform/kubernetes/pkg/volume/empty_dir" + "github.com/GoogleCloudPlatform/kubernetes/pkg/volume/util" ) -func newTestHost(t *testing.T, client client.Interface) volume.VolumeHost { +func newTestHost(t *testing.T, client client.Interface) (string, volume.VolumeHost) { tempDir, err := ioutil.TempDir("/tmp", "secret_volume_test.") if err != nil { t.Fatalf("can't make a temp rootdir: %v", err) } - return volume.NewFakeVolumeHost(tempDir, client, empty_dir.ProbeVolumePlugins()) + return tempDir, volume.NewFakeVolumeHost(tempDir, client, empty_dir.ProbeVolumePlugins()) } func TestCanSupport(t *testing.T) { pluginMgr := volume.VolumePluginMgr{} - pluginMgr.InitPlugins(ProbeVolumePlugins(), newTestHost(t, nil)) + _, host := newTestHost(t, nil) + pluginMgr.InitPlugins(ProbeVolumePlugins(), host) plugin, err := pluginMgr.FindPluginByName(secretPluginName) if err != nil { @@ -64,33 +66,15 @@ func TestPlugin(t *testing.T) { testVolumeName = "test_volume_name" testNamespace = "test_secret_namespace" testName = "test_secret_name" + + volumeSpec = volumeSpec(testVolumeName, testName) + secret = secret(testNamespace, testName) + client = testclient.NewSimpleFake(&secret) + pluginMgr = volume.VolumePluginMgr{} + _, host = newTestHost(t, client) ) - volumeSpec := &api.Volume{ - Name: testVolumeName, - VolumeSource: api.VolumeSource{ - Secret: &api.SecretVolumeSource{ - SecretName: testName, - }, - }, - } - - secret := api.Secret{ - ObjectMeta: api.ObjectMeta{ - Namespace: testNamespace, - Name: testName, - }, - Data: map[string][]byte{ - "data-1": []byte("value-1"), - "data-2": []byte("value-2"), - "data-3": []byte("value-3"), - }, - } - - client := testclient.NewSimpleFake(&secret) - - pluginMgr := volume.VolumePluginMgr{} - pluginMgr.InitPlugins(ProbeVolumePlugins(), newTestHost(t, client)) + pluginMgr.InitPlugins(ProbeVolumePlugins(), host) plugin, err := pluginMgr.FindPluginByName(secretPluginName) if err != nil { @@ -123,6 +107,152 @@ func TestPlugin(t *testing.T) { } } + doTestSecretDataInVolume(volumePath, secret, t) + doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, t) +} + +// Test the case where the 'ready' file has been created and the pod volume dir +// is a mountpoint. Mount should not be called. +func TestPluginIdempotent(t *testing.T) { + var ( + testPodUID = types.UID("test_pod_uid2") + testVolumeName = "test_volume_name" + testNamespace = "test_secret_namespace" + testName = "test_secret_name" + + volumeSpec = volumeSpec(testVolumeName, testName) + secret = secret(testNamespace, testName) + client = testclient.NewSimpleFake(&secret) + pluginMgr = volume.VolumePluginMgr{} + rootDir, host = newTestHost(t, client) + ) + + pluginMgr.InitPlugins(ProbeVolumePlugins(), host) + + plugin, err := pluginMgr.FindPluginByName(secretPluginName) + if err != nil { + t.Errorf("Can't find the plugin by name") + } + + podVolumeDir := fmt.Sprintf("%v/pods/test_pod_uid2/volumes/kubernetes.io~secret/test_volume_name", rootDir) + podMetadataDir := fmt.Sprintf("%v/pods/test_pod_uid2/plugins/kubernetes.io~secret/test_volume_name", rootDir) + pod := &api.Pod{ObjectMeta: api.ObjectMeta{UID: testPodUID}} + mounter := &mount.FakeMounter{} + mounter.MountPoints = []mount.MountPoint{ + { + Path: podVolumeDir, + }, + } + util.SetReady(podMetadataDir) + builder, err := plugin.NewBuilder(volume.NewSpecFromVolume(volumeSpec), pod, volume.VolumeOptions{}, mounter) + if err != nil { + t.Errorf("Failed to make a new Builder: %v", err) + } + if builder == nil { + t.Errorf("Got a nil Builder") + } + + volumePath := builder.GetPath() + err = builder.SetUp() + if err != nil { + t.Errorf("Failed to setup volume: %v", err) + } + + if len(mounter.Log) != 0 { + t.Errorf("Unexpected calls made to mounter: %v", mounter.Log) + } + + if _, err := os.Stat(volumePath); err != nil { + if !os.IsNotExist(err) { + t.Errorf("SetUp() failed unexpectedly: %v", err) + } + } else { + t.Errorf("volume path should not exist: %v", volumePath) + } +} + +// Test the case where the plugin's ready file exists, but the volume dir is not a +// mountpoint, which is the state the system will be in after reboot. The dir +// should be mounter and the secret data written to it. +func TestPluginReboot(t *testing.T) { + var ( + testPodUID = types.UID("test_pod_uid3") + testVolumeName = "test_volume_name" + testNamespace = "test_secret_namespace" + testName = "test_secret_name" + + volumeSpec = volumeSpec(testVolumeName, testName) + secret = secret(testNamespace, testName) + client = testclient.NewSimpleFake(&secret) + pluginMgr = volume.VolumePluginMgr{} + rootDir, host = newTestHost(t, client) + ) + + pluginMgr.InitPlugins(ProbeVolumePlugins(), host) + + plugin, err := pluginMgr.FindPluginByName(secretPluginName) + if err != nil { + t.Errorf("Can't find the plugin by name") + } + + pod := &api.Pod{ObjectMeta: api.ObjectMeta{UID: testPodUID}} + builder, err := plugin.NewBuilder(volume.NewSpecFromVolume(volumeSpec), pod, volume.VolumeOptions{}, &mount.FakeMounter{}) + if err != nil { + t.Errorf("Failed to make a new Builder: %v", err) + } + if builder == nil { + t.Errorf("Got a nil Builder") + } + + podMetadataDir := fmt.Sprintf("%v/pods/test_pod_uid3/plugins/kubernetes.io~secret/test_volume_name", rootDir) + util.SetReady(podMetadataDir) + volumePath := builder.GetPath() + if !strings.HasSuffix(volumePath, fmt.Sprintf("pods/test_pod_uid3/volumes/kubernetes.io~secret/test_volume_name")) { + t.Errorf("Got unexpected path: %s", volumePath) + } + + err = builder.SetUp() + if err != nil { + t.Errorf("Failed to setup volume: %v", err) + } + if _, err := os.Stat(volumePath); err != nil { + if os.IsNotExist(err) { + t.Errorf("SetUp() failed, volume path not created: %s", volumePath) + } else { + t.Errorf("SetUp() failed: %v", err) + } + } + + doTestSecretDataInVolume(volumePath, secret, t) + doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, t) +} + +func volumeSpec(volumeName, secretName string) *api.Volume { + return &api.Volume{ + Name: volumeName, + VolumeSource: api.VolumeSource{ + Secret: &api.SecretVolumeSource{ + SecretName: secretName, + }, + }, + } +} + +func secret(namespace, name string) api.Secret { + return api.Secret{ + ObjectMeta: api.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + Data: map[string][]byte{ + "data-1": []byte("value-1"), + "data-2": []byte("value-2"), + "data-3": []byte("value-3"), + }, + } +} + +func doTestSecretDataInVolume(volumePath string, secret api.Secret, t *testing.T) { for key, value := range secret.Data { secretDataHostPath := path.Join(volumePath, key) if _, err := os.Stat(secretDataHostPath); err != nil { @@ -139,8 +269,10 @@ func TestPlugin(t *testing.T) { } } } +} - cleaner, err := plugin.NewCleaner(testVolumeName, testPodUID, mount.New()) +func doTestCleanAndTeardown(plugin volume.VolumePlugin, podUID types.UID, testVolumeName, volumePath string, t *testing.T) { + cleaner, err := plugin.NewCleaner(testVolumeName, podUID, mount.New()) if err != nil { t.Errorf("Failed to make a new Cleaner: %v", err) }