You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ya...@apache.org on 2026/10/19 05:15:21 UTC

(airavata-custos) 01/01: use origin-seq form for project folders

This is an automated email from the ASF dual-hosted git repository.

yasithdev pushed a commit to branch project-folder-name
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit d4d0af8e7c0e1e2f85df9efbd5fd48f46bdba4e3
Author: Yasith Jayawardana <ya...@icloud.com>
AuthorDate: Mon Oct 19 01:14:59 2026 -0400

    use origin-seq form for project folders
---
 internal/server/admin_project_integration_test.go    |  2 +-
 internal/server/project.go                           | 10 ++--------
 pkg/posix/username.go                                |  6 ++++++
 pkg/service/compute_allocation.go                    |  7 ++-----
 pkg/service/compute_cluster_user_integration_test.go | 11 ++++++-----
 5 files changed, 17 insertions(+), 19 deletions(-)

diff --git a/internal/server/admin_project_integration_test.go b/internal/server/admin_project_integration_test.go
index 5b63ace0a..1e83ae78b 100644
--- a/internal/server/admin_project_integration_test.go
+++ b/internal/server/admin_project_integration_test.go
@@ -61,7 +61,7 @@ func TestAdminProject_CreateAssignRoleAndBlockPIChange(t *testing.T) {
 	}
 	var got ProjectResponse
 	_ = json.NewDecoder(doGet(t, srv, "/projects/"+created.ID, "admin", models.ProjectsRead).Body).Decode(&got)
