From 6875749583af0fac3160b6c8a1fbca1ab33d101d Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Wed, 29 Mar 2023 11:34:17 +0200 Subject: [PATCH 01/24] Simplify AddOdoProjectVolume and AddOdoMandatoryVolume --- pkg/dev/podmandev/pod.go | 4 ++-- .../adapters/kubernetes/component/adapter.go | 4 ++-- pkg/devfile/adapters/kubernetes/utils/utils.go | 18 ++++++++---------- .../adapters/kubernetes/utils/utils_test.go | 12 ++---------- 4 files changed, 14 insertions(+), 24 deletions(-) diff --git a/pkg/dev/podmandev/pod.go b/pkg/dev/podmandev/pod.go index 787715d14b7..f841dd4bdb4 100644 --- a/pkg/dev/podmandev/pod.go +++ b/pkg/dev/podmandev/pod.go @@ -55,8 +55,8 @@ func createPodFromComponent( return nil, nil, err } - utils.AddOdoProjectVolume(&containers) - utils.AddOdoMandatoryVolume(&containers) + utils.AddOdoProjectVolume(containers) + utils.AddOdoMandatoryVolume(containers) volumes := []corev1.Volume{ { diff --git a/pkg/devfile/adapters/kubernetes/component/adapter.go b/pkg/devfile/adapters/kubernetes/component/adapter.go index ac5afeb9c57..2e1e99a4a7a 100644 --- a/pkg/devfile/adapters/kubernetes/component/adapter.go +++ b/pkg/devfile/adapters/kubernetes/component/adapter.go @@ -422,8 +422,8 @@ func (a *Adapter) createOrUpdateComponent( } // Add the project volume before generating init containers - utils.AddOdoProjectVolume(&containers) - utils.AddOdoMandatoryVolume(&containers) + utils.AddOdoProjectVolume(containers) + utils.AddOdoMandatoryVolume(containers) containers, err = utils.UpdateContainersEntrypointsIfNeeded(a.Devfile, containers, commands.BuildCmd, commands.RunCmd, commands.DebugCmd) if err != nil { diff --git a/pkg/devfile/adapters/kubernetes/utils/utils.go b/pkg/devfile/adapters/kubernetes/utils/utils.go index ec95294b378..13923e41ce4 100644 --- a/pkg/devfile/adapters/kubernetes/utils/utils.go +++ b/pkg/devfile/adapters/kubernetes/utils/utils.go @@ -47,35 +47,33 @@ func GetOdoContainerVolumes(sourcePVCName string) []corev1.Volume { } // AddOdoProjectVolume adds the odo project volume to the containers -func AddOdoProjectVolume(containers *[]corev1.Container) { +func AddOdoProjectVolume(containers []corev1.Container) { if containers == nil { return } - for i, container := range *containers { - for _, env := range container.Env { + for i := range containers { + for _, env := range containers[i].Env { if env.Name == _envProjectsRoot { - container.VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{ + containers[i].VolumeMounts = append(containers[i].VolumeMounts, corev1.VolumeMount{ Name: storage.OdoSourceVolume, MountPath: env.Value, }) - (*containers)[i] = container } } } } // AddOdoMandatoryVolume adds the odo mandatory volumes to the containers -func AddOdoMandatoryVolume(containers *[]corev1.Container) { +func AddOdoMandatoryVolume(containers []corev1.Container) { if containers == nil { return } - for i, container := range *containers { - klog.V(2).Infof("Updating container %v with mandatory volume mounts", container.Name) - container.VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{ + for i := range containers { + klog.V(2).Infof("Updating container %v with mandatory volume mounts", containers[i].Name) + containers[i].VolumeMounts = append(containers[i].VolumeMounts, corev1.VolumeMount{ Name: storage.SharedDataVolumeName, MountPath: storage.SharedDataMountPath, }) - (*containers)[i] = container } } diff --git a/pkg/devfile/adapters/kubernetes/utils/utils_test.go b/pkg/devfile/adapters/kubernetes/utils/utils_test.go index d941a7f3eff..1a896d0beda 100644 --- a/pkg/devfile/adapters/kubernetes/utils/utils_test.go +++ b/pkg/devfile/adapters/kubernetes/utils/utils_test.go @@ -67,11 +67,7 @@ func TestAddOdoProjectVolume(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if tt.containers == nil { - AddOdoProjectVolume(nil) - } else { - AddOdoProjectVolume(&tt.containers) - } + AddOdoProjectVolume(tt.containers) for wantContainerName, wantMountPath := range tt.volMount { matched := false @@ -180,11 +176,7 @@ func TestAddOdoMandatoryVolume(t *testing.T) { }, } { t.Run(tt.name, func(t *testing.T) { - if tt.containers == nil { - AddOdoMandatoryVolume(nil) - } else { - AddOdoMandatoryVolume(&tt.containers) - } + AddOdoMandatoryVolume(tt.containers) for containerName, volMounts := range tt.wantVolumeMounts { c, ok := findContainerByName(tt.containers, containerName) From e66a531c2ce71a685db4f79f21b5623950def513 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Wed, 29 Mar 2023 11:39:35 +0200 Subject: [PATCH 02/24] Rename / Clarify HandleEphemeralStorage function --- pkg/devfile/adapters/kubernetes/component/adapter.go | 4 ++-- pkg/devfile/adapters/kubernetes/storage/utils.go | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/devfile/adapters/kubernetes/component/adapter.go b/pkg/devfile/adapters/kubernetes/component/adapter.go index 2e1e99a4a7a..8616ce88a43 100644 --- a/pkg/devfile/adapters/kubernetes/component/adapter.go +++ b/pkg/devfile/adapters/kubernetes/component/adapter.go @@ -380,8 +380,8 @@ func (a *Adapter) createOrUpdateComponent( Runtime: runtime, }) - // handle the ephemeral storage - err := storage.HandleEphemeralStorage(a.kubeClient, storageClient, componentName, isMainStorageEphemeral) + // Create the volume for the project sources, if not ephemeral + err := storage.HandleOdoSourceStorage(a.kubeClient, storageClient, componentName, isMainStorageEphemeral) if err != nil { return nil, false, err } diff --git a/pkg/devfile/adapters/kubernetes/storage/utils.go b/pkg/devfile/adapters/kubernetes/storage/utils.go index 4bf0810a9a0..d6fbda6d0a5 100644 --- a/pkg/devfile/adapters/kubernetes/storage/utils.go +++ b/pkg/devfile/adapters/kubernetes/storage/utils.go @@ -204,8 +204,10 @@ func generateVolumeNameFromPVC(pvc string) (volumeName string, err error) { return } -// HandleEphemeralStorage creates or deletes the ephemeral volume based on the preference setting -func HandleEphemeralStorage(client kclient.ClientInterface, storageClient storage.Client, componentName string, isEphemeral bool) error { +// HandleOdoSourceStorage creates or deletes the volume containing project sources, based on the preference setting +// - if Ephemeral preference is true, any PVC with labels "component=..." and "odo-source-pvc=odo-projects" is removed +// - if Ephemeral preference is false and no PVC with matching labels exists, it is created +func HandleOdoSourceStorage(client kclient.ClientInterface, storageClient storage.Client, componentName string, isEphemeral bool) error { selector := odolabels.Builder().WithComponentName(componentName).WithSourcePVC(storage.OdoSourceVolume).Selector() pvcs, err := client.ListPVCs(selector) if err != nil && !kerrors.IsNotFound(err) { From 40d6e06861df8580237ee102a1bb6f6285ba028f Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Wed, 29 Mar 2023 12:52:57 +0200 Subject: [PATCH 03/24] Regroup volume-specific code --- .../adapters/kubernetes/component/adapter.go | 59 ++++++++++--------- .../kubernetes/component/adapter_test.go | 2 +- .../adapters/kubernetes/utils/utils.go | 5 ++ 3 files changed, 37 insertions(+), 29 deletions(-) diff --git a/pkg/devfile/adapters/kubernetes/component/adapter.go b/pkg/devfile/adapters/kubernetes/component/adapter.go index 8616ce88a43..d25f72d4f36 100644 --- a/pkg/devfile/adapters/kubernetes/component/adapter.go +++ b/pkg/devfile/adapters/kubernetes/component/adapter.go @@ -369,29 +369,10 @@ func (a *Adapter) createOrUpdateComponent( deployment *appsv1.Deployment, ) (*appsv1.Deployment, bool, error) { - isMainStorageEphemeral := a.prefClient.GetEphemeralSourceVolume() - componentName := a.ComponentName runtime := component.GetComponentRuntimeFromDevfileMetadata(a.Devfile.Data.GetMetadata()) - storageClient := storagepkg.NewClient(componentName, a.AppName, storagepkg.ClientOptions{ - Client: a.kubeClient, - Runtime: runtime, - }) - - // Create the volume for the project sources, if not ephemeral - err := storage.HandleOdoSourceStorage(a.kubeClient, storageClient, componentName, isMainStorageEphemeral) - if err != nil { - return nil, false, err - } - - // From devfile info, create PVCs and return ephemeral storages - ephemerals, err := storagepkg.Push(storageClient, a.Devfile) - if err != nil { - return nil, false, err - } - // Set the labels labels := odolabels.GetLabels(componentName, a.AppName, runtime, odolabels.ComponentDevMode, true) @@ -421,29 +402,54 @@ func (a *Adapter) createOrUpdateComponent( return nil, false, fmt.Errorf("no valid components found in the devfile") } - // Add the project volume before generating init containers - utils.AddOdoProjectVolume(containers) - utils.AddOdoMandatoryVolume(containers) + initContainers := podTemplateSpec.Spec.InitContainers containers, err = utils.UpdateContainersEntrypointsIfNeeded(a.Devfile, containers, commands.BuildCmd, commands.RunCmd, commands.DebugCmd) if err != nil { return nil, false, err } - initContainers := podTemplateSpec.Spec.InitContainers + storageClient := storagepkg.NewClient(componentName, a.AppName, storagepkg.ClientOptions{ + Client: a.kubeClient, + Runtime: runtime, + }) + + // Create the volume for the project sources, if not ephemeral + err = storage.HandleOdoSourceStorage(a.kubeClient, storageClient, componentName, a.prefClient.GetEphemeralSourceVolume()) + if err != nil { + return nil, false, err + } + + // Create PVCs for non-ephemeral Volumes defined in the Devfile + // and returns the Ephemeral volumes defined in the Devfile + ephemerals, err := storagepkg.Push(storageClient, a.Devfile) + if err != nil { + return nil, false, err + } - // list all the pvcs for the component + // get all the PVCs from the cluster belonging to the component + // These PVCs have been created earlier with `storage.HandleOdoSourceStorage` and `storagepkg.Push` pvcs, err := a.kubeClient.ListPVCs(fmt.Sprintf("%v=%v", "component", componentName)) if err != nil { return nil, false, err } + var allVolumes []corev1.Volume + + // Get the name of the PVC for project sources + a map of (storageName => VolumeInfo) + // odoSourcePVCName will be empty when Ephemeral preference is true odoSourcePVCName, volumeNameToVolInfo, err := storage.GetVolumeInfos(pvcs) if err != nil { return nil, false, err } - var allVolumes []corev1.Volume + // Add the volumes for the projects source and the Odo-specific directory + odoMandatoryVolumes := utils.GetOdoContainerVolumes(odoSourcePVCName) + allVolumes = append(allVolumes, odoMandatoryVolumes...) + + // Add the volumeMounts for the project sources volume and the Odo-specific volume into the containers + utils.AddOdoProjectVolume(containers) + utils.AddOdoMandatoryVolume(containers) // Get PVC volumes and Volume Mounts pvcVolumes, err := storage.GetPersistentVolumesAndVolumeMounts(a.Devfile, containers, initContainers, volumeNameToVolInfo, parsercommon.DevfileOptions{}) @@ -458,9 +464,6 @@ func (a *Adapter) createOrUpdateComponent( } allVolumes = append(allVolumes, ephemeralVolumes...) - odoMandatoryVolumes := utils.GetOdoContainerVolumes(odoSourcePVCName) - allVolumes = append(allVolumes, odoMandatoryVolumes...) - selectorLabels := map[string]string{ "component": componentName, } diff --git a/pkg/devfile/adapters/kubernetes/component/adapter_test.go b/pkg/devfile/adapters/kubernetes/component/adapter_test.go index 3607997f4cd..cf354209179 100644 --- a/pkg/devfile/adapters/kubernetes/component/adapter_test.go +++ b/pkg/devfile/adapters/kubernetes/component/adapter_test.go @@ -130,7 +130,7 @@ func TestCreateOrUpdateComponent(t *testing.T) { }) ctrl := gomock.NewController(t) fakePrefClient := preference.NewMockClient(ctrl) - fakePrefClient.EXPECT().GetEphemeralSourceVolume() + fakePrefClient.EXPECT().GetEphemeralSourceVolume().AnyTimes() componentAdapter := NewKubernetesAdapter(fkclient, fakePrefClient, nil, nil, nil, nil, adapterCtx) _, _, err := componentAdapter.createOrUpdateComponent(tt.running, libdevfile.DevfileCommands{}, nil) diff --git a/pkg/devfile/adapters/kubernetes/utils/utils.go b/pkg/devfile/adapters/kubernetes/utils/utils.go index 13923e41ce4..77a048d5b07 100644 --- a/pkg/devfile/adapters/kubernetes/utils/utils.go +++ b/pkg/devfile/adapters/kubernetes/utils/utils.go @@ -21,6 +21,7 @@ func GetOdoContainerVolumes(sourcePVCName string) []corev1.Volume { var sourceVolume corev1.Volume if sourcePVCName != "" { + // Define a Persistent volume using the found PVC volume source sourceVolume = corev1.Volume{ Name: storage.OdoSourceVolume, VolumeSource: corev1.VolumeSource{ @@ -28,8 +29,12 @@ func GetOdoContainerVolumes(sourcePVCName string) []corev1.Volume { }, } } else { + // Define an Ephemeral volume using an EmptyDir volume source sourceVolume = corev1.Volume{ Name: storage.OdoSourceVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, } } From 4af560befd983d69e6c339e0b1fce7199879f627 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Wed, 29 Mar 2023 14:37:18 +0200 Subject: [PATCH 04/24] Move volume specific code to a separated function --- .../adapters/kubernetes/component/adapter.go | 124 ++++++++++-------- 1 file changed, 71 insertions(+), 53 deletions(-) diff --git a/pkg/devfile/adapters/kubernetes/component/adapter.go b/pkg/devfile/adapters/kubernetes/component/adapter.go index d25f72d4f36..8a3c3482d94 100644 --- a/pkg/devfile/adapters/kubernetes/component/adapter.go +++ b/pkg/devfile/adapters/kubernetes/component/adapter.go @@ -409,67 +409,17 @@ func (a *Adapter) createOrUpdateComponent( return nil, false, err } - storageClient := storagepkg.NewClient(componentName, a.AppName, storagepkg.ClientOptions{ - Client: a.kubeClient, - Runtime: runtime, - }) - - // Create the volume for the project sources, if not ephemeral - err = storage.HandleOdoSourceStorage(a.kubeClient, storageClient, componentName, a.prefClient.GetEphemeralSourceVolume()) + // Returns the volumes to add to the PodTemplate and adds volumeMounts to the containers and initContainers + volumes, err := a.buildVolumes(containers, initContainers) if err != nil { return nil, false, err } - - // Create PVCs for non-ephemeral Volumes defined in the Devfile - // and returns the Ephemeral volumes defined in the Devfile - ephemerals, err := storagepkg.Push(storageClient, a.Devfile) - if err != nil { - return nil, false, err - } - - // get all the PVCs from the cluster belonging to the component - // These PVCs have been created earlier with `storage.HandleOdoSourceStorage` and `storagepkg.Push` - pvcs, err := a.kubeClient.ListPVCs(fmt.Sprintf("%v=%v", "component", componentName)) - if err != nil { - return nil, false, err - } - - var allVolumes []corev1.Volume - - // Get the name of the PVC for project sources + a map of (storageName => VolumeInfo) - // odoSourcePVCName will be empty when Ephemeral preference is true - odoSourcePVCName, volumeNameToVolInfo, err := storage.GetVolumeInfos(pvcs) - if err != nil { - return nil, false, err - } - - // Add the volumes for the projects source and the Odo-specific directory - odoMandatoryVolumes := utils.GetOdoContainerVolumes(odoSourcePVCName) - allVolumes = append(allVolumes, odoMandatoryVolumes...) - - // Add the volumeMounts for the project sources volume and the Odo-specific volume into the containers - utils.AddOdoProjectVolume(containers) - utils.AddOdoMandatoryVolume(containers) - - // Get PVC volumes and Volume Mounts - pvcVolumes, err := storage.GetPersistentVolumesAndVolumeMounts(a.Devfile, containers, initContainers, volumeNameToVolInfo, parsercommon.DevfileOptions{}) - if err != nil { - return nil, false, err - } - allVolumes = append(allVolumes, pvcVolumes...) - - ephemeralVolumes, err := storage.GetEphemeralVolumesAndVolumeMounts(a.Devfile, containers, initContainers, ephemerals, parsercommon.DevfileOptions{}) - if err != nil { - return nil, false, err - } - allVolumes = append(allVolumes, ephemeralVolumes...) + podTemplateSpec.Spec.Volumes = volumes selectorLabels := map[string]string{ "component": componentName, } - podTemplateSpec.Spec.Volumes = allVolumes - deployParams := generator.DeploymentParams{ TypeMeta: generator.GetTypeMeta(kclient.DeploymentKind, kclient.DeploymentAPIVersion), ObjectMeta: deploymentObjectMeta, @@ -568,6 +518,74 @@ func (a *Adapter) createOrUpdateComponent( return deployment, newGeneration != originalGeneration, nil } +// buildVolumes: +// - (side effect on cluster) creates the PVC for the project sources if Epehemeral preference is false +// - (side effect on cluster) creates the PVCs for non-ephemeral volumes defined in the Devfile +// - (side effect on input parameters) adds volumeMounts to containers and initContainers for the PVCs and Ephemeral volumes +// - TODO(feloy) add volumeMounts for automounted volumes +// => Returns the list of Volumes to add to the PodTemplate +func (a *Adapter) buildVolumes(containers, initContainers []corev1.Container) ([]corev1.Volume, error) { + + runtime := component.GetComponentRuntimeFromDevfileMetadata(a.Devfile.Data.GetMetadata()) + + storageClient := storagepkg.NewClient(a.ComponentName, a.AppName, storagepkg.ClientOptions{ + Client: a.kubeClient, + Runtime: runtime, + }) + + // Create the PVC for the project sources, if not ephemeral + err := storage.HandleOdoSourceStorage(a.kubeClient, storageClient, a.ComponentName, a.prefClient.GetEphemeralSourceVolume()) + if err != nil { + return nil, err + } + + // Create PVCs for non-ephemeral Volumes defined in the Devfile + // and returns the Ephemeral volumes defined in the Devfile + ephemerals, err := storagepkg.Push(storageClient, a.Devfile) + if err != nil { + return nil, err + } + + // get all the PVCs from the cluster belonging to the component + // These PVCs have been created earlier with `storage.HandleOdoSourceStorage` and `storagepkg.Push` + pvcs, err := a.kubeClient.ListPVCs(fmt.Sprintf("%v=%v", "component", a.ComponentName)) + if err != nil { + return nil, err + } + + var allVolumes []corev1.Volume + + // Get the name of the PVC for project sources + a map of (storageName => VolumeInfo) + // odoSourcePVCName will be empty when Ephemeral preference is true + odoSourcePVCName, volumeNameToVolInfo, err := storage.GetVolumeInfos(pvcs) + if err != nil { + return nil, err + } + + // Add the volumes for the projects source and the Odo-specific directory + odoMandatoryVolumes := utils.GetOdoContainerVolumes(odoSourcePVCName) + allVolumes = append(allVolumes, odoMandatoryVolumes...) + + // Add the volumeMounts for the project sources volume and the Odo-specific volume into the containers + utils.AddOdoProjectVolume(containers) + utils.AddOdoMandatoryVolume(containers) + + // Get PVC volumes and Volume Mounts + pvcVolumes, err := storage.GetPersistentVolumesAndVolumeMounts(a.Devfile, containers, initContainers, volumeNameToVolInfo, parsercommon.DevfileOptions{}) + if err != nil { + return nil, err + } + allVolumes = append(allVolumes, pvcVolumes...) + + ephemeralVolumes, err := storage.GetEphemeralVolumesAndVolumeMounts(a.Devfile, containers, initContainers, ephemerals, parsercommon.DevfileOptions{}) + if err != nil { + return nil, err + } + allVolumes = append(allVolumes, ephemeralVolumes...) + + return allVolumes, nil +} + func (a *Adapter) createOrUpdateServiceForComponent(svc *corev1.Service, componentName string, ownerReference metav1.OwnerReference) error { oldSvc, err := a.kubeClient.GetOneService(a.ComponentName, a.AppName, true) originOwnerReferences := svc.OwnerReferences From 47fa83f58177f53ffc0525e56a7773946dbca9f1 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Wed, 29 Mar 2023 16:21:20 +0200 Subject: [PATCH 05/24] Add a new module configAutomount --- pkg/configAutomount/doc.go | 3 + pkg/configAutomount/interface.go | 28 ++++++ pkg/configAutomount/kubernetes.go | 35 ++++++++ pkg/configAutomount/mock.go | 49 +++++++++++ pkg/dev/kubedev/kubedev.go | 49 ++++++----- .../adapters/kubernetes/component/adapter.go | 38 ++++++--- .../kubernetes/component/adapter_test.go | 5 +- .../adapters/kubernetes/storage/utils.go | 11 +++ .../genericclioptions/clientset/clientset.go | 85 ++++++++++++------- scripts/mockgen.sh | 4 + 10 files changed, 244 insertions(+), 63 deletions(-) create mode 100644 pkg/configAutomount/doc.go create mode 100644 pkg/configAutomount/interface.go create mode 100644 pkg/configAutomount/kubernetes.go create mode 100644 pkg/configAutomount/mock.go diff --git a/pkg/configAutomount/doc.go b/pkg/configAutomount/doc.go new file mode 100644 index 00000000000..6d02a7fa0a1 --- /dev/null +++ b/pkg/configAutomount/doc.go @@ -0,0 +1,3 @@ +// configAutomount package provides functions to work with automounted configuration resources (Configmap, Secret, PVC) +// Specified at https://github.com/devfile/devworkspace-operator/blob/main/docs/additional-configuration.adoc#automatically-mounting-volumes-configmaps-and-secrets +package configAutomount diff --git a/pkg/configAutomount/interface.go b/pkg/configAutomount/interface.go new file mode 100644 index 00000000000..03e443c05b0 --- /dev/null +++ b/pkg/configAutomount/interface.go @@ -0,0 +1,28 @@ +package configAutomount + +type MountAs int +type VolumeType int + +const ( + MountAsFile MountAs = iota + 1 + MountAsSubpath + MountAsEnv +) + +const ( + VolumeTypePVC VolumeType = iota + 1 + VolumeTypeConfigmap + VolumeTypeSecret +) + +type AutomountInfo struct { + VolumeType VolumeType + VolumeName string + MountPath string + MountAs MountAs + ReadOnly bool +} + +type Client interface { + GetAutomountingVolumes() ([]AutomountInfo, error) +} diff --git a/pkg/configAutomount/kubernetes.go b/pkg/configAutomount/kubernetes.go new file mode 100644 index 00000000000..45cb55a57b9 --- /dev/null +++ b/pkg/configAutomount/kubernetes.go @@ -0,0 +1,35 @@ +package configAutomount + +import ( + "path/filepath" + + "github.com/redhat-developer/odo/pkg/kclient" +) + +type KubernetesClient struct { + kubeClient kclient.ClientInterface +} + +func NewKubernetesClient(kubeClient kclient.ClientInterface) KubernetesClient { + return KubernetesClient{ + kubeClient: kubeClient, + } +} + +func (o KubernetesClient) GetAutomountingVolumes() ([]AutomountInfo, error) { + pvcs, err := o.kubeClient.ListPVCs("controller.devfile.io/mount-to-devworkspace=true") + if err != nil { + return nil, err + } + var result []AutomountInfo + for _, pvc := range pvcs { + result = append(result, AutomountInfo{ + VolumeType: VolumeTypePVC, + VolumeName: pvc.Name, + MountPath: filepath.ToSlash(filepath.Join("/", "tmp", pvc.Name)), // TODO consider annotation "controller.devfile.io/mount-path" + MountAs: MountAsFile, + ReadOnly: false, // TODO consider annotation "controller.devfile.io/read-only" + }) + } + return result, nil +} diff --git a/pkg/configAutomount/mock.go b/pkg/configAutomount/mock.go new file mode 100644 index 00000000000..0931cf8b8b2 --- /dev/null +++ b/pkg/configAutomount/mock.go @@ -0,0 +1,49 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: pkg/configAutomount/interface.go + +// Package configAutomount is a generated GoMock package. +package configAutomount + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockClient is a mock of Client interface. +type MockClient struct { + ctrl *gomock.Controller + recorder *MockClientMockRecorder +} + +// MockClientMockRecorder is the mock recorder for MockClient. +type MockClientMockRecorder struct { + mock *MockClient +} + +// NewMockClient creates a new mock instance. +func NewMockClient(ctrl *gomock.Controller) *MockClient { + mock := &MockClient{ctrl: ctrl} + mock.recorder = &MockClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClient) EXPECT() *MockClientMockRecorder { + return m.recorder +} + +// GetAutomountingVolumes mocks base method. +func (m *MockClient) GetAutomountingVolumes() ([]AutomountInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAutomountingVolumes") + ret0, _ := ret[0].([]AutomountInfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAutomountingVolumes indicates an expected call of GetAutomountingVolumes. +func (mr *MockClientMockRecorder) GetAutomountingVolumes() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAutomountingVolumes", reflect.TypeOf((*MockClient)(nil).GetAutomountingVolumes)) +} diff --git a/pkg/dev/kubedev/kubedev.go b/pkg/dev/kubedev/kubedev.go index 523c8a04b81..1af5cc18209 100644 --- a/pkg/dev/kubedev/kubedev.go +++ b/pkg/dev/kubedev/kubedev.go @@ -10,6 +10,7 @@ import ( "github.com/redhat-developer/odo/pkg/binding" _delete "github.com/redhat-developer/odo/pkg/component/delete" + "github.com/redhat-developer/odo/pkg/configAutomount" "github.com/redhat-developer/odo/pkg/dev" "github.com/redhat-developer/odo/pkg/devfile" "github.com/redhat-developer/odo/pkg/exec" @@ -36,15 +37,16 @@ const ( ) type DevClient struct { - kubernetesClient kclient.ClientInterface - prefClient preference.Client - portForwardClient portForward.Client - watchClient watch.Client - bindingClient binding.Client - syncClient sync.Client - filesystem filesystem.Filesystem - execClient exec.Client - deleteClient _delete.Client + kubernetesClient kclient.ClientInterface + prefClient preference.Client + portForwardClient portForward.Client + watchClient watch.Client + bindingClient binding.Client + syncClient sync.Client + filesystem filesystem.Filesystem + execClient exec.Client + deleteClient _delete.Client + configAutomountClient configAutomount.Client } var _ dev.Client = (*DevClient)(nil) @@ -59,17 +61,19 @@ func NewDevClient( filesystem filesystem.Filesystem, execClient exec.Client, deleteClient _delete.Client, + configAutomountClient configAutomount.Client, ) *DevClient { return &DevClient{ - kubernetesClient: kubernetesClient, - prefClient: prefClient, - portForwardClient: portForwardClient, - watchClient: watchClient, - bindingClient: bindingClient, - syncClient: syncClient, - filesystem: filesystem, - execClient: execClient, - deleteClient: deleteClient, + kubernetesClient: kubernetesClient, + prefClient: prefClient, + portForwardClient: portForwardClient, + watchClient: watchClient, + bindingClient: bindingClient, + syncClient: syncClient, + filesystem: filesystem, + execClient: execClient, + deleteClient: deleteClient, + configAutomountClient: configAutomountClient, } } @@ -89,7 +93,13 @@ func (o *DevClient) Start( ) adapter := component.NewKubernetesAdapter( - o.kubernetesClient, o.prefClient, o.portForwardClient, o.bindingClient, o.syncClient, o.execClient, + o.kubernetesClient, + o.prefClient, + o.portForwardClient, + o.bindingClient, + o.syncClient, + o.execClient, + o.configAutomountClient, component.AdapterContext{ ComponentName: componentName, Context: path, @@ -170,6 +180,7 @@ func (o *DevClient) regenerateComponentAdapterFromWatchParams(parameters watch.W o.bindingClient, o.syncClient, o.execClient, + o.configAutomountClient, component.AdapterContext{ ComponentName: parameters.ComponentName, Context: parameters.Path, diff --git a/pkg/devfile/adapters/kubernetes/component/adapter.go b/pkg/devfile/adapters/kubernetes/component/adapter.go index 8a3c3482d94..6e6f95a0f92 100644 --- a/pkg/devfile/adapters/kubernetes/component/adapter.go +++ b/pkg/devfile/adapters/kubernetes/component/adapter.go @@ -15,6 +15,7 @@ import ( "github.com/redhat-developer/odo/pkg/binding" "github.com/redhat-developer/odo/pkg/component" + "github.com/redhat-developer/odo/pkg/configAutomount" "github.com/redhat-developer/odo/pkg/dev/common" "github.com/redhat-developer/odo/pkg/devfile/adapters" "github.com/redhat-developer/odo/pkg/devfile/adapters/kubernetes/storage" @@ -48,12 +49,13 @@ import ( // Adapter is a component adapter implementation for Kubernetes type Adapter struct { - kubeClient kclient.ClientInterface - prefClient preference.Client - portForwardClient portForward.Client - bindingClient binding.Client - syncClient sync.Client - execClient exec.Client + kubeClient kclient.ClientInterface + prefClient preference.Client + portForwardClient portForward.Client + bindingClient binding.Client + syncClient sync.Client + execClient exec.Client + configAutomountClient configAutomount.Client AdapterContext logger machineoutput.MachineEventLoggingClient @@ -78,17 +80,19 @@ func NewKubernetesAdapter( bindingClient binding.Client, syncClient sync.Client, execClient exec.Client, + configAutomountClient configAutomount.Client, context AdapterContext, ) Adapter { return Adapter{ - kubeClient: kubernetesClient, - prefClient: prefClient, - portForwardClient: portForwardClient, - bindingClient: bindingClient, - syncClient: syncClient, - execClient: execClient, - AdapterContext: context, - logger: machineoutput.NewMachineEventLoggingClient(), + kubeClient: kubernetesClient, + prefClient: prefClient, + portForwardClient: portForwardClient, + bindingClient: bindingClient, + syncClient: syncClient, + execClient: execClient, + configAutomountClient: configAutomountClient, + AdapterContext: context, + logger: machineoutput.NewMachineEventLoggingClient(), } } @@ -583,6 +587,12 @@ func (a *Adapter) buildVolumes(containers, initContainers []corev1.Container) ([ } allVolumes = append(allVolumes, ephemeralVolumes...) + automountVolumes, err := storage.GetAutomountVolumes(a.configAutomountClient, containers, initContainers) + if err != nil { + return nil, err + } + allVolumes = append(allVolumes, automountVolumes...) + return allVolumes, nil } diff --git a/pkg/devfile/adapters/kubernetes/component/adapter_test.go b/pkg/devfile/adapters/kubernetes/component/adapter_test.go index cf354209179..d987508b6f8 100644 --- a/pkg/devfile/adapters/kubernetes/component/adapter_test.go +++ b/pkg/devfile/adapters/kubernetes/component/adapter_test.go @@ -12,6 +12,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + "github.com/redhat-developer/odo/pkg/configAutomount" "github.com/redhat-developer/odo/pkg/libdevfile" "github.com/redhat-developer/odo/pkg/preference" "github.com/redhat-developer/odo/pkg/util" @@ -131,7 +132,9 @@ func TestCreateOrUpdateComponent(t *testing.T) { ctrl := gomock.NewController(t) fakePrefClient := preference.NewMockClient(ctrl) fakePrefClient.EXPECT().GetEphemeralSourceVolume().AnyTimes() - componentAdapter := NewKubernetesAdapter(fkclient, fakePrefClient, nil, nil, nil, nil, adapterCtx) + fakeConfigAutomount := configAutomount.NewMockClient(ctrl) + fakeConfigAutomount.EXPECT().GetAutomountingVolumes().AnyTimes() + componentAdapter := NewKubernetesAdapter(fkclient, fakePrefClient, nil, nil, nil, nil, fakeConfigAutomount, adapterCtx) _, _, err := componentAdapter.createOrUpdateComponent(tt.running, libdevfile.DevfileCommands{}, nil) // Checks for unexpected error cases diff --git a/pkg/devfile/adapters/kubernetes/storage/utils.go b/pkg/devfile/adapters/kubernetes/storage/utils.go index d6fbda6d0a5..580d12a116e 100644 --- a/pkg/devfile/adapters/kubernetes/storage/utils.go +++ b/pkg/devfile/adapters/kubernetes/storage/utils.go @@ -10,6 +10,7 @@ import ( parsercommon "github.com/devfile/library/v2/pkg/devfile/parser/data/v2/common" dfutil "github.com/devfile/library/v2/pkg/util" + "github.com/redhat-developer/odo/pkg/configAutomount" "github.com/redhat-developer/odo/pkg/kclient" odolabels "github.com/redhat-developer/odo/pkg/labels" "github.com/redhat-developer/odo/pkg/storage" @@ -243,3 +244,13 @@ func HandleOdoSourceStorage(client kclient.ClientInterface, storageClient storag } return nil } + +func GetAutomountVolumes(configAutomountClient configAutomount.Client, containers, initContainers []corev1.Container) ([]corev1.Volume, error) { + volumesInfos, err := configAutomountClient.GetAutomountingVolumes() + if err != nil { + return nil, err + } + _ = volumesInfos + // fmt.Printf(">>>\n%#v\n<<<\n", volumesInfos) + return nil, nil +} diff --git a/pkg/odo/genericclioptions/clientset/clientset.go b/pkg/odo/genericclioptions/clientset/clientset.go index b7d39c453e4..03b1b155277 100644 --- a/pkg/odo/genericclioptions/clientset/clientset.go +++ b/pkg/odo/genericclioptions/clientset/clientset.go @@ -14,6 +14,7 @@ package clientset import ( "github.com/spf13/cobra" + "github.com/redhat-developer/odo/pkg/configAutomount" "github.com/redhat-developer/odo/pkg/dev/kubedev" "github.com/redhat-developer/odo/pkg/dev/podmandev" "github.com/redhat-developer/odo/pkg/exec" @@ -46,6 +47,8 @@ const ( ALIZER = "DEP_ALIZER" // BINDING instantiates client for pkg/binding BINDING = "DEP_BINDING" + // CONFIG_AUTOMOUNT instantiates client for pkg/configAutomount + CONFIG_AUTOMOUNT = "DEP_CONFIG_AUTOMOUNT" // DELETE_COMPONENT instantiates client for pkg/component/delete DELETE_COMPONENT = "DEP_DELETE_COMPONENT" // DEPLOY instantiates client for pkg/deploy @@ -89,41 +92,56 @@ const ( // Clients will be created only once and be reused for sub-dependencies var subdeps map[string][]string = map[string][]string{ ALIZER: {REGISTRY}, + CONFIG_AUTOMOUNT: {KUBERNETES_NULLABLE, PODMAN_NULLABLE}, DELETE_COMPONENT: {KUBERNETES_NULLABLE, PODMAN_NULLABLE, EXEC}, DEPLOY: {KUBERNETES, FILESYSTEM}, - DEV: {BINDING, DELETE_COMPONENT, EXEC, FILESYSTEM, KUBERNETES_NULLABLE, PODMAN_NULLABLE, PORT_FORWARD, PREFERENCE, STATE, SYNC, WATCH}, - EXEC: {KUBERNETES_NULLABLE, PODMAN_NULLABLE}, - INIT: {ALIZER, FILESYSTEM, PREFERENCE, REGISTRY}, - LOGS: {KUBERNETES_NULLABLE, PODMAN_NULLABLE}, - PORT_FORWARD: {KUBERNETES_NULLABLE, EXEC, STATE}, - PROJECT: {KUBERNETES}, - REGISTRY: {FILESYSTEM, PREFERENCE, KUBERNETES_NULLABLE}, - STATE: {FILESYSTEM}, - SYNC: {EXEC}, - WATCH: {KUBERNETES_NULLABLE}, - BINDING: {PROJECT, KUBERNETES_NULLABLE}, + DEV: { + BINDING, + DELETE_COMPONENT, + CONFIG_AUTOMOUNT, + EXEC, + FILESYSTEM, + KUBERNETES_NULLABLE, + PODMAN_NULLABLE, + PORT_FORWARD, + PREFERENCE, + STATE, + SYNC, + WATCH, + }, + EXEC: {KUBERNETES_NULLABLE, PODMAN_NULLABLE}, + INIT: {ALIZER, FILESYSTEM, PREFERENCE, REGISTRY}, + LOGS: {KUBERNETES_NULLABLE, PODMAN_NULLABLE}, + PORT_FORWARD: {KUBERNETES_NULLABLE, EXEC, STATE}, + PROJECT: {KUBERNETES}, + REGISTRY: {FILESYSTEM, PREFERENCE, KUBERNETES_NULLABLE}, + STATE: {FILESYSTEM}, + SYNC: {EXEC}, + WATCH: {KUBERNETES_NULLABLE}, + BINDING: {PROJECT, KUBERNETES_NULLABLE}, /* Add sub-dependencies here, if any */ } type Clientset struct { - AlizerClient alizer.Client - BindingClient binding.Client - DeleteClient _delete.Client - DeployClient deploy.Client - DevClient dev.Client - ExecClient exec.Client - FS filesystem.Filesystem - InitClient _init.Client - KubernetesClient kclient.ClientInterface - LogsClient logs.Client - PodmanClient podman.Client - PortForwardClient portForward.Client - PreferenceClient preference.Client - ProjectClient project.Client - RegistryClient registry.Client - StateClient state.Client - SyncClient sync.Client - WatchClient watch.Client + AlizerClient alizer.Client + BindingClient binding.Client + ConfigAutomountClient configAutomount.Client + DeleteClient _delete.Client + DeployClient deploy.Client + DevClient dev.Client + ExecClient exec.Client + FS filesystem.Filesystem + InitClient _init.Client + KubernetesClient kclient.ClientInterface + LogsClient logs.Client + PodmanClient podman.Client + PortForwardClient portForward.Client + PreferenceClient preference.Client + ProjectClient project.Client + RegistryClient registry.Client + StateClient state.Client + SyncClient sync.Client + WatchClient watch.Client /* Add client by alphabetic order */ } @@ -200,6 +218,14 @@ func Fetch(command *cobra.Command, platform string) (*Clientset, error) { dep.ExecClient = exec.NewExecClient(dep.KubernetesClient) } } + if isDefined(command, CONFIG_AUTOMOUNT) { + switch platform { + case commonflags.PlatformPodman: + dep.ConfigAutomountClient = nil // Not supported + default: + dep.ConfigAutomountClient = configAutomount.NewKubernetesClient(dep.KubernetesClient) + } + } if isDefined(command, DELETE_COMPONENT) { dep.DeleteClient = _delete.NewDeleteComponentClient(dep.KubernetesClient, dep.PodmanClient, dep.ExecClient) } @@ -268,6 +294,7 @@ func Fetch(command *cobra.Command, platform string) (*Clientset, error) { dep.FS, dep.ExecClient, dep.DeleteClient, + dep.ConfigAutomountClient, ) } } diff --git a/scripts/mockgen.sh b/scripts/mockgen.sh index 9bcc9373304..7509516ccd3 100755 --- a/scripts/mockgen.sh +++ b/scripts/mockgen.sh @@ -95,3 +95,7 @@ $mockgen -source=pkg/exec/interface.go \ $mockgen -source=pkg/podman/interface.go \ -package podman \ -destination pkg/podman/mock.go + +$mockgen -source=pkg/configAutomount/interface.go \ + -package configAutomount \ + -destination pkg/configAutomount/mock.go From 84c110be23ffa1e524409f60733364118ec314ba Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Wed, 29 Mar 2023 18:33:24 +0200 Subject: [PATCH 06/24] Automount PVC (without options) --- .../adapters/kubernetes/storage/utils.go | 33 +++++++++++++++++-- pkg/storage/kubernetes.go | 11 +++++-- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/pkg/devfile/adapters/kubernetes/storage/utils.go b/pkg/devfile/adapters/kubernetes/storage/utils.go index 580d12a116e..94727aced34 100644 --- a/pkg/devfile/adapters/kubernetes/storage/utils.go +++ b/pkg/devfile/adapters/kubernetes/storage/utils.go @@ -250,7 +250,34 @@ func GetAutomountVolumes(configAutomountClient configAutomount.Client, container if err != nil { return nil, err } - _ = volumesInfos - // fmt.Printf(">>>\n%#v\n<<<\n", volumesInfos) - return nil, nil + + var result []corev1.Volume + for _, volumeInfo := range volumesInfos { + switch volumeInfo.VolumeType { + case configAutomount.VolumeTypePVC: + result = mountPVC(volumeInfo, containers, initContainers, result) + } + } + return result, nil +} + +func mountPVC(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, result []corev1.Volume) []corev1.Volume { + volumeName := "auto-" + volumeInfo.VolumeName + containerNameToMountPaths := map[string][]string{} + allContainers := containers + allContainers = append(allContainers, initContainers...) + for _, container := range allContainers { + containerNameToMountPaths[container.Name] = []string{volumeInfo.MountPath} + } + + addVolumeMountToContainers(containers, initContainers, volumeName, containerNameToMountPaths) + result = append(result, corev1.Volume{ + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: volumeInfo.VolumeName, + }, + }, + }) + return result } diff --git a/pkg/storage/kubernetes.go b/pkg/storage/kubernetes.go index d43235097fe..85c34f8601f 100644 --- a/pkg/storage/kubernetes.go +++ b/pkg/storage/kubernetes.go @@ -114,11 +114,16 @@ func (k kubernetesClient) List() (StorageList, error) { for _, container := range k.deployment.Spec.Template.Spec.Containers { for _, volumeMount := range container.VolumeMounts { - // avoid the volume mounts only from the init containers - // and the source volume mount + // avoid the volume mounts: + // - only from the init containers + // - of source volume + // - of automounted volumes _, initOK := initContainerVolumeMounts[volumeMount.Name] _, ok := containerVolumeMounts[volumeMount.Name] - if (!ok && initOK) || volumeMount.Name == OdoSourceVolume || volumeMount.Name == SharedDataVolumeName { + if (!ok && initOK) || + volumeMount.Name == OdoSourceVolume || + volumeMount.Name == SharedDataVolumeName || + strings.HasPrefix(volumeMount.Name, "auto-") { continue } From bde683b110da53bc2ce12970bd934f6b6d7ea5e5 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Wed, 29 Mar 2023 20:51:10 +0200 Subject: [PATCH 07/24] Add unit tests --- pkg/configAutomount/kubernetes.go | 7 +- pkg/configAutomount/kubernetes_test.go | 98 +++++++++++++++ .../adapters/kubernetes/storage/utils_test.go | 119 +++++++++++++++++- 3 files changed, 221 insertions(+), 3 deletions(-) create mode 100644 pkg/configAutomount/kubernetes_test.go diff --git a/pkg/configAutomount/kubernetes.go b/pkg/configAutomount/kubernetes.go index 45cb55a57b9..11d15062d4f 100644 --- a/pkg/configAutomount/kubernetes.go +++ b/pkg/configAutomount/kubernetes.go @@ -6,6 +6,11 @@ import ( "github.com/redhat-developer/odo/pkg/kclient" ) +const ( + labelMountName = "controller.devfile.io/mount-to-devworkspace" + labelMountValue = "true" +) + type KubernetesClient struct { kubeClient kclient.ClientInterface } @@ -17,7 +22,7 @@ func NewKubernetesClient(kubeClient kclient.ClientInterface) KubernetesClient { } func (o KubernetesClient) GetAutomountingVolumes() ([]AutomountInfo, error) { - pvcs, err := o.kubeClient.ListPVCs("controller.devfile.io/mount-to-devworkspace=true") + pvcs, err := o.kubeClient.ListPVCs(labelMountName + "=" + labelMountValue) if err != nil { return nil, err } diff --git a/pkg/configAutomount/kubernetes_test.go b/pkg/configAutomount/kubernetes_test.go new file mode 100644 index 00000000000..2c4bc1fc0ed --- /dev/null +++ b/pkg/configAutomount/kubernetes_test.go @@ -0,0 +1,98 @@ +package configAutomount + +import ( + "testing" + + gomock "github.com/golang/mock/gomock" + "github.com/google/go-cmp/cmp" + "github.com/redhat-developer/odo/pkg/kclient" + corev1 "k8s.io/api/core/v1" +) + +func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { + + defaultPVC1 := corev1.PersistentVolumeClaim{} + defaultPVC1.SetName("defaultPVC1") + defaultPVC1.SetLabels(map[string]string{ + labelMountName: labelMountValue, + }) + + defaultPVC2 := corev1.PersistentVolumeClaim{} + defaultPVC2.SetName("defaultPVC2") + defaultPVC2.SetLabels(map[string]string{ + labelMountName: labelMountValue, + }) + + type fields struct { + kubeClient func(ctrl *gomock.Controller) kclient.ClientInterface + } + tests := []struct { + name string + fields fields + want []AutomountInfo + wantErr bool + }{ + { + name: "Single default PVC", + fields: fields{ + kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface { + client := kclient.NewMockClientInterface(ctrl) + client.EXPECT().ListPVCs(gomock.Any()).Return([]corev1.PersistentVolumeClaim{defaultPVC1}, nil).AnyTimes() + return client + }, + }, + want: []AutomountInfo{ + { + VolumeType: VolumeTypePVC, + VolumeName: "defaultPVC1", + MountPath: "/tmp/defaultPVC1", + MountAs: MountAsFile, + }, + }, + wantErr: false, + }, + { + name: "Two default PVCs", + fields: fields{ + kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface { + client := kclient.NewMockClientInterface(ctrl) + client.EXPECT().ListPVCs(gomock.Any()).Return([]corev1.PersistentVolumeClaim{defaultPVC1, defaultPVC2}, nil).AnyTimes() + return client + }, + }, + want: []AutomountInfo{ + { + VolumeType: VolumeTypePVC, + VolumeName: "defaultPVC1", + MountPath: "/tmp/defaultPVC1", + MountAs: MountAsFile, + }, + { + VolumeType: VolumeTypePVC, + VolumeName: "defaultPVC2", + MountPath: "/tmp/defaultPVC2", + MountAs: MountAsFile, + }, + }, + wantErr: false, + }, + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctrl := gomock.NewController(t) + o := KubernetesClient{ + kubeClient: tt.fields.kubeClient(ctrl), + } + got, err := o.GetAutomountingVolumes() + if (err != nil) != tt.wantErr { + t.Errorf("KubernetesClient.GetAutomountingVolumes() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if diff := cmp.Diff(tt.want, got); diff != "" { + t.Errorf("KubernetesClient.GetAutomountingVolumes() mismatch (-want +got):\n%s", diff) + } + }) + } +} diff --git a/pkg/devfile/adapters/kubernetes/storage/utils_test.go b/pkg/devfile/adapters/kubernetes/storage/utils_test.go index 01e4c54304f..8d7927ea5cc 100644 --- a/pkg/devfile/adapters/kubernetes/storage/utils_test.go +++ b/pkg/devfile/adapters/kubernetes/storage/utils_test.go @@ -1,6 +1,7 @@ package storage import ( + "fmt" "testing" devfilev1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" @@ -9,12 +10,13 @@ import ( devfileParser "github.com/devfile/library/v2/pkg/devfile/parser" "github.com/devfile/library/v2/pkg/devfile/parser/data" parsercommon "github.com/devfile/library/v2/pkg/devfile/parser/data/v2/common" + "github.com/golang/mock/gomock" "github.com/google/go-cmp/cmp" + "github.com/redhat-developer/odo/pkg/configAutomount" + "github.com/redhat-developer/odo/pkg/testingutil" corev1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/redhat-developer/odo/pkg/testingutil" ) func TestGetPVC(t *testing.T) { @@ -472,3 +474,116 @@ func TestGetVolumesAndVolumeMounts(t *testing.T) { }) } } + +func TestGetAutomountVolumes(t *testing.T) { + + container1 := corev1.Container{ + Name: "container1", + Image: "image1", + } + container2 := corev1.Container{ + Name: "container2", + Image: "image2", + } + initContainer1 := corev1.Container{ + Name: "initContainer1", + Image: "image1", + } + initContainer2 := corev1.Container{ + Name: "initContainer2", + Image: "image2", + } + + type args struct { + configAutomountClient func(ctrl *gomock.Controller) configAutomount.Client + containers []corev1.Container + initContainers []corev1.Container + } + tests := []struct { + name string + args args + want []corev1.Volume + wantVolumeMounts []corev1.VolumeMount + wantErr bool + }{ + { + name: "No automounting volume", + args: args{ + configAutomountClient: func(ctrl *gomock.Controller) configAutomount.Client { + client := configAutomount.NewMockClient(ctrl) + client.EXPECT().GetAutomountingVolumes().Return([]configAutomount.AutomountInfo{}, nil) + return client + }, + containers: []corev1.Container{container1, container2}, + initContainers: []corev1.Container{initContainer1, initContainer2}, + }, + want: nil, + wantVolumeMounts: nil, + wantErr: false, + }, + { + name: "One PVC", + args: args{ + configAutomountClient: func(ctrl *gomock.Controller) configAutomount.Client { + info1 := configAutomount.AutomountInfo{ + VolumeType: configAutomount.VolumeTypePVC, + VolumeName: "pvc1", + MountPath: "/path/to/mount1", + MountAs: configAutomount.MountAsFile, + } + client := configAutomount.NewMockClient(ctrl) + client.EXPECT().GetAutomountingVolumes().Return([]configAutomount.AutomountInfo{info1}, nil) + return client + }, + containers: []corev1.Container{container1, container2}, + initContainers: []corev1.Container{initContainer1, initContainer2}, + }, + want: []v1.Volume{ + { + Name: "auto-pvc1", + VolumeSource: v1.VolumeSource{ + PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ + ClaimName: "pvc1", + }, + }, + }, + }, + wantVolumeMounts: []v1.VolumeMount{ + { + Name: "auto-pvc1", + MountPath: "/path/to/mount1", + }, + }, + wantErr: false, + }, + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctrl := gomock.NewController(t) + got, err := GetAutomountVolumes(tt.args.configAutomountClient(ctrl), tt.args.containers, tt.args.initContainers) + if (err != nil) != tt.wantErr { + t.Errorf("GetAutomountVolumes() error = %v, wantErr %v", err, tt.wantErr) + return + } + if diff := cmp.Diff(tt.want, got); diff != "" { + t.Errorf("GetAutomountVolumes() mismatch (-want +got):\n%s", diff) + } + + checkContainers := func(containers, initContainers []corev1.Container) error { + allContainers := containers + allContainers = append(allContainers, initContainers...) + for _, container := range allContainers { + if diff := cmp.Diff(tt.wantVolumeMounts, container.VolumeMounts); diff != "" { + return fmt.Errorf(diff) + } + } + return nil + } + + if err := checkContainers(tt.args.containers, tt.args.initContainers); err != nil { + t.Errorf("GetAutomountVolumes() containers error: %v", err) + } + }) + } +} From 0f7464a3482e85bd94881314719acec6ed71ffbe Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Thu, 30 Mar 2023 08:22:43 +0200 Subject: [PATCH 08/24] Separate functions --- pkg/configAutomount/kubernetes.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pkg/configAutomount/kubernetes.go b/pkg/configAutomount/kubernetes.go index 11d15062d4f..75e4df5fbd9 100644 --- a/pkg/configAutomount/kubernetes.go +++ b/pkg/configAutomount/kubernetes.go @@ -22,10 +22,23 @@ func NewKubernetesClient(kubeClient kclient.ClientInterface) KubernetesClient { } func (o KubernetesClient) GetAutomountingVolumes() ([]AutomountInfo, error) { + var result []AutomountInfo + + pvcs, err := o.getAutomountingPVCs() + if err != nil { + return nil, err + } + result = append(result, pvcs...) + + return result, nil +} + +func (o KubernetesClient) getAutomountingPVCs() ([]AutomountInfo, error) { pvcs, err := o.kubeClient.ListPVCs(labelMountName + "=" + labelMountValue) if err != nil { return nil, err } + var result []AutomountInfo for _, pvc := range pvcs { result = append(result, AutomountInfo{ From 227fdb3c29078756ad3d46d6f41eeec774ec65bf Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Thu, 30 Mar 2023 08:52:03 +0200 Subject: [PATCH 09/24] Mount secrets --- pkg/configAutomount/kubernetes.go | 25 ++++++++ pkg/configAutomount/kubernetes_test.go | 40 +++++++++++++ .../adapters/kubernetes/storage/utils.go | 39 ++++++++++--- .../adapters/kubernetes/storage/utils_test.go | 57 ++++++++++++++++++- 4 files changed, 152 insertions(+), 9 deletions(-) diff --git a/pkg/configAutomount/kubernetes.go b/pkg/configAutomount/kubernetes.go index 75e4df5fbd9..c9e4803211f 100644 --- a/pkg/configAutomount/kubernetes.go +++ b/pkg/configAutomount/kubernetes.go @@ -30,6 +30,12 @@ func (o KubernetesClient) GetAutomountingVolumes() ([]AutomountInfo, error) { } result = append(result, pvcs...) + secrets, err := o.getAutomountingSecrets() + if err != nil { + return nil, err + } + result = append(result, secrets...) + return result, nil } @@ -51,3 +57,22 @@ func (o KubernetesClient) getAutomountingPVCs() ([]AutomountInfo, error) { } return result, nil } + +func (o KubernetesClient) getAutomountingSecrets() ([]AutomountInfo, error) { + secrets, err := o.kubeClient.ListSecrets(labelMountName + "=" + labelMountValue) + if err != nil { + return nil, err + } + + var result []AutomountInfo + for _, secret := range secrets { + result = append(result, AutomountInfo{ + VolumeType: VolumeTypeSecret, + VolumeName: secret.Name, + MountPath: filepath.ToSlash(filepath.Join("/", "etc", "secret", secret.Name)), // TODO consider annotation "controller.devfile.io/mount-path" + MountAs: MountAsFile, // TODO consider annotation "controller.devfile.io/mount-as" + ReadOnly: false, // TODO consider annotation "controller.devfile.io/read-only" + }) + } + return result, nil +} diff --git a/pkg/configAutomount/kubernetes_test.go b/pkg/configAutomount/kubernetes_test.go index 2c4bc1fc0ed..5710d22f470 100644 --- a/pkg/configAutomount/kubernetes_test.go +++ b/pkg/configAutomount/kubernetes_test.go @@ -23,6 +23,18 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { labelMountName: labelMountValue, }) + defaultSecret1 := corev1.Secret{} + defaultSecret1.SetName("defaultSecret1") + defaultSecret1.SetLabels(map[string]string{ + labelMountName: labelMountValue, + }) + + defaultSecret2 := corev1.Secret{} + defaultSecret2.SetName("defaultSecret2") + defaultSecret2.SetLabels(map[string]string{ + labelMountName: labelMountValue, + }) + type fields struct { kubeClient func(ctrl *gomock.Controller) kclient.ClientInterface } @@ -38,6 +50,7 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface { client := kclient.NewMockClientInterface(ctrl) client.EXPECT().ListPVCs(gomock.Any()).Return([]corev1.PersistentVolumeClaim{defaultPVC1}, nil).AnyTimes() + client.EXPECT().ListSecrets(gomock.Any()).Return([]corev1.Secret{}, nil).AnyTimes() return client }, }, @@ -57,6 +70,7 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface { client := kclient.NewMockClientInterface(ctrl) client.EXPECT().ListPVCs(gomock.Any()).Return([]corev1.PersistentVolumeClaim{defaultPVC1, defaultPVC2}, nil).AnyTimes() + client.EXPECT().ListSecrets(gomock.Any()).Return([]corev1.Secret{}, nil).AnyTimes() return client }, }, @@ -76,6 +90,32 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { }, wantErr: false, }, + { + name: "Two default secrets", + fields: fields{ + kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface { + client := kclient.NewMockClientInterface(ctrl) + client.EXPECT().ListPVCs(gomock.Any()).Return([]corev1.PersistentVolumeClaim{}, nil).AnyTimes() + client.EXPECT().ListSecrets(gomock.Any()).Return([]corev1.Secret{defaultSecret1, defaultSecret2}, nil).AnyTimes() + return client + }, + }, + want: []AutomountInfo{ + { + VolumeType: VolumeTypeSecret, + VolumeName: "defaultSecret1", + MountPath: "/etc/secret/defaultSecret1", + MountAs: MountAsFile, + }, + { + VolumeType: VolumeTypeSecret, + VolumeName: "defaultSecret2", + MountPath: "/etc/secret/defaultSecret2", + MountAs: MountAsFile, + }, + }, + wantErr: false, + }, // TODO: Add test cases. } for _, tt := range tests { diff --git a/pkg/devfile/adapters/kubernetes/storage/utils.go b/pkg/devfile/adapters/kubernetes/storage/utils.go index 94727aced34..395ca97b80a 100644 --- a/pkg/devfile/adapters/kubernetes/storage/utils.go +++ b/pkg/devfile/adapters/kubernetes/storage/utils.go @@ -256,21 +256,19 @@ func GetAutomountVolumes(configAutomountClient configAutomount.Client, container switch volumeInfo.VolumeType { case configAutomount.VolumeTypePVC: result = mountPVC(volumeInfo, containers, initContainers, result) + case configAutomount.VolumeTypeSecret: + result = mountSecret(volumeInfo, containers, initContainers, result) } } return result, nil } func mountPVC(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, result []corev1.Volume) []corev1.Volume { - volumeName := "auto-" + volumeInfo.VolumeName - containerNameToMountPaths := map[string][]string{} - allContainers := containers - allContainers = append(allContainers, initContainers...) - for _, container := range allContainers { - containerNameToMountPaths[container.Name] = []string{volumeInfo.MountPath} - } + volumeName := "auto-pvc-" + volumeInfo.VolumeName + containerNameToMountPaths := getContainerNameToMountPaths(volumeInfo, containers, initContainers) addVolumeMountToContainers(containers, initContainers, volumeName, containerNameToMountPaths) + result = append(result, corev1.Volume{ Name: volumeName, VolumeSource: corev1.VolumeSource{ @@ -281,3 +279,30 @@ func mountPVC(volumeInfo configAutomount.AutomountInfo, containers, initContaine }) return result } + +func mountSecret(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, result []corev1.Volume) []corev1.Volume { + volumeName := "auto-secret-" + volumeInfo.VolumeName + + containerNameToMountPaths := getContainerNameToMountPaths(volumeInfo, containers, initContainers) + addVolumeMountToContainers(containers, initContainers, volumeName, containerNameToMountPaths) + + result = append(result, corev1.Volume{ + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: volumeInfo.VolumeName, + }, + }, + }) + return result +} + +func getContainerNameToMountPaths(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container) map[string][]string { + containerNameToMountPaths := map[string][]string{} + allContainers := containers + allContainers = append(allContainers, initContainers...) + for _, container := range allContainers { + containerNameToMountPaths[container.Name] = []string{volumeInfo.MountPath} + } + return containerNameToMountPaths +} diff --git a/pkg/devfile/adapters/kubernetes/storage/utils_test.go b/pkg/devfile/adapters/kubernetes/storage/utils_test.go index 8d7927ea5cc..0994fc99123 100644 --- a/pkg/devfile/adapters/kubernetes/storage/utils_test.go +++ b/pkg/devfile/adapters/kubernetes/storage/utils_test.go @@ -540,7 +540,7 @@ func TestGetAutomountVolumes(t *testing.T) { }, want: []v1.Volume{ { - Name: "auto-pvc1", + Name: "auto-pvc-pvc1", VolumeSource: v1.VolumeSource{ PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ ClaimName: "pvc1", @@ -550,12 +550,65 @@ func TestGetAutomountVolumes(t *testing.T) { }, wantVolumeMounts: []v1.VolumeMount{ { - Name: "auto-pvc1", + Name: "auto-pvc-pvc1", MountPath: "/path/to/mount1", }, }, wantErr: false, }, + { + name: "One PVC and one secret", + args: args{ + configAutomountClient: func(ctrl *gomock.Controller) configAutomount.Client { + info1 := configAutomount.AutomountInfo{ + VolumeType: configAutomount.VolumeTypePVC, + VolumeName: "pvc1", + MountPath: "/path/to/mount1", + MountAs: configAutomount.MountAsFile, + } + info2 := configAutomount.AutomountInfo{ + VolumeType: configAutomount.VolumeTypeSecret, + VolumeName: "secret2", + MountPath: "/path/to/mount2", + MountAs: configAutomount.MountAsFile, + } + client := configAutomount.NewMockClient(ctrl) + client.EXPECT().GetAutomountingVolumes().Return([]configAutomount.AutomountInfo{info1, info2}, nil) + return client + }, + containers: []corev1.Container{container1, container2}, + initContainers: []corev1.Container{initContainer1, initContainer2}, + }, + want: []v1.Volume{ + { + Name: "auto-pvc-pvc1", + VolumeSource: v1.VolumeSource{ + PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ + ClaimName: "pvc1", + }, + }, + }, + { + Name: "auto-secret-secret2", + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: "secret2", + }, + }, + }, + }, + wantVolumeMounts: []v1.VolumeMount{ + { + Name: "auto-pvc-pvc1", + MountPath: "/path/to/mount1", + }, + { + Name: "auto-secret-secret2", + MountPath: "/path/to/mount2", + }, + }, + wantErr: false, + }, // TODO: Add test cases. } for _, tt := range tests { From 8f162345b4158711cae36a7aed5ca56450c3abfc Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Thu, 30 Mar 2023 09:35:47 +0200 Subject: [PATCH 10/24] Mount configmaps --- pkg/configAutomount/kubernetes.go | 25 +++++++ pkg/configAutomount/kubernetes_test.go | 42 +++++++++++ .../adapters/kubernetes/storage/utils.go | 21 ++++++ .../adapters/kubernetes/storage/utils_test.go | 73 +++++++++++++++++++ pkg/kclient/configmap.go | 26 +++++++ pkg/kclient/interface.go | 3 + pkg/kclient/mock_Client.go | 15 ++++ 7 files changed, 205 insertions(+) create mode 100644 pkg/kclient/configmap.go diff --git a/pkg/configAutomount/kubernetes.go b/pkg/configAutomount/kubernetes.go index c9e4803211f..b76030e077c 100644 --- a/pkg/configAutomount/kubernetes.go +++ b/pkg/configAutomount/kubernetes.go @@ -36,6 +36,12 @@ func (o KubernetesClient) GetAutomountingVolumes() ([]AutomountInfo, error) { } result = append(result, secrets...) + cms, err := o.getAutomountingConfigmaps() + if err != nil { + return nil, err + } + result = append(result, cms...) + return result, nil } @@ -76,3 +82,22 @@ func (o KubernetesClient) getAutomountingSecrets() ([]AutomountInfo, error) { } return result, nil } + +func (o KubernetesClient) getAutomountingConfigmaps() ([]AutomountInfo, error) { + cms, err := o.kubeClient.ListConfigMaps(labelMountName + "=" + labelMountValue) + if err != nil { + return nil, err + } + + var result []AutomountInfo + for _, cm := range cms { + result = append(result, AutomountInfo{ + VolumeType: VolumeTypeConfigmap, + VolumeName: cm.Name, + MountPath: filepath.ToSlash(filepath.Join("/", "etc", "config", cm.Name)), // TODO consider annotation "controller.devfile.io/mount-path" + MountAs: MountAsFile, // TODO consider annotation "controller.devfile.io/mount-as" + ReadOnly: false, // TODO consider annotation "controller.devfile.io/read-only" + }) + } + return result, nil +} diff --git a/pkg/configAutomount/kubernetes_test.go b/pkg/configAutomount/kubernetes_test.go index 5710d22f470..81695607854 100644 --- a/pkg/configAutomount/kubernetes_test.go +++ b/pkg/configAutomount/kubernetes_test.go @@ -35,6 +35,18 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { labelMountName: labelMountValue, }) + defaultCM1 := corev1.ConfigMap{} + defaultCM1.SetName("defaultCM1") + defaultCM1.SetLabels(map[string]string{ + labelMountName: labelMountValue, + }) + + defaultCM2 := corev1.ConfigMap{} + defaultCM2.SetName("defaultCM2") + defaultCM2.SetLabels(map[string]string{ + labelMountName: labelMountValue, + }) + type fields struct { kubeClient func(ctrl *gomock.Controller) kclient.ClientInterface } @@ -51,6 +63,7 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { client := kclient.NewMockClientInterface(ctrl) client.EXPECT().ListPVCs(gomock.Any()).Return([]corev1.PersistentVolumeClaim{defaultPVC1}, nil).AnyTimes() client.EXPECT().ListSecrets(gomock.Any()).Return([]corev1.Secret{}, nil).AnyTimes() + client.EXPECT().ListConfigMaps(gomock.Any()).Return([]corev1.ConfigMap{}, nil).AnyTimes() return client }, }, @@ -71,6 +84,7 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { client := kclient.NewMockClientInterface(ctrl) client.EXPECT().ListPVCs(gomock.Any()).Return([]corev1.PersistentVolumeClaim{defaultPVC1, defaultPVC2}, nil).AnyTimes() client.EXPECT().ListSecrets(gomock.Any()).Return([]corev1.Secret{}, nil).AnyTimes() + client.EXPECT().ListConfigMaps(gomock.Any()).Return([]corev1.ConfigMap{}, nil).AnyTimes() return client }, }, @@ -97,6 +111,7 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { client := kclient.NewMockClientInterface(ctrl) client.EXPECT().ListPVCs(gomock.Any()).Return([]corev1.PersistentVolumeClaim{}, nil).AnyTimes() client.EXPECT().ListSecrets(gomock.Any()).Return([]corev1.Secret{defaultSecret1, defaultSecret2}, nil).AnyTimes() + client.EXPECT().ListConfigMaps(gomock.Any()).Return([]corev1.ConfigMap{}, nil).AnyTimes() return client }, }, @@ -116,6 +131,33 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { }, wantErr: false, }, + { + name: "Two default configmaps", + fields: fields{ + kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface { + client := kclient.NewMockClientInterface(ctrl) + client.EXPECT().ListPVCs(gomock.Any()).Return([]corev1.PersistentVolumeClaim{}, nil).AnyTimes() + client.EXPECT().ListSecrets(gomock.Any()).Return([]corev1.Secret{}, nil).AnyTimes() + client.EXPECT().ListConfigMaps(gomock.Any()).Return([]corev1.ConfigMap{defaultCM1, defaultCM2}, nil).AnyTimes() + return client + }, + }, + want: []AutomountInfo{ + { + VolumeType: VolumeTypeConfigmap, + VolumeName: "defaultCM1", + MountPath: "/etc/config/defaultCM1", + MountAs: MountAsFile, + }, + { + VolumeType: VolumeTypeConfigmap, + VolumeName: "defaultCM2", + MountPath: "/etc/config/defaultCM2", + MountAs: MountAsFile, + }, + }, + wantErr: false, + }, // TODO: Add test cases. } for _, tt := range tests { diff --git a/pkg/devfile/adapters/kubernetes/storage/utils.go b/pkg/devfile/adapters/kubernetes/storage/utils.go index 395ca97b80a..4f5c9551888 100644 --- a/pkg/devfile/adapters/kubernetes/storage/utils.go +++ b/pkg/devfile/adapters/kubernetes/storage/utils.go @@ -258,6 +258,8 @@ func GetAutomountVolumes(configAutomountClient configAutomount.Client, container result = mountPVC(volumeInfo, containers, initContainers, result) case configAutomount.VolumeTypeSecret: result = mountSecret(volumeInfo, containers, initContainers, result) + case configAutomount.VolumeTypeConfigmap: + result = mountConfigMap(volumeInfo, containers, initContainers, result) } } return result, nil @@ -297,6 +299,25 @@ func mountSecret(volumeInfo configAutomount.AutomountInfo, containers, initConta return result } +func mountConfigMap(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, result []corev1.Volume) []corev1.Volume { + volumeName := "auto-cm-" + volumeInfo.VolumeName + + containerNameToMountPaths := getContainerNameToMountPaths(volumeInfo, containers, initContainers) + addVolumeMountToContainers(containers, initContainers, volumeName, containerNameToMountPaths) + + result = append(result, corev1.Volume{ + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: volumeInfo.VolumeName, + }, + }, + }, + }) + return result +} + func getContainerNameToMountPaths(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container) map[string][]string { containerNameToMountPaths := map[string][]string{} allContainers := containers diff --git a/pkg/devfile/adapters/kubernetes/storage/utils_test.go b/pkg/devfile/adapters/kubernetes/storage/utils_test.go index 0994fc99123..8dcdc03f2bd 100644 --- a/pkg/devfile/adapters/kubernetes/storage/utils_test.go +++ b/pkg/devfile/adapters/kubernetes/storage/utils_test.go @@ -609,6 +609,79 @@ func TestGetAutomountVolumes(t *testing.T) { }, wantErr: false, }, + { + name: "One PVC, one secret and one configmap", + args: args{ + configAutomountClient: func(ctrl *gomock.Controller) configAutomount.Client { + info1 := configAutomount.AutomountInfo{ + VolumeType: configAutomount.VolumeTypePVC, + VolumeName: "pvc1", + MountPath: "/path/to/mount1", + MountAs: configAutomount.MountAsFile, + } + info2 := configAutomount.AutomountInfo{ + VolumeType: configAutomount.VolumeTypeSecret, + VolumeName: "secret2", + MountPath: "/path/to/mount2", + MountAs: configAutomount.MountAsFile, + } + info3 := configAutomount.AutomountInfo{ + VolumeType: configAutomount.VolumeTypeConfigmap, + VolumeName: "cm3", + MountPath: "/path/to/mount3", + MountAs: configAutomount.MountAsFile, + } + client := configAutomount.NewMockClient(ctrl) + client.EXPECT().GetAutomountingVolumes().Return([]configAutomount.AutomountInfo{info1, info2, info3}, nil) + return client + }, + containers: []corev1.Container{container1, container2}, + initContainers: []corev1.Container{initContainer1, initContainer2}, + }, + want: []v1.Volume{ + { + Name: "auto-pvc-pvc1", + VolumeSource: v1.VolumeSource{ + PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ + ClaimName: "pvc1", + }, + }, + }, + { + Name: "auto-secret-secret2", + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: "secret2", + }, + }, + }, + { + Name: "auto-cm-cm3", + VolumeSource: v1.VolumeSource{ + ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "cm3", + }, + }, + }, + }, + }, + wantVolumeMounts: []v1.VolumeMount{ + { + Name: "auto-pvc-pvc1", + MountPath: "/path/to/mount1", + }, + { + Name: "auto-secret-secret2", + MountPath: "/path/to/mount2", + }, + { + Name: "auto-cm-cm3", + MountPath: "/path/to/mount3", + }, + }, + wantErr: false, + }, // TODO: Add test cases. } for _, tt := range tests { diff --git a/pkg/kclient/configmap.go b/pkg/kclient/configmap.go new file mode 100644 index 00000000000..f05f82fd9ec --- /dev/null +++ b/pkg/kclient/configmap.go @@ -0,0 +1,26 @@ +package kclient + +import ( + "context" + "fmt" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ListConfigMaps lists all the configmaps based on the given label selector +func (c *Client) ListConfigMaps(labelSelector string) ([]corev1.ConfigMap, error) { + listOptions := metav1.ListOptions{} + if len(labelSelector) > 0 { + listOptions = metav1.ListOptions{ + LabelSelector: labelSelector, + } + } + + cmList, err := c.KubeClient.CoreV1().ConfigMaps(c.Namespace).List(context.TODO(), listOptions) + if err != nil { + return nil, fmt.Errorf("unable to get configmap list: %w", err) + } + + return cmList.Items, nil +} diff --git a/pkg/kclient/interface.go b/pkg/kclient/interface.go index 1bf53c6f198..1849f90f881 100644 --- a/pkg/kclient/interface.go +++ b/pkg/kclient/interface.go @@ -43,6 +43,9 @@ type ClientInterface interface { NewServiceBindingServiceObject(serviceNs string, unstructuredService unstructured.Unstructured, bindingName string) (bindingApi.Service, error) GetWorkloadKinds() ([]string, []schema.GroupVersionKind, error) + // configmap.go + ListConfigMaps(labelSelector string) ([]corev1.ConfigMap, error) + // deployment.go GetDeploymentByName(name string) (*appsv1.Deployment, error) GetOneDeployment(componentName, appName string, isPartOfComponent bool) (*appsv1.Deployment, error) diff --git a/pkg/kclient/mock_Client.go b/pkg/kclient/mock_Client.go index d12be1e2447..779f5a73ef4 100644 --- a/pkg/kclient/mock_Client.go +++ b/pkg/kclient/mock_Client.go @@ -1118,6 +1118,21 @@ func (mr *MockClientInterfaceMockRecorder) ListClusterServiceVersions() *gomock. return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListClusterServiceVersions", reflect.TypeOf((*MockClientInterface)(nil).ListClusterServiceVersions)) } +// ListConfigMaps mocks base method. +func (m *MockClientInterface) ListConfigMaps(labelSelector string) ([]v12.ConfigMap, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListConfigMaps", labelSelector) + ret0, _ := ret[0].([]v12.ConfigMap) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListConfigMaps indicates an expected call of ListConfigMaps. +func (mr *MockClientInterfaceMockRecorder) ListConfigMaps(labelSelector interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListConfigMaps", reflect.TypeOf((*MockClientInterface)(nil).ListConfigMaps), labelSelector) +} + // ListDynamicResources mocks base method. func (m *MockClientInterface) ListDynamicResources(namespace string, gvr schema.GroupVersionResource, selector string) (*unstructured.UnstructuredList, error) { m.ctrl.T.Helper() From 265969b81cb2df20b12cc53e89ce86a3e2563fa1 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Thu, 30 Mar 2023 10:06:36 +0200 Subject: [PATCH 11/24] Specific mount path --- pkg/configAutomount/kubernetes.go | 33 +++++++++++--- pkg/configAutomount/kubernetes_test.go | 60 ++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 7 deletions(-) diff --git a/pkg/configAutomount/kubernetes.go b/pkg/configAutomount/kubernetes.go index b76030e077c..6a94a15b9ff 100644 --- a/pkg/configAutomount/kubernetes.go +++ b/pkg/configAutomount/kubernetes.go @@ -9,6 +9,8 @@ import ( const ( labelMountName = "controller.devfile.io/mount-to-devworkspace" labelMountValue = "true" + + annotationMountPathName = "controller.devfile.io/mount-path" ) type KubernetesClient struct { @@ -53,10 +55,14 @@ func (o KubernetesClient) getAutomountingPVCs() ([]AutomountInfo, error) { var result []AutomountInfo for _, pvc := range pvcs { + mountPath := filepath.ToSlash(filepath.Join("/", "tmp", pvc.Name)) + if val, found := getMountPathFromAnnotation(pvc.Annotations); found { + mountPath = val + } result = append(result, AutomountInfo{ VolumeType: VolumeTypePVC, VolumeName: pvc.Name, - MountPath: filepath.ToSlash(filepath.Join("/", "tmp", pvc.Name)), // TODO consider annotation "controller.devfile.io/mount-path" + MountPath: mountPath, MountAs: MountAsFile, ReadOnly: false, // TODO consider annotation "controller.devfile.io/read-only" }) @@ -72,12 +78,16 @@ func (o KubernetesClient) getAutomountingSecrets() ([]AutomountInfo, error) { var result []AutomountInfo for _, secret := range secrets { + mountPath := filepath.ToSlash(filepath.Join("/", "etc", "secret", secret.Name)) + if val, found := getMountPathFromAnnotation(secret.Annotations); found { + mountPath = val + } result = append(result, AutomountInfo{ VolumeType: VolumeTypeSecret, VolumeName: secret.Name, - MountPath: filepath.ToSlash(filepath.Join("/", "etc", "secret", secret.Name)), // TODO consider annotation "controller.devfile.io/mount-path" - MountAs: MountAsFile, // TODO consider annotation "controller.devfile.io/mount-as" - ReadOnly: false, // TODO consider annotation "controller.devfile.io/read-only" + MountPath: mountPath, + MountAs: MountAsFile, // TODO consider annotation "controller.devfile.io/mount-as" + ReadOnly: false, // TODO consider annotation "controller.devfile.io/read-only" }) } return result, nil @@ -91,13 +101,22 @@ func (o KubernetesClient) getAutomountingConfigmaps() ([]AutomountInfo, error) { var result []AutomountInfo for _, cm := range cms { + mountPath := filepath.ToSlash(filepath.Join("/", "etc", "config", cm.Name)) + if val, found := getMountPathFromAnnotation(cm.Annotations); found { + mountPath = val + } result = append(result, AutomountInfo{ VolumeType: VolumeTypeConfigmap, VolumeName: cm.Name, - MountPath: filepath.ToSlash(filepath.Join("/", "etc", "config", cm.Name)), // TODO consider annotation "controller.devfile.io/mount-path" - MountAs: MountAsFile, // TODO consider annotation "controller.devfile.io/mount-as" - ReadOnly: false, // TODO consider annotation "controller.devfile.io/read-only" + MountPath: mountPath, + MountAs: MountAsFile, // TODO consider annotation "controller.devfile.io/mount-as" + ReadOnly: false, // TODO consider annotation "controller.devfile.io/read-only" }) } return result, nil } + +func getMountPathFromAnnotation(annotations map[string]string) (string, bool) { + val, found := annotations[annotationMountPathName] + return val, found +} diff --git a/pkg/configAutomount/kubernetes_test.go b/pkg/configAutomount/kubernetes_test.go index 81695607854..ed2bdd1afea 100644 --- a/pkg/configAutomount/kubernetes_test.go +++ b/pkg/configAutomount/kubernetes_test.go @@ -23,6 +23,15 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { labelMountName: labelMountValue, }) + pvcMountPath := corev1.PersistentVolumeClaim{} + pvcMountPath.SetName("pvcMountPath") + pvcMountPath.SetLabels(map[string]string{ + labelMountName: labelMountValue, + }) + pvcMountPath.SetAnnotations(map[string]string{ + annotationMountPathName: "/specific/pvc/mount/path", + }) + defaultSecret1 := corev1.Secret{} defaultSecret1.SetName("defaultSecret1") defaultSecret1.SetLabels(map[string]string{ @@ -35,6 +44,15 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { labelMountName: labelMountValue, }) + secretMountPath := corev1.Secret{} + secretMountPath.SetName("secretMountPath") + secretMountPath.SetLabels(map[string]string{ + labelMountName: labelMountValue, + }) + secretMountPath.SetAnnotations(map[string]string{ + annotationMountPathName: "/specific/secret/mount/path", + }) + defaultCM1 := corev1.ConfigMap{} defaultCM1.SetName("defaultCM1") defaultCM1.SetLabels(map[string]string{ @@ -47,6 +65,15 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { labelMountName: labelMountValue, }) + cmMountPath := corev1.ConfigMap{} + cmMountPath.SetName("cmMountPath") + cmMountPath.SetLabels(map[string]string{ + labelMountName: labelMountValue, + }) + cmMountPath.SetAnnotations(map[string]string{ + annotationMountPathName: "/specific/configmap/mount/path", + }) + type fields struct { kubeClient func(ctrl *gomock.Controller) kclient.ClientInterface } @@ -158,6 +185,39 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { }, wantErr: false, }, + { + name: "PVC, Secret and ConfigMap with non default mount paths", + fields: fields{ + kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface { + client := kclient.NewMockClientInterface(ctrl) + client.EXPECT().ListPVCs(gomock.Any()).Return([]corev1.PersistentVolumeClaim{pvcMountPath}, nil).AnyTimes() + client.EXPECT().ListSecrets(gomock.Any()).Return([]corev1.Secret{secretMountPath}, nil).AnyTimes() + client.EXPECT().ListConfigMaps(gomock.Any()).Return([]corev1.ConfigMap{cmMountPath}, nil).AnyTimes() + return client + }, + }, + want: []AutomountInfo{ + { + VolumeType: VolumeTypePVC, + VolumeName: "pvcMountPath", + MountPath: "/specific/pvc/mount/path", + MountAs: MountAsFile, + }, + { + VolumeType: VolumeTypeSecret, + VolumeName: "secretMountPath", + MountPath: "/specific/secret/mount/path", + MountAs: MountAsFile, + }, + { + VolumeType: VolumeTypeConfigmap, + VolumeName: "cmMountPath", + MountPath: "/specific/configmap/mount/path", + MountAs: MountAsFile, + }, + }, + wantErr: false, + }, // TODO: Add test cases. } for _, tt := range tests { From e33215a3c49449c84e10cc4df375475b1774b4d6 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Thu, 30 Mar 2023 10:31:28 +0200 Subject: [PATCH 12/24] MountAs annotation --- pkg/configAutomount/kubernetes.go | 28 ++++++++-- pkg/configAutomount/kubernetes_test.go | 75 ++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 4 deletions(-) diff --git a/pkg/configAutomount/kubernetes.go b/pkg/configAutomount/kubernetes.go index 6a94a15b9ff..92b9b9992a0 100644 --- a/pkg/configAutomount/kubernetes.go +++ b/pkg/configAutomount/kubernetes.go @@ -11,6 +11,7 @@ const ( labelMountValue = "true" annotationMountPathName = "controller.devfile.io/mount-path" + annotationMountAsName = "controller.devfile.io/mount-as" ) type KubernetesClient struct { @@ -78,16 +79,20 @@ func (o KubernetesClient) getAutomountingSecrets() ([]AutomountInfo, error) { var result []AutomountInfo for _, secret := range secrets { + mountAs := getMountAsFromAnnotation(secret.Annotations) mountPath := filepath.ToSlash(filepath.Join("/", "etc", "secret", secret.Name)) if val, found := getMountPathFromAnnotation(secret.Annotations); found { mountPath = val } + if mountAs == MountAsEnv { + mountPath = "" + } result = append(result, AutomountInfo{ VolumeType: VolumeTypeSecret, VolumeName: secret.Name, MountPath: mountPath, - MountAs: MountAsFile, // TODO consider annotation "controller.devfile.io/mount-as" - ReadOnly: false, // TODO consider annotation "controller.devfile.io/read-only" + MountAs: mountAs, + ReadOnly: false, // TODO consider annotation "controller.devfile.io/read-only" }) } return result, nil @@ -101,16 +106,20 @@ func (o KubernetesClient) getAutomountingConfigmaps() ([]AutomountInfo, error) { var result []AutomountInfo for _, cm := range cms { + mountAs := getMountAsFromAnnotation(cm.Annotations) mountPath := filepath.ToSlash(filepath.Join("/", "etc", "config", cm.Name)) if val, found := getMountPathFromAnnotation(cm.Annotations); found { mountPath = val } + if mountAs == MountAsEnv { + mountPath = "" + } result = append(result, AutomountInfo{ VolumeType: VolumeTypeConfigmap, VolumeName: cm.Name, MountPath: mountPath, - MountAs: MountAsFile, // TODO consider annotation "controller.devfile.io/mount-as" - ReadOnly: false, // TODO consider annotation "controller.devfile.io/read-only" + MountAs: mountAs, + ReadOnly: false, // TODO consider annotation "controller.devfile.io/read-only" }) } return result, nil @@ -120,3 +129,14 @@ func getMountPathFromAnnotation(annotations map[string]string) (string, bool) { val, found := annotations[annotationMountPathName] return val, found } + +func getMountAsFromAnnotation(annotations map[string]string) MountAs { + switch annotations[annotationMountAsName] { + case "subpath": + return MountAsSubpath + case "env": + return MountAsEnv + default: + return MountAsFile + } +} diff --git a/pkg/configAutomount/kubernetes_test.go b/pkg/configAutomount/kubernetes_test.go index ed2bdd1afea..226de2d74dc 100644 --- a/pkg/configAutomount/kubernetes_test.go +++ b/pkg/configAutomount/kubernetes_test.go @@ -53,6 +53,24 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { annotationMountPathName: "/specific/secret/mount/path", }) + secretMountAsSubpath := corev1.Secret{} + secretMountAsSubpath.SetName("secretMountAsSubpath") + secretMountAsSubpath.SetLabels(map[string]string{ + labelMountName: labelMountValue, + }) + secretMountAsSubpath.SetAnnotations(map[string]string{ + annotationMountAsName: "subpath", + }) + + secretMountAsEnv := corev1.Secret{} + secretMountAsEnv.SetName("secretMountAsEnv") + secretMountAsEnv.SetLabels(map[string]string{ + labelMountName: labelMountValue, + }) + secretMountAsEnv.SetAnnotations(map[string]string{ + annotationMountAsName: "env", + }) + defaultCM1 := corev1.ConfigMap{} defaultCM1.SetName("defaultCM1") defaultCM1.SetLabels(map[string]string{ @@ -74,6 +92,24 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { annotationMountPathName: "/specific/configmap/mount/path", }) + cmMountAsSubpath := corev1.ConfigMap{} + cmMountAsSubpath.SetName("cmMountAsSubpath") + cmMountAsSubpath.SetLabels(map[string]string{ + labelMountName: labelMountValue, + }) + cmMountAsSubpath.SetAnnotations(map[string]string{ + annotationMountAsName: "subpath", + }) + + cmMountAsEnv := corev1.ConfigMap{} + cmMountAsEnv.SetName("cmMountAsEnv") + cmMountAsEnv.SetLabels(map[string]string{ + labelMountName: labelMountValue, + }) + cmMountAsEnv.SetAnnotations(map[string]string{ + annotationMountAsName: "env", + }) + type fields struct { kubeClient func(ctrl *gomock.Controller) kclient.ClientInterface } @@ -218,6 +254,45 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { }, wantErr: false, }, + { + name: "Secret and ConfigMap with mount-as annotations", + fields: fields{ + kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface { + client := kclient.NewMockClientInterface(ctrl) + client.EXPECT().ListPVCs(gomock.Any()).Return([]corev1.PersistentVolumeClaim{}, nil).AnyTimes() + client.EXPECT().ListSecrets(gomock.Any()).Return([]corev1.Secret{secretMountAsSubpath, secretMountAsEnv}, nil).AnyTimes() + client.EXPECT().ListConfigMaps(gomock.Any()).Return([]corev1.ConfigMap{cmMountAsSubpath, cmMountAsEnv}, nil).AnyTimes() + return client + }, + }, + want: []AutomountInfo{ + { + VolumeType: VolumeTypeSecret, + VolumeName: "secretMountAsSubpath", + MountPath: "/etc/secret/secretMountAsSubpath", + MountAs: MountAsSubpath, + }, + { + VolumeType: VolumeTypeSecret, + VolumeName: "secretMountAsEnv", + MountPath: "", + MountAs: MountAsEnv, + }, + { + VolumeType: VolumeTypeConfigmap, + VolumeName: "cmMountAsSubpath", + MountPath: "/etc/config/cmMountAsSubpath", + MountAs: MountAsSubpath, + }, + { + VolumeType: VolumeTypeConfigmap, + VolumeName: "cmMountAsEnv", + MountPath: "", + MountAs: MountAsEnv, + }, + }, + wantErr: false, + }, // TODO: Add test cases. } for _, tt := range tests { From 95888a8432cddbac0a1e0170ae0b81095aa625ff Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Thu, 30 Mar 2023 13:01:14 +0200 Subject: [PATCH 13/24] Mounting cm/secret as env --- .../adapters/kubernetes/storage/utils.go | 68 +++++++++++++++++++ .../adapters/kubernetes/storage/utils_test.go | 47 ++++++++++++- 2 files changed, 114 insertions(+), 1 deletion(-) diff --git a/pkg/devfile/adapters/kubernetes/storage/utils.go b/pkg/devfile/adapters/kubernetes/storage/utils.go index 4f5c9551888..192a858e287 100644 --- a/pkg/devfile/adapters/kubernetes/storage/utils.go +++ b/pkg/devfile/adapters/kubernetes/storage/utils.go @@ -283,6 +283,16 @@ func mountPVC(volumeInfo configAutomount.AutomountInfo, containers, initContaine } func mountSecret(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, result []corev1.Volume) []corev1.Volume { + switch volumeInfo.MountAs { + case configAutomount.MountAsFile: + return mountSecretAsFile(volumeInfo, containers, initContainers, result) + case configAutomount.MountAsEnv: + return mountSecretAsEnv(volumeInfo, containers, initContainers, result) + } + return result +} + +func mountSecretAsFile(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, result []corev1.Volume) []corev1.Volume { volumeName := "auto-secret-" + volumeInfo.VolumeName containerNameToMountPaths := getContainerNameToMountPaths(volumeInfo, containers, initContainers) @@ -299,7 +309,39 @@ func mountSecret(volumeInfo configAutomount.AutomountInfo, containers, initConta return result } +func mountSecretAsEnv(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, result []corev1.Volume) []corev1.Volume { + for i := range containers { + addEnvFromToContainer(&containers[i], corev1.EnvFromSource{ + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: volumeInfo.VolumeName, + }, + }, + }) + } + for i := range initContainers { + addEnvFromToContainer(&initContainers[i], corev1.EnvFromSource{ + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: volumeInfo.VolumeName, + }, + }, + }) + } + return result +} + func mountConfigMap(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, result []corev1.Volume) []corev1.Volume { + switch volumeInfo.MountAs { + case configAutomount.MountAsFile: + return mountConfigMapAsFile(volumeInfo, containers, initContainers, result) + case configAutomount.MountAsEnv: + return mountConfigMapAsEnv(volumeInfo, containers, initContainers, result) + } + return result +} + +func mountConfigMapAsFile(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, result []corev1.Volume) []corev1.Volume { volumeName := "auto-cm-" + volumeInfo.VolumeName containerNameToMountPaths := getContainerNameToMountPaths(volumeInfo, containers, initContainers) @@ -318,6 +360,28 @@ func mountConfigMap(volumeInfo configAutomount.AutomountInfo, containers, initCo return result } +func mountConfigMapAsEnv(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, result []corev1.Volume) []corev1.Volume { + for i := range containers { + addEnvFromToContainer(&containers[i], corev1.EnvFromSource{ + ConfigMapRef: &corev1.ConfigMapEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: volumeInfo.VolumeName, + }, + }, + }) + } + for i := range initContainers { + addEnvFromToContainer(&initContainers[i], corev1.EnvFromSource{ + ConfigMapRef: &corev1.ConfigMapEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: volumeInfo.VolumeName, + }, + }, + }) + } + return result +} + func getContainerNameToMountPaths(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container) map[string][]string { containerNameToMountPaths := map[string][]string{} allContainers := containers @@ -327,3 +391,7 @@ func getContainerNameToMountPaths(volumeInfo configAutomount.AutomountInfo, cont } return containerNameToMountPaths } + +func addEnvFromToContainer(container *corev1.Container, envFrom corev1.EnvFromSource) { + container.EnvFrom = append(container.EnvFrom, envFrom) +} diff --git a/pkg/devfile/adapters/kubernetes/storage/utils_test.go b/pkg/devfile/adapters/kubernetes/storage/utils_test.go index 8dcdc03f2bd..0d6da74d33b 100644 --- a/pkg/devfile/adapters/kubernetes/storage/utils_test.go +++ b/pkg/devfile/adapters/kubernetes/storage/utils_test.go @@ -504,6 +504,7 @@ func TestGetAutomountVolumes(t *testing.T) { args args want []corev1.Volume wantVolumeMounts []corev1.VolumeMount + wantEnvFroms []corev1.EnvFromSource wantErr bool }{ { @@ -682,7 +683,48 @@ func TestGetAutomountVolumes(t *testing.T) { }, wantErr: false, }, - // TODO: Add test cases. + { + name: "One secret and one configmap mounted as Env", + args: args{ + configAutomountClient: func(ctrl *gomock.Controller) configAutomount.Client { + info1 := configAutomount.AutomountInfo{ + VolumeType: configAutomount.VolumeTypeSecret, + VolumeName: "secret1", + MountAs: configAutomount.MountAsEnv, + } + info2 := configAutomount.AutomountInfo{ + VolumeType: configAutomount.VolumeTypeConfigmap, + VolumeName: "cm2", + MountAs: configAutomount.MountAsEnv, + } + client := configAutomount.NewMockClient(ctrl) + client.EXPECT().GetAutomountingVolumes().Return([]configAutomount.AutomountInfo{info1, info2}, nil) + return client + }, + containers: []corev1.Container{container1, container2}, + initContainers: []corev1.Container{initContainer1, initContainer2}, + }, + want: nil, + wantVolumeMounts: nil, + wantEnvFroms: []corev1.EnvFromSource{ + { + SecretRef: &v1.SecretEnvSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "secret1", + }, + }, + }, + { + ConfigMapRef: &v1.ConfigMapEnvSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "cm2", + }, + }, + }, + }, + wantErr: false, + }, // TODO: Add test cases. + } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -703,6 +745,9 @@ func TestGetAutomountVolumes(t *testing.T) { if diff := cmp.Diff(tt.wantVolumeMounts, container.VolumeMounts); diff != "" { return fmt.Errorf(diff) } + if diff := cmp.Diff(tt.wantEnvFroms, container.EnvFrom); diff != "" { + return fmt.Errorf(diff) + } } return nil } From 13dd0f6f61ecdbe2a9aa8a63710baf1407b38a54 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Thu, 30 Mar 2023 15:25:07 +0200 Subject: [PATCH 14/24] Refacto: use inAllContainers + replace result with volume --- .../adapters/kubernetes/storage/utils.go | 122 +++++++++--------- 1 file changed, 59 insertions(+), 63 deletions(-) diff --git a/pkg/devfile/adapters/kubernetes/storage/utils.go b/pkg/devfile/adapters/kubernetes/storage/utils.go index 192a858e287..afd4c402e7f 100644 --- a/pkg/devfile/adapters/kubernetes/storage/utils.go +++ b/pkg/devfile/adapters/kubernetes/storage/utils.go @@ -14,7 +14,6 @@ import ( "github.com/redhat-developer/odo/pkg/kclient" odolabels "github.com/redhat-developer/odo/pkg/labels" "github.com/redhat-developer/odo/pkg/storage" - storagepkg "github.com/redhat-developer/odo/pkg/storage" corev1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" @@ -44,7 +43,7 @@ func GetVolumeInfos(pvcs []corev1.PersistentVolumeClaim) (odoSourcePVCName strin } storageName := odolabels.GetStorageName(pvc.Labels) - if storageName == storagepkg.OdoSourceVolume { + if storageName == storage.OdoSourceVolume { odoSourcePVCName = pvc.Name continue } @@ -251,27 +250,31 @@ func GetAutomountVolumes(configAutomountClient configAutomount.Client, container return nil, err } - var result []corev1.Volume + var volumes []corev1.Volume for _, volumeInfo := range volumesInfos { switch volumeInfo.VolumeType { case configAutomount.VolumeTypePVC: - result = mountPVC(volumeInfo, containers, initContainers, result) + volumes = mountPVC(volumeInfo, containers, initContainers, volumes) case configAutomount.VolumeTypeSecret: - result = mountSecret(volumeInfo, containers, initContainers, result) + volumes = mountSecret(volumeInfo, containers, initContainers, volumes) case configAutomount.VolumeTypeConfigmap: - result = mountConfigMap(volumeInfo, containers, initContainers, result) + volumes = mountConfigMap(volumeInfo, containers, initContainers, volumes) } } - return result, nil + return volumes, nil } -func mountPVC(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, result []corev1.Volume) []corev1.Volume { +func mountPVC(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, volumes []corev1.Volume) []corev1.Volume { volumeName := "auto-pvc-" + volumeInfo.VolumeName - containerNameToMountPaths := getContainerNameToMountPaths(volumeInfo, containers, initContainers) - addVolumeMountToContainers(containers, initContainers, volumeName, containerNameToMountPaths) + inAllContainers(containers, initContainers, func(container *corev1.Container) { + addVolumeMountToContainer(container, corev1.VolumeMount{ + Name: volumeName, + MountPath: volumeInfo.MountPath, + }) + }) - result = append(result, corev1.Volume{ + volumes = append(volumes, corev1.Volume{ Name: volumeName, VolumeSource: corev1.VolumeSource{ PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ @@ -279,26 +282,30 @@ func mountPVC(volumeInfo configAutomount.AutomountInfo, containers, initContaine }, }, }) - return result + return volumes } -func mountSecret(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, result []corev1.Volume) []corev1.Volume { +func mountSecret(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, volumes []corev1.Volume) []corev1.Volume { switch volumeInfo.MountAs { case configAutomount.MountAsFile: - return mountSecretAsFile(volumeInfo, containers, initContainers, result) + return mountSecretAsFile(volumeInfo, containers, initContainers, volumes) case configAutomount.MountAsEnv: - return mountSecretAsEnv(volumeInfo, containers, initContainers, result) + return mountSecretAsEnv(volumeInfo, containers, initContainers, volumes) } - return result + return volumes } -func mountSecretAsFile(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, result []corev1.Volume) []corev1.Volume { +func mountSecretAsFile(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, volumes []corev1.Volume) []corev1.Volume { volumeName := "auto-secret-" + volumeInfo.VolumeName - containerNameToMountPaths := getContainerNameToMountPaths(volumeInfo, containers, initContainers) - addVolumeMountToContainers(containers, initContainers, volumeName, containerNameToMountPaths) + inAllContainers(containers, initContainers, func(container *corev1.Container) { + addVolumeMountToContainer(container, corev1.VolumeMount{ + Name: volumeName, + MountPath: volumeInfo.MountPath, + }) + }) - result = append(result, corev1.Volume{ + volumes = append(volumes, corev1.Volume{ Name: volumeName, VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ @@ -306,48 +313,43 @@ func mountSecretAsFile(volumeInfo configAutomount.AutomountInfo, containers, ini }, }, }) - return result + return volumes } -func mountSecretAsEnv(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, result []corev1.Volume) []corev1.Volume { - for i := range containers { - addEnvFromToContainer(&containers[i], corev1.EnvFromSource{ +func mountSecretAsEnv(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, volumes []corev1.Volume) []corev1.Volume { + inAllContainers(containers, initContainers, func(container *corev1.Container) { + addEnvFromToContainer(container, corev1.EnvFromSource{ SecretRef: &corev1.SecretEnvSource{ LocalObjectReference: corev1.LocalObjectReference{ Name: volumeInfo.VolumeName, }, }, }) - } - for i := range initContainers { - addEnvFromToContainer(&initContainers[i], corev1.EnvFromSource{ - SecretRef: &corev1.SecretEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: volumeInfo.VolumeName, - }, - }, - }) - } - return result + }) + return volumes } -func mountConfigMap(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, result []corev1.Volume) []corev1.Volume { +func mountConfigMap(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, volumes []corev1.Volume) []corev1.Volume { switch volumeInfo.MountAs { case configAutomount.MountAsFile: - return mountConfigMapAsFile(volumeInfo, containers, initContainers, result) + return mountConfigMapAsFile(volumeInfo, containers, initContainers, volumes) case configAutomount.MountAsEnv: - return mountConfigMapAsEnv(volumeInfo, containers, initContainers, result) + return mountConfigMapAsEnv(volumeInfo, containers, initContainers, volumes) } - return result + return volumes } -func mountConfigMapAsFile(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, result []corev1.Volume) []corev1.Volume { +func mountConfigMapAsFile(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, volumes []corev1.Volume) []corev1.Volume { volumeName := "auto-cm-" + volumeInfo.VolumeName - containerNameToMountPaths := getContainerNameToMountPaths(volumeInfo, containers, initContainers) - addVolumeMountToContainers(containers, initContainers, volumeName, containerNameToMountPaths) + inAllContainers(containers, initContainers, func(container *corev1.Container) { + addVolumeMountToContainer(container, corev1.VolumeMount{ + Name: volumeName, + MountPath: volumeInfo.MountPath, + }) + }) - result = append(result, corev1.Volume{ + volumes = append(volumes, corev1.Volume{ Name: volumeName, VolumeSource: corev1.VolumeSource{ ConfigMap: &corev1.ConfigMapVolumeSource{ @@ -357,39 +359,33 @@ func mountConfigMapAsFile(volumeInfo configAutomount.AutomountInfo, containers, }, }, }) - return result + return volumes } -func mountConfigMapAsEnv(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, result []corev1.Volume) []corev1.Volume { - for i := range containers { - addEnvFromToContainer(&containers[i], corev1.EnvFromSource{ +func mountConfigMapAsEnv(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, volumes []corev1.Volume) []corev1.Volume { + inAllContainers(containers, initContainers, func(container *corev1.Container) { + addEnvFromToContainer(container, corev1.EnvFromSource{ ConfigMapRef: &corev1.ConfigMapEnvSource{ LocalObjectReference: corev1.LocalObjectReference{ Name: volumeInfo.VolumeName, }, }, }) + }) + return volumes +} + +func inAllContainers(containers, initContainers []corev1.Container, f func(container *corev1.Container)) { + for i := range containers { + f(&containers[i]) } for i := range initContainers { - addEnvFromToContainer(&initContainers[i], corev1.EnvFromSource{ - ConfigMapRef: &corev1.ConfigMapEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: volumeInfo.VolumeName, - }, - }, - }) + f(&initContainers[i]) } - return result } -func getContainerNameToMountPaths(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container) map[string][]string { - containerNameToMountPaths := map[string][]string{} - allContainers := containers - allContainers = append(allContainers, initContainers...) - for _, container := range allContainers { - containerNameToMountPaths[container.Name] = []string{volumeInfo.MountPath} - } - return containerNameToMountPaths +func addVolumeMountToContainer(container *corev1.Container, volumeMount corev1.VolumeMount) { + container.VolumeMounts = append(container.VolumeMounts, volumeMount) } func addEnvFromToContainer(container *corev1.Container, envFrom corev1.EnvFromSource) { From 1b7843ef3f563f24137ea71a4614fc6f0f60ff55 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Thu, 30 Mar 2023 16:14:08 +0200 Subject: [PATCH 15/24] Mounting cm/secret as subpath --- pkg/configAutomount/interface.go | 16 ++++- pkg/configAutomount/kubernetes.go | 17 +++++ pkg/configAutomount/kubernetes_test.go | 10 +++ .../adapters/kubernetes/storage/utils.go | 55 ++++++++++++++ .../adapters/kubernetes/storage/utils_test.go | 72 ++++++++++++++++++- 5 files changed, 165 insertions(+), 5 deletions(-) diff --git a/pkg/configAutomount/interface.go b/pkg/configAutomount/interface.go index 03e443c05b0..97f81d7e104 100644 --- a/pkg/configAutomount/interface.go +++ b/pkg/configAutomount/interface.go @@ -16,11 +16,21 @@ const ( ) type AutomountInfo struct { + // VolumeType gives the type of the volume (PVC, Secret, ConfigMap) VolumeType VolumeType + // VolumeName is the name of the resource to mount VolumeName string - MountPath string - MountAs MountAs - ReadOnly bool + // MountPath indicates on which path to mount the volume (empty if MountAs is Env) + MountPath string + // MountAs indicates how to mount the volume + // - File: by default + // - Env: As environment variables (for Secret and Configmap) + // - Subpath: As individual files in specific paths (For Secret and ConfigMap). Keys must be provided + MountAs MountAs + // ReadOnly indicates to mount the volume as Read-Only + ReadOnly bool + // Keys defines the list of keys to mount when MountAs is Subpath + Keys []string } type Client interface { diff --git a/pkg/configAutomount/kubernetes.go b/pkg/configAutomount/kubernetes.go index 92b9b9992a0..29eb66d9311 100644 --- a/pkg/configAutomount/kubernetes.go +++ b/pkg/configAutomount/kubernetes.go @@ -2,6 +2,7 @@ package configAutomount import ( "path/filepath" + "sort" "github.com/redhat-developer/odo/pkg/kclient" ) @@ -81,18 +82,26 @@ func (o KubernetesClient) getAutomountingSecrets() ([]AutomountInfo, error) { for _, secret := range secrets { mountAs := getMountAsFromAnnotation(secret.Annotations) mountPath := filepath.ToSlash(filepath.Join("/", "etc", "secret", secret.Name)) + var keys []string if val, found := getMountPathFromAnnotation(secret.Annotations); found { mountPath = val } if mountAs == MountAsEnv { mountPath = "" } + if mountAs == MountAsSubpath { + for k := range secret.Data { + keys = append(keys, k) + } + sort.Strings(keys) + } result = append(result, AutomountInfo{ VolumeType: VolumeTypeSecret, VolumeName: secret.Name, MountPath: mountPath, MountAs: mountAs, ReadOnly: false, // TODO consider annotation "controller.devfile.io/read-only" + Keys: keys, }) } return result, nil @@ -108,18 +117,26 @@ func (o KubernetesClient) getAutomountingConfigmaps() ([]AutomountInfo, error) { for _, cm := range cms { mountAs := getMountAsFromAnnotation(cm.Annotations) mountPath := filepath.ToSlash(filepath.Join("/", "etc", "config", cm.Name)) + var keys []string if val, found := getMountPathFromAnnotation(cm.Annotations); found { mountPath = val } if mountAs == MountAsEnv { mountPath = "" } + if mountAs == MountAsSubpath { + for k := range cm.Data { + keys = append(keys, k) + } + sort.Strings(keys) + } result = append(result, AutomountInfo{ VolumeType: VolumeTypeConfigmap, VolumeName: cm.Name, MountPath: mountPath, MountAs: mountAs, ReadOnly: false, // TODO consider annotation "controller.devfile.io/read-only" + Keys: keys, }) } return result, nil diff --git a/pkg/configAutomount/kubernetes_test.go b/pkg/configAutomount/kubernetes_test.go index 226de2d74dc..df7047c8c89 100644 --- a/pkg/configAutomount/kubernetes_test.go +++ b/pkg/configAutomount/kubernetes_test.go @@ -54,6 +54,10 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { }) secretMountAsSubpath := corev1.Secret{} + secretMountAsSubpath.Data = map[string][]byte{ + "secretKey1": []byte(""), + "secretKey2": []byte(""), + } secretMountAsSubpath.SetName("secretMountAsSubpath") secretMountAsSubpath.SetLabels(map[string]string{ labelMountName: labelMountValue, @@ -93,6 +97,10 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { }) cmMountAsSubpath := corev1.ConfigMap{} + cmMountAsSubpath.Data = map[string]string{ + "cmKey1": "", + "cmKey2": "", + } cmMountAsSubpath.SetName("cmMountAsSubpath") cmMountAsSubpath.SetLabels(map[string]string{ labelMountName: labelMountValue, @@ -271,6 +279,7 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { VolumeName: "secretMountAsSubpath", MountPath: "/etc/secret/secretMountAsSubpath", MountAs: MountAsSubpath, + Keys: []string{"secretKey1", "secretKey2"}, }, { VolumeType: VolumeTypeSecret, @@ -283,6 +292,7 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { VolumeName: "cmMountAsSubpath", MountPath: "/etc/config/cmMountAsSubpath", MountAs: MountAsSubpath, + Keys: []string{"cmKey1", "cmKey2"}, }, { VolumeType: VolumeTypeConfigmap, diff --git a/pkg/devfile/adapters/kubernetes/storage/utils.go b/pkg/devfile/adapters/kubernetes/storage/utils.go index afd4c402e7f..93617fea5f8 100644 --- a/pkg/devfile/adapters/kubernetes/storage/utils.go +++ b/pkg/devfile/adapters/kubernetes/storage/utils.go @@ -2,6 +2,7 @@ package storage import ( "fmt" + "path/filepath" "sort" "strings" @@ -291,6 +292,8 @@ func mountSecret(volumeInfo configAutomount.AutomountInfo, containers, initConta return mountSecretAsFile(volumeInfo, containers, initContainers, volumes) case configAutomount.MountAsEnv: return mountSecretAsEnv(volumeInfo, containers, initContainers, volumes) + case configAutomount.MountAsSubpath: + return mountSecretAsSubpath(volumeInfo, containers, initContainers, volumes) } return volumes } @@ -329,12 +332,38 @@ func mountSecretAsEnv(volumeInfo configAutomount.AutomountInfo, containers, init return volumes } +func mountSecretAsSubpath(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, volumes []corev1.Volume) []corev1.Volume { + volumeName := "auto-secret-" + volumeInfo.VolumeName + + inAllContainers(containers, initContainers, func(container *corev1.Container) { + for _, key := range volumeInfo.Keys { + addVolumeMountToContainer(container, corev1.VolumeMount{ + Name: volumeName, + MountPath: filepath.ToSlash(filepath.Join(volumeInfo.MountPath, key)), + SubPath: key, + }) + } + }) + + volumes = append(volumes, corev1.Volume{ + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: volumeInfo.VolumeName, + }, + }, + }) + return volumes +} + func mountConfigMap(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, volumes []corev1.Volume) []corev1.Volume { switch volumeInfo.MountAs { case configAutomount.MountAsFile: return mountConfigMapAsFile(volumeInfo, containers, initContainers, volumes) case configAutomount.MountAsEnv: return mountConfigMapAsEnv(volumeInfo, containers, initContainers, volumes) + case configAutomount.MountAsSubpath: + return mountConfigMapAsSubpath(volumeInfo, containers, initContainers, volumes) } return volumes } @@ -375,6 +404,32 @@ func mountConfigMapAsEnv(volumeInfo configAutomount.AutomountInfo, containers, i return volumes } +func mountConfigMapAsSubpath(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, volumes []corev1.Volume) []corev1.Volume { + volumeName := "auto-cm-" + volumeInfo.VolumeName + + inAllContainers(containers, initContainers, func(container *corev1.Container) { + for _, key := range volumeInfo.Keys { + addVolumeMountToContainer(container, corev1.VolumeMount{ + Name: volumeName, + MountPath: filepath.ToSlash(filepath.Join(volumeInfo.MountPath, key)), + SubPath: key, + }) + } + }) + + volumes = append(volumes, corev1.Volume{ + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: volumeInfo.VolumeName, + }, + }, + }, + }) + return volumes +} + func inAllContainers(containers, initContainers []corev1.Container, f func(container *corev1.Container)) { for i := range containers { f(&containers[i]) diff --git a/pkg/devfile/adapters/kubernetes/storage/utils_test.go b/pkg/devfile/adapters/kubernetes/storage/utils_test.go index 0d6da74d33b..13d360c0fd6 100644 --- a/pkg/devfile/adapters/kubernetes/storage/utils_test.go +++ b/pkg/devfile/adapters/kubernetes/storage/utils_test.go @@ -723,8 +723,76 @@ func TestGetAutomountVolumes(t *testing.T) { }, }, wantErr: false, - }, // TODO: Add test cases. - + }, + { + name: "One secret and one configmap mounted as Subpath", + args: args{ + configAutomountClient: func(ctrl *gomock.Controller) configAutomount.Client { + info1 := configAutomount.AutomountInfo{ + VolumeType: configAutomount.VolumeTypeSecret, + VolumeName: "secret1", + MountPath: "/path/to/secret1", + MountAs: configAutomount.MountAsSubpath, + Keys: []string{"secretKey1", "secretKey2"}, + } + info2 := configAutomount.AutomountInfo{ + VolumeType: configAutomount.VolumeTypeConfigmap, + VolumeName: "cm2", + MountPath: "/path/to/cm2", + MountAs: configAutomount.MountAsSubpath, + Keys: []string{"cmKey1", "cmKey2"}, + } + client := configAutomount.NewMockClient(ctrl) + client.EXPECT().GetAutomountingVolumes().Return([]configAutomount.AutomountInfo{info1, info2}, nil) + return client + }, + containers: []corev1.Container{container1, container2}, + initContainers: []corev1.Container{initContainer1, initContainer2}, + }, + want: []corev1.Volume{ + { + Name: "auto-secret-secret1", + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: "secret1", + }, + }, + }, + { + Name: "auto-cm-cm2", + VolumeSource: v1.VolumeSource{ + ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "cm2", + }, + }, + }, + }, + }, + wantVolumeMounts: []corev1.VolumeMount{ + { + Name: "auto-secret-secret1", + MountPath: "/path/to/secret1/secretKey1", + SubPath: "secretKey1", + }, + { + Name: "auto-secret-secret1", + MountPath: "/path/to/secret1/secretKey2", + SubPath: "secretKey2", + }, + { + Name: "auto-cm-cm2", + MountPath: "/path/to/cm2/cmKey1", + SubPath: "cmKey1", + }, + { + Name: "auto-cm-cm2", + MountPath: "/path/to/cm2/cmKey2", + SubPath: "cmKey2", + }, + }, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 7b02c21c8666881e9eab09ccb768a5d83af5a3df Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Thu, 30 Mar 2023 18:46:39 +0200 Subject: [PATCH 16/24] Read-only --- pkg/configAutomount/kubernetes.go | 7 +- pkg/configAutomount/kubernetes_test.go | 65 ++++++++++++++++++- .../adapters/kubernetes/storage/utils.go | 5 ++ 3 files changed, 72 insertions(+), 5 deletions(-) diff --git a/pkg/configAutomount/kubernetes.go b/pkg/configAutomount/kubernetes.go index 29eb66d9311..4ab2298a6ae 100644 --- a/pkg/configAutomount/kubernetes.go +++ b/pkg/configAutomount/kubernetes.go @@ -13,6 +13,7 @@ const ( annotationMountPathName = "controller.devfile.io/mount-path" annotationMountAsName = "controller.devfile.io/mount-as" + annotationReadOnlyName = "controller.devfile.io/read-only" ) type KubernetesClient struct { @@ -66,7 +67,7 @@ func (o KubernetesClient) getAutomountingPVCs() ([]AutomountInfo, error) { VolumeName: pvc.Name, MountPath: mountPath, MountAs: MountAsFile, - ReadOnly: false, // TODO consider annotation "controller.devfile.io/read-only" + ReadOnly: pvc.Annotations[annotationReadOnlyName] == "true", }) } return result, nil @@ -100,7 +101,7 @@ func (o KubernetesClient) getAutomountingSecrets() ([]AutomountInfo, error) { VolumeName: secret.Name, MountPath: mountPath, MountAs: mountAs, - ReadOnly: false, // TODO consider annotation "controller.devfile.io/read-only" + ReadOnly: secret.Annotations[annotationReadOnlyName] == "true", Keys: keys, }) } @@ -135,7 +136,7 @@ func (o KubernetesClient) getAutomountingConfigmaps() ([]AutomountInfo, error) { VolumeName: cm.Name, MountPath: mountPath, MountAs: mountAs, - ReadOnly: false, // TODO consider annotation "controller.devfile.io/read-only" + ReadOnly: cm.Annotations[annotationReadOnlyName] == "true", Keys: keys, }) } diff --git a/pkg/configAutomount/kubernetes_test.go b/pkg/configAutomount/kubernetes_test.go index df7047c8c89..93f9ea8b206 100644 --- a/pkg/configAutomount/kubernetes_test.go +++ b/pkg/configAutomount/kubernetes_test.go @@ -32,6 +32,15 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { annotationMountPathName: "/specific/pvc/mount/path", }) + roPVC := corev1.PersistentVolumeClaim{} + roPVC.SetName("roPVC") + roPVC.SetLabels(map[string]string{ + labelMountName: labelMountValue, + }) + roPVC.SetAnnotations(map[string]string{ + annotationReadOnlyName: "true", + }) + defaultSecret1 := corev1.Secret{} defaultSecret1.SetName("defaultSecret1") defaultSecret1.SetLabels(map[string]string{ @@ -75,6 +84,15 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { annotationMountAsName: "env", }) + roSecret := corev1.Secret{} + roSecret.SetName("roSecret") + roSecret.SetLabels(map[string]string{ + labelMountName: labelMountValue, + }) + roSecret.SetAnnotations(map[string]string{ + annotationReadOnlyName: "true", + }) + defaultCM1 := corev1.ConfigMap{} defaultCM1.SetName("defaultCM1") defaultCM1.SetLabels(map[string]string{ @@ -118,6 +136,15 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { annotationMountAsName: "env", }) + roCM := corev1.ConfigMap{} + roCM.SetName("roCM") + roCM.SetLabels(map[string]string{ + labelMountName: labelMountValue, + }) + roCM.SetAnnotations(map[string]string{ + annotationReadOnlyName: "true", + }) + type fields struct { kubeClient func(ctrl *gomock.Controller) kclient.ClientInterface } @@ -303,8 +330,42 @@ func TestKubernetesClient_GetAutomountingVolumes(t *testing.T) { }, wantErr: false, }, - // TODO: Add test cases. - } + { + name: "PVC, Secret and ConfigMap read-only", + fields: fields{ + kubeClient: func(ctrl *gomock.Controller) kclient.ClientInterface { + client := kclient.NewMockClientInterface(ctrl) + client.EXPECT().ListPVCs(gomock.Any()).Return([]corev1.PersistentVolumeClaim{roPVC}, nil).AnyTimes() + client.EXPECT().ListSecrets(gomock.Any()).Return([]corev1.Secret{roSecret}, nil).AnyTimes() + client.EXPECT().ListConfigMaps(gomock.Any()).Return([]corev1.ConfigMap{roCM}, nil).AnyTimes() + return client + }, + }, + want: []AutomountInfo{ + { + VolumeType: VolumeTypePVC, + VolumeName: "roPVC", + MountPath: "/tmp/roPVC", + MountAs: MountAsFile, + ReadOnly: true, + }, + { + VolumeType: VolumeTypeSecret, + VolumeName: "roSecret", + MountPath: "/etc/secret/roSecret", + MountAs: MountAsFile, + ReadOnly: true, + }, + { + VolumeType: VolumeTypeConfigmap, + VolumeName: "roCM", + MountPath: "/etc/config/roCM", + MountAs: MountAsFile, + ReadOnly: true, + }, + }, + wantErr: false, + }} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) diff --git a/pkg/devfile/adapters/kubernetes/storage/utils.go b/pkg/devfile/adapters/kubernetes/storage/utils.go index 93617fea5f8..88867adf723 100644 --- a/pkg/devfile/adapters/kubernetes/storage/utils.go +++ b/pkg/devfile/adapters/kubernetes/storage/utils.go @@ -272,6 +272,7 @@ func mountPVC(volumeInfo configAutomount.AutomountInfo, containers, initContaine addVolumeMountToContainer(container, corev1.VolumeMount{ Name: volumeName, MountPath: volumeInfo.MountPath, + ReadOnly: volumeInfo.ReadOnly, }) }) @@ -305,6 +306,7 @@ func mountSecretAsFile(volumeInfo configAutomount.AutomountInfo, containers, ini addVolumeMountToContainer(container, corev1.VolumeMount{ Name: volumeName, MountPath: volumeInfo.MountPath, + ReadOnly: volumeInfo.ReadOnly, }) }) @@ -341,6 +343,7 @@ func mountSecretAsSubpath(volumeInfo configAutomount.AutomountInfo, containers, Name: volumeName, MountPath: filepath.ToSlash(filepath.Join(volumeInfo.MountPath, key)), SubPath: key, + ReadOnly: volumeInfo.ReadOnly, }) } }) @@ -375,6 +378,7 @@ func mountConfigMapAsFile(volumeInfo configAutomount.AutomountInfo, containers, addVolumeMountToContainer(container, corev1.VolumeMount{ Name: volumeName, MountPath: volumeInfo.MountPath, + ReadOnly: volumeInfo.ReadOnly, }) }) @@ -413,6 +417,7 @@ func mountConfigMapAsSubpath(volumeInfo configAutomount.AutomountInfo, container Name: volumeName, MountPath: filepath.ToSlash(filepath.Join(volumeInfo.MountPath, key)), SubPath: key, + ReadOnly: volumeInfo.ReadOnly, }) } }) From 4a85bd8810295febb9edefdac78a2faaf74fd589 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Fri, 31 Mar 2023 13:24:54 +0200 Subject: [PATCH 17/24] Integration tests --- .../config-automount/as-env-configmap.yaml | 11 +++ .../config-automount/as-env-secret.yaml | 11 +++ .../config-automount/default-configmap.yaml | 9 ++ .../config-automount/default-pvc.yaml | 12 +++ .../config-automount/default-secret.yaml | 9 ++ .../mount-path-configmap.yaml | 11 +++ .../config-automount/mount-path-pvc.yaml | 14 +++ .../config-automount/mount-path-secret.yaml | 11 +++ .../config-automount/readonly-pvc.yaml | 14 +++ .../config-automount/subpath-configmap.yaml | 12 +++ .../config-automount/subpath-secret.yaml | 12 +++ tests/integration/cmd_dev_test.go | 90 +++++++++++++++++++ 12 files changed, 216 insertions(+) create mode 100644 tests/examples/manifests/config-automount/as-env-configmap.yaml create mode 100644 tests/examples/manifests/config-automount/as-env-secret.yaml create mode 100644 tests/examples/manifests/config-automount/default-configmap.yaml create mode 100644 tests/examples/manifests/config-automount/default-pvc.yaml create mode 100644 tests/examples/manifests/config-automount/default-secret.yaml create mode 100644 tests/examples/manifests/config-automount/mount-path-configmap.yaml create mode 100644 tests/examples/manifests/config-automount/mount-path-pvc.yaml create mode 100644 tests/examples/manifests/config-automount/mount-path-secret.yaml create mode 100644 tests/examples/manifests/config-automount/readonly-pvc.yaml create mode 100644 tests/examples/manifests/config-automount/subpath-configmap.yaml create mode 100644 tests/examples/manifests/config-automount/subpath-secret.yaml diff --git a/tests/examples/manifests/config-automount/as-env-configmap.yaml b/tests/examples/manifests/config-automount/as-env-configmap.yaml new file mode 100644 index 00000000000..c520f8ccccf --- /dev/null +++ b/tests/examples/manifests/config-automount/as-env-configmap.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: automount-env-configmap + labels: + controller.devfile.io/mount-to-devworkspace: "true" + annotations: + controller.devfile.io/mount-as: env +data: + foo4: bar4 + ping4: pong4 diff --git a/tests/examples/manifests/config-automount/as-env-secret.yaml b/tests/examples/manifests/config-automount/as-env-secret.yaml new file mode 100644 index 00000000000..c5f29d479f5 --- /dev/null +++ b/tests/examples/manifests/config-automount/as-env-secret.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + name: automount-env-secret + labels: + controller.devfile.io/mount-to-devworkspace: "true" + annotations: + controller.devfile.io/mount-as: env +stringData: + code4: "4567" + secret4: "PassWd4" diff --git a/tests/examples/manifests/config-automount/default-configmap.yaml b/tests/examples/manifests/config-automount/default-configmap.yaml new file mode 100644 index 00000000000..58626bec87d --- /dev/null +++ b/tests/examples/manifests/config-automount/default-configmap.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: automount-default-configmap + labels: + controller.devfile.io/mount-to-devworkspace: "true" +data: + foo1: bar1 + ping1: pong1 diff --git a/tests/examples/manifests/config-automount/default-pvc.yaml b/tests/examples/manifests/config-automount/default-pvc.yaml new file mode 100644 index 00000000000..bccd76fe16f --- /dev/null +++ b/tests/examples/manifests/config-automount/default-pvc.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + labels: + controller.devfile.io/mount-to-devworkspace: "true" + name: automount-default-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 2Gi diff --git a/tests/examples/manifests/config-automount/default-secret.yaml b/tests/examples/manifests/config-automount/default-secret.yaml new file mode 100644 index 00000000000..177efc1c4b1 --- /dev/null +++ b/tests/examples/manifests/config-automount/default-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: automount-default-secret + labels: + controller.devfile.io/mount-to-devworkspace: "true" +stringData: + code1: "1234" + secret1: "PassWd1" diff --git a/tests/examples/manifests/config-automount/mount-path-configmap.yaml b/tests/examples/manifests/config-automount/mount-path-configmap.yaml new file mode 100644 index 00000000000..07ae0cda9a4 --- /dev/null +++ b/tests/examples/manifests/config-automount/mount-path-configmap.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: automount-mount-path-configmap + labels: + controller.devfile.io/mount-to-devworkspace: "true" + annotations: + controller.devfile.io/mount-path: "/mnt/mount-path/configmap" +data: + foo2: bar2 + ping2: pong2 diff --git a/tests/examples/manifests/config-automount/mount-path-pvc.yaml b/tests/examples/manifests/config-automount/mount-path-pvc.yaml new file mode 100644 index 00000000000..a413a310598 --- /dev/null +++ b/tests/examples/manifests/config-automount/mount-path-pvc.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + labels: + controller.devfile.io/mount-to-devworkspace: "true" + annotations: + controller.devfile.io/mount-path: "/mnt/mount-path/pvc" + name: automount-mount-path-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 2Gi diff --git a/tests/examples/manifests/config-automount/mount-path-secret.yaml b/tests/examples/manifests/config-automount/mount-path-secret.yaml new file mode 100644 index 00000000000..f3db0f5c3dc --- /dev/null +++ b/tests/examples/manifests/config-automount/mount-path-secret.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + name: automount-mount-path-secret + labels: + controller.devfile.io/mount-to-devworkspace: "true" + annotations: + controller.devfile.io/mount-path: "/mnt/mount-path/secret" +stringData: + code2: "2345" + secret2: "PassWd2" diff --git a/tests/examples/manifests/config-automount/readonly-pvc.yaml b/tests/examples/manifests/config-automount/readonly-pvc.yaml new file mode 100644 index 00000000000..c7b1010ef4f --- /dev/null +++ b/tests/examples/manifests/config-automount/readonly-pvc.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + labels: + controller.devfile.io/mount-to-devworkspace: "true" + annotations: + controller.devfile.io/read-only: "true" + name: automount-readonly-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 2Gi diff --git a/tests/examples/manifests/config-automount/subpath-configmap.yaml b/tests/examples/manifests/config-automount/subpath-configmap.yaml new file mode 100644 index 00000000000..1daf4a5e4ec --- /dev/null +++ b/tests/examples/manifests/config-automount/subpath-configmap.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: automount-subpath-configmap + labels: + controller.devfile.io/mount-to-devworkspace: "true" + annotations: + controller.devfile.io/mount-path: "/mnt/subpaths" + controller.devfile.io/mount-as: subpath +data: + foo5: bar5 + ping5: pong5 diff --git a/tests/examples/manifests/config-automount/subpath-secret.yaml b/tests/examples/manifests/config-automount/subpath-secret.yaml new file mode 100644 index 00000000000..0a683abfde0 --- /dev/null +++ b/tests/examples/manifests/config-automount/subpath-secret.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Secret +metadata: + name: automount-subpath-secret + labels: + controller.devfile.io/mount-to-devworkspace: "true" + annotations: + controller.devfile.io/mount-path: "/mnt/subpaths" + controller.devfile.io/mount-as: subpath +stringData: + code5: "5678" + secret5: "PassWd5" diff --git a/tests/integration/cmd_dev_test.go b/tests/integration/cmd_dev_test.go index efd54fce0ad..36d5c9e4eae 100644 --- a/tests/integration/cmd_dev_test.go +++ b/tests/integration/cmd_dev_test.go @@ -591,6 +591,96 @@ ComponentSettings: helper.MatchAllInOutput(string(body), []string{"Hello from Node.js Starter Application!"}) }) }) + + When("Automount volumes are present in the namespace", func() { + + BeforeEach(func() { + commonVar.CliRunner.Run("apply", "-f", helper.GetExamplePath("manifests", "config-automount/")) + }) + + When("odo dev is executed", func() { + + var devSession helper.DevSession + + BeforeEach(func() { + var err error + devSession, _, _, _, err = helper.StartDevMode(helper.DevSessionOpts{}) + Expect(err).ToNot(HaveOccurred()) + }) + + AfterEach(func() { + devSession.Stop() + devSession.WaitEnd() + }) + + It("should mount the volumes", func() { + component := helper.NewComponent(cmpName, "app", labels.ComponentDevMode, commonVar.Project, commonVar.CliRunner) + + // Check volumes are mounted + for _, path := range []string{ + "/tmp/automount-default-pvc", + "/etc/config/automount-default-configmap", + "/etc/secret/automount-default-secret", + + "/tmp/automount-readonly-pvc", + + "/mnt/mount-path/pvc", + "/mnt/mount-path/configmap", + "/mnt/mount-path/secret", + } { + var output string + Eventually(func() bool { + output, _ = component.Exec("runtime", []string{"df", path}, nil) + return len(output) > 0 + }).WithPolling(1 * time.Second).WithTimeout(60 * time.Second).Should(BeTrue()) + // This checks this is really a mount + Expect(output).ToNot(ContainSubstring("overlay")) + } + + // Check files are present for configmap / secret + files := map[string]string{ + "/etc/config/automount-default-configmap/foo1": "bar1", + "/etc/config/automount-default-configmap/ping1": "pong1", + "/etc/secret/automount-default-secret/code1": "1234", + "/etc/secret/automount-default-secret/secret1": "PassWd1", + + "/mnt/mount-path/configmap/foo2": "bar2", + "/mnt/mount-path/configmap/ping2": "pong2", + "/mnt/mount-path/secret/code2": "2345", + "/mnt/mount-path/secret/secret2": "PassWd2", + + "/mnt/subpaths/foo5": "bar5", + "/mnt/subpaths/ping5": "pong5", + "/mnt/subpaths/code5": "5678", + "/mnt/subpaths/secret5": "PassWd5", + } + for file, content := range files { + output, _ := component.Exec("runtime", []string{"cat", file}, pointer.Bool(true)) + Expect(output).To(Equal(content)) + } + + envVars := map[string]string{ + "foo4": "bar4", + "ping4": "pong4", + + "code4": "4567", + "secret4": "PassWd4", + } + for name, value := range envVars { + output, _ := component.Exec("runtime", []string{"bash", "-c", "echo -n $" + name}, pointer.Bool(true)) + Expect(output).To(Equal(value)) + } + + // Default PVC is not read-only + component.Exec("runtime", []string{"touch", "/tmp/automount-default-pvc/newfile"}, pointer.Bool(true)) + + // Read-only PVC is read-only + _, stderr := component.Exec("runtime", []string{"touch", "/tmp/automount-readonly-pvc/newfile"}, pointer.Bool(false)) + Expect(stderr).To(ContainSubstring("Read-only file system")) + + }) + }) + }) }) Context("checking if odo dev matches local Devfile K8s resources and remote resources", func() { for _, devfile := range []struct { From 7451e234863f0d633424a43440e1c9b26cccfc78 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Fri, 31 Mar 2023 17:32:08 +0200 Subject: [PATCH 18/24] Rename label --- pkg/configAutomount/kubernetes.go | 2 +- tests/examples/manifests/config-automount/as-env-configmap.yaml | 2 +- tests/examples/manifests/config-automount/as-env-secret.yaml | 2 +- .../examples/manifests/config-automount/default-configmap.yaml | 2 +- tests/examples/manifests/config-automount/default-pvc.yaml | 2 +- tests/examples/manifests/config-automount/default-secret.yaml | 2 +- .../manifests/config-automount/mount-path-configmap.yaml | 2 +- tests/examples/manifests/config-automount/mount-path-pvc.yaml | 2 +- .../examples/manifests/config-automount/mount-path-secret.yaml | 2 +- tests/examples/manifests/config-automount/readonly-pvc.yaml | 2 +- .../examples/manifests/config-automount/subpath-configmap.yaml | 2 +- tests/examples/manifests/config-automount/subpath-secret.yaml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pkg/configAutomount/kubernetes.go b/pkg/configAutomount/kubernetes.go index 4ab2298a6ae..479750044cf 100644 --- a/pkg/configAutomount/kubernetes.go +++ b/pkg/configAutomount/kubernetes.go @@ -8,7 +8,7 @@ import ( ) const ( - labelMountName = "controller.devfile.io/mount-to-devworkspace" + labelMountName = "controller.devfile.io/mount-to-containers" labelMountValue = "true" annotationMountPathName = "controller.devfile.io/mount-path" diff --git a/tests/examples/manifests/config-automount/as-env-configmap.yaml b/tests/examples/manifests/config-automount/as-env-configmap.yaml index c520f8ccccf..2e2ac987f60 100644 --- a/tests/examples/manifests/config-automount/as-env-configmap.yaml +++ b/tests/examples/manifests/config-automount/as-env-configmap.yaml @@ -3,7 +3,7 @@ kind: ConfigMap metadata: name: automount-env-configmap labels: - controller.devfile.io/mount-to-devworkspace: "true" + controller.devfile.io/mount-to-containers: "true" annotations: controller.devfile.io/mount-as: env data: diff --git a/tests/examples/manifests/config-automount/as-env-secret.yaml b/tests/examples/manifests/config-automount/as-env-secret.yaml index c5f29d479f5..12309fb5c33 100644 --- a/tests/examples/manifests/config-automount/as-env-secret.yaml +++ b/tests/examples/manifests/config-automount/as-env-secret.yaml @@ -3,7 +3,7 @@ kind: Secret metadata: name: automount-env-secret labels: - controller.devfile.io/mount-to-devworkspace: "true" + controller.devfile.io/mount-to-containers: "true" annotations: controller.devfile.io/mount-as: env stringData: diff --git a/tests/examples/manifests/config-automount/default-configmap.yaml b/tests/examples/manifests/config-automount/default-configmap.yaml index 58626bec87d..13b03a520b7 100644 --- a/tests/examples/manifests/config-automount/default-configmap.yaml +++ b/tests/examples/manifests/config-automount/default-configmap.yaml @@ -3,7 +3,7 @@ kind: ConfigMap metadata: name: automount-default-configmap labels: - controller.devfile.io/mount-to-devworkspace: "true" + controller.devfile.io/mount-to-containers: "true" data: foo1: bar1 ping1: pong1 diff --git a/tests/examples/manifests/config-automount/default-pvc.yaml b/tests/examples/manifests/config-automount/default-pvc.yaml index bccd76fe16f..2062b6df6e8 100644 --- a/tests/examples/manifests/config-automount/default-pvc.yaml +++ b/tests/examples/manifests/config-automount/default-pvc.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: labels: - controller.devfile.io/mount-to-devworkspace: "true" + controller.devfile.io/mount-to-containers: "true" name: automount-default-pvc spec: accessModes: diff --git a/tests/examples/manifests/config-automount/default-secret.yaml b/tests/examples/manifests/config-automount/default-secret.yaml index 177efc1c4b1..25d559bc8cd 100644 --- a/tests/examples/manifests/config-automount/default-secret.yaml +++ b/tests/examples/manifests/config-automount/default-secret.yaml @@ -3,7 +3,7 @@ kind: Secret metadata: name: automount-default-secret labels: - controller.devfile.io/mount-to-devworkspace: "true" + controller.devfile.io/mount-to-containers: "true" stringData: code1: "1234" secret1: "PassWd1" diff --git a/tests/examples/manifests/config-automount/mount-path-configmap.yaml b/tests/examples/manifests/config-automount/mount-path-configmap.yaml index 07ae0cda9a4..8dfb176ad75 100644 --- a/tests/examples/manifests/config-automount/mount-path-configmap.yaml +++ b/tests/examples/manifests/config-automount/mount-path-configmap.yaml @@ -3,7 +3,7 @@ kind: ConfigMap metadata: name: automount-mount-path-configmap labels: - controller.devfile.io/mount-to-devworkspace: "true" + controller.devfile.io/mount-to-containers: "true" annotations: controller.devfile.io/mount-path: "/mnt/mount-path/configmap" data: diff --git a/tests/examples/manifests/config-automount/mount-path-pvc.yaml b/tests/examples/manifests/config-automount/mount-path-pvc.yaml index a413a310598..5c2d98b692a 100644 --- a/tests/examples/manifests/config-automount/mount-path-pvc.yaml +++ b/tests/examples/manifests/config-automount/mount-path-pvc.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: labels: - controller.devfile.io/mount-to-devworkspace: "true" + controller.devfile.io/mount-to-containers: "true" annotations: controller.devfile.io/mount-path: "/mnt/mount-path/pvc" name: automount-mount-path-pvc diff --git a/tests/examples/manifests/config-automount/mount-path-secret.yaml b/tests/examples/manifests/config-automount/mount-path-secret.yaml index f3db0f5c3dc..5a81c5c20e1 100644 --- a/tests/examples/manifests/config-automount/mount-path-secret.yaml +++ b/tests/examples/manifests/config-automount/mount-path-secret.yaml @@ -3,7 +3,7 @@ kind: Secret metadata: name: automount-mount-path-secret labels: - controller.devfile.io/mount-to-devworkspace: "true" + controller.devfile.io/mount-to-containers: "true" annotations: controller.devfile.io/mount-path: "/mnt/mount-path/secret" stringData: diff --git a/tests/examples/manifests/config-automount/readonly-pvc.yaml b/tests/examples/manifests/config-automount/readonly-pvc.yaml index c7b1010ef4f..991ea3114dd 100644 --- a/tests/examples/manifests/config-automount/readonly-pvc.yaml +++ b/tests/examples/manifests/config-automount/readonly-pvc.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: labels: - controller.devfile.io/mount-to-devworkspace: "true" + controller.devfile.io/mount-to-containers: "true" annotations: controller.devfile.io/read-only: "true" name: automount-readonly-pvc diff --git a/tests/examples/manifests/config-automount/subpath-configmap.yaml b/tests/examples/manifests/config-automount/subpath-configmap.yaml index 1daf4a5e4ec..96b9ff98ceb 100644 --- a/tests/examples/manifests/config-automount/subpath-configmap.yaml +++ b/tests/examples/manifests/config-automount/subpath-configmap.yaml @@ -3,7 +3,7 @@ kind: ConfigMap metadata: name: automount-subpath-configmap labels: - controller.devfile.io/mount-to-devworkspace: "true" + controller.devfile.io/mount-to-containers: "true" annotations: controller.devfile.io/mount-path: "/mnt/subpaths" controller.devfile.io/mount-as: subpath diff --git a/tests/examples/manifests/config-automount/subpath-secret.yaml b/tests/examples/manifests/config-automount/subpath-secret.yaml index 0a683abfde0..ce82d77f7fd 100644 --- a/tests/examples/manifests/config-automount/subpath-secret.yaml +++ b/tests/examples/manifests/config-automount/subpath-secret.yaml @@ -3,7 +3,7 @@ kind: Secret metadata: name: automount-subpath-secret labels: - controller.devfile.io/mount-to-devworkspace: "true" + controller.devfile.io/mount-to-containers: "true" annotations: controller.devfile.io/mount-path: "/mnt/subpaths" controller.devfile.io/mount-as: subpath From 19e03ff8a633fcd91eb086848127700eece66d6d Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Fri, 31 Mar 2023 18:03:03 +0200 Subject: [PATCH 19/24] Automount during odo deploy Exec command --- pkg/deploy/deploy.go | 55 ++++++++++++------- .../genericclioptions/clientset/clientset.go | 4 +- tests/integration/cmd_devfile_deploy_test.go | 17 ++++++ 3 files changed, 53 insertions(+), 23 deletions(-) diff --git a/pkg/deploy/deploy.go b/pkg/deploy/deploy.go index 26c702f6c7f..e576354025e 100644 --- a/pkg/deploy/deploy.go +++ b/pkg/deploy/deploy.go @@ -17,6 +17,8 @@ import ( "k8s.io/utils/pointer" "github.com/redhat-developer/odo/pkg/component" + "github.com/redhat-developer/odo/pkg/configAutomount" + "github.com/redhat-developer/odo/pkg/devfile/adapters/kubernetes/storage" "github.com/redhat-developer/odo/pkg/devfile/image" "github.com/redhat-developer/odo/pkg/kclient" odolabels "github.com/redhat-developer/odo/pkg/labels" @@ -29,16 +31,18 @@ import ( ) type DeployClient struct { - kubeClient kclient.ClientInterface - fs filesystem.Filesystem + kubeClient kclient.ClientInterface + configAutomountClient configAutomount.Client + fs filesystem.Filesystem } var _ Client = (*DeployClient)(nil) -func NewDeployClient(kubeClient kclient.ClientInterface, fs filesystem.Filesystem) *DeployClient { +func NewDeployClient(kubeClient kclient.ClientInterface, configAutomountClient configAutomount.Client, fs filesystem.Filesystem) *DeployClient { return &DeployClient{ - kubeClient: kubeClient, - fs: fs, + kubeClient: kubeClient, + configAutomountClient: configAutomountClient, + fs: fs, } } @@ -51,7 +55,7 @@ func (o *DeployClient) Deploy(ctx context.Context) error { appName = odocontext.GetApplication(ctx) ) - handler := newDeployHandler(ctx, o.fs, *devfileObj, path, o.kubeClient, appName, componentName) + handler := newDeployHandler(ctx, o.fs, *devfileObj, path, o.kubeClient, o.configAutomountClient, appName, componentName) err := o.buildPushAutoImageComponents(handler, *devfileObj) if err != nil { @@ -105,26 +109,28 @@ func (o *DeployClient) applyAutoK8sOrOcComponents(handler *deployHandler, devfil } type deployHandler struct { - ctx context.Context - fs filesystem.Filesystem - devfileObj parser.DevfileObj - path string - kubeClient kclient.ClientInterface - appName string - componentName string + ctx context.Context + fs filesystem.Filesystem + devfileObj parser.DevfileObj + path string + kubeClient kclient.ClientInterface + configAutomountClient configAutomount.Client + appName string + componentName string } var _ libdevfile.Handler = (*deployHandler)(nil) -func newDeployHandler(ctx context.Context, fs filesystem.Filesystem, devfileObj parser.DevfileObj, path string, kubeClient kclient.ClientInterface, appName string, componentName string) *deployHandler { +func newDeployHandler(ctx context.Context, fs filesystem.Filesystem, devfileObj parser.DevfileObj, path string, kubeClient kclient.ClientInterface, configAutomountClient configAutomount.Client, appName string, componentName string) *deployHandler { return &deployHandler{ - ctx: ctx, - fs: fs, - devfileObj: devfileObj, - path: path, - kubeClient: kubeClient, - appName: appName, - componentName: componentName, + ctx: ctx, + fs: fs, + devfileObj: devfileObj, + path: path, + kubeClient: kubeClient, + configAutomountClient: configAutomountClient, + appName: appName, + componentName: componentName, } } @@ -168,6 +174,13 @@ func (o *deployHandler) Execute(command v1alpha2.Command) error { podTemplateSpec.Spec.Containers[0].Command = []string{"/bin/sh"} podTemplateSpec.Spec.Containers[0].Args = getCmdline(command) + volumes, err := storage.GetAutomountVolumes(o.configAutomountClient, podTemplateSpec.Spec.Containers, podTemplateSpec.Spec.InitContainers) + if err != nil { + return err + } + + podTemplateSpec.Spec.Volumes = volumes + // Create a Kubernetes Job and use the container image referenced by command.Exec.Component // Get the component for the command with command.Exec.Component getJobName := func() string { diff --git a/pkg/odo/genericclioptions/clientset/clientset.go b/pkg/odo/genericclioptions/clientset/clientset.go index 03b1b155277..fceb428adc4 100644 --- a/pkg/odo/genericclioptions/clientset/clientset.go +++ b/pkg/odo/genericclioptions/clientset/clientset.go @@ -94,7 +94,7 @@ var subdeps map[string][]string = map[string][]string{ ALIZER: {REGISTRY}, CONFIG_AUTOMOUNT: {KUBERNETES_NULLABLE, PODMAN_NULLABLE}, DELETE_COMPONENT: {KUBERNETES_NULLABLE, PODMAN_NULLABLE, EXEC}, - DEPLOY: {KUBERNETES, FILESYSTEM}, + DEPLOY: {KUBERNETES, FILESYSTEM, CONFIG_AUTOMOUNT}, DEV: { BINDING, DELETE_COMPONENT, @@ -230,7 +230,7 @@ func Fetch(command *cobra.Command, platform string) (*Clientset, error) { dep.DeleteClient = _delete.NewDeleteComponentClient(dep.KubernetesClient, dep.PodmanClient, dep.ExecClient) } if isDefined(command, DEPLOY) { - dep.DeployClient = deploy.NewDeployClient(dep.KubernetesClient, dep.FS) + dep.DeployClient = deploy.NewDeployClient(dep.KubernetesClient, dep.ConfigAutomountClient, dep.FS) } if isDefined(command, INIT) { dep.InitClient = _init.NewInitClient(dep.FS, dep.PreferenceClient, dep.RegistryClient, dep.AlizerClient) diff --git a/tests/integration/cmd_devfile_deploy_test.go b/tests/integration/cmd_devfile_deploy_test.go index a4ccfe0f570..af314ef6b1d 100644 --- a/tests/integration/cmd_devfile_deploy_test.go +++ b/tests/integration/cmd_devfile_deploy_test.go @@ -653,6 +653,23 @@ CMD ["npm", "start"] }) }) }) + + When("Automount volumes are present in the namespace", func() { + + BeforeEach(func() { + commonVar.CliRunner.Run("apply", "-f", helper.GetExamplePath("manifests", "config-automount/")) + }) + + It("should mount the volumes", func() { + helper.Cmd("odo", "deploy").Should(func(session *gexec.Session) { + component := helper.NewComponent(cmpName, "app", labels.ComponentDeployMode, commonVar.Project, commonVar.CliRunner) + jobDef := component.GetJobDef() + // We only check that at least one volume is automounted + // More tests are executed on `odo dev`, see "Automount volumes are present in the namespace" on odo dev tests. + Expect(jobDef.Spec.Template.Spec.Volumes[0].Name).To(Equal("auto-pvc-automount-default-pvc")) + }) + }) + }) }) // More details on https://github.com/devfile/api/issues/852#issuecomment-1211928487 From 69561a13c397acf8ff649f1024cb19ee326c394a Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Mon, 3 Apr 2023 10:42:14 +0200 Subject: [PATCH 20/24] Add documentation --- .../podman-support-limitations.md | 3 ++ .../advanced/air-gap-environment.md | 36 ++++++----------- .../advanced/automounting-volumes.md | 39 +++++++++++++++++++ 3 files changed, 53 insertions(+), 25 deletions(-) create mode 100644 docs/website/docs/user-guides/advanced/automounting-volumes.md diff --git a/docs/website/docs/development/architecture/podman-support-limitations.md b/docs/website/docs/development/architecture/podman-support-limitations.md index 160286d2868..19226bf88bc 100644 --- a/docs/website/docs/development/architecture/podman-support-limitations.md +++ b/docs/website/docs/development/architecture/podman-support-limitations.md @@ -58,3 +58,6 @@ When running `odo dev` on cluster, if you make changes to the Devfile affecting Pre-Stop events defined in the Devfile are not triggered when running `odo dev` on Podman. +## Auto-mounting volumes + +[Auto-mounting volumes](/docs/user-guides/advanced/automounting-volumes) is not supported when working on Podman. diff --git a/docs/website/docs/user-guides/advanced/air-gap-environment.md b/docs/website/docs/user-guides/advanced/air-gap-environment.md index 5e906f6c09c..a2d884da0ea 100644 --- a/docs/website/docs/user-guides/advanced/air-gap-environment.md +++ b/docs/website/docs/user-guides/advanced/air-gap-environment.md @@ -127,31 +127,17 @@ commands: id: install ``` -You can also provide additional environment variables to the container: +You can also provide additional environment variables to the container, by creating an auto-mounted configmap: ```yaml -[...] -components: -- container: - args: - - tail - - -f - - /dev/null - endpoints: - - name: http-node - targetPort: 3000 - - exposure: none - name: debug - targetPort: 5858 - env: - - name: DEBUG_PORT - value: "5858" -# highlight-start - - name: npm_config_registry - value: https://your_local_registry -# highlight-end - image: your_secure_registry/nodejs-16:latest - memoryLimit: 1024Mi - mountSources: true - name: runtime +apiVersion: v1 +kind: ConfigMap +metadata: + name: proxy-config + labels: + controller.devfile.io/mount-to-containers: "true" + annotations: + controller.devfile.io/mount-as: env +data: + npm_config_registry: "https://your_local_registry" ``` diff --git a/docs/website/docs/user-guides/advanced/automounting-volumes.md b/docs/website/docs/user-guides/advanced/automounting-volumes.md new file mode 100644 index 00000000000..376b7e6c07b --- /dev/null +++ b/docs/website/docs/user-guides/advanced/automounting-volumes.md @@ -0,0 +1,39 @@ +--- +title: Automounting Volumes +sidebar_position: 8 +--- + +Existing configmaps, secrets, and persistent volume claims on the cluster can be mounted automatically to all containers created by `odo`. These resources can be configured by applying the appropriate labels. + +To mark a resource for mounting to containers created by `odo`, apply the following label to the resource: + +```yaml +metadata: + labels: + controller.devfile.io/mount-to-containers: "true" +``` + +By default, resources will be mounted based on the resource name: + +- Secrets will be mounted to `/etc/secret/` + +- Configmaps will be mounted to `/etc/config/` + +- Persistent volume claims will be mounted to `/tmp/` + +Mounting resources can be additionally configured via annotations: + +- `controller.devfile.io/mount-path`: configure where the resource should be mounted + +- `controller.devfile.io/mount-as`: for secrets and configmaps only, configure how the resource should be mounted to the container + + - If `controller.devfile.io/mount-as: file`, the configmap/secret will be mounted as files within the mount path. This is the default behavior. + + - If `controller.devfile.io/mount-as: subpath`, the keys and values in the configmap/secret will be mounted as files within the mount path using subpath volume mounts. + + - If `controller.devfile.io/mount-as: env`, the keys and values in the configmap/secret will be mounted as environment variables in all containers. + + When `file` is used, the configmap is mounted as a directory within the containers, erasing any files/directories already present. When `subpath` is used, each key in the configmap/secret is mounted as a subpath volume mount in the mount path, leaving existing files intact but preventing changes to the secret/configmap from propagating into the containers without a restart. + +- `controller.devfile.io/read-only`: for persistent volume claims, mount the resource as read-only + From db949e341f20e53e302ce9ee7a56d21c245ac484 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Mon, 3 Apr 2023 16:05:16 +0200 Subject: [PATCH 21/24] Fix TODO --- pkg/devfile/adapters/kubernetes/component/adapter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/devfile/adapters/kubernetes/component/adapter.go b/pkg/devfile/adapters/kubernetes/component/adapter.go index 6e6f95a0f92..09aaf660354 100644 --- a/pkg/devfile/adapters/kubernetes/component/adapter.go +++ b/pkg/devfile/adapters/kubernetes/component/adapter.go @@ -526,7 +526,7 @@ func (a *Adapter) createOrUpdateComponent( // - (side effect on cluster) creates the PVC for the project sources if Epehemeral preference is false // - (side effect on cluster) creates the PVCs for non-ephemeral volumes defined in the Devfile // - (side effect on input parameters) adds volumeMounts to containers and initContainers for the PVCs and Ephemeral volumes -// - TODO(feloy) add volumeMounts for automounted volumes +// - (side effect on input parameters) adds volumeMounts for automounted volumes // => Returns the list of Volumes to add to the PodTemplate func (a *Adapter) buildVolumes(containers, initContainers []corev1.Container) ([]corev1.Volume, error) { From 903cb0e7a5907b14997aef076837ce37d18ba61d Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Thu, 6 Apr 2023 15:28:31 +0200 Subject: [PATCH 22/24] Review --- docs/website/docs/user-guides/advanced/air-gap-environment.md | 2 +- docs/website/docs/user-guides/advanced/automounting-volumes.md | 2 +- pkg/configAutomount/kubernetes_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/website/docs/user-guides/advanced/air-gap-environment.md b/docs/website/docs/user-guides/advanced/air-gap-environment.md index a2d884da0ea..fed1c9de25d 100644 --- a/docs/website/docs/user-guides/advanced/air-gap-environment.md +++ b/docs/website/docs/user-guides/advanced/air-gap-environment.md @@ -127,7 +127,7 @@ commands: id: install ``` -You can also provide additional environment variables to the container, by creating an auto-mounted configmap: +You can also provide additional environment variables to the container, by creating an [auto-mounted configmap](/docs/user-guides/advanced/automounting-volumes): ```yaml apiVersion: v1 diff --git a/docs/website/docs/user-guides/advanced/automounting-volumes.md b/docs/website/docs/user-guides/advanced/automounting-volumes.md index 376b7e6c07b..ea9e21aea43 100644 --- a/docs/website/docs/user-guides/advanced/automounting-volumes.md +++ b/docs/website/docs/user-guides/advanced/automounting-volumes.md @@ -3,7 +3,7 @@ title: Automounting Volumes sidebar_position: 8 --- -Existing configmaps, secrets, and persistent volume claims on the cluster can be mounted automatically to all containers created by `odo`. These resources can be configured by applying the appropriate labels. +Existing [ConfigMaps](https://kubernetes.io/docs/concepts/configuration/configmap/), [Secrets](https://kubernetes.io/docs/concepts/configuration/secret/), and [Persistent Volume Claims](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) on the cluster can be mounted automatically to all containers created by `odo`. These resources can be configured by applying the appropriate labels. To mark a resource for mounting to containers created by `odo`, apply the following label to the resource: diff --git a/pkg/configAutomount/kubernetes_test.go b/pkg/configAutomount/kubernetes_test.go index 93f9ea8b206..57be9c657a0 100644 --- a/pkg/configAutomount/kubernetes_test.go +++ b/pkg/configAutomount/kubernetes_test.go @@ -3,7 +3,7 @@ package configAutomount import ( "testing" - gomock "github.com/golang/mock/gomock" + "github.com/golang/mock/gomock" "github.com/google/go-cmp/cmp" "github.com/redhat-developer/odo/pkg/kclient" corev1 "k8s.io/api/core/v1" From b3f1a93ce7e70ac498680484291fa87fed89fb25 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Thu, 6 Apr 2023 15:54:12 +0200 Subject: [PATCH 23/24] Fix indentation --- docs/website/docs/user-guides/advanced/automounting-volumes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/website/docs/user-guides/advanced/automounting-volumes.md b/docs/website/docs/user-guides/advanced/automounting-volumes.md index ea9e21aea43..546459cf2e0 100644 --- a/docs/website/docs/user-guides/advanced/automounting-volumes.md +++ b/docs/website/docs/user-guides/advanced/automounting-volumes.md @@ -33,7 +33,7 @@ Mounting resources can be additionally configured via annotations: - If `controller.devfile.io/mount-as: env`, the keys and values in the configmap/secret will be mounted as environment variables in all containers. - When `file` is used, the configmap is mounted as a directory within the containers, erasing any files/directories already present. When `subpath` is used, each key in the configmap/secret is mounted as a subpath volume mount in the mount path, leaving existing files intact but preventing changes to the secret/configmap from propagating into the containers without a restart. + When `file` is used, the configmap is mounted as a directory within the containers, erasing any files/directories already present. When `subpath` is used, each key in the configmap/secret is mounted as a subpath volume mount in the mount path, leaving existing files intact but preventing changes to the secret/configmap from propagating into the containers without a restart. - `controller.devfile.io/read-only`: for persistent volume claims, mount the resource as read-only From 8dd7ce531f3a677d02b4cd911f853e805ec381ea Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Wed, 12 Apr 2023 13:21:23 +0200 Subject: [PATCH 24/24] Rename labels/annotations --- .../user-guides/advanced/air-gap-environment.md | 4 ++-- .../user-guides/advanced/automounting-volumes.md | 14 +++++++------- pkg/configAutomount/kubernetes.go | 8 ++++---- .../config-automount/as-env-configmap.yaml | 4 ++-- .../manifests/config-automount/as-env-secret.yaml | 4 ++-- .../config-automount/default-configmap.yaml | 2 +- .../manifests/config-automount/default-pvc.yaml | 2 +- .../manifests/config-automount/default-secret.yaml | 2 +- .../config-automount/mount-path-configmap.yaml | 4 ++-- .../manifests/config-automount/mount-path-pvc.yaml | 4 ++-- .../config-automount/mount-path-secret.yaml | 4 ++-- .../manifests/config-automount/readonly-pvc.yaml | 4 ++-- .../config-automount/subpath-configmap.yaml | 6 +++--- .../manifests/config-automount/subpath-secret.yaml | 6 +++--- 14 files changed, 34 insertions(+), 34 deletions(-) diff --git a/docs/website/docs/user-guides/advanced/air-gap-environment.md b/docs/website/docs/user-guides/advanced/air-gap-environment.md index fed1c9de25d..11b7a642fff 100644 --- a/docs/website/docs/user-guides/advanced/air-gap-environment.md +++ b/docs/website/docs/user-guides/advanced/air-gap-environment.md @@ -135,9 +135,9 @@ kind: ConfigMap metadata: name: proxy-config labels: - controller.devfile.io/mount-to-containers: "true" + devfile.io/auto-mount: "true" annotations: - controller.devfile.io/mount-as: env + devfile.io/mount-as: env data: npm_config_registry: "https://your_local_registry" ``` diff --git a/docs/website/docs/user-guides/advanced/automounting-volumes.md b/docs/website/docs/user-guides/advanced/automounting-volumes.md index 546459cf2e0..f9d0de64274 100644 --- a/docs/website/docs/user-guides/advanced/automounting-volumes.md +++ b/docs/website/docs/user-guides/advanced/automounting-volumes.md @@ -10,7 +10,7 @@ To mark a resource for mounting to containers created by `odo`, apply the follow ```yaml metadata: labels: - controller.devfile.io/mount-to-containers: "true" + devfile.io/auto-mount: "true" ``` By default, resources will be mounted based on the resource name: @@ -23,17 +23,17 @@ By default, resources will be mounted based on the resource name: Mounting resources can be additionally configured via annotations: -- `controller.devfile.io/mount-path`: configure where the resource should be mounted +- `devfile.io/mount-path`: configure where the resource should be mounted -- `controller.devfile.io/mount-as`: for secrets and configmaps only, configure how the resource should be mounted to the container +- `devfile.io/mount-as`: for secrets and configmaps only, configure how the resource should be mounted to the container - - If `controller.devfile.io/mount-as: file`, the configmap/secret will be mounted as files within the mount path. This is the default behavior. + - If `devfile.io/mount-as: file`, the configmap/secret will be mounted as files within the mount path. This is the default behavior. - - If `controller.devfile.io/mount-as: subpath`, the keys and values in the configmap/secret will be mounted as files within the mount path using subpath volume mounts. + - If `devfile.io/mount-as: subpath`, the keys and values in the configmap/secret will be mounted as files within the mount path using subpath volume mounts. - - If `controller.devfile.io/mount-as: env`, the keys and values in the configmap/secret will be mounted as environment variables in all containers. + - If `devfile.io/mount-as: env`, the keys and values in the configmap/secret will be mounted as environment variables in all containers. When `file` is used, the configmap is mounted as a directory within the containers, erasing any files/directories already present. When `subpath` is used, each key in the configmap/secret is mounted as a subpath volume mount in the mount path, leaving existing files intact but preventing changes to the secret/configmap from propagating into the containers without a restart. -- `controller.devfile.io/read-only`: for persistent volume claims, mount the resource as read-only +- `devfile.io/read-only`: for persistent volume claims, mount the resource as read-only diff --git a/pkg/configAutomount/kubernetes.go b/pkg/configAutomount/kubernetes.go index 479750044cf..787a9f24da4 100644 --- a/pkg/configAutomount/kubernetes.go +++ b/pkg/configAutomount/kubernetes.go @@ -8,12 +8,12 @@ import ( ) const ( - labelMountName = "controller.devfile.io/mount-to-containers" + labelMountName = "devfile.io/auto-mount" labelMountValue = "true" - annotationMountPathName = "controller.devfile.io/mount-path" - annotationMountAsName = "controller.devfile.io/mount-as" - annotationReadOnlyName = "controller.devfile.io/read-only" + annotationMountPathName = "devfile.io/mount-path" + annotationMountAsName = "devfile.io/mount-as" + annotationReadOnlyName = "devfile.io/read-only" ) type KubernetesClient struct { diff --git a/tests/examples/manifests/config-automount/as-env-configmap.yaml b/tests/examples/manifests/config-automount/as-env-configmap.yaml index 2e2ac987f60..23cd280c46d 100644 --- a/tests/examples/manifests/config-automount/as-env-configmap.yaml +++ b/tests/examples/manifests/config-automount/as-env-configmap.yaml @@ -3,9 +3,9 @@ kind: ConfigMap metadata: name: automount-env-configmap labels: - controller.devfile.io/mount-to-containers: "true" + devfile.io/auto-mount: "true" annotations: - controller.devfile.io/mount-as: env + devfile.io/mount-as: env data: foo4: bar4 ping4: pong4 diff --git a/tests/examples/manifests/config-automount/as-env-secret.yaml b/tests/examples/manifests/config-automount/as-env-secret.yaml index 12309fb5c33..28021f19cbf 100644 --- a/tests/examples/manifests/config-automount/as-env-secret.yaml +++ b/tests/examples/manifests/config-automount/as-env-secret.yaml @@ -3,9 +3,9 @@ kind: Secret metadata: name: automount-env-secret labels: - controller.devfile.io/mount-to-containers: "true" + devfile.io/auto-mount: "true" annotations: - controller.devfile.io/mount-as: env + devfile.io/mount-as: env stringData: code4: "4567" secret4: "PassWd4" diff --git a/tests/examples/manifests/config-automount/default-configmap.yaml b/tests/examples/manifests/config-automount/default-configmap.yaml index 13b03a520b7..224e636c15b 100644 --- a/tests/examples/manifests/config-automount/default-configmap.yaml +++ b/tests/examples/manifests/config-automount/default-configmap.yaml @@ -3,7 +3,7 @@ kind: ConfigMap metadata: name: automount-default-configmap labels: - controller.devfile.io/mount-to-containers: "true" + devfile.io/auto-mount: "true" data: foo1: bar1 ping1: pong1 diff --git a/tests/examples/manifests/config-automount/default-pvc.yaml b/tests/examples/manifests/config-automount/default-pvc.yaml index 2062b6df6e8..2d196d536a7 100644 --- a/tests/examples/manifests/config-automount/default-pvc.yaml +++ b/tests/examples/manifests/config-automount/default-pvc.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: labels: - controller.devfile.io/mount-to-containers: "true" + devfile.io/auto-mount: "true" name: automount-default-pvc spec: accessModes: diff --git a/tests/examples/manifests/config-automount/default-secret.yaml b/tests/examples/manifests/config-automount/default-secret.yaml index 25d559bc8cd..6a6ee6bc550 100644 --- a/tests/examples/manifests/config-automount/default-secret.yaml +++ b/tests/examples/manifests/config-automount/default-secret.yaml @@ -3,7 +3,7 @@ kind: Secret metadata: name: automount-default-secret labels: - controller.devfile.io/mount-to-containers: "true" + devfile.io/auto-mount: "true" stringData: code1: "1234" secret1: "PassWd1" diff --git a/tests/examples/manifests/config-automount/mount-path-configmap.yaml b/tests/examples/manifests/config-automount/mount-path-configmap.yaml index 8dfb176ad75..1a04c869db6 100644 --- a/tests/examples/manifests/config-automount/mount-path-configmap.yaml +++ b/tests/examples/manifests/config-automount/mount-path-configmap.yaml @@ -3,9 +3,9 @@ kind: ConfigMap metadata: name: automount-mount-path-configmap labels: - controller.devfile.io/mount-to-containers: "true" + devfile.io/auto-mount: "true" annotations: - controller.devfile.io/mount-path: "/mnt/mount-path/configmap" + devfile.io/mount-path: "/mnt/mount-path/configmap" data: foo2: bar2 ping2: pong2 diff --git a/tests/examples/manifests/config-automount/mount-path-pvc.yaml b/tests/examples/manifests/config-automount/mount-path-pvc.yaml index 5c2d98b692a..157c5ff8701 100644 --- a/tests/examples/manifests/config-automount/mount-path-pvc.yaml +++ b/tests/examples/manifests/config-automount/mount-path-pvc.yaml @@ -2,9 +2,9 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: labels: - controller.devfile.io/mount-to-containers: "true" + devfile.io/auto-mount: "true" annotations: - controller.devfile.io/mount-path: "/mnt/mount-path/pvc" + devfile.io/mount-path: "/mnt/mount-path/pvc" name: automount-mount-path-pvc spec: accessModes: diff --git a/tests/examples/manifests/config-automount/mount-path-secret.yaml b/tests/examples/manifests/config-automount/mount-path-secret.yaml index 5a81c5c20e1..f812bc51365 100644 --- a/tests/examples/manifests/config-automount/mount-path-secret.yaml +++ b/tests/examples/manifests/config-automount/mount-path-secret.yaml @@ -3,9 +3,9 @@ kind: Secret metadata: name: automount-mount-path-secret labels: - controller.devfile.io/mount-to-containers: "true" + devfile.io/auto-mount: "true" annotations: - controller.devfile.io/mount-path: "/mnt/mount-path/secret" + devfile.io/mount-path: "/mnt/mount-path/secret" stringData: code2: "2345" secret2: "PassWd2" diff --git a/tests/examples/manifests/config-automount/readonly-pvc.yaml b/tests/examples/manifests/config-automount/readonly-pvc.yaml index 991ea3114dd..a368bfd872f 100644 --- a/tests/examples/manifests/config-automount/readonly-pvc.yaml +++ b/tests/examples/manifests/config-automount/readonly-pvc.yaml @@ -2,9 +2,9 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: labels: - controller.devfile.io/mount-to-containers: "true" + devfile.io/auto-mount: "true" annotations: - controller.devfile.io/read-only: "true" + devfile.io/read-only: "true" name: automount-readonly-pvc spec: accessModes: diff --git a/tests/examples/manifests/config-automount/subpath-configmap.yaml b/tests/examples/manifests/config-automount/subpath-configmap.yaml index 96b9ff98ceb..a2d446c6b86 100644 --- a/tests/examples/manifests/config-automount/subpath-configmap.yaml +++ b/tests/examples/manifests/config-automount/subpath-configmap.yaml @@ -3,10 +3,10 @@ kind: ConfigMap metadata: name: automount-subpath-configmap labels: - controller.devfile.io/mount-to-containers: "true" + devfile.io/auto-mount: "true" annotations: - controller.devfile.io/mount-path: "/mnt/subpaths" - controller.devfile.io/mount-as: subpath + devfile.io/mount-path: "/mnt/subpaths" + devfile.io/mount-as: subpath data: foo5: bar5 ping5: pong5 diff --git a/tests/examples/manifests/config-automount/subpath-secret.yaml b/tests/examples/manifests/config-automount/subpath-secret.yaml index ce82d77f7fd..c0e56a3f053 100644 --- a/tests/examples/manifests/config-automount/subpath-secret.yaml +++ b/tests/examples/manifests/config-automount/subpath-secret.yaml @@ -3,10 +3,10 @@ kind: Secret metadata: name: automount-subpath-secret labels: - controller.devfile.io/mount-to-containers: "true" + devfile.io/auto-mount: "true" annotations: - controller.devfile.io/mount-path: "/mnt/subpaths" - controller.devfile.io/mount-as: subpath + devfile.io/mount-path: "/mnt/subpaths" + devfile.io/mount-as: subpath stringData: code5: "5678" secret5: "PassWd5"