diff --git a/Justfile b/Justfile index e2aae4b5..caff4bb2 100644 --- a/Justfile +++ b/Justfile @@ -71,11 +71,14 @@ symlink-binaries: binlocal="{{ justfile_directory() }}/.local/bin" mkdir -p $binlocal - # Create symlinks only if they don't already exist + # Create or update symlinks, skipping binaries in our own .local/bin to avoid circular references for binary in aws az pulumi; do - if [ ! -e "$binlocal/$binary" ]; then - ln -sf "$(which $binary)" "$binlocal/$binary" + target="$(PATH="${PATH//$binlocal:/}" which "$binary" 2>/dev/null)" + if [ -z "$target" ]; then + printf 'WARNING: %s not found in PATH (excluding %s), skipping\n' "$binary" "$binlocal" >&2 + continue fi + ln -sf "$target" "$binlocal/$binary" done install-thumbprint: @@ -195,6 +198,7 @@ test-e2e URL="": cli: mkdir -p .local/bin goreleaser build --single-target --snapshot --clean -o .local/bin/ptd + @codesign --force --sign - .local/bin/ptd 2>/dev/null ############################################################################# # Check targets diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 13bc2b36..4a5f262b 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -169,6 +169,38 @@ spec: positronConfig: sessionTimeoutKillHours: 4 + # Session label injection — presence of this block enables the feature. + # The controller reads a field from each session pod, matches entries against + # searchRegex, and writes one numbered label per match (user-group-1, user-group-2, …). + # See docs/team-operator/README.md for full details. + sessionLabels: + # Dot-path into the pod spec identifying the field with the comma-separated group list. + # Supports array index notation. Default: "spec.containers[0].args" + sourceField: "spec.containers[0].args" + + # Sub-key used to extract the value from the resolved field: + # args slice → flag name whose next element is the value + # map field → map key to look up (e.g. annotation or label key) + # string field → ignored; field value used directly + # Default: "--container-user-groups" + sourceKey: "--container-user-groups" + + # Regex applied to each comma-separated entry. Only matching entries become labels. + # Default: "_entra_[^ ,]+" + searchRegex: "_entra_[^ ,]+" + + # Prefix stripped from the start of each matched entry before it becomes the label value. + # Default: "_" + trimPrefix: "_" + + # Prefix for generated label keys. Default "user-group-" produces user-group-1, user-group-2, … + labelKeyPrefix: "user-group-" + + # When true, forces re-labeling of all existing session pods for this site. + # Use after changing searchRegex or trimPrefix to update already-processed pods. + # Set back to false (or omit) once done. Default: false + reprocess: false + # Posit Connect (Publishing) connect: replicas: 2 diff --git a/docs/team-operator/README.md b/docs/team-operator/README.md index f1c0339e..5b261f3a 100644 --- a/docs/team-operator/README.md +++ b/docs/team-operator/README.md @@ -66,6 +66,10 @@ kubectl edit site main -n posit-team kubectl logs -n posit-team deploy/team-operator ``` +## Session Label Injection + +See [posit-dev/team-operator#123](https://github.com/posit-dev/team-operator/pull/123) for full details. Configuration schema is in [CONFIGURATION.md](../CONFIGURATION.md). + ## Related Documentation - [Site Management Guide](../guides/product-team-site-management.md) - For product teams diff --git a/python-pulumi/src/ptd/pulumi_resources/aws_workload_helm.py b/python-pulumi/src/ptd/pulumi_resources/aws_workload_helm.py index ca2c4950..96563872 100644 --- a/python-pulumi/src/ptd/pulumi_resources/aws_workload_helm.py +++ b/python-pulumi/src/ptd/pulumi_resources/aws_workload_helm.py @@ -701,7 +701,7 @@ def _define_kube_state_metrics(self, release: str, version: str): "valuesContent": yaml.dump( { "metricLabelsAllowlist": [ - "pods=[launcher-instance-id]", + "pods=[launcher-instance-id,user-group-*]", ] } ), diff --git a/python-pulumi/src/ptd/pulumi_resources/azure_workload_helm.py b/python-pulumi/src/ptd/pulumi_resources/azure_workload_helm.py index ce5ed9d3..46219f81 100644 --- a/python-pulumi/src/ptd/pulumi_resources/azure_workload_helm.py +++ b/python-pulumi/src/ptd/pulumi_resources/azure_workload_helm.py @@ -755,7 +755,7 @@ def _define_kube_state_metrics(self, release: str, version: str): "valuesContent": yaml.dump( { "metricLabelsAllowlist": [ - "pods=[launcher-instance-id]", + "pods=[launcher-instance-id,user-group-*]", ] } ), diff --git a/python-pulumi/src/ptd/pulumi_resources/team_operator.py b/python-pulumi/src/ptd/pulumi_resources/team_operator.py index 66addea4..5c5b45f5 100644 --- a/python-pulumi/src/ptd/pulumi_resources/team_operator.py +++ b/python-pulumi/src/ptd/pulumi_resources/team_operator.py @@ -2,6 +2,7 @@ import pulumi import pulumi_kubernetes as kubernetes +import yaml import ptd import ptd.workload @@ -75,6 +76,17 @@ def _define_posit_team_namespace(self): ), ) + def _any_site_has_session_labels(self) -> bool: + """Return True if any site.yaml in this workload has workbench.sessionLabels set.""" + for site_name in self.workload.cfg.sites: + site_yaml_path = self.workload.site_yaml(site_name) + if not site_yaml_path.exists(): + continue + site_dict = yaml.safe_load(site_yaml_path.read_text()) + if site_dict.get("spec", {}).get("workbench", {}).get("sessionLabels"): + return True + return False + def _define_helm_release(self): # Parse self.image (from _define_image) into repository and tag # Format is either "repo@sha256:digest" or "repo:tag" @@ -134,6 +146,11 @@ def _define_helm_release(self): "enable": not self.cluster_cfg.team_operator_skip_crds, "keep": True, }, + # Enable the session group label controller only if at least one site + # has workbench.sessionLabels configured in its site.yaml. + "sessionGroupLabels": { + "enable": self._any_site_has_session_labels(), + }, } # OCI Helm chart from public repository