Skip to content
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
36 changes: 35 additions & 1 deletion internal/adapters/out/runtime/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ func (a *Adapter) createContainer(ctx context.Context, spec ports.WorkloadSpec,
},
netConfig,
nil,
spec.ServerID,
containerName(spec),
)
if err != nil {
return "", fmt.Errorf("failed to create container: %w", err)
Expand Down Expand Up @@ -562,3 +562,37 @@ func shortID(s string) string {
}
return s
}

// containerName builds a human-readable container name in the form
// username.projectslug.servername, falling back gracefully when fields are absent.
func containerName(spec ports.WorkloadSpec) string {
var parts []string
if spec.OwnerUsername != "" {
parts = append(parts, sanitizeNamePart(spec.OwnerUsername))
}
if spec.ProjectSlug != "" {
parts = append(parts, sanitizeNamePart(spec.ProjectSlug))
}
if spec.ServerName != "" {
parts = append(parts, sanitizeNamePart(spec.ServerName))
}
if len(parts) == 0 {
return spec.ServerID
}
return strings.Join(parts, ".")
}

// sanitizeNamePart strips characters not allowed in Docker container names.
var nameReplacer = strings.NewReplacer(" ", "-", "@", "-", "/", "-")

func sanitizeNamePart(s string) string {
s = nameReplacer.Replace(strings.ToLower(strings.TrimSpace(s)))
var out []byte
for i := 0; i < len(s); i++ {
c := s[i]
if c >= 'a' && c <= 'z' || c >= '0' && c <= '9' || c == '-' || c == '_' || c == '.' {
out = append(out, c)
}
}
return strings.Trim(string(out), "-.")
}
12 changes: 7 additions & 5 deletions internal/application/ports/workload.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ type RunningServer struct {
// It is a superset of the old ServerOperationPayload — all existing fields carry over.
type WorkloadSpec struct {
// Identity / Tenancy
OwnerID string `json:"owner_id"`
ServerID string `json:"server_id"`
BlueprintID string `json:"blueprint_id"`
ProjectID string `json:"project_id"`
ProjectSlug string `json:"project_slug"`
OwnerID string `json:"owner_id"`
OwnerUsername string `json:"owner_username,omitempty"`
ServerID string `json:"server_id"`
ServerName string `json:"server_name,omitempty"`
BlueprintID string `json:"blueprint_id"`
ProjectID string `json:"project_id"`
ProjectSlug string `json:"project_slug"`

// Blueprint details required for provision/start
Image string `json:"image"`
Expand Down
17 changes: 11 additions & 6 deletions pkg/labels/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ const (
ManagedBy = "kleff.io/managed_by"

ManagedByValue = "kleff-daemon"

// ComposeProject is the standard Docker Compose label used by Portainer to group containers.
ComposeProject = "com.docker.compose.project"
ComposeProjectValue = "kleff-local"
)

type WorkloadLabels struct {
Expand All @@ -24,12 +28,13 @@ type WorkloadLabels struct {

func (l *WorkloadLabels) ToMap() map[string]string {
m := map[string]string{
OwnerID: l.OwnerID,
WorkloadID: l.ServerID, // new key
ServerID: l.ServerID, // deprecated alias — kept during transition
BlueprintID: l.BlueprintID,
NodeID: l.NodeID,
ManagedBy: ManagedByValue,
OwnerID: l.OwnerID,
WorkloadID: l.ServerID, // new key
ServerID: l.ServerID, // deprecated alias — kept during transition
BlueprintID: l.BlueprintID,
NodeID: l.NodeID,
ManagedBy: ManagedByValue,
ComposeProject: ComposeProjectValue,
}
if l.ProjectID != "" {
m[ProjectID] = l.ProjectID
Expand Down
Loading