Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for dependentProjects in devfiles #1205

Merged
merged 2 commits into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions controllers/workspace/devworkspace_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,10 @@ func (r *DevWorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request
return r.failWorkspace(workspace, fmt.Sprintf("Failed to process workspace environment variables: %s", err), metrics.ReasonBadRequest, reqLogger, &reconcileStatus), nil
}

// Validate that projects, dependentProjects, and starterProjects do not collide
if err := projects.ValidateAllProjects(&workspace.Spec.Template); err != nil {
return r.failWorkspace(workspace, fmt.Sprintf("Invalid devfile: %s", err), metrics.ReasonBadRequest, reqLogger, &reconcileStatus), nil
}
// Add init container to clone projects
projectCloneOptions := projects.Options{
Image: workspace.Config.Workspace.ProjectCloneConfig.Image,
Expand Down
6 changes: 6 additions & 0 deletions pkg/library/annotate/plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,10 @@ func AddSourceAttributesForTemplate(sourceID string, template *dw.DevWorkspaceTe
}
template.StarterProjects[idx].Attributes.PutString(constants.PluginSourceAttribute, sourceID)
}
for idx, project := range template.DependentProjects {
if project.Attributes == nil {
template.DependentProjects[idx].Attributes = attributes.Attributes{}
}
template.DependentProjects[idx].Attributes.PutString(constants.PluginSourceAttribute, sourceID)
}
}
10 changes: 9 additions & 1 deletion pkg/library/container/mountSources.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,9 @@ func handleMountSources(k8sContainer *corev1.Container, devfileContainer *dw.Con
//
// 2. If the workspace has a starter project that is selected, its name will be used as the project source path.
//
// 3. Otherwise, the returned project source path will be an empty string.
// 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
Expand All @@ -118,6 +120,12 @@ func getProjectSourcePath(workspace *dw.DevWorkspaceTemplateSpec) (string, error
// 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
}

Expand Down
2 changes: 2 additions & 0 deletions pkg/library/defaults/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ func ApplyDefaultTemplate(workspace *common.DevWorkspaceWithConfig) {
}
defaultCopy := workspace.Config.Workspace.DefaultTemplate.DeepCopy()
originalProjects := workspace.Spec.Template.Projects
originalDependentProjects := workspace.Spec.Template.DependentProjects
workspace.Spec.Template.DevWorkspaceTemplateSpecContent = *defaultCopy
workspace.Spec.Template.Projects = append(workspace.Spec.Template.Projects, originalProjects...)
workspace.Spec.Template.DependentProjects = append(workspace.Spec.Template.DependentProjects, originalDependentProjects...)
}

func NeedsDefaultTemplate(workspace *common.DevWorkspaceWithConfig) bool {
Expand Down
47 changes: 46 additions & 1 deletion pkg/library/projects/clone.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package projects

import (
"fmt"
"strings"

dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
controllerv1alpha1 "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
Expand All @@ -41,12 +42,56 @@ type Options struct {
Env []corev1.EnvVar
}

// ValidateAllProjectsvalidates that no two projects, dependentProjects or starterProjects (if one is selected) share
// the same name or cloned path
func ValidateAllProjects(workspace *dw.DevWorkspaceTemplateSpec) error {
// Map of project names to project sources (project, dependentProject, starterProject)
projectNames := map[string][]string{}
// Map of project clone paths to project sources ("project <name>", "starterProject <name>", "dependentProject <name>")
clonePaths := map[string][]string{}

for idx, project := range workspace.Projects {
projectNames[project.Name] = append(projectNames[project.Name], "projects")
clonePath := GetClonePath(&workspace.Projects[idx])
clonePaths[clonePath] = append(clonePaths[clonePath], fmt.Sprintf("project %s", project.Name))
}

for idx, project := range workspace.DependentProjects {
projectNames[project.Name] = append(projectNames[project.Name], "dependentProjects")
clonePath := GetClonePath(&workspace.DependentProjects[idx])
clonePaths[clonePath] = append(clonePaths[clonePath], fmt.Sprintf("dependentProject %s", project.Name))
}

starterProject, err := GetStarterProject(workspace)
if err != nil {
return err
}
if starterProject != nil {
projectNames[starterProject.Name] = append(projectNames[starterProject.Name], "starterProjects")
// Starter projects do not have a clonePath field
clonePaths[starterProject.Name] = append(clonePaths[starterProject.Name], fmt.Sprintf("starterProject %s", starterProject.Name))
AObuchow marked this conversation as resolved.
Show resolved Hide resolved
}

for projectName, projectTypes := range projectNames {
if len(projectTypes) > 1 {
return fmt.Errorf("found multiple projects with the same name '%s' in: %s", projectName, strings.Join(projectTypes, ", "))
}
}
for clonePath, projects := range clonePaths {
if len(projects) > 1 {
return fmt.Errorf("found multiple projects with the same clone path (%s): %s", clonePath, strings.Join(projects, ", "))
}
}

return nil
}

func GetProjectCloneInitContainer(workspace *dw.DevWorkspaceTemplateSpec, options Options, proxyConfig *controllerv1alpha1.Proxy) (*corev1.Container, error) {
starterProject, err := GetStarterProject(workspace)
if err != nil {
return nil, err
}
if len(workspace.Projects) == 0 && starterProject == nil {
if len(workspace.Projects) == 0 && len(workspace.DependentProjects) == 0 && starterProject == nil {
return nil, nil
}
if workspace.Attributes.GetString(constants.ProjectCloneAttribute, nil) == constants.ProjectCloneDisable {
Expand Down
1 change: 1 addition & 0 deletions project-clone/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func main() {
}

projects := workspace.Projects
projects = append(projects, workspace.DependentProjects...)

starterProject, err := projectslib.GetStarterProject(workspace)
if err != nil {
Expand Down
8 changes: 8 additions & 0 deletions webhook/workspace/handler/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func (h *WebhookHandler) ValidateDevfile(ctx context.Context, req admission.Requ
events := workspace.Events
projects := workspace.Projects
starterProjects := workspace.StarterProjects
dependentProjects := workspace.DependentProjects

var devfileErrors []string

Expand All @@ -59,6 +60,13 @@ func (h *WebhookHandler) ValidateDevfile(ctx context.Context, req admission.Requ
}
}

if dependentProjects != nil {
dependentProjectsErrors := devfilevalidation.ValidateProjects(dependentProjects)
if dependentProjectsErrors != nil {
devfileErrors = append(devfileErrors, dependentProjectsErrors.Error())
}
}

// validate starter projects
if starterProjects != nil {
starterProjectErrors := devfilevalidation.ValidateStarterProjects(starterProjects)
Expand Down
Loading