{"id":18624241,"url":"https://github.com/wish/ctl","last_synced_at":"2025-07-05T23:08:27.092Z","repository":{"id":42075854,"uuid":"197240469","full_name":"wish/ctl","owner":"wish","description":"multi-cluster kubectl","archived":false,"fork":false,"pushed_at":"2023-10-11T21:04:22.000Z","size":82923,"stargazers_count":31,"open_issues_count":6,"forks_count":6,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-19T22:18:48.922Z","etag":null,"topics":["kubectl","kubernetes","multi-cluster"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wish.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-07-16T17:40:45.000Z","updated_at":"2024-06-13T12:19:30.000Z","dependencies_parsed_at":"2024-06-19T00:24:06.670Z","dependency_job_id":"47c81ac1-3d06-49bf-ad54-690b35875614","html_url":"https://github.com/wish/ctl","commit_stats":null,"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"purl":"pkg:github/wish/ctl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wish%2Fctl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wish%2Fctl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wish%2Fctl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wish%2Fctl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wish","download_url":"https://codeload.github.com/wish/ctl/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wish%2Fctl/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263819235,"owners_count":23516122,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["kubectl","kubernetes","multi-cluster"],"created_at":"2024-11-07T04:28:01.890Z","updated_at":"2025-07-05T23:08:27.048Z","avatar_url":"https://github.com/wish.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ctl\n\n[![Build Status](https://travis-ci.org/wish/ctl.svg?branch=master)](https://travis-ci.org/wish/ctl)\n[![Code Coverage](https://codecov.io/gh/wish/ctl/branch/master/graph/badge.svg)](https://codecov.io/gh/wish/ctl/)\n[![Go Report Card](https://goreportcard.com/badge/github.com/wish/ctl)](https://goreportcard.com/report/github.com/wish/ctl)\n\nA kubectl-like helper for interacting with multiple-clusters concurrently.\n___\n# Table of Contents\n\n- [Overview](#overview)\n- [Getting started](#getting-started)\n- [Usage](#usage)\n  - [Commands](#commands)\n  - [Types](#types)\n  - [Multi-cluster usage](#multi-cluster-usage)\n  - [Flags](#flags)\n  - [Logs](#logs)\n  - [Config](#config)\n  - [Cron](#cron)\n  - [Adhoc Jobs](#adhoc-jobs)\n- [Setup and configuration](#setup-and-configuration)\n  - [Labels](#labels)\n  - [Hiding clusters](#hiding-clusters)\n  - [Default columns](#default-columns)\n\n# Overview\nCtl is a tool that helps with using multiple Kubernetes clusters simultaneously.\n\n# Getting Started\nYou should have kubectl set up before using ctl. At the least, you should have a kubeconfig file. For wish, see [setting up k8s envs](https://github.com/contextlogic/k8s/wiki/Setting-up-your-environment-for-k8s).\n\nYou can compile the binary by running `make`. Move the binary created in `bin` to your `/bin` folder.\n\n# Usage\n\nExample:\n\n```shell\n$ ctl get pods bye --k8s_env=dev\nCONTEXT               NAMESPACE  NAME                  READY  STATUS     RESTARTS  AGE      K8S_ENV  AZ\napp-05-dev.k8s.local  default    bye-1565193600-trc9d  0/1    Succeeded  0         2h17m4s  dev      us-west-1a\napp-05-dev.k8s.local  default    bye-1565197200-hq2z6  0/1    Succeeded  0         1h17m5s  dev      us-west-1a\napp-05-dev.k8s.local  default    bye-1565200800-zg44s  0/1    Succeeded  0         17m6s    dev      us-west-1a\n```\n\n## Commands\nThe commands get, describe and log function and look similarly to that in kubectl. Get prints out a table of found resources. Describe prints out details about each found resource.\n\nThe main commands follow the format\n\n```shell\n$ ctl [command] [TYPE] [NAME] [flags]\n```\n\nThe two main operations are currently get and describe. Get prints out a table with found resources and describe prints out details about each found resource.\n\n## Types\n\nCurrently, ctl supports the following resource types (with shorthand in parentheses):\n- pods (po)\n- cronjobs\n- jobs\n- deployments (deploy)\n- replicasets (rs)\n- configmaps (cm)\n- k8s_env \n\n*k8s_env list all possible k8s_env for the clusters\n\n## Multi-cluster usage\nCtl operates across multiple cluster, but it may not be desired to work on all of them. For the advanced user, you may directly specify which cluster(s) to use via the context flag:\n\n```shell\n--context=app-05-dev.k8s.local,test-cluster.k8s.local\n```\n\nHowevever, for most users, we have added functionality to make it easier to specify and narrow down clusters.\n\nWhen configured, new labels can be added to all clusters. For example, at Wish ctl now has three global flags: k8s_env, az and region. You may use any combination of these to narrow down which clusters to query on.\n\n```shell\n$ ctl get pods --k8s_env=dev\n```\n\nAdditional columns are added to get output from labels. These columns are set by the config. See [configuration](#setup-and-configuration) for more details.\n\n## Flags\n### Namespace\nMost objects can be scoped to a namespace. By default, ctl will operate on the specified namespace in your ctl config for adhoc jobs but otherwise operates on all namespaces, but you may specify a single namespace with the `-n, --namespace` flag. For more expensive operations, specifying contexts and namespaces can improve the performance.\n\n### Labels\nObjects can be filtered by labels. The cluster-level labels above are shorthands for these labels filters (`--k8s_env=dev` is equivalent to `-l k8s_env=dev`). Use flag `-l, --label`. You can set any number of labels together: `-l a=b,b=c -l c!=d` is valid.\n\nThere are three types of label filters:\n\n**Equal**  \n`a=b`. The value of label `a` must equal `b`.\n\n**Not equal**  \n`a!=b`. The value of label `a` cannot be `b`.\n\n**Set in**  \n`a in (b,c,d)`. The value of label `a` must be one of `b`, `c` or `d`.\n\n### Get Label Columns\nFor the get command, you can add columns in the table output that corresponds to the value of such label.\n\n## Logs\nLogging syntax is quite different from that of other commands. As logging is only for pods, the syntax is just `ctl logs [NAME]`.\n\nCtl by default logs from the first container of a pod instead of asking to specify a container. If you want logs from a container that is not the first, then you must use the `-c, --container` flag.\n\nA big feature of ctl is the support for multiple logs at a time. As all of ctl uses regex search, if multiple pods are matched, then logs from all of them are printed.\n\nAdditionally, this feature supports the `-f, --follow` option which constantly streams logs.\n\n## Run\nThe run command is a wrapper over `kubectl apply`. It requires cluster config to set up.\n\nThe syntax is `ctl run APPNAME [flags]`. The label flags can be used to narrow down which cluster to run on. If there are multiple clusters in which a run is specified on, ctl randomly picks one.\n\nCtl prints out the command before running.\n\n## Config\nCluster level configs are cached. To update this cached data, run `ctl config fetch`. Sometimes you may need to delete the ctl config folder. This is normally at `$XDG_CONFIG_DIR/ctl` or `~/.config/ctl`.\n\n## Cron\nMost cronjob features can be accessed through the base ctl commands. The `ctl cron` command allows for diect manipulation of k8s cronjobs. You may see `ctl help cron` for more details.\n\n## Adhoc Jobs\nThere are 4 main commands associated with running adhoc jobs, `ctl up`, `ctl down`, `ctl login`, and `ctl cp in/out`.\n\n### ctl up APPNAME [flags]\n`ctl up APPNAME` will check the ctl-config for the APPNAME and run kubectl apply to its associated manifest. The user can supply 4 flags to this command:\n\n* `--deadline=\u003cyour deadline\u003e`, will check the manifest file for the string **\"{ACTIVE_DEADLINE_SECONDS}\"** and replace it with what was set in the flag. This flag determines the activeDeadlineSeconds of the job. When the job life the exceeded the deadline set, the job along with its associated resource will be deleted. The current default is _4 hours_.\n* `--cpu=\u003cyour cpu\u003e`, will check the manifest file for the string **{CPU}** and replace it with what was set in the flag. If no value is set, it will check ctl-config for a default value and if none is found, it will use _0.5_ as a default.\n* `--memory=\u003cyour memory\u003e`, will check the manifest file for the string **\"{ACTIVE_DEADLINE_SECODS}\"** and replace it with what was set in the flag. If no value is set, it will check ctl-config for a default value and if none is found, it will use _128Mi_ as a default.\n* `--user=\u003cyour user\u003e`, will check the manifest file for the string **{USER}** and replace it with what was set in the flag. This flag is used for spawning the job and finding the ad hoc pods associated with your name. The default name used is the user's hostname.\n* `--image=\u003cyour image\u003e`, will check the manifest file for the container specified by `--container` and override this container's image with your image\n* `--container=\u003cyour container\u003e`, will check the manifest file for your container and update it's image with the image specified by `--image` \n\n_Note that the [TTLAfterFinished](https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/) feature must be enabled on your kubernetes cluster_\n\n### ctl down APPNAME [flags]\n`ctl down APPNAME` will simply look through all of the clusters and namespaces for the users' jobs created through `ctl up` and delete them.\n\n* `--user=\u003cyour user\u003e`, will check for jobs spawned by the user. The default name used is the user's hostname.\n\n### ctl login APPNAME [flags]\n`ctl login APPNAME` will run a `kubectl exec` command on your following job. the command to be run will be defined in the ctl-config configmap and will use the pod associated with the job (if there is one). If there is no existing pod, `ctl up APPNAME` will be run before this command is executed on the newly created pod. \n\nAlso, when running login, it will give the name of the pod spawned by the job so the user can use `ctl cp`\n\n* `--user=\u003cyour user\u003e`, will check for jobs spawned by the user. The default name used is the user's hostname.\n* `--container=\u003cyour container\u003e`, will check the pod for the following container and run the command on that container. If no container is specified it will use the first one found if there are multiple containers.\n* `--python=\u003cyour python script\u003e`, will run your python script if specified or start a python shell on login\n\n### ctl cp in/out APPNAME SOURCE [flags]\n`ctl cp in/out APPNAME SOURCE` will copy files into the adhoc job pod for the given app name(`ctl cp in`) or out of the pod (`ctl cp out`) using `kubectl cp`\n\n* `--out=\u003cyour output destination\u003e`, the destination of the copied files. If no destination is set it will default to **/tmp/ctl**\n* `--container=\u003cyour container\u003e`, will check the pod for the following container and run the command on that container. If no container is specified it will use the first one found if there are multiple containers.\n\n# Setup and Configuration\nThis section refers to the optional setup on the server-side of configuration ctl for all users. To use the optional features of ctl (such as `ctl up`, `ctl down`, and `ctl login`), a ConfigMap should be added to clusters.\n\nThis ConfigMap should be located in namespace `ctl` and have name `ctl-config`.\n\n## Adhoc Job Config Setup\nTo use the ad hoc job feature above, the ctl-config must be configured in a certain way. This example will use [jsonnet](https://jsonnet.org/) to make the config file. This jsonnet can generate json and yaml files. Anything with `\u003ctext\u003e` wrapped you should change to your specific needs.\n\n```yaml\n[\n    {\n        apiVersion: 'v1',\n        kind: 'ConfigMap',\n        metadata: {\n            namespace: 'ctl',\n            name: 'ctl-config',\n        },\n        data: {\n            k8s_env: \u003cYOUR ENVIRONMENT\u003e,\n            region: \u003cYOUR REGION\u003e,\n            _hidden: \u003cTRUE OR FALSE\u003e,\n            _run: std.toString({\n                \"\u003cYOUR APPNAME\u003e\": {\n                    resources: {\n                        cpu: \"\u003cYOUR DEFAULT CPU\u003e\",\n                        memory: \"\u003cYOUR DEFAULT MEMORY\u003e\"\n                    },\n                    active: \u003cTRUE OR FALSE\u003e,\n                    login_command: \"\u003cYOUR LOGIN COMMAND HERE\u003e\",\n                    manifest: std.manifestJson(\n                        \u003cYOUR JOB MANNIFEST HERE\u003e\n\n                        for example \n\n                        {\n                            apiVersion: 'batch/v1',\n                            kind: 'Job',\n                            metadata: {\n                            name: '\u003cYOUR APPNAME\u003e-{USER}',\n                            namespace: 'ctl-oneoff',\n                            },\n                            spec: {\n                                activeDeadlineSeconds: \"{ACTIVE_DEADLINE_SECONDS}\",\n                                ttlSecondsAfterFinished: 0,\n                                template: {\n                                    metadata: {\n                                        labels: {\n                                            name: '\u003cYOUR APPNAME\u003e-{USER}',\n                                            tier: 'merchant-oneoff-pod',\n                                        },\n                                        namespace: 'merchant-oneoff',\n                                    },\n                                    spec: {\n                                        restartPolicy: Never,\n                                        containers: [\n                                            {\n                                                name: '\u003cYOUR APPNAME\u003e-pod-{USER}',\n                                                command: ['/bin/bash', '-c', '--'],\n                                                args: ['while true; do sleep 30; done;'],\n                                                image: perl,\n                                                resources: {\n                                                    requests: {\n                                                        cpu: '{CPU}',\n                                                        memory: '{MEMORY}',\n                                                    },\n                                                },\n                                            }\n                                        ],\n                                    }\n                                },\n                            }\n                        }\n                    ),\n                }\n            })\n        },\n    }\n]\n```\n\n## Labels\nAll fields in the ConfigMap that are not prefixed with an underscore are set as cluster-level labels. These labels will be propagated down to the objects on each cluster. Setting these labels are useful for describing and filtering the clusters.\n\n## Hiding clusters\nA cluster can be hidden from users by setting `_hidden` to be true.\n\n## Default columns\nYou can set default label columns to be printed with get output via `_default_columns`. Set this to be a comma separated list in string format. E.g. `\"_default_columns\":\"col1,able\"`.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwish%2Fctl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwish%2Fctl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwish%2Fctl/lists"}