-	if got.Ref != "INTERNAL-"+created.ID[:8] {
+	if got.Ref != "internal-"+created.ID[:8] {
 		t.Fatalf("ref: %q", got.Ref)
 	}
 	if roles := projectRoles(t, srv, created.ID); len(roles) != 1 || roles[f.piID] != "PI" {
diff --git a/internal/server/project.go b/internal/server/project.go
index 0221353d3..137b94e82 100644
--- a/internal/server/project.go
+++ b/internal/server/project.go
@@ -18,7 +18,6 @@
 package server
 
 import (
-	"cmp"
 	"errors"
 	"net/http"
 	"strings"
@@ -27,6 +26,7 @@ import (
 	"github.com/apache/airavata-custos/pkg/common"
 	"github.com/apache/airavata-custos/pkg/identity"
 	"github.com/apache/airavata-custos/pkg/models"
+	"github.com/apache/airavata-custos/pkg/posix"
 )
 
 type updateProjectRequest struct {
@@ -79,7 +79,7 @@ func (s *Server) getProject(w http.ResponseWriter, r *http.Request) {
 
 // projectResponseFrom builds the API response shape from a JOIN-fetched row.
 func projectResponseFrom(p *store.ProjectWithPI) ProjectResponse {
-	v := ProjectResponse{Project: p.Project, ProjectPIEmail: p.PIEmail, Ref: projectRef(p.Project)}
+	v := ProjectResponse{Project: p.Project, ProjectPIEmail: p.PIEmail, Ref: posix.ProjectRef(p.Project)}
 	if full := strings.TrimSpace(p.PIFirstName + " " + p.PILastName); full != "" {
 		v.ProjectPIDisplayName = full
 	} else if p.PIEmail != "" {
@@ -88,12 +88,6 @@ func projectResponseFrom(p *store.ProjectWithPI) ProjectResponse {
 	return v
 }
 
-// projectRef names a project by its source, type first: ACCESS-TG-CIS230001, or INTERNAL-3f2a9c1e for an
-// internal project, which has no originated id.
-func projectRef(p models.Project) string {
-	return strings.ToUpper(p.Origination) + "-" + cmp.Or(p.OriginatedID, p.ID[:min(8, len(p.ID))])
-}
-
 // @Summary	Update a project's status
 // @Tags	Projects
 // @Security	BearerAuth
diff --git a/pkg/posix/username.go b/pkg/posix/username.go
index 2e60f5e9f..9bc515e7a 100644
--- a/pkg/posix/username.go
+++ b/pkg/posix/username.go
@@ -20,6 +20,7 @@
 package posix
 
 import (
+	"cmp"
 	"errors"
 	"fmt"
 	"os"
@@ -36,6 +37,11 @@ const MaxLoginLen = 32
 
 var ErrUnbuildableUsername = errors.New("posix: cannot build username from empty first and last name")
 
+// ProjectRef names a project by its source and id, e.g., ACCESS + TG-CIS230001 -> access-tgcis230001.
+func ProjectRef(p models.Project) string {
+	return strings.ToLower(p.Origination) + "-" + Normalize(cmp.Or(p.OriginatedID, p.ID[:min(8, len(p.ID))]))
+}
+
 // BuildBase returns the unsuffixed username. 'truncated' is set when the name
 // portion was shortened to fit the 32-char POSIX login cap.
 func BuildBase(u *models.User, prefix string) (string, bool, error) {
diff --git a/pkg/service/compute_allocation.go b/pkg/service/compute_allocation.go
index e30a266b8..ce10c71b6 100644
--- a/pkg/service/compute_allocation.go
+++ b/pkg/service/compute_allocation.go
@@ -18,7 +18,6 @@
 package service
 
 import (
-	"cmp"
 	"context"
 	"database/sql"
 	"fmt"
@@ -96,8 +95,7 @@ func (s *Service) CreateComputeAllocation(ctx context.Context, alloc *models.Com
 		return nil, fmt.Errorf("%w: compute cluster %q not found", ErrInvalidInput, alloc.ComputeClusterID)
 	}
 
-	// Named after the project, remains same through renewals.
-	name := cmp.Or(posix.Normalize(proj.OriginatedID), posix.Normalize(proj.ID))
+	// Named after the project's ref, the name the portal shows, so it remains the same through renewals.
 	siblings, err := s.allocs.FindByCluster(ctx, alloc.ComputeClusterID)
 	if err != nil {
 		return nil, fmt.Errorf("lookup cluster allocations: %w", err)
@@ -108,8 +106,7 @@ func (s *Service) CreateComputeAllocation(ctx context.Context, alloc *models.Com
 			taken[*a.PosixGroup] = true
 		}
 	}
-	// Normalized names never contain "-", so a "-N" suffix cannot meet another allocation's base name.
-	base := "proj-" + name
+	base := posix.ProjectRef(*proj)
 	group := base[:min(len(base), posix.MaxLoginLen)]
 	for n := 2; taken[group]; n++ {
 		suffix := "-" + strconv.Itoa(n)
diff --git a/pkg/service/compute_cluster_user_integration_test.go b/pkg/service/compute_cluster_user_integration_test.go
index 1016634de..6dd6fa63d 100644
--- a/pkg/service/compute_cluster_user_integration_test.go
+++ b/pkg/service/compute_cluster_user_integration_test.go
@@ -24,6 +24,7 @@ import (
 	"errors"
 	"fmt"
 	"slices"
+	"strings"
 	"sync"
 	"testing"
 	"time"
@@ -32,7 +33,6 @@ import (
 	"github.com/jmoiron/sqlx"
 
 	"github.com/apache/airavata-custos/pkg/models"
-	"github.com/apache/airavata-custos/pkg/posix"
 )
 
 func TestMarkComputeClusterUserProvisioned_RoundTrip(t *testing.T) {
@@ -424,9 +424,10 @@ func TestCreateComputeAllocation_SuffixesACollidingPosixGroup(t *testing.T) {
 		t.Fatalf("create cluster: %v", err)
 	}
 	pi := seedUser(t, database, fmt.Sprintf("pi-%s@example.edu", uuid.NewString()))
-	grant := uuid.NewString()[:8]
+	// Refs that agree up to the 32-character limit collide once cut.
+	grant := "CIS" + uuid.NewString()[:8] + strings.Repeat("X", 16)
 	var groups []string
-	for _, originated := range []string{"CIS-" + grant, "cis" + grant, "CIS" + grant, ""} {
+	for _, originated := range []string{grant + "a", grant + "b", grant + "c", ""} {
 		project, err := svc.CreateProject(ctx(), &models.Project{Title: "grp", Origination: "TEST", ProjectPIID: pi, Status: models.ProjectActive, OriginatedID: originated})
 		if err != nil {
 			t.Fatalf("create project: %v", err)
@@ -438,11 +439,11 @@ func TestCreateComputeAllocation_SuffixesACollidingPosixGroup(t *testing.T) {
 			t.Fatalf("create allocation for %q: %v", originated, err)
 		}
 		groups = append(groups, *alloc.PosixGroup)
-		if originated == "" && groups[3] != ("proj-" + posix.Normalize(project.ID))[:posix.MaxLoginLen] {
+		if originated == "" && groups[3] != "test-"+project.ID[:8] {
 			t.Fatalf("group %s, want one named after project id %s", groups[3], project.ID)
 		}
 	}
-	if want := "proj-cis" + grant; !slices.Equal(groups[:3], []string{want, want + "-2", want + "-3"}) {
+	if want := "test-" + strings.ToLower(grant); !slices.Equal(groups[:3], []string{want, want[:30] + "-2", want[:30] + "-3"}) {
 		t.Fatalf("groups %v, want %s, -2, -3", groups, want)
 	}
 }