-
Notifications
You must be signed in to change notification settings - Fork 54
/
mountSources.go
145 lines (130 loc) · 5.28 KB
/
mountSources.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//
// Copyright (c) 2019-2024 Red Hat, Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package container
import (
"path"
dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
corev1 "k8s.io/api/core/v1"
"github.com/devfile/devworkspace-operator/pkg/constants"
devfileConstants "github.com/devfile/devworkspace-operator/pkg/library/constants"
projectslib "github.com/devfile/devworkspace-operator/pkg/library/projects"
)
// HasMountSources evaluates whether project sources should be mounted in the given container component.
// MountSources is by default true for non-plugin components, unless they have dedicatedPod set
// TODO:
// - Support dedicatedPod field
// - Find way to track is container component comes from plugin
func HasMountSources(devfileContainer *dw.ContainerComponent) bool {
var mountSources bool
if devfileContainer.MountSources == nil {
mountSources = true
} else {
mountSources = *devfileContainer.MountSources
}
return mountSources
}
// AnyMountSources checks HasMountSources for each container component in a devfile. If a component in the slice
// is not a ContainerComponent, it is ignored.
func AnyMountSources(devfileComponents []dw.Component) bool {
for _, component := range devfileComponents {
if component.Container != nil && HasMountSources(component.Container) {
return true
}
}
return false
}
// handleMountSources adds a volumeMount to a container if the corresponding devfile container has
// mountSources enabled.
func handleMountSources(k8sContainer *corev1.Container, devfileContainer *dw.ContainerComponent, workspace *dw.DevWorkspaceTemplateSpec) error {
if !HasMountSources(devfileContainer) {
return nil
}
var sourceMapping string
if vm := getProjectsVolumeMount(k8sContainer); vm != nil {
// Container already mounts projects volume; need to set env vars according to mountPath
// TODO: see issue https://github.com/devfile/api/issues/290
sourceMapping = vm.MountPath
} else {
sourceMapping = devfileContainer.SourceMapping
if sourceMapping == "" {
// Sanity check -- this value should be defaulted to `/projects` but may not be
// if struct was not processed by k8s
sourceMapping = constants.DefaultProjectsSourcesRoot
}
k8sContainer.VolumeMounts = append(k8sContainer.VolumeMounts, corev1.VolumeMount{
Name: devfileConstants.ProjectsVolumeName,
MountPath: sourceMapping,
})
}
projectsSourcePath, err := getProjectSourcePath(workspace)
if err != nil {
return err
}
// The $PROJECT_ROOT and $PROJECT_SOURCE environment variables must be the first
// environment variables defined in the container so that other environment variables can reference them.
k8sContainer.Env = append([]corev1.EnvVar{
{
Name: devfileConstants.ProjectsRootEnvVar,
Value: sourceMapping,
},
{
Name: devfileConstants.ProjectsSourceEnvVar,
Value: path.Join(sourceMapping, projectsSourcePath),
}}, k8sContainer.Env...)
return nil
}
// getProjectSourcePath gets the path, relative to PROJECTS_ROOT, that should be used for the PROJECT_SOURCE env var.
// Returns an error if there was a problem retrieving the selected starter project.
//
// The project source path is determined based on the following priorities:
//
// 1. If the workspace has at least one regular project, the first one will be selected.
// If the first project has a clone path, it will be used, otherwise the project's name will be used as the project source path.
//
// 2. If the workspace has a starter project that is selected, its name will be used as the project source path.
//
// 3. If the workspace has any dependentProjects, the first one will be selected.
//
// 4. Otherwise, the returned project source path will be an empty string.
func getProjectSourcePath(workspace *dw.DevWorkspaceTemplateSpec) (string, error) {
projects := workspace.Projects
// If there are any projects, return the first one's clone path
if len(projects) > 0 {
return projectslib.GetClonePath(&projects[0]), nil
}
// No projects, check if we have a selected starter project
selectedStarterProject, err := projectslib.GetStarterProject(workspace)
if err != nil {
return "", err
} else if selectedStarterProject != nil {
// Starter projects do not allow specifying a clone path, so use the name
return selectedStarterProject.Name, nil
}
// Finally, check if there are any dependent projects
if len(workspace.DependentProjects) > 0 {
return projectslib.GetClonePath(&workspace.DependentProjects[0]), nil
}
return "", nil
}
// getProjectsVolumeMount returns the projects volumeMount in a container, if it is defined; if it does not exist,
// returns nil.
func getProjectsVolumeMount(k8sContainer *corev1.Container) *corev1.VolumeMount {
for _, vm := range k8sContainer.VolumeMounts {
if vm.Name == devfileConstants.ProjectsVolumeName {
return &vm
}
}
return nil
}