{"id":17239787,"url":"https://github.com/boz/ephemerald","last_synced_at":"2026-03-15T17:33:14.704Z","repository":{"id":57522378,"uuid":"88327149","full_name":"boz/ephemerald","owner":"boz","description":"Ephemeral docker-based server instances for (parallel) testing","archived":false,"fork":false,"pushed_at":"2019-10-16T23:15:41.000Z","size":5413,"stargazers_count":55,"open_issues_count":0,"forks_count":9,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-04T09:04:29.520Z","etag":null,"topics":["docker","golang","parallel-tests","testing-tools"],"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/boz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-04-15T05:52:36.000Z","updated_at":"2024-11-28T04:42:00.000Z","dependencies_parsed_at":"2022-08-26T23:41:43.984Z","dependency_job_id":null,"html_url":"https://github.com/boz/ephemerald","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/boz/ephemerald","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boz%2Fephemerald","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boz%2Fephemerald/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boz%2Fephemerald/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boz%2Fephemerald/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/boz","download_url":"https://codeload.github.com/boz/ephemerald/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boz%2Fephemerald/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30547960,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-15T15:03:43.933Z","status":"ssl_error","status_checked_at":"2026-03-15T15:03:37.630Z","response_time":61,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["docker","golang","parallel-tests","testing-tools"],"created_at":"2024-10-15T05:49:48.051Z","updated_at":"2026-03-15T17:33:14.668Z","avatar_url":"https://github.com/boz.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ephemerald\n\nEphemerald manages pools of short-lived servers to be used for testing purposes.  It was built to allow paralallel integration tests.\n\n[![asciicast](https://asciinema.org/a/117629.png)](https://asciinema.org/a/117629)\n\nIt has [REST API](#api) for accessing server instances from any language and comes with a built-in [go client](net/client.go). See the [examples](_example/) directory for example configurations and client usage.\n\nThe ephemerald server can run on a remote host; container connection parameters are rewritten so that the client\nconnects to the right place.  This way ephemerald can run on a large server and be used from less powerful\ndevelopment machines.\n\n* [Running](#running)\n* [Configuration](#building)\n  * [Params](#params)\n  * [Container](#container)\n  * [Lifecycle Actions](#lifecycle-actions)\n    * [noop](#noop)\n    * [exec](#exec)\n    * [http.get](#httpget)\n    * [tcp.connect](#tcpconnect)\n    * [postgres.exec](#postgresexec)\n    * [postgres.ping](#postgresping)\n    * [postgres.truncate](#postgrestruncate)\n    * [redis.exec](#redisexec)\n    * [redis.ping](#redisping)\n    * [redis.truncate](#redistruncate)\n* [API](#api)\n  * [Checkout](#checkout)\n  * [Return](#return)\n  * [Batch Checkout](#batch-checkout)\n  * [Batch Return](#batch-return)\n* [Building](#building)\n* [Installing](#installing)\n  * [Homebrew](#homebrew)\n  * [Binary](#binary)\n  * [Source](#source)\n* [TODO](#todo)\n\n## Running\n\nTo run the server, supply a configuration file:\n\n```sh\n$ ephemerald -c config.yaml\n```\n\nPress Q to quit the server.\n\n### Flags\n\n * `--help` print help message.\n * `-p \u003cport\u003e` changes the listen port.  Defaults to 6000\n * `--ui stream` will dump the event steam to the console in lieu of a curses-esque UI.\n * `--ui none` will not print any UI information (useful with `--log-file /dev/stdout`)\n * `--log-file \u003cpath\u003e` write logs to file at `path`.  Defaults to `/dev/null`\n * `--log-level \u003clevel\u003e` log level.  defaults to `info`.  Options are `debug`,`info`,`warn`,`error`\n\nNote: use Ctrl-C to stop the server wen not in `--ui tui` mode (`SIGINT`,`SIGQUIT` always work too)\n\nFor example, to see only log messages (at debug level) use:\n\n```sh\n$ ephememerald --ui none --log-level debug --log-file /dev/stdout -c config.yaml\n```\n\n## Configuration\n\nContainer pools are configured in a yaml (or json) file.  Each pool has options for the container parameters and\nfor lifecycle actions.\n\nThe following configuration creates a single pool called \"pg\" which maintains five containers from the \"postgres\" image and\nexposes port 5432 to clients.  See the [`params`](#params) and [`actions`](#lifecycle-actions) below for documentation on those fields.\n\n```yaml\npools:\n  pg:\n    image: postgres\n    size: 5\n    port: 5432\n    params:\n      username: postgres\n      database: postgres\n      url: postgres://{{.Username}}:@{{.Hostname}}:{{.Port}}/{{.Database}}?sslmode=disable\n    actions:\n      healthcheck:\n        type: postgres.ping\n        retries: 10\n        delay:   50ms\n      initialize:\n        type:    exec\n        path:    make\n        args:    [ 'db:migrate' ]\n        env:     [ 'DATABASE_URL={{.Url}}' ]\n        timeout: 10s\n      reset:\n        type: postgres.truncate\n```\n\nSee [example/config.yaml](_example/config.yaml) for a full working configuration.\n\n### Params\n\nThe `params` entry allows for declaring parameters needed for connecting to the service.  There are three fields\nwith arbitrary values: `username`, `password`, `database`.\n\nThe `url` can be a golang template and will be executed with access to the following fields:\n\nName | Value\n--- | ---\nHostname | The hostname that the container can be connected at\nPort | The (automatically-generated) port number that is mapped to the exposed container port\nUsername | The `username` field declared in `params`\nPassword | The `password` field declared in `params`\nDatabase | The `database` field declared in `params`\n\nA `params` section for postgres may look like this:\n\n```yaml\nusername: postgres\ndatabase: postgres\nurl: postgres://{{.Username}}:{{.Password}}@{{.Hostname}}:{{.Port}}/{{.Database}}?sslmode=disable\n```\n\n### Container\n\nThe `container` section is passed-through to docker when creating the container.  The available\noptions are:\n\n * labels\n * env\n * cmd\n * volumes\n * entrypoint\n * user\n * capadd\n * capdrop\n\n### Lifecycle Actions\n\nThere are three lifecycle actions: `healthcheck`, `initialize`, and `reset`.\n\n * `healthcheck` is used to determine when the container is ready to be used.\n * `initialize` is used to initialize the container (run migrations, etc...)\n * `reset` may be used to revert the container to a state where it can be used again.\n\nAll of them are optional (though `healthcheck` should be used).  If `reset` is not given,\nthe container will be killed and a new one will be created to replace it.\n\nEach action has, at a minimum, the following three parameters:\n\nName | Default | Description\n---  | --- | ---\nretries | 3 | number of times to retry the action\ntimeout | 1s | amount of time to allow for the action\ndelay | 500ms | amount of time to delay before retrying\n\n`timeout` and `delay` are durations; they must have a unit suffix as described [here](https://golang.org/pkg/time/#ParseDuration).\n\nNote: actions may have different defaults for these fields.\n\n#### noop\n\nDoes nothing.  Useful as the `reset` action so that a container is always reused.\n\n#### exec\n\nExecute a command on the host operating system.  Useful for running migrations to initialize a database.\n\nExtra Parameters:\n\nName | Default | Description\n--- | --- | ---\ncommand| `\"\"` | command to execute\nargs | `[]` | command-line arguments\nenv | `[]` | environment variables\ndir | `\"\"` | directory to execute in.\n\nThe `env` entries may be templates with access to the same fields as the [`params`](#params) url template.  Additionally,\nthe following environment variables are always set:\n\n * `EPHEMERALD_ID`\n * `EPHEMERALD_HOSTNAME`\n * `EPHEMERALD_PORT`\n * `EPHEMERALD_USERNAME`\n * `EPHEMERALD_PASSWORD`\n * `EPHEMERALD_DATABASE`\n * `EPHEMERALD_URL`\n\nIf `dir` is not set, the working directory of the server isused.\n\n#### http.get\n\nRun a HTTP GET request.\n\nExtra Parameters:\n\nName | Default | Description\n--- | --- | ---\nurl | `\"\"` | url to request\n\nIf `url` is blank, the `url` from the [`params`](#params) is used.\n\nIf `url` is not blank, it may be a template which has access to the same fields that [`params`](#params) url template does.\n\n#### tcp.connect\n\nConnect to the exposed container port over TCP.\n\n#### postgres.exec\n\nExecutes a query on the database.\n\nExtra Parameters:\n\nName | Default | Description\n--- | --- | ---\ncommand | `\"SELECT 1=1\"` | query to execute\nargs | `[]` | values to be escaped with positional arguments in `command`.\n\nExample:\n\n```yaml\ntype:     postgres.exec\ncommand: 'INSERT INTO users (name) VALUES ($1)'\nargs:    \"Robert'); DROP TABLE STUDENTS;--\"\n```\n\n#### postgres.ping\n\nPings the database.  Useful for healthcheck.\n\n#### postgres.truncate\n\nRuns `TRUNCATE TABLE x CASCADE` for all tables `x`.\n\nExtra Parameters:\n\nName | Default | Description\n--- | --- | ---\nexclude | `[]` | an array of table names to not truncate (eg migration versions)\n\n#### redis.exec\n\nExecute a redis command.\n\nExtra Parameters:\n\nName | Default | Description\n--- | --- | ---\ncommand | `\"PING\"` | redis command to execute\n\n#### redis.ping\n\nThis is an alias for `redis.exec`.\n\n#### redis.truncate\n\nThis is an alias for `redis.exec` with a default command of `\"FLUSHALL\"`.\n\n## API\n\nThere is a REST API for clients to checkout and return items from one or more pools.\n\n### Checkout\n\n`POST /checkout/{pool}` checks out an instance from the given pool and returns\nthat instance's parameters:\n\n```sh\n$ curl -s -XPOST localhost:6000/checkout/postgres | jq\n{\n  \"id\":\"8482c266192f013346d03f71b2aa6d4b647909e3502ac525039bdd0fe9fcac30\",\n  \"hostname\":\"localhost\",\n  \"port\":\"34031\",\n  \"username\":\"postgres\",\n  \"database\":\"postgres\",\n  \"url\":\"postgres://postgres:@localhost:34031/postgres?sslmode=disable\"\n}\n```\n\n### Return\n\n`DELETE /return/{pool}/{id}` returns the instance given by `id` to the pool `pool`:\n\n```sh\n$ curl -s -XDELETE localhost:6000/return/postgres/8482c266192f013346d03f71b2aa6d4b647909e3502ac525039bdd0fe9fcac30\n```\n\n### Batch Checkout\n\n`POST /checkout` checks out an instance from every configured pool.\n\n```sh\n$ curl -s -XPOST localhost:6000/checkout | tee checkout.json | jq\n{\n  \"postgres\": {\n    \"id\": \"2dedf5dbe9cc8d7a0cd71ed75455c7310db79aea44925562b82c01b959d85e7e\",\n    \"hostname\": \"localhost\",\n    \"port\": \"34023\",\n    \"username\": \"postgres\",\n    \"database\": \"postgres\",\n    \"url\": \"postgres://postgres:@localhost:34023/postgres?sslmode=disable\"\n  },\n  \"redis\": {\n    \"id\": \"a8dbf5043c7145510f48ccffa6f1e20b9f2c8140dda73d567a29dc2ec823ca46\",\n    \"hostname\": \"localhost\",\n    \"port\": \"34019\",\n    \"database\": \"0\",\n    \"url\": \"redis://localhost:34019/0\"\n  },\n  \"vault\": {\n    \"id\": \"11f4752d5e0b762c65b05809c9500a6e0a20ee4a79b861638a084adf77dbfb78\",\n    \"hostname\": \"localhost\",\n    \"port\": \"34021\",\n    \"url\": \"http://localhost:34021\"\n  }\n}\n```\n\n### Batch Return\n\n`DELETE /return` returns multiple instances at once.  Meant to be used with [batch checkout](#batch-checkout).\n\n```sh\n$ cat checkout.json\n{\n  \"postgres\": {\n    \"id\": \"2dedf5dbe9cc8d7a0cd71ed75455c7310db79aea44925562b82c01b959d85e7e\"\n  },\n  \"redis\": {\n    \"id\": \"a8dbf5043c7145510f48ccffa6f1e20b9f2c8140dda73d567a29dc2ec823ca46\"\n  },\n  \"vault\": {\n    \"id\": \"11f4752d5e0b762c65b05809c9500a6e0a20ee4a79b861638a084adf77dbfb78\"\n  }\n}\n$ curl -XDELETE -H'Content-Type: application/json' -d @checkout.json localhost:6000/return\n```\n\nNote that the complete response from [batch checkout](#batch-checkout) may be sent.  The only requirement is the `id` field for each pool instance.\n\n## Building\n\n```sh\n$ govendor get -d github.com/boz/ephemerald/...\n$ cd $GOPATH/src/github.com/boz/ephemerald\n$ make server example\n```\nRun the example server and client in separate terminals\n\n```sh\n$ ./ephemerald/ephemerald -c _example/config.yaml\n```\n\n```sh\n$ ./_example/example\n```\n\n## Installing\n\n### Source\n\nFollow the [building](#building) steps then run `make install`:\n\n```sh\n$ make install\n```\n\n### Binary\n\nDownload the [latest release](https://github.com/boz/ephemerald/releases/latest) for your system.  Unpack the archive and put the binary in your path.\n\n```sh\n$ release=\"https://github.com/boz/ephemerald/releases/download/v0.3.1/ephemerald_Linux_x86_64.tar.gz\"\n$ curl -L \"$release\" | tar -C /tmp -zxv\n$ /tmp/ephemerald -c config.yaml\n```\n\n### Homebrew\n\n```\n$ brew install boz/repo/ephemerald\n```\n\n## TODO\n\n * Configuration\n   * Current parsing is a disaster\n   * Allow yaml\n   * Allow built-in defaults (postgres, redis, etc...)\n * Polish/Optimize/Cleanup/Refactor UI.\n * Re-add remote actions (websockets API)\n * Clients: nodejs, ruby, python, etc...\n * Documentation\n * Tests\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fboz%2Fephemerald","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fboz%2Fephemerald","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fboz%2Fephemerald/lists"}