diff --git a/pkg/library/home/persistentHome.go b/pkg/library/home/persistentHome.go index 5da933a9d..4ffe8c747 100644 --- a/pkg/library/home/persistentHome.go +++ b/pkg/library/home/persistentHome.go @@ -29,6 +29,8 @@ import ( // Returns a modified copy of the given DevWorkspace's Template Spec which contains an additional // Devfile volume 'persistentHome' that is mounted to `/home/user/` for every container component defined in the DevWorkspace. +// This feature assumes that the container image of the first merged container component in the unflattened devfile contains an +// /entrypoint.sh script that initializes persistentHome for the homeVolumeMount PVC. // An error is returned if the addition of the 'persistentHome' volume would result // in an invalid DevWorkspace. func AddPersistentHomeVolume(workspace *common.DevWorkspaceWithConfig) (*v1alpha2.DevWorkspaceTemplateSpec, error) { @@ -44,6 +46,24 @@ func AddPersistentHomeVolume(workspace *common.DevWorkspaceWithConfig) (*v1alpha Path: constants.HomeUserDirectory, } + var firstUnflattenedComponent v1alpha2.Component + for _, component := range dwTemplateSpecCopy.Components { + if component.Container == nil { + continue + } + + var err error + component.Attributes.Get(constants.MergedContributionsAttribute, &err) + if err == nil { + firstUnflattenedComponent = component + break + } + } + + if firstUnflattenedComponent.Name != "" { + addPersistentStorageInitContainer(dwTemplateSpecCopy, firstUnflattenedComponent.Container.Image) + } + dwTemplateSpecCopy.Components = append(dwTemplateSpecCopy.Components, homeVolume) for _, component := range dwTemplateSpecCopy.Components { if component.Container == nil { @@ -95,3 +115,32 @@ func storageStrategySupportsPersistentHome(workspace *common.DevWorkspaceWithCon storageClass := workspace.Spec.Template.Attributes.GetString(constants.DevWorkspaceStorageTypeAttribute, nil) return storageClass != constants.EphemeralStorageClassType } + +// Add an init container that runs /entrypoint.sh to initialize the persistent home directory. +// This function assumes that the provided image contains the /entrypoint.sh script. +// If the script is not present in the image, the init container will not fail the workspace from starting. +func addPersistentStorageInitContainer(dwTemplateSpecCopy *v1alpha2.DevWorkspaceTemplateSpec, image string) { + initContainerComponent := v1alpha2.Component{ + Name: "init-persistent-home", + ComponentUnion: v1alpha2.ComponentUnion{ + Container: &v1alpha2.ContainerComponent{ + Container: v1alpha2.Container{ + Image: image, + Command: []string{"/bin/sh", "-c", "/entrypoint.sh || true"}, + }, + }, + }, + } + + dwTemplateSpecCopy.Components = append(dwTemplateSpecCopy.Components, initContainerComponent) + + dwTemplateSpecCopy.Commands = append(dwTemplateSpecCopy.Commands, v1alpha2.Command{ + Id: "init-persistent-home", + CommandUnion: v1alpha2.CommandUnion{ + Apply: &v1alpha2.ApplyCommand{ + Component: "init-persistent-home", + }, + }, + }) + dwTemplateSpecCopy.Events.PreStart = append(dwTemplateSpecCopy.Events.PreStart, "init-persistent-home") +}