Last active
February 13, 2026 15:07
-
-
Save rleap-m/ea7718c3b323b2134d092c30afad86d8 to your computer and use it in GitHub Desktop.
Inspect Kubernetes RBAC artifacts generated by MKE4 to represent orgs, teams, and grants
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| SCRIPT_NAME="$(basename "$0")" | |
| VERSION="1.0.0" | |
| usage() { | |
| cat <<EOF | |
| Usage: | |
| ${SCRIPT_NAME} <command> [options] | |
| Purpose: | |
| Inspect Kubernetes RBAC artifacts generated by MKE4 to represent orgs, teams, and grants. | |
| Commands: | |
| topology List MKE org/team ClusterRoles (aggregate roots + team layers) | |
| crb List MKE ClusterRoleBindings (cluster-scoped bindings) | |
| rb List MKE RoleBindings across all namespaces (namespaced bindings) | |
| bindings List both CRBs and RBs | |
| all List topology + bindings | |
| Options: | |
| --org <name> Filter by mke.org.name | |
| --team <name> Filter by mke.team.name | |
| --user <username> Filter by subject username (subjects[].name), e.g. "jdoe" | |
| --user-id <mke-name> Filter by mke.user.name label, e.g. "mke-name-6d6f7368..." | |
| --user-type <type> Filter by mke.user.type (e.g. ldap-group, ldap, member, individual) | |
| --list-subjects For bindings, print one row per subject (adds SUBJ_NAME column) | |
| --list-selectors For topology, print one row per selector term (structured columns) | |
| --debug Print the kubectl command being executed (readable, truncated) | |
| -v, --version Show script version | |
| -h, --help Show this help | |
| Notes: | |
| - Kubernetes label selectors do not support OR. When no filters are provided, the tool queries: | |
| 1) org/team-labeled objects (mke.org.name present) | |
| 2) individual-user-labeled objects (mke.user.type=individual) | |
| - --user filters by subjects[].name and therefore applies to individual-user bindings. It is | |
| implemented by fetching the individual-user domain and filtering in the template. | |
| Examples: | |
| ${SCRIPT_NAME} topology | |
| ${SCRIPT_NAME} topology --list-selectors | |
| ${SCRIPT_NAME} crb | |
| ${SCRIPT_NAME} crb --user-type ldap-group | |
| ${SCRIPT_NAME} crb --user jdoe | |
| ${SCRIPT_NAME} crb --user-id mke-name-6d6f7368697572 | |
| ${SCRIPT_NAME} rb --user jdoe | |
| ${SCRIPT_NAME} rb --user-type individual | |
| EOF | |
| } | |
| version() { | |
| echo "${SCRIPT_NAME} version ${VERSION}" | |
| } | |
| die() { echo "ERROR: $*" >&2; exit 1; } | |
| debug="0" | |
| list_subjects="0" | |
| list_selectors="0" | |
| debug_kubectl() { | |
| local resource="$1" | |
| local selector="$2" | |
| local template_desc="$3" | |
| local template_len="$4" | |
| if [[ "$debug" == "1" ]]; then | |
| { | |
| echo "DEBUG:" | |
| echo " kubectl get ${resource} \\" | |
| echo " -l \"${selector}\" \\" | |
| echo " -o go-template='${template_desc}…' (len=${template_len})" | |
| echo | |
| } >&2 | |
| fi | |
| } | |
| print_table() { | |
| # Split only on tabs so spaces inside fields don't create fake columns. | |
| column -t -s $'\t' | |
| } | |
| command="${1:-}" | |
| if [[ -z "${command}" || "${command}" == "-h" || "${command}" == "--help" ]]; then | |
| usage | |
| exit 0 | |
| fi | |
| if [[ "${command}" == "-v" || "${command}" == "--version" ]]; then | |
| version | |
| exit 0 | |
| fi | |
| if [[ "${command}" == --* ]]; then | |
| echo "ERROR: Missing command." >&2 | |
| echo >&2 | |
| echo "Expected one of: topology, crb, rb, bindings, all" >&2 | |
| echo >&2 | |
| echo "Example:" >&2 | |
| echo " ${SCRIPT_NAME} crb --user-type ldap-group" >&2 | |
| exit 1 | |
| fi | |
| shift || true | |
| org="" | |
| team="" | |
| user="" # subjects[].name (human username) | |
| user_id="" # mke.user.name (opaque/stable id label) | |
| user_type="" | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| --org) org="${2:-}"; shift 2 ;; | |
| --team) team="${2:-}"; shift 2 ;; | |
| --user) user="${2:-}"; shift 2 ;; | |
| --user-id) user_id="${2:-}"; shift 2 ;; | |
| --user-type) user_type="${2:-}"; shift 2 ;; | |
| --list-subjects) list_subjects="1"; shift ;; | |
| --list-selectors) list_selectors="1"; shift ;; | |
| --debug) debug="1"; shift ;; | |
| -v|--version) version; exit 0 ;; | |
| -h|--help) usage; exit 0 ;; | |
| *) die "Unknown argument: $1" ;; | |
| esac | |
| done | |
| # ----------------------------- | |
| # Domain selection logic | |
| # ----------------------------- | |
| # org/team domain: objects labeled with mke.org.name (org/team topology, membership CRBs, grant RBs) | |
| # user domain: objects labeled with mke.user.* (individual user grants via RB/CRB) | |
| # | |
| # Because label selectors can’t do OR, we sometimes query both domains and merge output. | |
| want_org_domain() { | |
| # If org/team filters are used, user explicitly wants org-domain. | |
| if [[ -n "${org}" || -n "${team}" ]]; then | |
| return 0 | |
| fi | |
| # If user-id or subject user is requested, it’s user-domain. | |
| if [[ -n "${user_id}" || -n "${user}" ]]; then | |
| return 1 | |
| fi | |
| # If user-type is provided: | |
| # - individual => user-domain only | |
| # - anything else => org-domain only | |
| if [[ -n "${user_type}" ]]; then | |
| if [[ "${user_type}" == "individual" ]]; then | |
| return 1 | |
| fi | |
| return 0 | |
| fi | |
| # No filters => include org-domain. | |
| return 0 | |
| } | |
| want_user_domain() { | |
| # If org/team filters are used, user explicitly wants org-domain only. | |
| if [[ -n "${org}" || -n "${team}" ]]; then | |
| return 1 | |
| fi | |
| # If user-id or subject user is requested, include user-domain. | |
| if [[ -n "${user_id}" || -n "${user}" ]]; then | |
| return 0 | |
| fi | |
| # If user-type is provided: | |
| # - individual => user-domain | |
| # - anything else => org-domain only | |
| if [[ -n "${user_type}" ]]; then | |
| if [[ "${user_type}" == "individual" ]]; then | |
| return 0 | |
| fi | |
| return 1 | |
| fi | |
| # No filters => include user-domain (default individual grants). | |
| return 0 | |
| } | |
| # ----------------------------- | |
| # Label selector builders | |
| # ----------------------------- | |
| build_selector_org_domain() { | |
| # Base: anything with mke.org.name label | |
| local sel="mke.org.name" | |
| [[ -n "${org}" ]] && sel="${sel},mke.org.name=${org}" | |
| [[ -n "${team}" ]] && sel="${sel},mke.team.name=${team}" | |
| [[ -n "${user_type}" ]] && sel="${sel},mke.user.type=${user_type}" | |
| echo "${sel}" | |
| } | |
| build_selector_user_domain() { | |
| # Base: user-grant objects | |
| # If user_id provided, prefer exact match. | |
| if [[ -n "${user_id}" ]]; then | |
| local sel="mke.user.name=${user_id}" | |
| [[ -n "${user_type}" ]] && sel="${sel},mke.user.type=${user_type}" | |
| echo "${sel}" | |
| return | |
| fi | |
| # If user_type provided, use it (often "individual" here). | |
| if [[ -n "${user_type}" ]]; then | |
| echo "mke.user.type=${user_type}" | |
| return | |
| fi | |
| # Default: individual grants | |
| echo "mke.user.type=individual" | |
| } | |
| build_selector_topology() { | |
| # Topology objects are org/team ClusterRoles; they live in org-domain. | |
| local sel="mke.org.name" | |
| [[ -n "${org}" ]] && sel="${sel},mke.org.name=${org}" | |
| [[ -n "${team}" ]] && sel="${sel},mke.team.name=${team}" | |
| echo "${sel}" | |
| } | |
| # ----------------------------- | |
| # Commands | |
| # ----------------------------- | |
| list_topology() { | |
| local selector template | |
| selector="$(build_selector_topology)" | |
| if [[ "${list_selectors}" == "1" ]]; then | |
| # One row per selector term: | |
| # - matchLabels => SEL_TYPE=label, SEL_KEY=key, SEL_OP="=", SEL_VALUES=value | |
| # - matchExpressions => SEL_TYPE=expr, SEL_KEY=key, SEL_OP=<operator>, SEL_VALUES=csv or <none> | |
| # | |
| # Note: kubectl go-template does NOT include sprig funcs like join, so CSV is built manually. | |
| template='{{printf "NAME\tORG\tTEAM\tAGG\tRULES\tSEL_TYPE\tSEL_KEY\tSEL_OP\tSEL_VALUES\n"}}{{range .items}}{{ $it := . }}{{ $agg := "no" }}{{ if $it.aggregationRule }}{{ $agg = "yes" }}{{ end }}{{ $rules := "no" }}{{ if $it.rules }}{{ $rules = "yes" }}{{ end }}{{ $org := (or (index $it.metadata.labels "mke.org.name") "<none>") }}{{ $team := (or (index $it.metadata.labels "mke.team.name") "<none>") }}{{ if and $it.aggregationRule $it.aggregationRule.clusterRoleSelectors }}{{ range $it.aggregationRule.clusterRoleSelectors }}{{ if .matchLabels }}{{ range $k, $v := .matchLabels }}{{ printf "%s\t%s\t%s\t%s\t%s\tlabel\t%s\t=\t%s\n" $it.metadata.name $org $team $agg $rules $k $v }}{{ end }}{{ end }}{{ if .matchExpressions }}{{ range .matchExpressions }}{{ $vals := "<none>" }}{{ if .values }}{{ $vals = "" }}{{ $first := true }}{{ range .values }}{{ if not $first }}{{ $vals = printf "%s,%s" $vals . }}{{ else }}{{ $vals = printf "%s" . }}{{ end }}{{ $first = false }}{{ end }}{{ end }}{{ printf "%s\t%s\t%s\t%s\t%s\texpr\t%s\t%s\t%s\n" $it.metadata.name $org $team $agg $rules .key .operator $vals }}{{ end }}{{ end }}{{ end }}{{ else }}{{ printf "%s\t%s\t%s\t%s\t%s\t<none>\t<none>\t<none>\t<none>\n" $it.metadata.name $org $team $agg $rules }}{{ end }}{{end}}' | |
| debug_kubectl \ | |
| "clusterroles" \ | |
| "${selector}" \ | |
| "topology (list) columns: NAME ORG TEAM AGG RULES SEL_TYPE SEL_KEY SEL_OP SEL_VALUES" \ | |
| "${#template}" | |
| kubectl get clusterroles -l "${selector}" -o go-template="${template}" | print_table | |
| return | |
| fi | |
| # Default: compact view with selector-group count (RULES before SEL_COUNT for column alignment w/ list-selectors) | |
| template='{{printf "NAME\tORG\tTEAM\tAGG\tRULES\tSEL_COUNT\n"}}{{range .items}}{{ $agg := "no" }}{{ if .aggregationRule }}{{ $agg = "yes" }}{{ end }}{{ $rules := "no" }}{{ if .rules }}{{ $rules = "yes" }}{{ end }}{{ $selc := 0 }}{{ if .aggregationRule }}{{ $selc = (len .aggregationRule.clusterRoleSelectors) }}{{ end }}{{printf "%s\t%s\t%s\t%s\t%s\t%d\n" .metadata.name (or (index .metadata.labels "mke.org.name") "<none>") (or (index .metadata.labels "mke.team.name") "<none>") $agg $rules $selc}}{{end}}' | |
| debug_kubectl \ | |
| "clusterroles" \ | |
| "${selector}" \ | |
| "topology columns: NAME ORG TEAM AGG RULES SEL_COUNT" \ | |
| "${#template}" | |
| kubectl get clusterroles -l "${selector}" -o go-template="${template}" | print_table | |
| } | |
| list_crb() { | |
| local selector_org selector_user header rows_org rows_user | |
| selector_org="$(build_selector_org_domain)" | |
| selector_user="$(build_selector_user_domain)" | |
| if [[ "${list_subjects}" == "1" ]]; then | |
| header="NAME\tORG\tTEAM\tUSER_TYPE\tSUBJ_KIND\tSUBJ_NAME\tROLE_REF\n" | |
| # Org-domain rows (no subject-name filtering here) | |
| rows_org='{{range .items}}{{ $it := . }}{{ $org := (or (index $it.metadata.labels "mke.org.name") "<none>") }}{{ $team := (or (index $it.metadata.labels "mke.team.name") "<none>") }}{{ $ut := (or (index $it.metadata.labels "mke.user.type") "<none>") }}{{ if $it.subjects }}{{range $it.subjects}}{{printf "%s\t%s\t%s\t%s\t%s\t%s\t%s/%s\n" $it.metadata.name $org $team $ut (or .kind "<none>") (or .name "<none>") $it.roleRef.kind $it.roleRef.name}}{{end}}{{ else }}{{printf "%s\t%s\t%s\t%s\t%s\t%s\t%s/%s\n" $it.metadata.name $org $team $ut "<none>" "<none>" $it.roleRef.kind $it.roleRef.name}}{{ end }}{{end}}' | |
| # User-domain rows (optionally filter by --user username) | |
| if [[ -n "${user}" ]]; then | |
| rows_user='{{ $want := "'"${user}"'" }}{{range .items}}{{ $it := . }}{{ $org := (or (index $it.metadata.labels "mke.org.name") "<none>") }}{{ $team := (or (index $it.metadata.labels "mke.team.name") "<none>") }}{{ $ut := (or (index $it.metadata.labels "mke.user.type") "<none>") }}{{ if $it.subjects }}{{range $it.subjects}}{{ if eq .name $want }}{{printf "%s\t%s\t%s\t%s\t%s\t%s\t%s/%s\n" $it.metadata.name $org $team $ut (or .kind "<none>") (or .name "<none>") $it.roleRef.kind $it.roleRef.name}}{{ end }}{{end}}{{ end }}{{end}}' | |
| else | |
| rows_user="${rows_org}" | |
| fi | |
| { | |
| printf "%b" "${header}" | |
| if want_org_domain; then | |
| debug_kubectl "clusterrolebindings" "${selector_org}" "CRB (list) columns: NAME ORG TEAM USER_TYPE SUBJ_KIND SUBJ_NAME ROLE_REF (org-domain)" "${#rows_org}" | |
| kubectl get clusterrolebindings -l "${selector_org}" -o go-template="${rows_org}" || true | |
| fi | |
| if want_user_domain; then | |
| debug_kubectl "clusterrolebindings" "${selector_user}" "CRB (list) columns: NAME ORG TEAM USER_TYPE SUBJ_KIND SUBJ_NAME ROLE_REF (user-domain)" "${#rows_user}" | |
| kubectl get clusterrolebindings -l "${selector_user}" -o go-template="${rows_user}" || true | |
| fi | |
| } | print_table | |
| return | |
| fi | |
| header="NAME\tORG\tTEAM\tUSER_TYPE\tSUBJ_KIND\tSUBJ_COUNT\tROLE_REF\n" | |
| rows_org='{{range .items}}{{ $sc := 0 }}{{ if .subjects }}{{ $sc = (len .subjects) }}{{ end }}{{ $sk := "<none>" }}{{ if .subjects }}{{ $sk = (or (index .subjects 0).kind "<none>") }}{{ end }}{{printf "%s\t%s\t%s\t%s\t%s\t%d\t%s/%s\n" .metadata.name (or (index .metadata.labels "mke.org.name") "<none>") (or (index .metadata.labels "mke.team.name") "<none>") (or (index .metadata.labels "mke.user.type") "<none>") $sk $sc .roleRef.kind .roleRef.name}}{{end}}' | |
| if [[ -n "${user}" ]]; then | |
| # Only print rows where first subject matches the requested username (common shape for individual grants). | |
| rows_user='{{ $want := "'"${user}"'" }}{{range .items}}{{ if and .subjects (eq (index .subjects 0).name $want) }}{{ $sc := 1 }}{{ $sk := (or (index .subjects 0).kind "<none>") }}{{printf "%s\t%s\t%s\t%s\t%s\t%d\t%s/%s\n" .metadata.name (or (index .metadata.labels "mke.org.name") "<none>") (or (index .metadata.labels "mke.team.name") "<none>") (or (index .metadata.labels "mke.user.type") "<none>") $sk $sc .roleRef.kind .roleRef.name}}{{ end }}{{end}}' | |
| else | |
| rows_user="${rows_org}" | |
| fi | |
| { | |
| printf "%b" "${header}" | |
| if want_org_domain; then | |
| debug_kubectl "clusterrolebindings" "${selector_org}" "CRB columns: NAME ORG TEAM USER_TYPE SUBJ_KIND SUBJ_COUNT ROLE_REF (org-domain)" "${#rows_org}" | |
| kubectl get clusterrolebindings -l "${selector_org}" -o go-template="${rows_org}" || true | |
| fi | |
| if want_user_domain; then | |
| debug_kubectl "clusterrolebindings" "${selector_user}" "CRB columns: NAME ORG TEAM USER_TYPE SUBJ_KIND SUBJ_COUNT ROLE_REF (user-domain)" "${#rows_user}" | |
| kubectl get clusterrolebindings -l "${selector_user}" -o go-template="${rows_user}" || true | |
| fi | |
| } | print_table | |
| } | |
| list_rb() { | |
| local selector_org selector_user header rows_org rows_user | |
| selector_org="$(build_selector_org_domain)" | |
| selector_user="$(build_selector_user_domain)" | |
| if [[ "${list_subjects}" == "1" ]]; then | |
| header="NAMESPACE\tNAME\tORG\tTEAM\tUSER_TYPE\tBINDING_TYPE\tSUBJ_KIND\tSUBJ_NAME\tROLE_REF\n" | |
| rows_org='{{range .items}}{{ $it := . }}{{ $org := (or (index $it.metadata.labels "mke.org.name") "<none>") }}{{ $team := (or (index $it.metadata.labels "mke.team.name") "<none>") }}{{ $ut := (or (index $it.metadata.labels "mke.user.type") "<none>") }}{{ $bt := "enforcement" }}{{ if eq (index $it.metadata.labels "mke.grant.record") "true" }}{{ $bt = "record" }}{{ end }}{{ if $it.subjects }}{{range $it.subjects}}{{printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s/%s\n" $it.metadata.namespace $it.metadata.name $org $team $ut $bt (or .kind "<none>") (or .name "<none>") $it.roleRef.kind $it.roleRef.name}}{{end}}{{ else }}{{printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s/%s\n" $it.metadata.namespace $it.metadata.name $org $team $ut $bt "<none>" "<none>" $it.roleRef.kind $it.roleRef.name}}{{ end }}{{end}}' | |
| if [[ -n "${user}" ]]; then | |
| rows_user='{{ $want := "'"${user}"'" }}{{range .items}}{{ $it := . }}{{ $org := (or (index $it.metadata.labels "mke.org.name") "<none>") }}{{ $team := (or (index $it.metadata.labels "mke.team.name") "<none>") }}{{ $ut := (or (index $it.metadata.labels "mke.user.type") "<none>") }}{{ $bt := "enforcement" }}{{ if eq (index $it.metadata.labels "mke.grant.record") "true" }}{{ $bt = "record" }}{{ end }}{{ if $it.subjects }}{{range $it.subjects}}{{ if eq .name $want }}{{printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s/%s\n" $it.metadata.namespace $it.metadata.name $org $team $ut $bt (or .kind "<none>") (or .name "<none>") $it.roleRef.kind $it.roleRef.name}}{{ end }}{{end}}{{ end }}{{end}}' | |
| else | |
| rows_user="${rows_org}" | |
| fi | |
| { | |
| printf "%b" "${header}" | |
| if want_org_domain; then | |
| debug_kubectl "rolebindings (all namespaces)" "${selector_org}" "RB (list) columns: NAMESPACE NAME ORG TEAM USER_TYPE BINDING_TYPE SUBJ_KIND SUBJ_NAME ROLE_REF (org-domain)" "${#rows_org}" | |
| kubectl get rolebindings -A -l "${selector_org}" -o go-template="${rows_org}" || true | |
| fi | |
| if want_user_domain; then | |
| debug_kubectl "rolebindings (all namespaces)" "${selector_user}" "RB (list) columns: NAMESPACE NAME ORG TEAM USER_TYPE BINDING_TYPE SUBJ_KIND SUBJ_NAME ROLE_REF (user-domain)" "${#rows_user}" | |
| kubectl get rolebindings -A -l "${selector_user}" -o go-template="${rows_user}" || true | |
| fi | |
| } | print_table | |
| return | |
| fi | |
| header="NAMESPACE\tNAME\tORG\tTEAM\tUSER_TYPE\tBINDING_TYPE\tSUBJ_KIND\tSUBJ_COUNT\tROLE_REF\n" | |
| rows_org='{{range .items}}{{ $sc := 0 }}{{ if .subjects }}{{ $sc = (len .subjects) }}{{ end }}{{ $sk := "<none>" }}{{ if .subjects }}{{ $sk = (or (index .subjects 0).kind "<none>") }}{{ end }}{{ $bt := "enforcement" }}{{ if eq (index .metadata.labels "mke.grant.record") "true" }}{{ $bt = "record" }}{{ end }}{{printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%d\t%s/%s\n" .metadata.namespace .metadata.name (or (index .metadata.labels "mke.org.name") "<none>") (or (index .metadata.labels "mke.team.name") "<none>") (or (index .metadata.labels "mke.user.type") "<none>") $bt $sk $sc .roleRef.kind .roleRef.name}}{{end}}' | |
| if [[ -n "${user}" ]]; then | |
| rows_user='{{ $want := "'"${user}"'" }}{{range .items}}{{ if and .subjects (eq (index .subjects 0).name $want) }}{{ $sc := 1 }}{{ $sk := (or (index .subjects 0).kind "<none>") }}{{ $bt := "enforcement" }}{{ if eq (index .metadata.labels "mke.grant.record") "true" }}{{ $bt = "record" }}{{ end }}{{printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%d\t%s/%s\n" .metadata.namespace .metadata.name (or (index .metadata.labels "mke.org.name") "<none>") (or (index .metadata.labels "mke.team.name") "<none>") (or (index .metadata.labels "mke.user.type") "<none>") $bt $sk $sc .roleRef.kind .roleRef.name}}{{ end }}{{end}}' | |
| else | |
| rows_user="${rows_org}" | |
| fi | |
| { | |
| printf "%b" "${header}" | |
| if want_org_domain; then | |
| debug_kubectl "rolebindings (all namespaces)" "${selector_org}" "RB columns: NAMESPACE NAME ORG TEAM USER_TYPE BINDING_TYPE SUBJ_KIND SUBJ_COUNT ROLE_REF (org-domain)" "${#rows_org}" | |
| kubectl get rolebindings -A -l "${selector_org}" -o go-template="${rows_org}" || true | |
| fi | |
| if want_user_domain; then | |
| debug_kubectl "rolebindings (all namespaces)" "${selector_user}" "RB columns: NAMESPACE NAME ORG TEAM USER_TYPE BINDING_TYPE SUBJ_KIND SUBJ_COUNT ROLE_REF (user-domain)" "${#rows_user}" | |
| kubectl get rolebindings -A -l "${selector_user}" -o go-template="${rows_user}" || true | |
| fi | |
| } | print_table | |
| } | |
| list_bindings() { | |
| echo "== ClusterRoleBindings ==" | |
| list_crb | |
| echo | |
| echo "== RoleBindings (all namespaces) ==" | |
| list_rb | |
| } | |
| list_all() { | |
| echo "== RBAC topology (org/team roles) ==" | |
| list_topology | |
| echo | |
| echo "== RBAC enforcement bindings ==" | |
| list_bindings | |
| } | |
| case "${command}" in | |
| topology) list_topology ;; | |
| crb) list_crb ;; | |
| rb) list_rb ;; | |
| bindings) list_bindings ;; | |
| all) list_all ;; | |
| *) die "Unknown command: ${command} (try --help)" ;; | |
| esac |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment