Skip to content

Commit f15d0af

Browse files
committed
document extensibility using service.provider
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
1 parent 955e4ed commit f15d0af

File tree

2 files changed

+107
-2
lines changed

2 files changed

+107
-2
lines changed

docs/extension.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# About
2+
3+
Compose application model defines `service` as an abstraction for a computing unit managing (a subset of)
4+
application needs, which can interact with other service by relying on network(s). Docker Compose is designed
5+
to use the Docker Engine ("Moby") API to manage services as containers, but the abstraction _could_ also cover
6+
many other runtimes, typically cloud services or services natively provided by host.
7+
8+
Compose extensibility model has been designed to extend the `service` support to runtimes accessible through
9+
third-party tooling.
10+
11+
# Architecture
12+
13+
Compose extensibility relies on the `provider` attribute to select the actual binary responsible to manage
14+
resource(s) to actually run a service.
15+
16+
```yaml
17+
database:
18+
provider:
19+
type: awesomecloud
20+
options:
21+
type: mysql
22+
size: 256
23+
```
24+
25+
`provider.type` tells Compose the binary to run, which can be either:
26+
- another Docker CLI plugin (typically, `model` to run `docker-model`)
27+
- an executable in user's `PATH`
28+
29+
To be a valid Compose extension, provider command *MUST* accept subcommand `compose` (which can be hidden)
30+
with subcommands `up` and `down`.
31+
32+
## Up lifecycle
33+
34+
To execute application `up` lifecycle, Compose executes the provider with dedicated command `compose up` passing
35+
project name, service name and options. `provider.options` are translated as command line flags :
36+
```console
37+
awesomecloud compose --project-name <NAME> up --type=mysql --size=256 "database"
38+
```
39+
40+
> __Note:__ `project-name` _should_ be used by provider to tag resources
41+
> set for project, so that later execution with `down` subcommand releases
42+
> all allocated resources set for project.
43+
44+
## Communication with Compose
45+
46+
Provider can interact with Compose using `stdout` as a channel, sending JSON line delimited messages.
47+
JSON messages MUST include a `type` and a `message` attribute.
48+
```json
49+
{ "type": "info", "message": "preparing mysql ..." }
50+
```
51+
52+
`type` can be either:
53+
- `info` to report status to the user. Compose will render message as service state in the progress UI
54+
- `error` to let user know something went wrong with details on the error. Compose will render message as service failure reason.
55+
- `setenv` for plugin to tell compose how dependent services can access the created resource. See next section for details
56+
57+
```mermaid
58+
sequenceDiagram
59+
Shell->>Compose: docker compose up
60+
Compose->>Provider: compose up --project-name=xx --foo=bar "database"
61+
Provider--)Compose: json { "info": "pulling 25%" }
62+
Compose-)Shell: pulling 25%
63+
Provider--)Compose: json { "info": "pulling 50%" }
64+
Compose-)Shell: pulling 50%
65+
Provider--)Compose: json { "info": "pulling 75%" }
66+
Compose-)Shell: pulling 75%
67+
Provider--)Compose: json { "setenv": "URL=http://cloud.com/abcd:1234" }
68+
Compose-)Compose: set DATABASE_URL
69+
Provider-)Compose: EOF (command complete) exit 0
70+
Compose-)Shell: service started
71+
```
72+
73+
## Connection to a service managed by provider
74+
75+
A service in the Compose application can declare dependency on a service managed by an external provider:
76+
77+
```yaml
78+
services:
79+
app:
80+
image: myapp
81+
depends_on:
82+
- database
83+
84+
database:
85+
provider:
86+
type: awesomecloud
87+
```
88+
89+
As provider command sends a `setenv` JSON message, the configured variable is injected into dependent service,
90+
prefixed by service name. In this illustration example, as `awesomecloud compose up` sends message:
91+
```json
92+
{"type": "setenv", "message": "URL=https://awesomecloud.com/db:1234"}
93+
```
94+
`app` service which depends on service managed by provider will get `DATABASE_URL` environment variable injected.
95+
96+
> __Note:__ `compose up` provider command _MUST_ be idempotent. If resource is already running, command _MUST_ set
97+
> the same environment variables for dependent services to be configured.
98+
99+
## Down lifecycle
100+
101+
`down` lifecycle is equivalent to `up` with the `<provider> compose --project-name <NAME> down <SERVICE>` command.
102+
Provider is responsible to release all resources associated with the service.

pkg/compose/plugins.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func (s *composeService) runPlugin(ctx context.Context, project *types.Project,
5959
return err
6060
}
6161

62-
cmd := s.setupPluginCommand(ctx, project, provider, plugin.Path, command)
62+
cmd := s.setupPluginCommand(ctx, project, service, plugin.Path, command)
6363

6464
eg := errgroup.Group{}
6565
stdout, err := cmd.StdoutPipe()
@@ -130,11 +130,14 @@ func (s *composeService) getPluginBinaryPath(providerType string) (*manager.Plug
130130
return manager.GetPlugin(providerType, s.dockerCli, &cobra.Command{})
131131
}
132132

133-
func (s *composeService) setupPluginCommand(ctx context.Context, project *types.Project, provider types.ServiceProviderConfig, path, command string) *exec.Cmd {
133+
func (s *composeService) setupPluginCommand(ctx context.Context, project *types.Project, service types.ServiceConfig, path, command string) *exec.Cmd {
134+
provider := *service.Provider
135+
134136
args := []string{"compose", "--project-name", project.Name, command}
135137
for k, v := range provider.Options {
136138
args = append(args, fmt.Sprintf("--%s=%s", k, v))
137139
}
140+
args = append(args, service.Name)
138141

139142
cmd := exec.CommandContext(ctx, path, args...)
140143
// Remove DOCKER_CLI_PLUGIN... variable so plugin can detect it run standalone

0 commit comments

Comments
 (0)