Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4406908fbe | |||
| b00be78af9 | |||
| ba88471869 | |||
| 7b8947332a | |||
| 1fce9cfc7a | |||
| 57a9865c18 | |||
| 7b526c83c0 | |||
| 3f34f8e1c8 |
@@ -1 +0,0 @@
|
||||
github: [privilegedescalation]
|
||||
@@ -1,2 +0,0 @@
|
||||
self-hosted-runner:
|
||||
labels: []
|
||||
@@ -1,132 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# ci-health-check.sh — Scan all privilegedescalation repos for CI/CD health
|
||||
# Run from: /paperclip/privilegedescalation/engineering/hugh
|
||||
# Requires: GH_TOKEN set (use: export GH_TOKEN=$(bash ./get-github-token.sh))
|
||||
#
|
||||
# Plugin repo discovery
|
||||
# ---------------------
|
||||
# PLUGIN_REPOS is populated dynamically from the GitHub org so newly created
|
||||
# plugin repos are picked up automatically. The filter is:
|
||||
# - non-archived, public repos in the privilegedescalation org
|
||||
# - name starts with "headlamp-"
|
||||
# - excludes "headlamp-agent-skills" (skills bundle, not a Headlamp plugin)
|
||||
# If discovery fails (network error, GH_TOKEN missing, API outage), we fall
|
||||
# back to a hardcoded list so the health check still produces a useful report.
|
||||
#
|
||||
# Failure Categories:
|
||||
# - code: test/lint/build/typecheck failures on main
|
||||
# - infra: startup_failure, timed_out, runner issues
|
||||
# - pending: action_required (awaiting review/approval) - informational only
|
||||
set -euo pipefail
|
||||
|
||||
ORG="privilegedescalation"
|
||||
|
||||
# Hardcoded fallback — kept in sync manually as a safety net for discovery failures.
|
||||
PLUGIN_REPOS_FALLBACK=(
|
||||
headlamp-polaris-plugin
|
||||
headlamp-rook-plugin
|
||||
headlamp-sealed-secrets-plugin
|
||||
headlamp-intel-gpu-plugin
|
||||
headlamp-tns-csi-plugin
|
||||
headlamp-kube-vip-plugin
|
||||
headlamp-plugin-template
|
||||
headlamp-argocd-plugin
|
||||
)
|
||||
|
||||
mapfile -t PLUGIN_REPOS < <(
|
||||
gh api --paginate "orgs/${ORG}/repos" \
|
||||
--jq '.[] | select(.archived == false and .visibility == "public" and (.name | startswith("headlamp-")) and .name != "headlamp-agent-skills") | .name' \
|
||||
2>/dev/null | sort
|
||||
)
|
||||
|
||||
if [ ${#PLUGIN_REPOS[@]} -eq 0 ]; then
|
||||
echo "WARNING: dynamic repo discovery returned no results — using hardcoded fallback" >&2
|
||||
PLUGIN_REPOS=("${PLUGIN_REPOS_FALLBACK[@]}")
|
||||
fi
|
||||
|
||||
# Private repos not visible to dynamic discovery
|
||||
PLUGIN_REPOS+=("infra")
|
||||
|
||||
echo "=== CI/CD Health Check — $(date -u '+%Y-%m-%d %H:%M UTC') ==="
|
||||
echo ""
|
||||
|
||||
failures=0
|
||||
warnings=0
|
||||
process_pending=0
|
||||
|
||||
for repo in "${PLUGIN_REPOS[@]}"; do
|
||||
echo "--- ${repo} ---"
|
||||
|
||||
# Get last 10 runs (wider window to catch intermittent failures)
|
||||
runs=$(gh run list --repo "${ORG}/${repo}" --limit 10 --json name,conclusion,headBranch,updatedAt 2>/dev/null || echo "[]")
|
||||
|
||||
if [ "$runs" = "[]" ]; then
|
||||
echo " WARNING: No workflow runs found"
|
||||
((warnings++)) || true
|
||||
continue
|
||||
fi
|
||||
|
||||
total=$(echo "$runs" | jq 'length')
|
||||
|
||||
# Categorize failures:
|
||||
# - code failures: test/lint/build on main
|
||||
# - infra failures: startup_failure, timed_out
|
||||
# - process pending: action_required
|
||||
|
||||
code_failures=$(echo "$runs" | jq '[.[] | select(.headBranch=="main" and .conclusion=="failure" and .name!="Release" and .name!="E2E Tests")] | length')
|
||||
infra_failures=$(echo "$runs" | jq '[.[] | select(.conclusion=="startup_failure" or .conclusion=="timed_out")] | length')
|
||||
action_required=$(echo "$runs" | jq '[.[] | select(.conclusion=="action_required")] | length')
|
||||
|
||||
if [ "$code_failures" -gt 0 ]; then
|
||||
echo " FAIL (code): ${code_failures} CI failure(s) in last ${total} runs on main:"
|
||||
echo "$runs" | jq -r '.[] | select(.headBranch=="main" and .conclusion=="failure" and .name!="Release" and .name!="E2E Tests") | " - \(.name) (\(.updatedAt))"'
|
||||
((failures++)) || true
|
||||
fi
|
||||
|
||||
if [ "$infra_failures" -gt 0 ]; then
|
||||
echo " FAIL (infra): ${infra_failures} infrastructure failure(s):"
|
||||
echo "$runs" | jq -r '.[] | select(.conclusion=="startup_failure" or .conclusion=="timed_out") | " - \(.name): \(.conclusion) (\(.updatedAt))"'
|
||||
((failures++)) || true
|
||||
fi
|
||||
|
||||
if [ "$code_failures" -eq 0 ] && [ "$infra_failures" -eq 0 ]; then
|
||||
echo " OK: CI passing on main"
|
||||
fi
|
||||
|
||||
# Process pending — informational only (awaiting review/approval)
|
||||
if [ "$action_required" -gt 0 ]; then
|
||||
echo " INFO: ${action_required} workflow run(s) awaiting action (dual approval, review, etc.):"
|
||||
echo "$runs" | jq -r '.[] | select(.conclusion=="action_required") | " - \(.name) on \(.headBranch) (\(.updatedAt))"'
|
||||
((process_pending++)) || true
|
||||
fi
|
||||
|
||||
# Surface E2E test failures as warnings (infra blocker: RBAC not yet applied — PRI-494)
|
||||
e2e_failures=$(echo "$runs" | jq '[.[] | select(.headBranch=="main" and .name=="E2E Tests" and .conclusion=="failure")] | length')
|
||||
if [ "$e2e_failures" -gt 0 ]; then
|
||||
echo " WARN: E2E Tests failing on main (${e2e_failures} failure(s)) — RBAC bootstrap pending (PRI-494)"
|
||||
((warnings++)) || true
|
||||
fi
|
||||
|
||||
# Surface Release failures as warnings — with graceful skip in place, these indicate real errors
|
||||
release_failures=$(echo "$runs" | jq '[.[] | select(.name=="Release" and .conclusion=="failure")] | length')
|
||||
if [ "$release_failures" -gt 0 ]; then
|
||||
echo " WARN: Release workflow has ${release_failures} failure(s) — investigate (PRI-380 secrets still pending)"
|
||||
((warnings++)) || true
|
||||
fi
|
||||
|
||||
# Check latest release
|
||||
latest_release=$(gh api "repos/${ORG}/${repo}/releases" --jq '.[0].tag_name // "none"' 2>/dev/null || echo "error")
|
||||
echo " Latest release: ${latest_release}"
|
||||
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo "=== Summary ==="
|
||||
echo "Repos scanned: ${#PLUGIN_REPOS[@]}"
|
||||
echo "With failures: ${failures}"
|
||||
echo "With warnings: ${warnings}"
|
||||
echo "With pending approval: ${process_pending}"
|
||||
|
||||
if [ "$failures" -gt 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,84 +0,0 @@
|
||||
# GitHub Actions Workflows
|
||||
|
||||
This directory contains reusable and repo-specific GitHub Actions workflows for the privilegedescalation organization.
|
||||
|
||||
## Available Tools on Runners
|
||||
|
||||
### Always Available
|
||||
- `curl` - HTTP client (use this instead of `gh` CLI for API calls)
|
||||
- `jq` - JSON processor
|
||||
- `bash` - Shell
|
||||
- `git` - Version control
|
||||
- `docker` / `podman` - Container runtime (depending on runner)
|
||||
|
||||
### NOT Available (must install if needed)
|
||||
- `gh` CLI - GitHub CLI is **not** pre-installed on runners. Use `curl` with the GitHub API instead.
|
||||
|
||||
## Best Practices
|
||||
|
||||
### GitHub API Calls
|
||||
Instead of using `gh` CLI (which is not installed), use `curl` with the GitHub API:
|
||||
|
||||
```yaml
|
||||
- name: Set PR label
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
REPO: ${{ github.repository }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
run: |
|
||||
curl -sf \
|
||||
-X POST \
|
||||
-H "Authorization: Bearer ${GH_TOKEN}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
"https://api.github.com/repos/${REPO}/issues/${PR_NUMBER}/labels" \
|
||||
-d '{"labels":["label-name"]}'
|
||||
```
|
||||
|
||||
### Workflow Validation
|
||||
Run actionlint locally before pushing:
|
||||
|
||||
```bash
|
||||
actionlint -color .github/workflows/*.yaml
|
||||
```
|
||||
|
||||
### Reusable Workflows
|
||||
- `plugin-ci.yaml` - Standard CI for Headlamp plugins
|
||||
- `plugin-e2e.yaml` - E2E testing for Headlamp plugins
|
||||
- `dual-approval-check.yaml` - Checks for CTO and QA approval
|
||||
- `detect-pr-pipeline.yaml` - Detects Pipeline A vs Pipeline B based on changed files
|
||||
|
||||
## Workflow Naming Convention
|
||||
|
||||
- Use kebab-case: `my-workflow.yaml`
|
||||
- Be descriptive: `plugin-ci.yaml` not `ci.yaml`
|
||||
- For reusable workflows, keep the name clear about its purpose
|
||||
|
||||
## Required Gates
|
||||
|
||||
All PRs must pass:
|
||||
1. `actionlint` validation (workflow YAML syntax)
|
||||
2. Shell script validation (if scripts are used)
|
||||
3. Any repo-specific CI checks
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Getting Changed Files
|
||||
Use `tj-actions/changed-files`:
|
||||
|
||||
```yaml
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v47
|
||||
with:
|
||||
files_separator: '\n'
|
||||
```
|
||||
|
||||
### Setting Job Outputs
|
||||
```yaml
|
||||
- name: Set output
|
||||
id: detect
|
||||
run: |
|
||||
echo "pipeline-type=pipeline-a" >> $GITHUB_OUTPUT
|
||||
```
|
||||
|
||||
Access in downstream jobs: `${{ jobs.job-name.outputs.pipeline-type }}`
|
||||
@@ -1,22 +0,0 @@
|
||||
name: CI/CD Health Check
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 8 * * 1-5' # Every weekday at 8 AM UTC
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
health-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Run CI/CD health check
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
run: |
|
||||
if [ -z "$GITEA_TOKEN" ]; then
|
||||
echo "::warning::GITEA_TOKEN not configured — health check may have limited data."
|
||||
fi
|
||||
./.github/scripts/ci-health-check.sh
|
||||
@@ -1,82 +0,0 @@
|
||||
name: Detect PR Pipeline Type
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main, dev, uat]
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
test-detection-logic:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 2
|
||||
|
||||
env:
|
||||
HEAD_REF: ${{ github.head_ref }}
|
||||
BASE_REF: ${{ github.base_ref }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
run: |
|
||||
git clone --depth=1 "https://x-access-token:${{ secrets.GITEA_TOKEN }}@git.farh.net/${{ github.repository }}.git" .
|
||||
git fetch origin "$BASE_REF" --depth=1
|
||||
git fetch origin +refs/pull/*/head:refs/pull/*/head --depth=1
|
||||
git checkout "${{ github.sha }}"
|
||||
|
||||
- name: Run detection tests
|
||||
run: bash scripts/test-detect-pipeline.sh
|
||||
|
||||
detect-pipeline:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
|
||||
env:
|
||||
HEAD_REF: ${{ github.head_ref }}
|
||||
BASE_REF: ${{ github.base_ref }}
|
||||
|
||||
outputs:
|
||||
pipeline-type: ${{ steps.detect.outputs.pipeline-type }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
run: |
|
||||
git clone --depth=1 "https://x-access-token:${{ secrets.GITEA_TOKEN }}@git.farh.net/${{ github.repository }}.git" .
|
||||
git fetch origin "$BASE_REF" --depth=1
|
||||
git fetch origin +refs/pull/*/head:refs/pull/*/head --depth=1
|
||||
git checkout "${{ github.sha }}"
|
||||
|
||||
- name: Get changed files
|
||||
run: |
|
||||
mkdir -p /tmp/pr-detect
|
||||
git fetch origin "$BASE_REF" --depth=1 2>/dev/null
|
||||
git fetch origin +refs/pull/*/head:refs/pull/*/head --depth=1 2>/dev/null
|
||||
git diff --name-only "origin/$BASE_REF" HEAD > /tmp/pr-detect/changed_files.txt
|
||||
echo "Files found: $(wc -l < /tmp/pr-detect/changed_files.txt)"
|
||||
cat /tmp/pr-detect/changed_files.txt
|
||||
|
||||
- name: Detect pipeline type
|
||||
id: detect
|
||||
run: |
|
||||
pipeline=$(bash scripts/detect-pipeline.sh < /tmp/pr-detect/changed_files.txt)
|
||||
|
||||
echo "pipeline-type=$pipeline" >> $GITHUB_OUTPUT
|
||||
echo "Detected pipeline: $pipeline"
|
||||
|
||||
- name: Set PR label
|
||||
if: github.event_name == 'pull_request'
|
||||
continue-on-error: true
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
REPO: ${{ github.repository }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
PIPELINE_TYPE: ${{ steps.detect.outputs.pipeline-type }}
|
||||
run: |
|
||||
curl -sf \
|
||||
-X POST \
|
||||
-H "Authorization: Bearer ${GH_TOKEN}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
"https://api.github.com/repos/${REPO}/issues/${PR_NUMBER}/labels" \
|
||||
-d "{\"labels\":[\"${PIPELINE_TYPE}\"]}"
|
||||
@@ -1,52 +0,0 @@
|
||||
name: PR Validation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
env:
|
||||
HEAD_REF: ${{ github.head_ref }}
|
||||
BASE_REF: ${{ github.base_ref }}
|
||||
run: |
|
||||
git clone --depth=1 "https://x-access-token:${{ secrets.GITEA_TOKEN }}@git.farh.net/${{ github.repository }}.git" .
|
||||
git fetch origin "$BASE_REF" --depth=1
|
||||
git fetch origin +refs/pull/*/head:refs/pull/*/head --depth=1
|
||||
git checkout "${{ github.sha }}"
|
||||
|
||||
- name: Install actionlint
|
||||
run: |
|
||||
ACTIONLINT_VERSION="1.7.7"
|
||||
mkdir -p "$HOME/.local/bin"
|
||||
apt-get install -y wget -qq >/dev/null 2>&1 || true
|
||||
wget -qO- "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" \
|
||||
| tar -xz -C "$HOME/.local/bin" actionlint
|
||||
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Validate workflow YAML with actionlint
|
||||
run: actionlint -shellcheck="" -color .github/workflows/*.yaml
|
||||
|
||||
- name: Install shellcheck
|
||||
run: |
|
||||
SC_VERSION="v0.10.0"
|
||||
mkdir -p "$HOME/.local/bin"
|
||||
wget -qO- "https://github.com/koalaman/shellcheck/releases/download/${SC_VERSION}/shellcheck-${SC_VERSION}.linux.x86_64.tar.xz" \
|
||||
| tar -xJ --strip-components=1 -C "$HOME/.local/bin" "shellcheck-${SC_VERSION}/shellcheck"
|
||||
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Shellcheck scripts
|
||||
run: |
|
||||
if ls .github/scripts/*.sh 1>/dev/null 2>&1; then
|
||||
for script in .github/scripts/*.sh; do
|
||||
echo "Checking ${script}..."
|
||||
shellcheck --severity=warning "$script"
|
||||
done
|
||||
else
|
||||
echo "No shell scripts to check"
|
||||
fi
|
||||
@@ -1,27 +0,0 @@
|
||||
name: Renovate
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 2 * * 6' # Saturday 2:00 UTC — aligns with "every weekend" in renovate-config.json
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
renovate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Run Renovate
|
||||
env:
|
||||
RENOVATE_TOKEN: ${{ secrets.RENOVATE_TOKEN }}
|
||||
RENOVATE_PLATFORM: gitea
|
||||
RENOVATE_ENDPOINT: https://git.farh.net
|
||||
RENOVATE_AUTODISCOVER: "true"
|
||||
LOG_LEVEL: debug
|
||||
run: |
|
||||
npx renovate \
|
||||
--token="${RENOVATE_TOKEN}" \
|
||||
--platform=gitea \
|
||||
--endpoint=https://git.farh.net \
|
||||
--configurationFile=renovate-config.json
|
||||
@@ -1,66 +0,0 @@
|
||||
name: Stale Release Branch Cleanup
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 9 * * 1' # Weekly every Monday at 09:00 UTC
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry_run:
|
||||
description: 'Dry run (no changes made)'
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
cleanup-stale-branches:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: |
|
||||
.github
|
||||
sparse-checkout-cone-mode: false
|
||||
|
||||
- name: Fetch all branches
|
||||
run: git fetch --all --prune
|
||||
|
||||
- name: Find and clean stale release branches
|
||||
id: stale
|
||||
env:
|
||||
DRY_RUN: ${{ github.event.inputs.dry_run || false }}
|
||||
run: |
|
||||
DAYS=14
|
||||
|
||||
# Find release branches older than 14 days not on main
|
||||
for branch in $(git for-each-ref --format '%(refname:strip=3)' 'refs/remotes/origin/release/*' 'refs/remotes/origin/v[0-9]*'); do
|
||||
ts=$(git log -1 --format='%ct' "refs/remotes/origin/$branch")
|
||||
if [ -z "$ts" ]; then
|
||||
continue
|
||||
fi
|
||||
age_days=$(( ($(date +%s) - ts) / 86400 ))
|
||||
|
||||
if [ "$age_days" -gt "$DAYS" ]; then
|
||||
# Check if branch has been merged into main
|
||||
if git merge-base --is-ancestor "refs/remotes/origin/$branch" main 2>/dev/null; then
|
||||
echo "Merged branch found: $branch (age: ${age_days}d)"
|
||||
if [ "$DRY_RUN" == "true" ]; then
|
||||
echo "Would delete merged branch: $branch"
|
||||
else
|
||||
echo "Deleting merged branch: $branch"
|
||||
if ! git push origin --delete "$branch" 2>&1; then
|
||||
echo "::warning::Failed to delete branch: $branch"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Report dry run results
|
||||
if: github.event.inputs.dry_run == 'true'
|
||||
run: |
|
||||
echo "Dry run complete. No branches were deleted."
|
||||
echo ""
|
||||
echo "Release branches found:"
|
||||
git for-each-ref --format '%(refname:strip=3) - %(committerdate:relative)' \
|
||||
'refs/remotes/origin/release/*' 'refs/remotes/origin/v[0-9]*' 2>/dev/null || echo "None found"
|
||||
@@ -1 +0,0 @@
|
||||
github: [privilegedescalation]
|
||||
@@ -1,73 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1 +1,3 @@
|
||||
# .github
|
||||
# Privileged Escalation
|
||||
|
||||
Org-level content, social media queue, and community responses.
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: "Six Headlamp Plugins Nobody Asked For"
|
||||
date: 2026-03-07
|
||||
author: Privileged Escalation
|
||||
type: blog
|
||||
status: draft
|
||||
---
|
||||
|
||||
# Six Headlamp Plugins Nobody Asked For
|
||||
|
||||
There's a particular kind of optimism that only exists in open source. It's the belief that if you build something genuinely useful, put it on GitHub, list it on Artifact Hub, write actual documentation, and then wait — someone will eventually find it.
|
||||
|
||||
We're currently in the "wait" phase.
|
||||
|
||||
## What We Actually Built
|
||||
|
||||
Privileged Escalation makes [Headlamp](https://headlamp.dev/) plugins. If you don't know what Headlamp is: it's a CNCF-listed Kubernetes dashboard that was designed to be extended. If you don't know what Kubernetes is, this blog post is going to be a rough ride.
|
||||
|
||||
We have six plugins. Each one takes something you'd normally do with `kubectl`, a terminal, and quiet desperation, and puts it in a web UI that your teammates might actually use.
|
||||
|
||||
**[headlamp-polaris-plugin](https://github.com/privilegedescalation/headlamp-polaris-plugin)** — Surfaces Fairwinds Polaris audit results directly in Headlamp. Cluster score in the app bar, per-namespace breakdowns, exemption management from the UI instead of annotation YAML editing. Recently hit v0.6.0 with dark mode support, because apparently that's what it takes to be taken seriously in 2026.
|
||||
|
||||
**[headlamp-tns-csi-plugin](https://github.com/privilegedescalation/headlamp-tns-csi-plugin)** — TrueNAS CSI driver visibility and storage benchmarking via kbench. If you've ever wondered whether your NFS share is actually performing the way iX Systems promised, this is the plugin that tells you the uncomfortable truth.
|
||||
|
||||
**[headlamp-rook-plugin](https://github.com/privilegedescalation/headlamp-rook-plugin)** — Rook-Ceph cluster health, pool status, and CSI driver monitoring. For people who chose distributed storage and now live with the consequences.
|
||||
|
||||
**[headlamp-sealed-secrets-plugin](https://github.com/privilegedescalation/headlamp-sealed-secrets-plugin)** — Bitnami Sealed Secrets management with client-side RSA-OAEP and AES-256-GCM encryption. Your plaintext never leaves the browser. We're fairly proud of this one, which is why it hurts that it has zero stars.
|
||||
|
||||
**[headlamp-intel-gpu-plugin](https://github.com/privilegedescalation/headlamp-intel-gpu-plugin)** — Intel GPU device visibility and resource monitoring. For the subset of people running Intel GPUs in Kubernetes, which is a smaller group than Intel's marketing department would like.
|
||||
|
||||
**[headlamp-kube-vip-plugin](https://github.com/privilegedescalation/headlamp-kube-vip-plugin)** — kube-vip virtual IP and load balancer visibility. Because sometimes you just need to know if the VIP is actually where it's supposed to be.
|
||||
|
||||
## Why Headlamp Plugins
|
||||
|
||||
The Kubernetes dashboard space is... let's call it "stratified." There are expensive commercial options that do everything. There are free options that do almost nothing. And then there's Headlamp, which does a reasonable amount and lets you extend it.
|
||||
|
||||
We chose the extension path. Every plugin installs through Headlamp's native plugin system — no separate deployments, no new URLs to bookmark, no "please also install this sidecar that needs its own RBAC." You add a plugin and it appears in the sidebar. That's it.
|
||||
|
||||
This matters because the alternative is what most teams actually do: they `kubectl` their way through everything, pipe JSON through `jq`, and call it observability. It works. It's also miserable if you're trying to onboard anyone who doesn't have muscle memory for `kubectl get pods -n rook-ceph -o jsonpath='{.items[*].status.phase}'`.
|
||||
|
||||
## The Honest Part
|
||||
|
||||
We launched all six plugins in the same week. Combined star count across all repos: zero. Combined fork count: one, and we're not entirely sure it was intentional.
|
||||
|
||||
Our CI is sometimes in a state that could charitably be described as "aspirational." We filed a bug against ourselves about E2E tests that have never passed because we haven't set up the test infrastructure yet. We committed LICENSE badges to READMEs before we committed the actual LICENSE files.
|
||||
|
||||
This is normal. This is what early open source looks like before the narrative gets cleaned up. We'd rather be honest about it than pretend we emerged fully formed with 200 stars and a contributor covenant.
|
||||
|
||||
## What's Next
|
||||
|
||||
We're working on getting every plugin listed on Artifact Hub with proper metadata, fixing the CI pipelines that are currently failing for reasons ranging from "missing secrets" to "format check disagreements," and writing the kind of documentation that makes people confident enough to actually install something.
|
||||
|
||||
If you run Headlamp and any of these plugins sound useful, try one. If something breaks, file an issue. If it works and you like it, a star would be nice. We're not above admitting that.
|
||||
|
||||
All plugins are Apache-2.0 licensed. All repos are at [github.com/privilegedescalation](https://github.com/privilegedescalation).
|
||||
@@ -0,0 +1,236 @@
|
||||
# FAQ: Headlamp Plugins for Kubernetes Operators
|
||||
|
||||
**Context**: For operators who are thinking about observability, visibility, and management during/after KubeCon. Answer real questions with real context, not marketing language.
|
||||
|
||||
---
|
||||
|
||||
## Observability & Visibility
|
||||
|
||||
### Q: I have a Prometheus stack already. Why do I need Headlamp plugins?
|
||||
|
||||
A: You probably don't need them. Prometheus is good at what it does: metrics. But Prometheus is not a dashboard. You still need to *see* your cluster in human terms — what's running, where, and why it matters.
|
||||
|
||||
Headlamp plugins show you the cluster state in the UI. Your Prometheus metrics live somewhere else. They're complementary, not competitive.
|
||||
|
||||
If you're happy with kubectl and Prometheus graphs, keep going. If you find yourself switching between tools, Headlamp might fit.
|
||||
|
||||
---
|
||||
|
||||
### Q: Is this "observability"? I thought we needed traces, metrics, logs...
|
||||
|
||||
A: You're thinking of the marketing definition. In practice, operators need:
|
||||
1. To see what's running (cluster state)
|
||||
2. To understand if it's healthy (metrics)
|
||||
3. To know what went wrong (logs, events)
|
||||
|
||||
Headlamp handles #1. Your existing stack handles #2 and #3. The magic is in integrating them, not replacing them.
|
||||
|
||||
Our plugins sit in the UI where you're already looking. That's the whole point.
|
||||
|
||||
---
|
||||
|
||||
## Individual Plugins
|
||||
|
||||
### Q: When should I use the Rook plugin?
|
||||
|
||||
A: When you're running Rook/Ceph and you're tired of bouncing between Ceph's CLI tools and Kubernetes dashboards to understand cluster health.
|
||||
|
||||
The Rook plugin shows:
|
||||
- Cluster status (capacity, degradation, health warnings)
|
||||
- Pool health (replication status, PG states)
|
||||
- OSD states (up/down, full/nearfull)
|
||||
- Filesystem status
|
||||
|
||||
Instead of `ceph osd tree`, `ceph df`, `rook ceph osd status`... you look at one place.
|
||||
|
||||
**Not for**: Teams that want deep Ceph debugging. For that, you still need Ceph's native tools.
|
||||
|
||||
---
|
||||
|
||||
### Q: What's the GPU plugin actually for?
|
||||
|
||||
A: Seeing which nodes have GPUs, how much capacity you have, and which workloads are using them.
|
||||
|
||||
If you're running ML workloads, inference servers, or anything with accelerators, you need to know:
|
||||
- Which nodes have what hardware
|
||||
- What's currently running on those nodes
|
||||
- Whether utilization is balanced
|
||||
|
||||
Kubectl doesn't show you that easily. Prometheus might have the metrics if you instrument everything correctly. The GPU plugin shows it at a glance.
|
||||
|
||||
**Not for**: Teams not using GPUs. This is a specialized tool.
|
||||
|
||||
---
|
||||
|
||||
### Q: Why a sealed-secrets plugin? Isn't that a security risk — showing secrets in a UI?
|
||||
|
||||
A: The plugin doesn't show the secret *values*. It shows:
|
||||
- Which secrets exist
|
||||
- Which workloads reference them
|
||||
- Where they're mounted
|
||||
- Rotation status (if you implement that)
|
||||
|
||||
That's visibility without exposure. It answers "what secrets are in my cluster?" not "what are the passwords?"
|
||||
|
||||
Teams using sealed-secrets are usually the ones who care about secret governance. This plugin gives you governance visibility without breaking the security model.
|
||||
|
||||
---
|
||||
|
||||
### Q: What's the difference between your plugins and Rancher/Lens/other dashboards?
|
||||
|
||||
A: They're trying to be the entire dashboard. We're building plugins for the gaps.
|
||||
|
||||
If you like Headlamp's design but want specific functionality (Rook management, GPU visibility, sealed-secrets governance), our plugins slot in.
|
||||
|
||||
If you prefer Rancher's philosophy, great. Use Rancher. Our plugins are built for people who want a lightweight UI + specialized functionality, not an all-in-one platform.
|
||||
|
||||
---
|
||||
|
||||
## Operational Questions
|
||||
|
||||
### Q: Do I need to run Headlamp to use these plugins?
|
||||
|
||||
A: Yes. Our plugins extend Headlamp. Headlamp is lightweight (single container), but you need to be running it.
|
||||
|
||||
If you're not using Headlamp, these plugins don't help. If you are, they extend what you can see.
|
||||
|
||||
---
|
||||
|
||||
### Q: How do you handle RBAC? Can my developers see things they shouldn't?
|
||||
|
||||
A: Headlamp respects your cluster's RBAC. If a developer can't run `kubectl get secrets`, they can't see them in the plugin either.
|
||||
|
||||
Your security boundaries are your security boundaries. Our tools don't bypass them.
|
||||
|
||||
---
|
||||
|
||||
### Q: What's the upgrade path? Will my existing configuration break?
|
||||
|
||||
A: We try not to break things. Honest answer: we're still young. Check release notes before upgrading. If you find a breaking change, file an issue and we'll help.
|
||||
|
||||
If you need stability guarantees, we're not there yet. We're a small team shipping useful things, not a enterprise product with backwards-compatibility promises.
|
||||
|
||||
---
|
||||
|
||||
### Q: Can I run Headlamp + plugins in an air-gapped environment?
|
||||
|
||||
A: Yes. If you can run Headlamp, you can run the plugins. No external dependencies, no phone-home telemetry.
|
||||
|
||||
The only requirement: your cluster can reach the Headlamp instance (network security is your problem).
|
||||
|
||||
---
|
||||
|
||||
## Adoption & Getting Started
|
||||
|
||||
### Q: How do I know if these plugins are worth the effort?
|
||||
|
||||
A: Try one. Pick the one that solves a problem you're actually having.
|
||||
|
||||
Rook users: Use the Rook plugin for a week. See if it saves time. If not, delete it.
|
||||
GPU users: Use the GPU plugin. See if you'd miss it.
|
||||
Sealed-secrets users: Use the plugin for secret governance.
|
||||
|
||||
Don't add plugins "just in case." Add them when they're solving a real problem.
|
||||
|
||||
---
|
||||
|
||||
### Q: What's the support story? If something breaks, what happens?
|
||||
|
||||
A: GitHub issues. We're responsive (usually within 24-48 hours). If it's a security issue, email the maintainers directly (see repo).
|
||||
|
||||
We're not a SaaS with SLAs. We're open source with humans behind it who care. That's the tradeoff.
|
||||
|
||||
---
|
||||
|
||||
### Q: Where do I submit feature requests?
|
||||
|
||||
A: GitHub issues with the `feature-request` label. Be specific. "Make it faster" doesn't help. "Show OSD versions in the Rook plugin" does.
|
||||
|
||||
---
|
||||
|
||||
## Technical Depth
|
||||
|
||||
### Q: How much overhead do these plugins add?
|
||||
|
||||
A: Minimal. Plugins are JavaScript that runs in your browser. They query your cluster API, same as kubectl does.
|
||||
|
||||
If you're running Headlamp already, adding plugins is negligible overhead.
|
||||
|
||||
---
|
||||
|
||||
### Q: Can I modify the plugins for my own use?
|
||||
|
||||
A: Yes. All plugins are Apache-2.0 licensed. Fork, modify, deploy. We appreciate improvements back in PRs, but no obligation.
|
||||
|
||||
---
|
||||
|
||||
### Q: Do these plugins work with managed Kubernetes (EKS, GKE, AKS)?
|
||||
|
||||
A: If Headlamp works with your platform, the plugins work. Headlamp just needs API access.
|
||||
|
||||
We develop against standard Kubernetes. If you hit a managed-service-specific issue, let us know.
|
||||
|
||||
---
|
||||
|
||||
## When to Say No
|
||||
|
||||
### Q: Should I use these in production?
|
||||
|
||||
A: Depends on what you mean by "production." If you mean "will it crash my cluster," no. Headlamp + plugins are read-only.
|
||||
|
||||
If you mean "is this enterprise-grade," probably not yet. We're under 1 year old. We're useful, not bulletproof.
|
||||
|
||||
Try it. Monitor it. Have a fallback (you do have kubectl, right?). If it fails, switch back.
|
||||
|
||||
---
|
||||
|
||||
### Q: Can these plugins replace my existing monitoring stack?
|
||||
|
||||
A: No. Don't try. This is visibility, not comprehensive monitoring.
|
||||
|
||||
You still need logs, metrics, traces, alerting. We're the UI layer for cluster state + specialized views.
|
||||
|
||||
---
|
||||
|
||||
## Getting Help
|
||||
|
||||
### Q: I found a bug. What do I do?
|
||||
|
||||
A: GitHub issue with:
|
||||
- What you were doing
|
||||
- What happened
|
||||
- What you expected to happen
|
||||
- Your Kubernetes version
|
||||
- Your Headlamp version
|
||||
- Plugin version
|
||||
|
||||
Specificity helps. "It doesn't work" doesn't. "When I click the Rook tab, I get a 403 error" does.
|
||||
|
||||
---
|
||||
|
||||
### Q: I want to contribute. Where do I start?
|
||||
|
||||
A: GitHub issues with `good first issue` label. Read the CONTRIBUTING.md in each repo. Start small.
|
||||
|
||||
We're a small team. contributions that improve things make a real difference.
|
||||
|
||||
---
|
||||
|
||||
## The Honest Version
|
||||
|
||||
Headlamp plugins are for people who:
|
||||
- Are already running Kubernetes in production
|
||||
- Understand their observability gaps
|
||||
- Want small, focused tools instead of monolithic platforms
|
||||
- Are comfortable with "good enough" software from small teams
|
||||
|
||||
If you need enterprise support, SLAs, and hand-holding, we're not it (yet). If you want useful tools that respect your workflow and don't try to be everything, we might be.
|
||||
|
||||
Try us. If we don't fit, no hard feelings. There are plenty of other dashboards. Find the one that works for your team.
|
||||
|
||||
---
|
||||
|
||||
**Last updated**: March 13, 2026
|
||||
**Audience**: Kubernetes operators, platform engineers, storage admins
|
||||
**Tone**: Honest, not salesy, specific, realistic about limitations
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
# KubeCon EU 2026 — Response & Tactical Post Templates
|
||||
|
||||
**Status**: Ready-to-deploy. Update dates/times as conference progresses. Use if conversations align with these narratives.
|
||||
|
||||
---
|
||||
|
||||
## Pre-KubeCon (March 21-22)
|
||||
|
||||
### Template 1: The Headlamp Moment
|
||||
|
||||
**Platform**: Twitter/X
|
||||
**Trigger**: When #KubeCon hashtag begins heating up, someone mentions "dashboard" or "UI"
|
||||
|
||||
**Post**:
|
||||
if you're heading to #KubeCon and you're thinking "I wish I could see what's actually happening in my cluster without opening 6 different tools," we have 6 plugins for that.
|
||||
|
||||
see you in Amsterdam.
|
||||
|
||||
**CMO Note**: Soft sell. Positions us as understaters. Uses first-person ("we have") rather than "check out." Timing: Friday-Saturday before conference opens.
|
||||
|
||||
---
|
||||
|
||||
### Template 2: The "Cold Take" on Platform Engineering
|
||||
|
||||
**Platform**: Bluesky
|
||||
**Trigger**: Platform engineering talks announced, or engineering teams mention "observability as a competitive advantage"
|
||||
|
||||
**Post**:
|
||||
Platform teams spend 2024 building observability. They spent 2025 fighting with it. KubeCon 2026 is about finally making it *work*.
|
||||
|
||||
(Hint: Headlamp makes the "finally" part easier.)
|
||||
|
||||
**CMO Note**: Positions us as people who understand the maturity curve. Not condescending. Acknowledges that good observability is *work* not just tooling. Implies we've thought about this problem space.
|
||||
|
||||
---
|
||||
|
||||
## Main Conference (March 23-26)
|
||||
|
||||
### Template 3: The "We're Not Doing That" Take
|
||||
|
||||
**Platform**: Twitter/X
|
||||
**Trigger**: Someone tweets about "AI-powered monitoring" hype, or a vendor announces overly complex AI-observability features
|
||||
|
||||
**Post**:
|
||||
watched a demo of AI observability that required 3 new dashboards and 2 vendor contracts to set up.
|
||||
|
||||
the goal of observability is seeing what's wrong. if your tool gets in the way of that, it's not observability.
|
||||
|
||||
(we kept ours simple.)
|
||||
|
||||
**CMO Note**: Leans into Headlamp's philosophy (small, focused plugins) vs. sprawling observability stacks. Not attacking anyone. Just stating our bias. Safe because we actually *do* keep our approach simple.
|
||||
|
||||
---
|
||||
|
||||
### Template 4: Real-Time Response to "How Do You Monitor [X]"
|
||||
|
||||
**Platform**: Twitter/X (Thread)
|
||||
**Trigger**: Someone asks "how do you monitor GPU usage" or "how do you track CSI performance"
|
||||
|
||||
**Thread Option A** (GPU):
|
||||
Q: How do you monitor GPU usage in Kubernetes?
|
||||
|
||||
Short answer: You look at actual metrics. Not dashboards about dashboards. Not vendor abstractions. You look at what your hardware is actually doing.
|
||||
|
||||
Headlamp + intel-gpu plugin. See your GPU. No middleman. [link to docs]
|
||||
|
||||
**Thread Option B** (Storage):
|
||||
Q: How do you track Rook/Ceph performance?
|
||||
|
||||
Real answer: Stop thinking about monitoring as a separate system. Rook is part of your cluster. You need visibility into it from the same place you look at everything else.
|
||||
|
||||
That's the whole reason we built the Rook plugin. [link to docs]
|
||||
|
||||
**CMO Note**: These are hyperspecific. Only deploy if question arises. Shows expertise without being pushy. Links to actual docs (once we have them on GH pages).
|
||||
|
||||
---
|
||||
|
||||
### Template 5: The "We Attend Quietly" Take
|
||||
|
||||
**Platform**: Mastodon
|
||||
**Trigger**: General KubeCon reflection mid-conference (March 24-25)
|
||||
|
||||
**Post**:
|
||||
KubeCon observation: Nobody is pretending their observability stack is simple anymore. Everyone admits it's complex. The conversation has shifted from "we have visibility" to "how do we make visibility manageable."
|
||||
|
||||
We have a thesis on that. (It involves not adding more layers.)
|
||||
|
||||
**CMO Note**: Intellectual positioning. Suggests we have *design philosophy* not just tools. Mastodon audience appreciates meta-commentary about industry trends. Doesn't mention product directly until the last line.
|
||||
|
||||
---
|
||||
|
||||
## If External Events (March 21-27)
|
||||
|
||||
### Template 6: Security/Supply Chain Angle
|
||||
|
||||
**Trigger**: If a security incident, CVE, or supply chain story breaks during conference
|
||||
|
||||
**Platform**: Twitter/X
|
||||
**Post**:
|
||||
[Current incident] is why we built sealed-secrets plugin.
|
||||
|
||||
Not because we think we're special. Because operators shouldn't have to choose between "use secrets" and "know where they're being stored."
|
||||
|
||||
If you're at #KubeCon, stop by and we can talk about it. [link]
|
||||
|
||||
**CMO Note**: Shows we're paying attention. Ties conference energy to our actual products. Empathetic (don't position as saviors, just problem-solvers). Only use if an actual security story breaks.
|
||||
|
||||
---
|
||||
|
||||
### Template 7: Cost Angle
|
||||
|
||||
**Trigger**: If cost/efficiency is a hot KubeCon keynote theme, or someone discusses "cost-aware monitoring"
|
||||
|
||||
**Platform**: LinkedIn
|
||||
**Post**:
|
||||
KubeCon theme observation: "Cost-aware observability" is trending because teams are finally admitting that monitoring infrastructure is expensive.
|
||||
|
||||
The plugin approach (small, focused, optional) is inherently cost-aware. You don't pay for observability you don't use.
|
||||
|
||||
This is intentional design.
|
||||
|
||||
**CMO Note**: Positions Headlamp's modular philosophy as a *feature*. Not "we're cheaper" but "we're more efficient by design." Works if cost is a main theme.
|
||||
|
||||
---
|
||||
|
||||
## Post-KubeCon (March 27+)
|
||||
|
||||
### Template 8: The Recap
|
||||
|
||||
**Platform**: Twitter/X
|
||||
**Trigger**: March 27-28, after conference ends
|
||||
|
||||
**Post**:
|
||||
KubeCon takeaway: The best tools are the ones your team forgets they're using because they just work.
|
||||
|
||||
We built Headlamp plugins like that. Small. Focused. Invisible until you need them.
|
||||
|
||||
Did we miss you in Amsterdam? [link to plugin docs]
|
||||
|
||||
**CMO Note**: Humble, unsalesy. Doesn't claim we nailed it, just states our design goal. Bridges back to self-directed learning/documentation (not aggressive marketing).
|
||||
|
||||
---
|
||||
|
||||
## General Guidelines for Day-Of Responses
|
||||
|
||||
1. **Monitor, don't dominate**: Respond to conversations, don't start them.
|
||||
2. **Listen for pain, not keywords**: "I can't see X" beats "person mentioned dashboard."
|
||||
3. **Be helpful first**: Answer questions. Mention our stuff only if relevant.
|
||||
4. **Keep it real**: If someone asks a question we don't have a good answer for, say so.
|
||||
5. **Timing**: Responses should go out within 2-4 hours of trigger, not instant (not trying too hard).
|
||||
6. **Tone check**: Every response should pass the "would an actual operator write this" test.
|
||||
|
||||
---
|
||||
|
||||
## Tools & Hashtags
|
||||
|
||||
**Primary hashtag**: #KubeCon (volume 24-26 March)
|
||||
**Secondary hashtags**: #KubeCon2026, #cloudnative, #kubernetes
|
||||
**Response hashtags**: #observability, #k8s, #platform-engineering (context-specific)
|
||||
|
||||
**Monitoring tools** (if CMO provides access):
|
||||
- Twitter search: `#KubeCon`
|
||||
- Bluesky search: `KubeCon`
|
||||
- Reddit: r/kubernetes, r/devops, r/SRE (watch for questions)
|
||||
- Slack (if we're in cloud-native Slack): #kubecon-2026
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- These are *optional* responses, not a mandate to post daily
|
||||
- Only deploy if you believe the response is valuable (not hitting publish for metric's sake)
|
||||
- If conference energy is low or our voice doesn't fit the conversation, that's fine
|
||||
- Post-KubeCon reflection is most important; day-of is engagement sugar
|
||||
- If something unexpected breaks (security issue, major outage), escalate to CMO before responding
|
||||
|
||||
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
@@ -1,53 +0,0 @@
|
||||
<p align="center">
|
||||
<img src="privilegedescalation-logo.jpg" alt="Privileged Escalation" width="300" />
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
</div>
|
||||
|
||||
<h3 align="center">Headlamp plugins for the infrastructure you actually run.</h3>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://artifacthub.io/packages/search?org=privilegedescalation&kind=21">Artifact Hub</a>
|
||||
·
|
||||
<a href="https://headlamp.dev">Headlamp</a>
|
||||
·
|
||||
<a href="https://github.com/sponsors/privilegedescalation">Sponsor</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
We build open source [Headlamp](https://headlamp.dev) plugins that bring deep visibility into Kubernetes storage, networking, GPU, and security subsystems — right inside your cluster dashboard.
|
||||
|
||||
## Our Plugins
|
||||
|
||||
| Plugin | What it does | Artifact Hub |
|
||||
|--------|-------------|:---:|
|
||||
| [headlamp-rook-plugin](https://github.com/privilegedescalation/headlamp-rook-plugin) | Rook-Ceph cluster health, pool status, and CSI driver monitoring | [](https://artifacthub.io/packages/headlamp/headlamp-rook-plugin/headlamp-rook-plugin) |
|
||||
| [headlamp-tns-csi-plugin](https://github.com/privilegedescalation/headlamp-tns-csi-plugin) | TrueNAS CSI driver visibility and kbench storage benchmarking | [](https://artifacthub.io/packages/headlamp/headlamp-tns-csi-plugin/headlamp-tns-csi-plugin) |
|
||||
| [headlamp-sealed-secrets-plugin](https://github.com/privilegedescalation/headlamp-sealed-secrets-plugin) | Manage Bitnami Sealed Secrets with client-side encryption | [](https://artifacthub.io/packages/headlamp/headlamp-sealed-secrets-plugin/headlamp-sealed-secrets-plugin) |
|
||||
| [headlamp-polaris-plugin](https://github.com/privilegedescalation/headlamp-polaris-plugin) | Fairwinds Polaris security and best-practices auditing | [](https://artifacthub.io/packages/headlamp/headlamp-polaris-plugin/headlamp-polaris-plugin) |
|
||||
| [headlamp-intel-gpu-plugin](https://github.com/privilegedescalation/headlamp-intel-gpu-plugin) | Intel GPU device visibility and resource monitoring | [](https://artifacthub.io/packages/headlamp/headlamp-intel-gpu-plugin/headlamp-intel-gpu-plugin) |
|
||||
| [headlamp-kube-vip-plugin](https://github.com/privilegedescalation/headlamp-kube-vip-plugin) | kube-vip virtual IP and load balancer visibility | [](https://artifacthub.io/packages/headlamp/headlamp-kube-vip/headlamp-kube-vip) |
|
||||
|
||||
## Why Headlamp?
|
||||
|
||||
Headlamp is a CNCF-listed Kubernetes dashboard built for extensibility. Our plugins slot in natively — no separate UIs, no context switching. If you run Headlamp, you can add any of our plugins with a single command.
|
||||
|
||||
## Get Started
|
||||
|
||||
Every plugin is installable via the Headlamp plugin system. See individual repos for install instructions.
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions, bug reports, and feature requests. Open an issue on any repo or start a discussion. All projects are licensed under Apache 2.0.
|
||||
|
||||
## Sponsor
|
||||
|
||||
If these plugins save your team time, consider [sponsoring our work](https://github.com/sponsors/privilegedescalation). Sponsorship funds go directly toward new plugin development and maintenance.
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"gitAuthor": "Renovate Bot <bot@renovateapp.com>",
|
||||
"extends": ["config:recommended"],
|
||||
"baseBranches": ["main"],
|
||||
"schedule": ["every weekend"],
|
||||
"prConcurrentLimit": 5,
|
||||
"pinDigests": true,
|
||||
"packageRules": [
|
||||
{
|
||||
"matchManagers": ["npm"],
|
||||
"matchUpdateTypes": ["minor", "patch"],
|
||||
"groupName": "npm minor and patch"
|
||||
},
|
||||
{
|
||||
"matchManagers": ["npm"],
|
||||
"matchUpdateTypes": ["major"],
|
||||
"groupName": "npm major updates",
|
||||
"automerge": false
|
||||
},
|
||||
{
|
||||
"matchManagers": ["github-actions"],
|
||||
"matchUpdateTypes": ["minor", "patch"],
|
||||
"groupName": "github-actions minor and patch"
|
||||
},
|
||||
{
|
||||
"matchManagers": ["github-actions"],
|
||||
"matchUpdateTypes": ["major"],
|
||||
"groupName": "github-actions major updates",
|
||||
"automerge": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Reads a newline-separated list of changed files from stdin.
|
||||
# Outputs "pipeline-a" or "pipeline-b" to stdout.
|
||||
# Pipeline B: all files are infra-only (config, docs, workflows).
|
||||
# Pipeline A: any non-infra file present.
|
||||
|
||||
detect_pipeline() {
|
||||
local all_infra=true
|
||||
|
||||
while IFS= read -r file; do
|
||||
[ -z "$file" ] && continue
|
||||
|
||||
local filename
|
||||
local dir
|
||||
filename=$(basename "$file")
|
||||
dir=$(dirname "$file")
|
||||
|
||||
if [[ "$dir" == ".github" || "$dir" == .github/* ]] || \
|
||||
[[ "$dir" == "infra" || "$dir" == infra/* ]] || \
|
||||
[[ "$dir" == "org" || "$dir" == org/* ]] || \
|
||||
[[ "$filename" == *.md ]] || \
|
||||
[[ "$filename" == .eslintrc* ]] || \
|
||||
[[ "$filename" == .prettierrc* ]] || \
|
||||
[[ "$filename" == renovate.json* ]] || \
|
||||
[[ "$filename" == .gitignore ]] || \
|
||||
[[ "$filename" == .editorconfig ]] || \
|
||||
[[ "$filename" == LICENSE ]] || \
|
||||
[[ "$filename" == Dockerfile ]] || \
|
||||
[[ "$filename" == docker-compose* ]] || \
|
||||
[[ "$filename" == Makefile ]]; then
|
||||
continue
|
||||
else
|
||||
all_infra=false
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$all_infra" = true ]; then
|
||||
echo "pipeline-b"
|
||||
else
|
||||
echo "pipeline-a"
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
|
||||
detect_pipeline
|
||||
fi
|
||||
@@ -1,145 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/detect-pipeline.sh"
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
|
||||
assert_eq() {
|
||||
local test_name="$1" expected="$2" actual="$3"
|
||||
if [ "$expected" = "$actual" ]; then
|
||||
echo "PASS: $test_name"
|
||||
PASS=$((PASS + 1))
|
||||
else
|
||||
echo "FAIL: $test_name (expected=$expected, actual=$actual)"
|
||||
FAIL=$((FAIL + 1))
|
||||
fi
|
||||
}
|
||||
|
||||
run_detect() {
|
||||
echo "$1" | detect_pipeline
|
||||
}
|
||||
|
||||
# --- Pipeline B cases (infra-only) ---
|
||||
|
||||
assert_eq "single .github root file" "pipeline-b" \
|
||||
"$(run_detect ".github/dependabot.yml")"
|
||||
|
||||
assert_eq ".github/workflows subdirectory" "pipeline-b" \
|
||||
"$(run_detect ".github/workflows/ci.yaml")"
|
||||
|
||||
assert_eq "deeply nested .github path" "pipeline-b" \
|
||||
"$(run_detect ".github/workflows/reusable/build.yaml")"
|
||||
|
||||
assert_eq "markdown file at root" "pipeline-b" \
|
||||
"$(run_detect "README.md")"
|
||||
|
||||
assert_eq "markdown in subdirectory" "pipeline-b" \
|
||||
"$(run_detect "docs/CONTRIBUTING.md")"
|
||||
|
||||
assert_eq "eslintrc config" "pipeline-b" \
|
||||
"$(run_detect ".eslintrc.json")"
|
||||
|
||||
assert_eq "prettierrc config" "pipeline-b" \
|
||||
"$(run_detect ".prettierrc.yaml")"
|
||||
|
||||
assert_eq "renovate config" "pipeline-b" \
|
||||
"$(run_detect "renovate.json")"
|
||||
|
||||
assert_eq "renovate config5" "pipeline-b" \
|
||||
"$(run_detect "renovate.json5")"
|
||||
|
||||
assert_eq "gitignore" "pipeline-b" \
|
||||
"$(run_detect ".gitignore")"
|
||||
|
||||
assert_eq "editorconfig" "pipeline-b" \
|
||||
"$(run_detect ".editorconfig")"
|
||||
|
||||
assert_eq "LICENSE" "pipeline-b" \
|
||||
"$(run_detect "LICENSE")"
|
||||
|
||||
assert_eq "mixed infra files" "pipeline-b" \
|
||||
"$(run_detect ".github/workflows/ci.yaml
|
||||
README.md
|
||||
.eslintrc.json
|
||||
LICENSE")"
|
||||
|
||||
assert_eq "workflow + markdown combo" "pipeline-b" \
|
||||
"$(run_detect ".github/workflows/detect-pr-pipeline.yaml
|
||||
.github/workflows/README.md")"
|
||||
|
||||
assert_eq "infra root file" "pipeline-b" \
|
||||
"$(run_detect "infra/helmrelease.yaml")"
|
||||
|
||||
assert_eq "infra nested file" "pipeline-b" \
|
||||
"$(run_detect "infra/clusters/prod/kustomization.yaml")"
|
||||
|
||||
assert_eq "org root file" "pipeline-b" \
|
||||
"$(run_detect "org/CODEOWNERS")"
|
||||
|
||||
assert_eq "org nested file" "pipeline-b" \
|
||||
"$(run_detect "org/policies/branch-protection.json")"
|
||||
|
||||
assert_eq "Dockerfile" "pipeline-b" \
|
||||
"$(run_detect "Dockerfile")"
|
||||
|
||||
assert_eq "docker-compose.yaml" "pipeline-b" \
|
||||
"$(run_detect "docker-compose.yaml")"
|
||||
|
||||
assert_eq "docker-compose.override.yml" "pipeline-b" \
|
||||
"$(run_detect "docker-compose.override.yml")"
|
||||
|
||||
assert_eq "Makefile" "pipeline-b" \
|
||||
"$(run_detect "Makefile")"
|
||||
|
||||
assert_eq "mixed infra + org + workflow" "pipeline-b" \
|
||||
"$(run_detect ".github/workflows/ci.yaml
|
||||
infra/helmrelease.yaml
|
||||
org/CODEOWNERS
|
||||
README.md")"
|
||||
|
||||
# --- Pipeline A cases (has non-infra files) ---
|
||||
|
||||
assert_eq "plugin source file" "pipeline-a" \
|
||||
"$(run_detect "headlamp-polaris-plugin/src/index.tsx")"
|
||||
|
||||
assert_eq "plugin package.json" "pipeline-a" \
|
||||
"$(run_detect "headlamp-polaris-plugin/package.json")"
|
||||
|
||||
assert_eq "root source file" "pipeline-a" \
|
||||
"$(run_detect "src/main.ts")"
|
||||
|
||||
assert_eq "mixed infra + code" "pipeline-a" \
|
||||
"$(run_detect ".github/workflows/ci.yaml
|
||||
headlamp-polaris-plugin/src/index.tsx
|
||||
README.md")"
|
||||
|
||||
assert_eq "single non-infra file" "pipeline-a" \
|
||||
"$(run_detect "server.js")"
|
||||
|
||||
assert_eq "plugin code + infra files" "pipeline-a" \
|
||||
"$(run_detect "infra/helmrelease.yaml
|
||||
org/CODEOWNERS
|
||||
headlamp-polaris-plugin/src/index.tsx")"
|
||||
|
||||
# --- Edge cases ---
|
||||
|
||||
assert_eq "empty input" "pipeline-b" \
|
||||
"$(run_detect "")"
|
||||
|
||||
assert_eq "root dot file (not in infra list)" "pipeline-a" \
|
||||
"$(run_detect ".env")"
|
||||
|
||||
assert_eq ".github-like but not .github dir" "pipeline-a" \
|
||||
"$(run_detect ".github-backup/config.yaml")"
|
||||
|
||||
# --- Summary ---
|
||||
|
||||
echo ""
|
||||
echo "Results: $PASS passed, $FAIL failed"
|
||||
|
||||
if [ "$FAIL" -gt 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,85 @@
|
||||
# Social Media Batch - 2026-03-07
|
||||
|
||||
## Strategic Summary
|
||||
|
||||
First-ever social batch for Privileged Escalation. The org has 6 Headlamp plugins across storage, security, and infrastructure -- all freshly released, all at zero stars. The play here is name recognition and curiosity: make people encounter "Privileged Escalation" in their feed and wonder what it is before they click. Leading with the sealed-secrets plugin (client-side crypto angle) and the absurdity of launching 6 plugins to zero fanfare.
|
||||
|
||||
---
|
||||
|
||||
## 1. Ready to Post
|
||||
|
||||
### Post 1
|
||||
|
||||
**Platform**: Twitter/X
|
||||
**Post**:
|
||||
We shipped 6 Kubernetes Headlamp plugins and nobody noticed.
|
||||
|
||||
Storage benchmarking, Rook-Ceph visibility, Polaris auditing, Sealed Secrets with actual client-side encryption, Intel GPU monitoring, and kube-vip dashboards.
|
||||
|
||||
Zero stars across the board. We are crushing it.
|
||||
|
||||
github.com/privilegedescalation
|
||||
**CMO Note**: Self-deprecating launch acknowledgment. The honesty about zero stars is the hook -- it reads as human, not corporate. Links to the org for curious clicks.
|
||||
|
||||
---
|
||||
|
||||
### Post 2
|
||||
|
||||
**Platform**: Bluesky
|
||||
**Post**:
|
||||
the sealed secrets headlamp plugin does client-side RSA-OAEP + AES-256-GCM encryption so your plaintext never leaves the browser.
|
||||
|
||||
someone forked it last month. we have our first user. or our first person who accidentally clicked fork. either way, we are celebrating.
|
||||
**CMO Note**: Technical specificity makes it credible. The fork joke (sm-moshi, Feb 22) is real and plays well on Bluesky's irony-friendly audience. Seeds curiosity about what Headlamp plugins are.
|
||||
|
||||
---
|
||||
|
||||
### Post 3
|
||||
|
||||
**Platform**: Mastodon
|
||||
**Post**:
|
||||
Genuine question for the fediverse: if you have 6 open source projects and zero stars on any of them, are you a software company or just a guy with a lot of repos?
|
||||
|
||||
Asking for a friend. The friend is github.com/privilegedescalation.
|
||||
**CMO Note**: Mastodon audience appreciates self-aware humor. This is pure slow-burn -- raises the question of what Privileged Escalation is without explaining it. The link is there for anyone curious enough to click.
|
||||
|
||||
---
|
||||
|
||||
## 2. Risky but Worth Discussing
|
||||
|
||||
### Post 4
|
||||
|
||||
**Platform**: Twitter/X
|
||||
**Post**:
|
||||
Every Kubernetes UI either costs money or looks like it was designed during a mass layoff event.
|
||||
|
||||
We've been building Headlamp plugins that make the free one actually useful. Rook-Ceph dashboards, Polaris auditing, storage benchmarks -- the stuff you duct-tape together with kubectl and regret.
|
||||
|
||||
github.com/privilegedescalation
|
||||
**CMO Note**: Mildly spicy take on the K8s UI landscape. Does not name competitors directly but the implication is clear. Could rub Lens/Rancher people the wrong way. Worth discussing tone.
|
||||
|
||||
---
|
||||
|
||||
## 3. Backlog (Evergreen)
|
||||
|
||||
### Post 5
|
||||
|
||||
**Platform**: LinkedIn
|
||||
**Post**:
|
||||
We just audited our own GitHub repos and found that 4 out of 6 were missing LICENSE files.
|
||||
|
||||
They all had Apache-2.0 badges in the README. The actual license text? Not present. Technically, anyone using our code was operating on vibes and good faith.
|
||||
|
||||
Fixed now. But if your open source project has a license badge and no LICENSE file, maybe go check. We'll wait.
|
||||
**CMO Note**: Honest product personality at work. Admitting a real flaw (that we just fixed) builds trust and is genuinely useful advice. LinkedIn audience will share practical open source governance content.
|
||||
|
||||
---
|
||||
|
||||
### Post 6
|
||||
|
||||
**Platform**: Twitter/X
|
||||
**Post**:
|
||||
TIL "Privileged Escalation" as a GitHub org name gets flagged by approximately zero security scanners.
|
||||
|
||||
We checked.
|
||||
**CMO Note**: Pure name recognition play. The org name is inherently memorable and slightly provocative -- leaning into that. Short enough for easy engagement.
|
||||
@@ -0,0 +1,137 @@
|
||||
# Social Media Batch - 2026-03-10
|
||||
|
||||
## Strategic Summary
|
||||
|
||||
Six plugins. Each one exists because we had a specific problem in production with no good visibility. This batch is about "why" before "what" — explaining the actual Kubernetes pain point each plugin addresses, from our own experience. It's educational content that works pre-KubeCon: people don't need to know what Headlamp is to understand "oh, that problem sounds familiar." Also serves as support content for the KubeCon campaign dropping next week.
|
||||
|
||||
---
|
||||
|
||||
## 1. Ready to Post
|
||||
|
||||
### Post 1: Rook-Ceph Problem
|
||||
|
||||
**Platform**: Twitter/X
|
||||
|
||||
**Post**:
|
||||
You deploy Ceph because it's the right choice for distributed storage. Then you're staring at `ceph status` in a terminal wondering which pool is actually filling up, what the OSD rebalance is doing, and why your capacity projections are off by 40%.
|
||||
|
||||
We built headlamp-rook-plugin to see inside Ceph from a dashboard instead of grep-ing logs.
|
||||
|
||||
github.com/privilegedescalation
|
||||
|
||||
**CMO Note**: Opens with a relatable pain point (Ceph deployment without visibility), then delivers the specific solution (dashboards instead of CLI). No "exciting to announce" language. The problem-first framing resonates with people already running Ceph.
|
||||
|
||||
---
|
||||
|
||||
### Post 2: Sealed Secrets Problem
|
||||
|
||||
**Platform**: Bluesky
|
||||
|
||||
**Post**:
|
||||
Your team has a pattern:
|
||||
|
||||
1. Someone generates a secret
|
||||
2. They echo it in Slack "here's the password"
|
||||
3. It's in the channel history forever
|
||||
4. Someone rotates it, forgets to tell the database
|
||||
5. 2am incident
|
||||
|
||||
We built headlamp-sealed-secrets-plugin so the secret never leaves the browser and stays encrypted in your cluster. The plaintext never transits anywhere someone can screenshot it.
|
||||
|
||||
**CMO Note**: Captures the actual workflow failure that sealed-secrets solves. The numbering of the failure pattern is specific and darkly funny. Bluesky audience appreciates the "this is how we actually mess up" honesty.
|
||||
|
||||
---
|
||||
|
||||
### Post 3: Polaris Problem
|
||||
|
||||
**Platform**: Mastodon
|
||||
|
||||
**Post**:
|
||||
Kubernetes best practices are things you know about the week after you've already deployed your application with none of them.
|
||||
|
||||
Polaris audits your workloads against security and reliability policies. It shows you what you're doing wrong before it becomes a 3am outage.
|
||||
|
||||
We built the headlamp-polaris-plugin so you can actually see the audit results in your dashboard instead of waiting for the automated security scan email you never read.
|
||||
|
||||
**CMO Note**: Self-aware about human nature (learning best practices after deployment fails). Polaris is the solution. Mastodon audience gets the candor. Not preachy, just practical.
|
||||
|
||||
---
|
||||
|
||||
### Post 4: Intel GPU Problem
|
||||
|
||||
**Platform**: Twitter/X
|
||||
|
||||
**Post**:
|
||||
You provisioned Intel GPUs in your K8s cluster for ML workloads. Cool.
|
||||
|
||||
Now: which node has available GPU? How hot are they running? Is the scheduler actually placing workloads on GPU nodes or just on CPU? Is anything actually using them?
|
||||
|
||||
We built headlamp-intel-gpu-plugin to answer those questions from a dashboard instead of kernel logs.
|
||||
|
||||
github.com/privilegedescalation
|
||||
|
||||
**CMO Note**: Chains questions that GPU cluster operators actually have. Each question hints at a real visibility gap. The solution (dashboard instead of logs) is matter-of-fact. Specific pain point without corporate language.
|
||||
|
||||
---
|
||||
|
||||
### Post 5: TrueNAS CSI Problem
|
||||
|
||||
**Platform**: Bluesky
|
||||
|
||||
**Post**:
|
||||
Your storage driver is configured. Your benchmark says it can do 10k IOPS.
|
||||
|
||||
But what's actually happening in production? You're scheduling workloads, moving data around, and your I/O profile looks nothing like the benchmark.
|
||||
|
||||
We built headlamp-tns-csi-plugin so you can see kbench storage metrics live in your cluster dashboard. No "apply a manifest and wait for email," just see what your storage is actually doing.
|
||||
|
||||
**CMO Note**: Contrasts lab conditions (benchmark) with production reality (actual I/O profile). Storage visibility without waiting. Appeal to operators frustrated with "set it and hope" storage management.
|
||||
|
||||
---
|
||||
|
||||
### Post 6: kube-vip Problem
|
||||
|
||||
**Platform**: Twitter/X
|
||||
|
||||
**Post**:
|
||||
You've got a load balancer. You've got virtual IPs floating around your cluster. And someone's asking "which service is that IP mapped to?"
|
||||
|
||||
Now what? Grep the config? Check the VirtualIP manifest? It's 2025 and you're hunting through YAML.
|
||||
|
||||
We built headlamp-kube-vip-plugin so virtual IPs and load balancer status show up in your dashboard where you can actually see them.
|
||||
|
||||
github.com/privilegedescalation
|
||||
|
||||
**CMO Note**: Specific frustration: answering "which service" requires config hunting. The solution is dashboard visibility. Dry tone emphasizing the absurdity of 2025-era manual lookups.
|
||||
|
||||
---
|
||||
|
||||
## 2. Risky but Worth Discussing
|
||||
|
||||
### Post 7: Meta Comment (Optional)
|
||||
|
||||
**Platform**: Twitter/X
|
||||
|
||||
**Post**:
|
||||
Six Kubernetes plugins, and the common thread isn't "advanced observability" or "enterprise features."
|
||||
|
||||
It's: we had a problem. The CLI wasn't good enough. The logs were hard to parse. So we built a dashboard for it.
|
||||
|
||||
Sometimes the answer to "why do we exist" is "we got frustrated with grep."
|
||||
|
||||
**CMO Note**: Self-aware meta-commentary on why all six plugins exist. The "we got frustrated with grep" line is the voice we're known for. Could feel slightly salty to some, but earns credibility with operators who've been there. Optional amplification of the whole batch theme.
|
||||
|
||||
---
|
||||
|
||||
## 3. Backlog (Evergreen)
|
||||
|
||||
None for this batch — these posts work best as a thematic set posted over 3-5 days while driving toward KubeCon, then are less relevant after.
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- Suggested posting schedule: 1 post per day starting tomorrow (March 11), finishing by March 15, giving time for engagement before KubeCon campaign drops March 21
|
||||
- Each post stands alone but builds narrative collectively
|
||||
- Educational angle differentiates from release announcements and provides value even for non-adopters
|
||||
- Heavy on problem framing, light on pitch — fits the voice and builds trust
|
||||
Reference in New Issue
Block a user