diff --git a/pkg/restic/backupper.go b/pkg/restic/backupper.go index f05a3aea9..100e71c32 100644 --- a/pkg/restic/backupper.go +++ b/pkg/restic/backupper.go @@ -115,9 +115,27 @@ func (b *backupper) BackupPodVolumes(backup *arkv1api.Backup, pod *corev1api.Pod var ( errs []error volumeSnapshots = make(map[string]string) + podVolumes = make(map[string]corev1api.Volume) ) + // put the pod's volumes in a map for efficient lookup below + for _, podVolume := range pod.Spec.Volumes { + podVolumes[podVolume.Name] = podVolume + } + for _, volumeName := range volumesToBackup { + if !volumeExists(podVolumes, volumeName) { + log.Warnf("No volume named %s found in pod %s/%s, skipping", volumeName, pod.Namespace, pod.Name) + continue + } + + // hostPath volumes are not supported because they're not mounted into /var/lib/kubelet/pods, so our + // daemonset pod has no way to access their data. + if isHostPathVolume(podVolumes, volumeName) { + log.Warnf("Volume %s in pod %s/%s is a hostPath volume which is not supported for restic backup, skipping", volumeName, pod.Namespace, pod.Name) + continue + } + volumeBackup := newPodVolumeBackup(backup, pod, volumeName, repo.Spec.ResticIdentifier) if err := errorOnly(b.repoManager.arkClient.ArkV1().PodVolumeBackups(volumeBackup.Namespace).Create(volumeBackup)); err != nil { @@ -152,6 +170,20 @@ ForEachVolume: return volumeSnapshots, errs } +func volumeExists(podVolumes map[string]corev1api.Volume, volumeName string) bool { + _, found := podVolumes[volumeName] + return found +} + +func isHostPathVolume(podVolumes map[string]corev1api.Volume, volumeName string) bool { + volume, found := podVolumes[volumeName] + if !found { + return false + } + + return volume.HostPath != nil +} + func newPodVolumeBackup(backup *arkv1api.Backup, pod *corev1api.Pod, volumeName, repoIdentifier string) *arkv1api.PodVolumeBackup { return &arkv1api.PodVolumeBackup{ ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/restic/backupper_test.go b/pkg/restic/backupper_test.go new file mode 100644 index 000000000..662c67289 --- /dev/null +++ b/pkg/restic/backupper_test.go @@ -0,0 +1,51 @@ +/* +Copyright 2018 the Heptio Ark contributors. + +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 restic + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + corev1api "k8s.io/api/core/v1" +) + +func TestVolumeExists(t *testing.T) { + podVolumes := map[string]corev1api.Volume{ + "foo": {}, + "bar": {}, + } + + assert.True(t, volumeExists(podVolumes, "foo")) + assert.True(t, volumeExists(podVolumes, "bar")) + assert.False(t, volumeExists(podVolumes, "non-existent volume")) +} + +func TestIsHostPathVolume(t *testing.T) { + podVolumes := map[string]corev1api.Volume{ + "foo": { + VolumeSource: corev1api.VolumeSource{ + HostPath: &corev1api.HostPathVolumeSource{}, + }, + }, + "bar": {}, + } + + assert.True(t, isHostPathVolume(podVolumes, "foo")) + assert.False(t, isHostPathVolume(podVolumes, "bar")) + assert.False(t, isHostPathVolume(podVolumes, "non-existent volume")) +}