github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/docs/client-opts.md (about)

     1  # Client options
     2  
     3  The containerd client was built to be easily extended by consumers.
     4  The goal is that the execution flow of the calls remain the same across implementations while `Opts` are written to extend functionality.
     5  To accomplish this we depend on the `Opts` pattern in Go.
     6  
     7  ## Method Calls
     8  
     9  For many functions and methods within the client package you will generally see variadic args as the last parameter.
    10  
    11  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`.
    12  
    13  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.
    14  
    15  ```go
    16  func (c *Client) NewContainer(ctx context.Context, id string, opts ...NewContainerOpts) (Container, error) {
    17  }
    18  ```
    19  
    20  ## Extending the Client
    21  
    22  As a consumer of the containerd client you need to be able to add your domain specific functionality.
    23  There are a few ways of doing this, changing the client code, submitting a PR to the containerd client, or forking the client.
    24  These ways of extending the client should only be considered after every other method has been tried.
    25  
    26  The proper and supported way of extending the client is to build a package of `Opts` that define your application specific logic.
    27  
    28  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.
    29  
    30  #### Bad Extension Example
    31  
    32  ```go
    33  // example code
    34  container, err := client.NewContainer(ctx, id)
    35  
    36  // add volumes with their config and bind mounts
    37  container.Labels["volumes"] = VolumeConfig{}
    38  container.Spec.Binds  = append({"/var/lib/docker/volumes..."})
    39  ```
    40  
    41  #### Good Extension Example
    42  
    43  ```go
    44  // example code
    45  import "github.com/docker/docker"
    46  import "github.com/docker/libnetwork"
    47  
    48  container, err := client.NewContainer(ctx, id,
    49  	docker.WithVolume("volume-name"),
    50  	libnetwork.WithOverlayNetwork("cluster-network"),
    51  )
    52  ```
    53  
    54  There are a few advantages using this model.
    55  
    56  1. Your application code is not scattered in the execution flow of the containerd client.
    57  2. Your code can be unit tested without mocking the containerd client.
    58  3. Contributors can better follow your containerd implementation and understand when and where your application logic is added to standard containerd client calls.
    59  
    60  ## Example SpecOpt
    61  
    62  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.
    63  
    64  ```go
    65  package monitor
    66  
    67  import (
    68  	"github.com/containerd/containerd/oci"
    69  	specs "github.com/opencontainers/runtime-spec/specs-go"
    70  )
    71  
    72  // WithHtop configures a container to monitor the host system via `htop`
    73  func WithHtop(s *specs.Spec) error {
    74  	// make sure we are in the host pid namespace
    75  	if err := oci.WithHostNamespace(specs.PIDNamespace)(s); err != nil {
    76  		return err
    77  	}
    78  	// make sure we set htop as our arg
    79  	s.Process.Args = []string{"htop"}
    80  	// make sure we have a tty set for htop
    81  	if err := oci.WithTTY(s); err != nil {
    82  		return err
    83  	}
    84  	return nil
    85  }
    86  ```
    87  
    88  Adding your new option to spec generation is as easy as importing your new package and adding the option when creating a spec.
    89  
    90  ```go
    91  import "github.com/crosbymichael/monitor"
    92  
    93  container, err := client.NewContainer(ctx, id,
    94  	containerd.WithNewSpec(oci.WithImageConfig(image), monitor.WithHtop),
    95  )
    96  ```
    97  
    98  You can see the full code and run the monitor container [here](https://github.com/crosbymichael/monitor).