Add doc for extending client opts
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
parent
1f04eddad1
commit
1907116bdd
99
docs/client-opts.md
Normal file
99
docs/client-opts.md
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
# Client Options
|
||||||
|
|
||||||
|
The containerd client was built to be easily extended by consumers.
|
||||||
|
The goal is that the execution flow of the calls remain the same across implementations while `Opts` are written to extend functionality.
|
||||||
|
To accomplish this we depend on the `Opts` pattern in Go.
|
||||||
|
|
||||||
|
## Method Calls
|
||||||
|
|
||||||
|
For many functions and methods within the client package you will generally see variadic args as the last parameter.
|
||||||
|
|
||||||
|
If we look at the `NewContainer` method on the client we can see that it has a required argument of `id` and then additional `NewContainerOpts`.
|
||||||
|
|
||||||
|
There are a few built in options that allow the container to be created with an existing spec, `WithSpec`, and snapshot opts for creating or using an existing snapshot.
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (c *Client) NewContainer(ctx context.Context, id string, opts ...NewContainerOpts) (Container, error) {
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Extending the Client
|
||||||
|
|
||||||
|
As a consumer of the containerd client you need to be able add your domain specific functionality.
|
||||||
|
There are a few ways of doing this, changing the client code, submitting a PR to the containerd client, or forking the client.
|
||||||
|
These ways of extending the client should only be considered after every other method has been tried.
|
||||||
|
|
||||||
|
The proper and supported way of extending the client is to build a package of `Opts` that define your application specific logic.
|
||||||
|
|
||||||
|
As an example, if Docker is integrating containerd support and needs to add concepts such as Volumes, they would create a `docker` package with options.
|
||||||
|
|
||||||
|
#### Bad Extension Example
|
||||||
|
|
||||||
|
```go
|
||||||
|
// example code
|
||||||
|
container, err := client.NewContainer(ctx, id)
|
||||||
|
|
||||||
|
// add volumes with their config and bind mounts
|
||||||
|
container.Labels["volumes"] = VolumeConfig{}
|
||||||
|
container.Spec.Binds = append({"/var/lib/docker/volumes..."})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Good Extension Example
|
||||||
|
|
||||||
|
```go
|
||||||
|
// example code
|
||||||
|
import "github.com/docker/docker"
|
||||||
|
import "github.com/docker/libnetwork"
|
||||||
|
|
||||||
|
container, err := client.NewContainer(ctx, id,
|
||||||
|
docker.WithVolume("volume-name"),
|
||||||
|
libnetwork.WithOverlayNetwork("cluster-network"),
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
There are a few advantages using this model.
|
||||||
|
|
||||||
|
1. Your application code is not scattered in the execution flow of the containerd client.
|
||||||
|
2. Your code can be unit tested without mocking the containerd client.
|
||||||
|
3. Contributors can better follow your containerd implementation and understand when and where your application logic is added to standard containerd client calls.
|
||||||
|
|
||||||
|
## Example SpecOpt
|
||||||
|
|
||||||
|
If we want to make a `SpecOpt` to setup a container to monitor the host system with `htop` it can be easily done without ever touching a line of code in the containerd repository.
|
||||||
|
|
||||||
|
```go
|
||||||
|
package monitor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/containerd/containerd"
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WithHtop configures a container to monitor the host system via `htop`
|
||||||
|
func WithHtop(s *specs.Spec) error {
|
||||||
|
// make sure we are in the host pid namespace
|
||||||
|
if err := containerd.WithHostNamespace(specs.PIDNamespace)(s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// make sure we set htop as our arg
|
||||||
|
s.Process.Args = []string{"htop"}
|
||||||
|
// make sure we have a tty set for htop
|
||||||
|
if err := containerd.WithTTY(s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Adding your new option to spec generation is as easy as importing your new package and adding the option when creating a spec.
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/crosbymichael/monitor"
|
||||||
|
|
||||||
|
spec, err := containerd.GenerateSpec(
|
||||||
|
containerd.WithImageConfig(ctx, image),
|
||||||
|
monitor.WithHtop,
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
You can see the full code and run the monitor container [here](https://github.com/crosbymichael/monitor).
|
Loading…
Reference in New Issue
Block a user