You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by la...@apache.org on 2026/06/03 00:24:54 UTC

(airavata-custos) 02/04: throw an error when the first and last names are empty when building the cluster username

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

lahirujayathilake pushed a commit to branch provisioner-integration
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit 14d9e76b64ffe92b54306c9763746f6b9db6df89
Author: lahiruj <la...@gmail.com>
AuthorDate: Tue Jun 2 17:58:45 2026 -0400

    throw an error when the first and last names are empty when building the cluster username
---
 .../ACCESS/AMIE-Processor/config.yaml.example      |  3 --
 connectors/ACCESS/AMIE-Processor/config/config.go  | 16 +++--------
 pkg/posix/username.go                              | 16 +++++++----
 pkg/posix/username_test.go                         | 33 ++++++++++++++++++++--
 4 files changed, 44 insertions(+), 24 deletions(-)

diff --git a/connectors/ACCESS/AMIE-Processor/config.yaml.example b/connectors/ACCESS/AMIE-Processor/config.yaml.example
index af0149ffd..528c9039f 100644
--- a/connectors/ACCESS/AMIE-Processor/config.yaml.example
+++ b/connectors/ACCESS/AMIE-Processor/config.yaml.example
@@ -19,6 +19,3 @@ amie:
 log:
   level: "info"
   format: "text"
-
-provisioner:
-  type: "noop"
diff --git a/connectors/ACCESS/AMIE-Processor/config/config.go b/connectors/ACCESS/AMIE-Processor/config/config.go
index 7daa093c7..a365aae1e 100644
--- a/connectors/ACCESS/AMIE-Processor/config/config.go
+++ b/connectors/ACCESS/AMIE-Processor/config/config.go
@@ -27,11 +27,10 @@ import (
 )
 
 type Config struct {
-	Server      ServerConfig      `yaml:"server"`
-	Database    DatabaseConfig    `yaml:"database"`
-	AMIE        AMIEConfig        `yaml:"amie"`
-	Log         LogConfig         `yaml:"log"`
-	Provisioner ProvisionerConfig `yaml:"provisioner"`
+	Server   ServerConfig   `yaml:"server"`
+	Database DatabaseConfig `yaml:"database"`
+	AMIE     AMIEConfig     `yaml:"amie"`
+	Log      LogConfig      `yaml:"log"`
 }
 
 type ServerConfig struct {
@@ -60,10 +59,6 @@ type LogConfig struct {
 	Format string `yaml:"format"` // "text" or "json"
 }
 
-type ProvisionerConfig struct {
-	Type string `yaml:"type"` // "noop" or "slurm"
-}
-
 // Load reads config from a YAML file and applies environment variable overrides.
 func Load(path string) (*Config, error) {
 	data, err := os.ReadFile(path)
@@ -113,9 +108,6 @@ func applyDefaults(cfg *Config) {
 	if cfg.Log.Format == "" {
 		cfg.Log.Format = "text"
 	}
-	if cfg.Provisioner.Type == "" {
-		cfg.Provisioner.Type = "noop"
-	}
 }
 
 func applyEnvOverrides(cfg *Config) {
diff --git a/pkg/posix/username.go b/pkg/posix/username.go
index 64b00b5b8..e5fbd34c9 100644
--- a/pkg/posix/username.go
+++ b/pkg/posix/username.go
@@ -20,6 +20,8 @@
 package posix
 
 import (
+	"errors"
+	"fmt"
 	"os"
 	"strings"
 	"unicode"
@@ -29,9 +31,11 @@ import (
 
 const MaxCollisionSuffix = 999
 
-// BuildBase returns the unsuffixed username and a flag set when the name
-// portion was truncated to fit the 32-char Unix login limit.
-func BuildBase(u *models.User, prefix string) (string, bool) {
+var ErrUnbuildableUsername = errors.New("posix: cannot build username from empty first and last name")
+
+// 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) {
 	first := Normalize(u.FirstName)
 	last := Normalize(u.LastName)
 
@@ -44,17 +48,17 @@ func BuildBase(u *models.User, prefix string) (string, bool) {
 	case first != "":
 		local = first
 	default:
-		local = "user"
+		return "", false, fmt.Errorf("%w: user %q (first=%q last=%q)", ErrUnbuildableUsername, u.ID, u.FirstName, u.LastName)
 	}
 
-	// Reserve 3 chars for a numeric collision suffix (up to "999").
+	// 32 = POSIX login cap; -1 separator, -3 reserved for collision suffix (up to "999").
 	maxLocal := 32 - len(prefix) - 1 - 3
 	truncated := false
 	if len(local) > maxLocal {
 		local = local[:maxLocal]
 		truncated = true
 	}
-	return prefix + "-" + local, truncated
+	return prefix + "-" + local, truncated, nil
 }
 
 func Normalize(s string) string {
diff --git a/pkg/posix/username_test.go b/pkg/posix/username_test.go
index a8601dad2..57b4f28f4 100644
--- a/pkg/posix/username_test.go
+++ b/pkg/posix/username_test.go
@@ -18,6 +18,7 @@
 package posix
 
 import (
+	"errors"
 	"strings"
 	"testing"
 
@@ -55,8 +56,7 @@ func TestBuildBase(t *testing.T) {
 			wantBase: "custos-alice", wantTrunc: false,
 		},
 		{
-			name: "non-ASCII stripped",
-			// "Aña" normalizes to "aa"; first letter 'a' + "kili" (from "Şəkili")
+			name:  "non-ASCII stripped",
 			first: "Aña", last: "Şəkili", prefix: "custos",
 			wantBase: "custos-akili", wantTrunc: false,
 		},
@@ -75,7 +75,10 @@ func TestBuildBase(t *testing.T) {
 	for _, tc := range tests {
 		t.Run(tc.name, func(t *testing.T) {
 			u := &models.User{FirstName: tc.first, MiddleName: tc.middle, LastName: tc.last}
-			got, trunc := BuildBase(u, tc.prefix)
+			got, trunc, err := BuildBase(u, tc.prefix)
+			if err != nil {
+				t.Fatalf("BuildBase: %v", err)
+			}
 
 			if trunc != tc.wantTrunc {
 				t.Errorf("truncated = %v, want %v", trunc, tc.wantTrunc)
@@ -93,6 +96,30 @@ func TestBuildBase(t *testing.T) {
 	}
 }
 
+func TestBuildBase_UnbuildableReturnsError(t *testing.T) {
+	cases := []struct {
+		name  string
+		first string
+		last  string
+	}{
+		{"both empty", "", ""},
+		{"both non-ASCII only", "李", "王"},
+		{"both punctuation only", "...", "---"},
+	}
+	for _, tc := range cases {
+		t.Run(tc.name, func(t *testing.T) {
+			u := &models.User{ID: "u-1", FirstName: tc.first, LastName: tc.last}
+			got, _, err := BuildBase(u, "custos")
+			if !errors.Is(err, ErrUnbuildableUsername) {
+				t.Fatalf("err = %v, want ErrUnbuildableUsername", err)
+			}
+			if got != "" {
+				t.Errorf("expected empty username on error, got %q", got)
+			}
+		})
+	}
+}
+
 func TestNormalize(t *testing.T) {
 	tests := []struct {
 		in   string