{"id":16862148,"url":"https://github.com/maelvls/hack-your-controller-in-bash","last_synced_at":"2026-04-20T10:32:25.722Z","repository":{"id":41090698,"uuid":"502150024","full_name":"maelvls/hack-your-controller-in-bash","owner":"maelvls","description":"Presentation given at KCD Berlin 2022. In the presentation, we show how to create a simple Bash controller, with the example ExternalSecrets and Vault.","archived":false,"fork":false,"pushed_at":"2023-06-18T10:10:22.000Z","size":3824,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-17T00:37:10.207Z","etag":null,"topics":["bash","external-secrets","external-secrets-operator","kubectl","kubernetes","vault"],"latest_commit_sha":null,"homepage":"https://maelvls.dev/hack-your-controller-in-bash/","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/maelvls.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2022-06-10T18:54:35.000Z","updated_at":"2022-07-15T20:33:09.000Z","dependencies_parsed_at":"2024-10-13T14:46:02.416Z","dependency_job_id":null,"html_url":"https://github.com/maelvls/hack-your-controller-in-bash","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/maelvls/hack-your-controller-in-bash","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maelvls%2Fhack-your-controller-in-bash","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maelvls%2Fhack-your-controller-in-bash/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maelvls%2Fhack-your-controller-in-bash/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maelvls%2Fhack-your-controller-in-bash/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maelvls","download_url":"https://codeload.github.com/maelvls/hack-your-controller-in-bash/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maelvls%2Fhack-your-controller-in-bash/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32043020,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-20T00:18:06.643Z","status":"online","status_checked_at":"2026-04-20T02:00:06.527Z","response_time":94,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["bash","external-secrets","external-secrets-operator","kubectl","kubernetes","vault"],"created_at":"2024-10-13T14:34:40.500Z","updated_at":"2026-04-20T10:32:25.699Z","avatar_url":"https://github.com/maelvls.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Hack your Kubernetes controller in Bash in 10 minutes!\n\n| [🔴 YouTube Recording][kcd-berlin-2022], [🎓️ Slides][slides] ([backup][backup]) |\n| ----------------------------- |\n\n[![Hack your own Kuberntes Controller in less than 10 min in Bash - M. Valais \u0026 A. Le Squéren, OneStock](http://img.youtube.com/vi/Nwz66AT3AGY/0.jpg)][kcd-berlin-2022]\n\n[kcd-berlin-2022]: https://www.youtube.com/watch?v=Nwz66AT3AGY\u0026t=632s \"Hack your own Kuberntes Controller in less than 10 min in Bash - M. Valais \u0026 A. Le Squéren, OneStock\"\n\n![Screenshot from 2022-07-05 11-22-50](https://user-images.githubusercontent.com/2195781/177296057-299539d2-8de6-444f-bf58-3f1cc59fad0a.png)\n\n[slides]: https://maelvls.dev/hack-your-controller-in-bash/ \"Slides of the presentation 'Hack your Kubernetes controller in Bash in 10 minutes!'\"\n[backup]: https://slides.com/maelvls/hack-your-kubernetes-controller-in-10-minutes \"Slides of the presentation 'Hack your Kubernetes controller in Bash in 10 minutes!'\"\n\nOn 30 June 2022, Antoine Le Squéren and Maël Valais presented \"Hack your\nKubernetes controller in Bash in 10 minutes!\" at Kubernetetes Community\nDays Berlin. The rest of this document shows how to run the \"one-liner\ncontroller\" we talked about in the presentation.\n\n## Try the one-liner controller for yourself (`controller.sh`)\n\nIn the presentation, we show a controller that takes for form of a\n\"one-liner\" that you can copy-paste in your terminal.\n\n\n\n```sh\nkubectl get externalsecret --watch -ojson \\\n  | jq 'select(.status.conditions[]?.reason == \"SecretSyncedError\")' --unbuffered \\\n  | jq '.spec.data[0].remoteRef | \"\\(.key) \\(.property)\"' -r --unbuffered \\\n  | while read key property; do\n    vault kv put $key $property=somerandomvalue\n  done\n```\n\n![Screenshot from 2022-07-05 11-23-09](https://user-images.githubusercontent.com/2195781/177296067-05cc0a96-6be3-454e-aecb-2a36ca3286d1.png)\n\nThe file `controller.sh` contains the above command.\n\nTo try this controller, you will need to install the following tools:\n\n- `docker` which you can get with [`colima`](https://github.com/abiosoft/colima)\n  on M1 and Intel Macs (instead of Docker Desktop for Mac).\n- [`kubectl`](https://kubernetes.io/docs/tasks/tools/install-kubectl/) 1.24 or above (required for `--subresource`),\n- [`k3d`](https://k3d.io/v5.4.3/#installation).\n\nThen, run the following command. The command creates a K3s cluster\nand installs Vault and external-secrets, as well as an ExternalSecret\nobject called `postgres` for demonstration purposes:\n\n```sh\n./setup.sh\n```\n\nRun the following long-running command (this is an optional step):\n\n```console\n$ kubectl get externalsecret --watch\nNAME       KEY                     PROPERTY   READY   REASON         MESSAGE\npostgres   secret/dev-1/postgres   password   True    SecretSynced   Secret was synced\npostgres   secret/dev-1/postgres   password   True    SecretSynced   Secret was synced\n```\n\nOpen a second shell session to create a tunnel to Vault with\nthe following command:\n\n```console\nkubectl port-forward -n vault vault-0 8200\n```\n\nIn a third shell session, run the controller with the command:\n\n```sh\nexport VAULT_ADDR=http://localhost:8200 VAULT_TOKEN=root\n./controller.sh\n```\n\nLooking at the first shell session (the one with `kubectl get externalsecret --watch`)\nyou will see the `postgres` external secret going from `SecretSyncedError` to\n`SecretSynced`:\n\n```text\nNAME       KEY                     PROPERTY   READY   REASON             MESSAGE\npostgres   secret/dev-1/postgres   password   False   SecretSyncedError  could not get secret data from provider\npostgres   secret/dev-1/postgres   password   True    SecretSynced       Secret was synced\n```\n\nThe controller's output isn't great. It just shows what `vault put` shows\nwhen an external secret is processed.\n\n```console\n$ ./controller.sh\n======= Secret Path =======\nsecret/data/dev-1/postgres\n\n======= Metadata =======\nKey                Value\n---                -----\ncreated_time       2022-06-27T15:41:59.346502796Z\ncustom_metadata    \u003cnil\u003e\ndeletion_time      n/a\ndestroyed          false\nversion            1\n```\n\n(Optional) You can now run the controller in a Pod. Run the following\ntwo commands to build the container and\n\n```sh\ndocker buildx build . -t controller:local -o type=docker,dest=img.tar \u0026\u0026 k3d images import img.tar\nkubectl apply -f ./deploy.yaml\n```\n\n\u003e **⁉️ `docker build` vs. `docker buildx build`**: In the above command, we use the\n\u003e `buildx` subcommand, also called BuildKit. Unlike the traditional `docker build`\n\u003e command, with BuildKit, it is possible to save the Docker-compatible image tarball\n\u003e directly to disk using `-o type=docker,dest=img.tar`.\n\nCheck that the controller is running:\n\n```console\n$ kubectl logs -f deployment/controller\n======= Secret Path =======\nsecret/data/dev-1/postgres\n\n======= Metadata =======\nKey                Value\n---                -----\ncreated_time       2022-06-27T15:41:59.346502796Z\ncustom_metadata    \u003cnil\u003e\ndeletion_time      n/a\ndestroyed          false\nversion            2\n```\n\nTo see if the controller is working, you can run the following command:\n\n```sh\nvault kv metadata delete secret/dev-1/postgres\n```\n\nIn the first shell session where `kubectl get externalsecret --watch` is running,\nyou will see the external secret from `SecretSynced` to `SecretSyncedError`\nand back to `SecretSyncedError`:\n\n```text\nNAME       KEY                     PROPERTY   READY   REASON             MESSAGE\npostgres   secret/dev-1/postgres   password   False   SecretSyncedError  could not get secret data from provider\npostgres   secret/dev-1/postgres   password   True    SecretSynced       Secret was synced\npostgres   secret/dev-1/postgres   password   True    SecretSynced       Secret was synced\npostgres   secret/dev-1/postgres   password   True    SecretSynced       Secret was synced\npostgres   secret/dev-1/postgres   password   False   SecretSyncedError  could not get secret data from provider\npostgres   secret/dev-1/postgres   password   True    SecretSynced       Secret was synced\n```\n\n## Go further: try the advanced Bash controller (`controller-with-conditions.sh`)\n\nAs we demonstrated during the presentation, one-liner `controller.sh` has a\nvery uninformative logs.\n\nOn top of poor logs, `controller.sh` does not inform Kubernetes users why a\nparticular ExternalSecret object does not seem to be picked up by the\ncontroller. Nothing shows in the status, and nothing shows in events.\n\nThe file `controller-with-conditions.sh` is similar to `controller.sh`, except\nfor three aspects:\n\n1. The user is now alerted of problems with the condition `Created`.\n2. The user now has to set the annotation `create: true` to enable the\n   auto-generation of the secret in Vault.\n3. The logs are now well structured.\n\nIn a first shell session, turn on port-forwarding to Vault with the command:\n\n```console\nkubectl port-forward -n vault vault-0 8200\n```\n\nIn a second shell session, run the controller with the command:\n\n```sh\nexport VAULT_ADDR=http://localhost:8200 VAULT_TOKEN=root\n./controller-with-conditions.sh\n```\n\nYou can now \"enable\" the behavior on the external secret `postgres` with the\nfollowing command:\n\n```sh\nkubectl annotate externalsecret postgres create=true\n```\n\nThe logs should look like this:\n\n```console\n$ ./controller-with-conditions.sh\ninfo: started watching ExternalSecrets.\npostgres: the ExternalSecret is Ready=False, let us create a random password and put it in Vault.\npostgres: the Vault secret was created.\npostgres: inconsistency: Created is True but SecretSyncedError is False. Attempting to recreate the secret in Vault to fix this issue.\npostgres: the ExternalSecret is Ready=False, let us create a random password and put it in Vault.\npostgres: the Vault secret was created.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaelvls%2Fhack-your-controller-in-bash","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaelvls%2Fhack-your-controller-in-bash","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaelvls%2Fhack-your-controller-in-bash/lists"}