{"id":15192149,"url":"https://github.com/timtosi/fishfinger","last_synced_at":"2025-10-02T06:32:38.738Z","repository":{"id":57494007,"uuid":"80561209","full_name":"TimTosi/fishfinger","owner":"TimTosi","description":"[Go] - FishFinger is a Docker-Compose lightweight programmatic library written in Go.","archived":true,"fork":false,"pushed_at":"2017-09-24T11:12:26.000Z","size":9308,"stargazers_count":33,"open_issues_count":0,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-08-14T02:23:59.941Z","etag":null,"topics":["docker","docker-compose","go","golang","libcompose"],"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/TimTosi.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}},"created_at":"2017-01-31T20:53:13.000Z","updated_at":"2023-08-25T11:24:44.000Z","dependencies_parsed_at":"2022-09-04T15:40:14.273Z","dependency_job_id":null,"html_url":"https://github.com/TimTosi/fishfinger","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/TimTosi/fishfinger","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimTosi%2Ffishfinger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimTosi%2Ffishfinger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimTosi%2Ffishfinger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimTosi%2Ffishfinger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TimTosi","download_url":"https://codeload.github.com/TimTosi/fishfinger/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimTosi%2Ffishfinger/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":277968828,"owners_count":25907418,"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","status":"online","status_checked_at":"2025-10-02T02:00:08.890Z","response_time":67,"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":["docker","docker-compose","go","golang","libcompose"],"created_at":"2024-09-27T21:05:25.557Z","updated_at":"2025-10-02T06:32:36.554Z","avatar_url":"https://github.com/TimTosi.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"![FishFinger](assets/fishfinger-logo-640-210.png)\n\n[![GoDoc](https://godoc.org/github.com/timtosi/fishfinger?status.svg)](https://godoc.org/github.com/timtosi/fishfinger)\n[![Go Report Card](https://goreportcard.com/badge/github.com/timtosi/fishfinger)](https://goreportcard.com/report/github.com/timtosi/fishfinger)\n\n## What is FishFinger ?\nFishFinger is a Dockerfile and Docker-Compose lightweight programmatic library\nwritten in Go. This project provides easy to use abstractions of official Docker\nlibraries with no other dependency than [official Go library](https://github.com/golang/go)\nand [official Docker libraries](https://github.com/docker/).\n\nFishFinger also provides a one possible solution to deal with [container initialization delay](https://github.com/docker/compose/issues/374)\nand makes room for further improvement through user-defined solutions.\n\nThis project is fully tested and comes with an [extensive documentation](https://godoc.org/github.com/timtosi/fishfinger).\n\n## Quickstart\n\nFirst of all, you need to install and configure the latest [Docker version](https://docs.docker.com/engine/installation/)\non your machine.\n\nThen go get this repository:\n```golang\ngo get -d github.com/timtosi/fishfinger\n```\n\nFinally, check how to use the components provided:\n\n* [Use one or more services from a Compose file](#basic-compose-usage)\n* [Use one or more services from a Compose file with a Backoff](#backoff-compose-usage)\n\n## Use a Compose File\n\nThe `fishfinger.Compose` component represents an abstraction of the [libcompose](https://github.com/docker/libcompose)\nlibrary. It provides functions allowing the user to use a [Compose file](https://docs.docker.com/compose/compose-file/)\nprogrammatically.\n\nAn extensive documentation of this component is available [here](https://godoc.org/github.com/TimTosi/fishfinger).\n\n### Basic Compose Usage\n\nLet's say you have a [basic Compose file](examples/compose-basic/docker-compose.yaml)\nwhere you defined three redis services.\n\nFor instance, you could want to be able to write a test suite that will directly\nmake use of your containers instead of simply mocking their behaviour through\nburdensome to write functions.\n\nFirst of all, let's create a new Compose file handler by using the\n`fishfinger.NewCompose` function that takes the path to your Compose file as an\nargument.\n\n```go\nfunc main() {\n\tc, err := fishfinger.NewCompose(\"./docker-compose.yaml\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n```\n\nTime to start services.\n\n```go\nfunc main() {\n\tc, err := fishfinger.NewCompose(\"./docker-compose.yaml\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n    if err := c.Start(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n```\n\nAs no argument is provided, all services will be started, but `Compose.Start`\nfunction accepts a variable number of service name in argument and will start\nthem in the order specified.\n\nAt this stage, you should have a neat redis cluster running. The Redis client\nports you previously set for each of your services are the `6379` but you had to\nspecify different available ports on your host machine. You can hard code the\ndifferent ports OR you can use the `Compose.Port` function to find the correct\nport to use from a service name and the combination of the port exposed and the\nprotocol used such as `6379/tcp`. This function returns the full address in the\nfollowing form `\u003chost\u003e:\u003cport\u003e`.\n\n```go\nfunc main() {\n\tc, err := fishfinger.NewCompose(\"./docker-compose.yaml\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n    if err := c.Start(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n    addr, err := c.Port(\"redis-01\", \"6379/tcp\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n```\n\nThe `Compose.Port` function will return the full address in the following form\n`\u003chost\u003e:\u003cport\u003e`. Now you can instanciate your redis client and do whatever stuff\nyou want.\n\nIn the end, you maybe want to clean the containers created. Just use the\n`Compose.Stop` function just as you would use the `Compose.Start` function.\n\n```go\nfunc main() {\n\tc, err := fishfinger.NewCompose(\"./docker-compose.yaml\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n    if err := c.Start(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n    addr, err := c.Port(\"redis-01\", \"6379/tcp\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n    if err := c.Stop(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n```\nAs no argument is provided, all services will be stopped.\n\nComplete working code can be found [here](examples/compose-basic/main.go).\n\n\n### Backoff Compose Usage\n\nWhen you want to use Docker in a programmatic way, you can encounter a particular\nissues regarding the definition of a container's `ready` state: the container state\nand the state of the software running inside the container are decoupled.\n\nThat means a container will be considered `ready` regardless of the software\nstate, leading to programmatic errors. Moreover, that is not because the main\ndockerized software is running that you consider your container ready.\n\nLet's say you have a [basic Compose file](examples/compose-backoff/docker-compose.yaml)\nwhere you defined a service running a SQL database. In this case, you are using\nit for testing purposes because you do not want to use a mocking driver.\nBasically, you could build an image with all your data preloaded but currently\nyou do not want to rebuild your image each time you update your dataset because\nit's evolving frequently. \n\nAs you can see at [line #40](examples/compose-backoff/docker-compose.yaml#L40),\nin this example, you find a solution by using a container that mounts a volume\nwhere you put all .sql scripts you use for populating the database at\ninitialization.\n\nHere is a visualisation of the three `ready` states this Docker container has :\n\n```\n+--------------------+     +-------------+     +-------------------+\n| CONTAINER IS READY | --\u003e | MYSQL IS UP | --\u003e | DATA ARE INSERTED |\n+--------------------+     +-------------+     +-------------------+\n```\n\nYou can't rely on Docker to know when your container is ready and you can't rely\non a time constant because the data insertion step is variable depending on the\ndata you will have to set.\n\nThere are several ways to tackle this problem and FishFinger allow you to resolve\nthis easily by using the `Compose.StartBackoff` function.\n\nFirst of all, let's create a new Compose file handler by using the\n`fishfinger.NewCompose` function that takes the path to your Compose file as an\nargument.\n\n```go\nfunc main() {\n\tc, err := fishfinger.NewCompose(\"./docker-compose.yaml\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n```\n\nNow, time to start the MySQL service with the `Compose.StartBackoff` function.\n\n\n```go\nfunc main() {\n\tc, err := fishfinger.NewCompose(\"./docker-compose.yaml\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tif err := c.StartBackoff(fishfinger.SocketBackoff, \"datastore:9090/tcp\"); err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n```\n\nThis function takes two arguments: the Backoff function used and a variable\nnumber of `strings` composed of the service name, a port and a protocol used\nsuch as `redis:6379/tcp`.The Backoff function used here is the one provided by\ndefault by the Fishfinger project but you are expected to provide another one\nthat suits your needs.\n\n\n```go\nfunc SocketBackoff(c *Compose, service, port string) error {\n\tvar (\n\t\tmsg  string\n\t\tconn net.Conn\n\t)\n\n\taddr, err := c.Port(service, port)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor ; msg != \"ready\\n\"; time.Sleep(5 * time.Second) {\n\t\tif conn, err = net.Dial(\"tcp\", addr); err == nil {\n\t\t\tfmt.Fprintf(conn, \"ready\\n\")\n\t\t\tmsg, _ = bufio.NewReader(conn).ReadString('\\n')\n\t\t\tconn.Close()\n\t\t}\n\t\tfmt.Printf(\"Retry connection in 5s.\\n\")\n\t}\n\treturn nil\n}\n```\n\nIt's only keep trying to connect to a specific port exposed by the container.\nThe fact is the function will not find any remote listener until all data is\ncorrectly loaded, as you can see [here](https://github.com/TimTosi/mechanist/blob/master/sql/mysqld.sh#L26L29).\nIn this way, you are assured everything is ready to be processed by the rest of\nyour program.\n\nComplete working code can be found [here](examples/compose-backoff/main.go).\n\n## License\nEvery file provided here is available under the [MIT License](http://opensource.org/licenses/MIT).\n\nThe logo is a derivative of [\"BLUE_GOPHER.png\"](https://github.com/ashleymcnamara/gophers/blob/master/BLUE_GOPHER.png)\nby [Ashley McNamara](https://github.com/ashleymcnamara), used under [CC BY](https://creativecommons.org/licenses/by/2.0/).\nThis logo is licensed under [CC BY](https://creativecommons.org/licenses/by/2.0/) by Tim Tosi.\n\n## Not Good Enough ?\nIf you encouter any issue by using what is provided here, please\n[let me know](https://github.com/TimTosi/fishfinger/issues) ! \nHelp me to improve by sending your thoughts to timothee.tosi@gmail.com !\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimtosi%2Ffishfinger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftimtosi%2Ffishfinger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimtosi%2Ffishfinger/lists"}