{"action":"opened","issue":{"url":"https://api.github.com/repos/michaelsauter/crane/issues/170","labels_url":"https://api.github.com/repos/michaelsauter/crane/issues/170/labels{/name}","comments_url":"https://api.github.com/repos/michaelsauter/crane/issues/170/comments","events_url":"https://api.github.com/repos/michaelsauter/crane/issues/170/events","html_url":"https://github.com/michaelsauter/crane/issues/170","id":64328866,"number":170,"title":"Extra dependencies for Dependency-Management","user":{"login":"adrianhurt","id":1089650,"avatar_url":"https://avatars.githubusercontent.com/u/1089650?v=3","gravatar_id":"","url":"https://api.github.com/users/adrianhurt","html_url":"https://github.com/adrianhurt","followers_url":"https://api.github.com/users/adrianhurt/followers","following_url":"https://api.github.com/users/adrianhurt/following{/other_user}","gists_url":"https://api.github.com/users/adrianhurt/gists{/gist_id}","starred_url":"https://api.github.com/users/adrianhurt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/adrianhurt/subscriptions","organizations_url":"https://api.github.com/users/adrianhurt/orgs","repos_url":"https://api.github.com/users/adrianhurt/repos","events_url":"https://api.github.com/users/adrianhurt/events{/privacy}","received_events_url":"https://api.github.com/users/adrianhurt/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":0,"created_at":"2015-03-25T17:37:40Z","updated_at":"2015-03-25T17:37:40Z","closed_at":null,"body":"Now there are 3 different kinds of dependencies between containers: `link`, `volumes-from` and `net`. They allow crane to know the order it has to lift multiple containers. These 3 kind of dependencies correspond with the docker ones. In docker, they declare not only a dependency but also the way each container is affected by the other. However, when you try to manage multiple containers with crane, there are some situations where you need to establish an extra dependency without any other implication between them. I think a way to declare this extra dependency would allow us to tell crane that it has to lift the containers in a specific order.\r\n\r\n# Automated Nginx\r\nImagine a nginx container that configures itself watching the containers instead of linking them manually ([jwilder/nginx-proxy](https://registry.hub.docker.com/u/jwilder/nginx-proxy/) is a good example). Then there's no need to define any `link` between each web container and nginx one. However, it's obvious there's a dependency between them. Each web container __needs__ the nginx to work. One possible way to declare this could be:\r\n```\r\ncontainers:\r\n  web1:\r\n    run: …\r\n    needs: [\"nginx\"]\r\n  web2:\r\n    run: …\r\n    needs: [\"nginx\"]\r\n\r\n  nginx:\r\n    image: jwilder/nginx-proxy\r\n    run:\r\n      publish: [\"80:80\"]\r\n      …\r\n```\r\nThen, you can lift for example `web1` with `crane lift --cascade-dependencies=all web1`. Of course, you could solve this adding a group for each combination `webN-nginx` and lift the group, but I think it's interesting to declare this kind of dependency within the container's declaration.\r\n\r\n# Restoring a Mysql backup\r\nFor example, imagine 3 containers: `db`, `db-restore-and-backup` and `web`. The container `db-restore-and-backup` needs to be started after `db` and it checks the content of the database. If there is no data, it restore the last backup it has. And of course it is in charge of backup the database everyday as well. \r\n```\r\ncontainers:\r\n  db:\r\n    run:\r\n      ...\r\n  db-restore-and-backup:\r\n    run:\r\n      link: [\"db:db\"]\r\n      volume: [\"/db/backups:/db/backups\"]\r\n      ...\r\n  web:\r\n    run:\r\n      link: [\"db:db\"]\r\n      …\r\n```\r\nThen the `web` container needs to access to the database so we declare it with a `link: [\"db:db\"]`. However, when the containers are lifted the first time, the database is empty. It implies that `web` container should be started not only after `db` but also after `db-restore-and-backup`. I know there are different ways to solve that:\r\n* Using sleeps... no way.\r\n* Using extra \"waiter\" containers that uses `volumes-from` to workaround this issue. It lets you to define correctly the order of lifting each container, but I think adding extra containers and fake `volumes-from` declarations is not a very elegant way to solve this.\r\n* Using active waiting loops to check when the database is ready to use. This needs to be done within the execution of the `web` container before starting the app. Well, I'm doing that now but I think it isn't as elegant as we could desire. Firstly, you must also define this extra dependency with fake `volumes-from` or using group declarations (I need to be sure `db-restore-and-backup` is always executed with `web`).\r\n\r\nFor that reasons, I think that the ability of declaring this extra dependency with the addition of the [support of container hooks feature](https://github.com/michaelsauter/crane/pull/159) would be the perfect solution for this kind of things. It could be resolve with something like this:\r\n```\r\ncontainers:\r\n  db:\r\n    ...\r\n  db-restore-and-backup:\r\n    ...\r\n  web:\r\n    run:\r\n      link: [\"db:db\"]\r\n      …\r\n    needs: [\"db-restore-and-backup\"]\r\n\r\nhooks:\r\n  db-restore-and-backup:\r\n    pre-start: for i in `seq 1 10`; do sleep 1 && docker exec db mysql -u root -prootpass database -e \"show tables\" && exit 0 || echo retrying...; done; exit 1\r\n  web:\r\n    pre-start:  for i in `seq 1 10`; do sleep 1 && docker exec db mysql -u root -prootpass database -e \"describe one_of_my_tables\" && exit 0 || echo retrying...; done; exit 1\r\n```\r\nWith this, I'm sure that the order of execution will be `db` -> `db-restore-and-backup` -> `web`. And also that `db-restore-and-backup` will be started when the database is ready to use, and `web` when it has also the data initialized correctly, not before.\r\n\r\n# Other options\r\nAnother alternative could be the use of `wait-for` and `wait-until`.\r\nFor the nginx example, we could use `wait-for`:\r\n```\r\ncontainers:\r\n  web1:\r\n    run: …\r\n    wait-for: [\"nginx\"]\r\n  web2:\r\n    run: …\r\n    wait-for: [\"nginx\"]\r\n\r\n  nginx:\r\n    image: jwilder/nginx-proxy\r\n    run:\r\n      publish: [\"80:80\"]\r\n      …\r\n```\r\nAnd for the mysql example, we could use `wait-until`:\r\n```\r\ncontainers:\r\n  db:\r\n    ...\r\n  db-restore-and-backup:\r\n    run:\r\n      link: [\"db:db\"]\r\n      …\r\n    wait-until:\r\n      db: mysql -u root -prootpass database -e \"show tables\"\r\n  web:\r\n    run:\r\n      link: [\"db:db\"]\r\n      …\r\n    wait-for: [\"db-restore-and-backup\"]\r\n    wait-until:\r\n      db: mysql -u root -prootpass database -e \"describe one_of_my_tables\"\r\n```\r\nWhere `wait-until` would wait until the specified command would return a _0 status_ (with an active waiting loop and `docker exec` functionality)."}}