Skip to content

Commit

Permalink
Validate that projects and dependentProjects do not collide
Browse files Browse the repository at this point in the history
Add verification step when starting workspace to make sure that no two
projects (/dependentProjects/starterProjects) share the same name nor
have the same clone path.

Signed-off-by: Angel Misevski <[email protected]>
  • Loading branch information
amisevsk committed Nov 28, 2023
1 parent 53eef37 commit 5299844
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 0 deletions.
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
45 changes: 45 additions & 0 deletions 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,6 +42,50 @@ 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.Projects[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))
}

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 {
Expand Down

0 comments on commit 5299844

Please sign in to comment.