diff --git a/pkg/clouds/pulumi/kubernetes/simple_container.go b/pkg/clouds/pulumi/kubernetes/simple_container.go index f8463943..47d4ec5c 100644 --- a/pkg/clouds/pulumi/kubernetes/simple_container.go +++ b/pkg/clouds/pulumi/kubernetes/simple_container.go @@ -49,6 +49,15 @@ const ( LabelParentStack = "simple-container.com/parent-stack" LabelClientStack = "simple-container.com/client-stack" LabelCustomStack = "simple-container.com/custom-stack" + + // Standard Kubernetes labels (https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/) + LabelKubernetesName = "app.kubernetes.io/name" + LabelKubernetesInstance = "app.kubernetes.io/instance" + LabelKubernetesVersion = "app.kubernetes.io/version" + LabelKubernetesComponent = "app.kubernetes.io/component" + LabelKubernetesPartOf = "app.kubernetes.io/part-of" + LabelKubernetesManagedBy = "app.kubernetes.io/managed-by" + LabelKubernetesCreatedBy = "app.kubernetes.io/created-by" ) // sanitizeK8sResourceName converts a name to be RFC 1123 compliant for Kubernetes resources @@ -144,6 +153,13 @@ func NewSimpleContainer(ctx *sdk.Context, args *SimpleContainerArgs, opts ...sdk LabelScEnv: args.ScEnv, } + // Add standard Kubernetes labels + appLabels[LabelKubernetesName] = sanitizedService + appLabels[LabelKubernetesInstance] = sanitizedDeployment + appLabels[LabelKubernetesPartOf] = "simple-container" + appLabels[LabelKubernetesManagedBy] = "simple-container" + appLabels[LabelKubernetesCreatedBy] = "simple-container-cli" + // Add parentEnv labels for custom stacks if args.ParentEnv != nil && lo.FromPtr(args.ParentEnv) != "" && lo.FromPtr(args.ParentEnv) != args.ScEnv { appLabels[LabelParentEnv] = lo.FromPtr(args.ParentEnv) @@ -182,6 +198,15 @@ func NewSimpleContainer(ctx *sdk.Context, args *SimpleContainerArgs, opts ...sdk appAnnotations[k] = v } + // Copy standard Kubernetes labels from annotations if provided + // This allows users to specify version and component via annotations + if version, ok := appAnnotations["app.kubernetes.io/version"]; ok { + appLabels[LabelKubernetesVersion] = version + } + if component, ok := appAnnotations["app.kubernetes.io/component"]; ok { + appLabels[LabelKubernetesComponent] = component + } + // Namespace // Sanitize namespace name to comply with Kubernetes RFC 1123 requirements sanitizedNamespace := sanitizeK8sName(args.Namespace) @@ -458,6 +483,17 @@ func NewSimpleContainer(ctx *sdk.Context, args *SimpleContainerArgs, opts ...sdk }, } } + // Use minimal, stable labels for selector to avoid breaking deployments when labels are added + selectorLabels := map[string]string{ + LabelAppType: appLabels[LabelAppType], + LabelAppName: appLabels[LabelAppName], + LabelScEnv: appLabels[LabelScEnv], + } + // Add parent-stack label to selector if present for proper multi-stack isolation + if val, ok := appLabels[LabelParentStack]; ok { + selectorLabels[LabelParentStack] = val + } + deployment, err := v1.NewDeployment(ctx, sanitizedDeployment, &v1.DeploymentArgs{ Metadata: &metav1.ObjectMetaArgs{ Name: sdk.String(sanitizedDeployment), @@ -469,7 +505,7 @@ func NewSimpleContainer(ctx *sdk.Context, args *SimpleContainerArgs, opts ...sdk Strategy: strategy, Replicas: sdk.Int(args.Replicas), Selector: &metav1.LabelSelectorArgs{ - MatchLabels: sdk.ToStringMap(appLabels), + MatchLabels: sdk.ToStringMap(selectorLabels), }, Template: &corev1.PodTemplateSpecArgs{ Metadata: &metav1.ObjectMetaArgs{ @@ -565,7 +601,7 @@ ${proto}://${domain} { Annotations: sdk.ToStringMap(serviceAnnotations), }, Spec: &corev1.ServiceSpecArgs{ - Selector: sdk.ToStringMap(appLabels), + Selector: sdk.ToStringMap(selectorLabels), Ports: servicePorts, Type: serviceType, }, @@ -623,7 +659,7 @@ ${proto}://${domain} { if args.PodDisruption != nil { pdbArgs := policyv1.PodDisruptionBudgetSpecArgs{ Selector: &metav1.LabelSelectorArgs{ - MatchLabels: sdk.ToStringMap(appLabels), + MatchLabels: sdk.ToStringMap(selectorLabels), }, } if args.PodDisruption.MinAvailable != nil {