From 14d67d1ec72b69653d664dffa9bfed59ce66dba5 Mon Sep 17 00:00:00 2001 From: Oscar Zhou <100548325+oscarzhou-portainer@users.noreply.github.com> Date: Fri, 21 Nov 2025 12:44:42 +1300 Subject: [PATCH] fix(edgestack): external label on k8s application deployed by edgestack [BE-12318] (#1385) --- api/edge/edge.go | 7 +++++++ api/http/models/kubernetes/application.go | 1 + api/kubernetes/yaml.go | 24 +++++++++++++++++------ api/kubernetes/yaml_test.go | 11 +++++++++++ api/portainer.go | 4 ++++ 5 files changed, 41 insertions(+), 6 deletions(-) diff --git a/api/edge/edge.go b/api/edge/edge.go index e4acbaa69..f4128892f 100644 --- a/api/edge/edge.go +++ b/api/edge/edge.go @@ -70,6 +70,13 @@ type ( // ReadyRePullImage is a flag to indicate whether the auto update is trigger to re-pull image // Deprecated(2.36): use DeployerOptionsPayload.ForceRecreate instead ReadyRePullImage bool + + // CreatedBy is the username that created this stack + // Used for adding labels to Kubernetes manifests + CreatedBy string + // CreatedByUserId is the user ID that created this stack + // Used for adding labels to Kubernetes manifests + CreatedByUserId string } DeployerOptionsPayload struct { diff --git a/api/http/models/kubernetes/application.go b/api/http/models/kubernetes/application.go index d562ed334..e93121416 100644 --- a/api/http/models/kubernetes/application.go +++ b/api/http/models/kubernetes/application.go @@ -40,6 +40,7 @@ type K8sApplication struct { Resource K8sApplicationResource `json:"Resource,omitempty"` HorizontalPodAutoscaler *autoscalingv2.HorizontalPodAutoscaler `json:"HorizontalPodAutoscaler,omitempty" swaggerignore:"true"` CustomResourceMetadata CustomResourceMetadata `json:"CustomResourceMetadata,omitempty"` + StackKind string `json:"StackKind,omitempty"` } type Metadata struct { diff --git a/api/kubernetes/yaml.go b/api/kubernetes/yaml.go index 0210cff15..ee0526001 100644 --- a/api/kubernetes/yaml.go +++ b/api/kubernetes/yaml.go @@ -13,11 +13,13 @@ import ( ) const ( - labelPortainerAppStack = "io.portainer.kubernetes.application.stack" - labelPortainerAppStackID = "io.portainer.kubernetes.application.stackid" - labelPortainerAppName = "io.portainer.kubernetes.application.name" - labelPortainerAppOwner = "io.portainer.kubernetes.application.owner" - labelPortainerAppKind = "io.portainer.kubernetes.application.kind" + labelPortainerAppStack = "io.portainer.kubernetes.application.stack" + labelPortainerAppStackID = "io.portainer.kubernetes.application.stackid" + labelPortainerAppName = "io.portainer.kubernetes.application.name" + labelPortainerAppOwner = "io.portainer.kubernetes.application.owner" + labelPortainerAppOwnerId = "io.portainer.kubernetes.application.owner.id" + labelPortainerAppKind = "io.portainer.kubernetes.application.kind" + labelPortainerAppStackKind = "io.portainer.kubernetes.application.stackKind" ) // KubeAppLabels are labels applied to all resources deployed in a kubernetes stack @@ -25,18 +27,28 @@ type KubeAppLabels struct { StackID int StackName string Owner string + OwnerId string Kind string + StackKind string } // ToMap converts KubeAppLabels to a map[string]string func (kal *KubeAppLabels) ToMap() map[string]string { - return map[string]string{ + labels := map[string]string{ labelPortainerAppStackID: strconv.Itoa(kal.StackID), labelPortainerAppStack: stackutils.SanitizeLabel(kal.StackName), labelPortainerAppName: stackutils.SanitizeLabel(kal.StackName), labelPortainerAppOwner: stackutils.SanitizeLabel(kal.Owner), labelPortainerAppKind: kal.Kind, + labelPortainerAppOwnerId: kal.OwnerId, } + + // Add optional labels only if they are non-empty + if kal.StackKind != "" { + labels[labelPortainerAppStackKind] = kal.StackKind + } + + return labels } // GetHelmAppLabels returns the labels to be applied to portainer deployed helm applications diff --git a/api/kubernetes/yaml_test.go b/api/kubernetes/yaml_test.go index 9b68353f3..56935fa38 100644 --- a/api/kubernetes/yaml_test.go +++ b/api/kubernetes/yaml_test.go @@ -40,6 +40,7 @@ metadata: io.portainer.kubernetes.application.kind: git io.portainer.kubernetes.application.name: best-name io.portainer.kubernetes.application.owner: best-owner + io.portainer.kubernetes.application.owner.id: "" io.portainer.kubernetes.application.stack: best-name io.portainer.kubernetes.application.stackid: "123" name: busybox @@ -88,6 +89,7 @@ metadata: io.portainer.kubernetes.application.kind: git io.portainer.kubernetes.application.name: best-name io.portainer.kubernetes.application.owner: best-owner + io.portainer.kubernetes.application.owner.id: "" io.portainer.kubernetes.application.stack: best-name io.portainer.kubernetes.application.stackid: "123" name: busybox @@ -177,6 +179,7 @@ items: io.portainer.kubernetes.application.kind: git io.portainer.kubernetes.application.name: best-name io.portainer.kubernetes.application.owner: best-owner + io.portainer.kubernetes.application.owner.id: "" io.portainer.kubernetes.application.stack: best-name io.portainer.kubernetes.application.stackid: "123" name: web @@ -198,6 +201,7 @@ items: io.portainer.kubernetes.application.kind: git io.portainer.kubernetes.application.name: best-name io.portainer.kubernetes.application.owner: best-owner + io.portainer.kubernetes.application.owner.id: "" io.portainer.kubernetes.application.stack: best-name io.portainer.kubernetes.application.stackid: "123" name: redis @@ -221,6 +225,7 @@ items: io.portainer.kubernetes.application.kind: git io.portainer.kubernetes.application.name: best-name io.portainer.kubernetes.application.owner: best-owner + io.portainer.kubernetes.application.owner.id: "" io.portainer.kubernetes.application.stack: best-name io.portainer.kubernetes.application.stackid: "123" name: web @@ -303,6 +308,7 @@ metadata: io.portainer.kubernetes.application.kind: git io.portainer.kubernetes.application.name: best-name io.portainer.kubernetes.application.owner: best-owner + io.portainer.kubernetes.application.owner.id: "" io.portainer.kubernetes.application.stack: best-name io.portainer.kubernetes.application.stackid: "123" name: busybox @@ -329,6 +335,7 @@ metadata: io.portainer.kubernetes.application.kind: git io.portainer.kubernetes.application.name: best-name io.portainer.kubernetes.application.owner: best-owner + io.portainer.kubernetes.application.owner.id: "" io.portainer.kubernetes.application.stack: best-name io.portainer.kubernetes.application.stackid: "123" name: web @@ -348,6 +355,7 @@ metadata: io.portainer.kubernetes.application.kind: git io.portainer.kubernetes.application.name: best-name io.portainer.kubernetes.application.owner: best-owner + io.portainer.kubernetes.application.owner.id: "" io.portainer.kubernetes.application.stack: best-name io.portainer.kubernetes.application.stackid: "123" name: busybox @@ -397,6 +405,7 @@ metadata: io.portainer.kubernetes.application.kind: git io.portainer.kubernetes.application.name: best-name io.portainer.kubernetes.application.owner: best-owner + io.portainer.kubernetes.application.owner.id: "" io.portainer.kubernetes.application.stack: best-name io.portainer.kubernetes.application.stackid: "123" name: web @@ -619,6 +628,7 @@ metadata: io.portainer.kubernetes.application.kind: git io.portainer.kubernetes.application.name: best-name io.portainer.kubernetes.application.owner: best-owner + io.portainer.kubernetes.application.owner.id: "" io.portainer.kubernetes.application.stack: best-name io.portainer.kubernetes.application.stackid: "123" --- @@ -630,6 +640,7 @@ metadata: io.portainer.kubernetes.application.kind: git io.portainer.kubernetes.application.name: best-name io.portainer.kubernetes.application.owner: best-owner + io.portainer.kubernetes.application.owner.id: "" io.portainer.kubernetes.application.stack: best-name io.portainer.kubernetes.application.stackid: "123" ` diff --git a/api/portainer.go b/api/portainer.go index 0ffef85d9..bc94bfedd 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -347,6 +347,10 @@ type ( DeploymentType EdgeStackDeploymentType `json:"DeploymentType"` // Uses the manifest's namespaces instead of the default one UseManifestNamespaces bool + // The username id which created this stack + CreatedByUserId string `example:"1"` + // The username which created this stack + CreatedBy string `example:"admin"` } EdgeStackStatusForEnv struct {