feat: dockerutils lib to start container images
This commit is contained in:
parent
4931c63c10
commit
940e04371c
@ -71,6 +71,12 @@ func (builder *Builder) Mounts(mounts map[string]string) *Builder {
|
|||||||
return builder
|
return builder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Network add the container with aliasses to a specific network
|
||||||
|
func (builder *Builder) Network(networkName string, aliasses ...string) *Builder {
|
||||||
|
builder.networks[networkName] = aliasses
|
||||||
|
return builder
|
||||||
|
}
|
||||||
|
|
||||||
// Port defines a port forwarding from the host machine to the container
|
// Port defines a port forwarding from the host machine to the container
|
||||||
// Examples:
|
// Examples:
|
||||||
// - 8080:8080
|
// - 8080:8080
|
||||||
@ -109,92 +115,118 @@ func (builder *Builder) Start(ctx context.Context) (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if ports are available
|
// Network: portbinding Host->Container
|
||||||
exposedPorts, portBindings, err := nat.ParsePortSpecs(builder.ports)
|
exposedPorts, portBindings, err := nat.ParsePortSpecs(builder.ports)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("Failed to parse ports %v: %v", builder.ports, err)
|
return "", fmt.Errorf("unabel to parse ports: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(portBindings) > 0 {
|
if len(portBindings) > 0 {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
builder.containerConfig.ExposedPorts = exposedPorts
|
builder.containerConfig.ExposedPorts = exposedPorts
|
||||||
builder.hostConfig.PortBindings = portBindings
|
builder.hostConfig.PortBindings = portBindings
|
||||||
}
|
}
|
||||||
|
|
||||||
// add endpoint settings to existing networks
|
// Network: Add container to container networks
|
||||||
networkExist := make(map[string]bool, 0)
|
// Add the container to the first defined container network, if any one is
|
||||||
for networkName := range builder.networks {
|
// defined. If no one is defined, the docker API will add the container to
|
||||||
networkExist[networkName] = false
|
// their default bridge docker0. The other networks will be added to the
|
||||||
}
|
// container after the container start.
|
||||||
|
var (
|
||||||
networks, err := builder.client.NetworkList(ctx, types.NetworkListOptions{})
|
networkNames = make([]string, 0)
|
||||||
if err != nil {
|
networks = make([]types.NetworkResource, 0)
|
||||||
return "", fmt.Errorf("Failed to list networks: %v", err)
|
)
|
||||||
}
|
if len(builder.networks) > 0 {
|
||||||
|
for networkName := range builder.networks {
|
||||||
for _, nw := range networks {
|
networkNames = append(networkNames, networkName)
|
||||||
if aliases, present := builder.networks[nw.Name]; present {
|
|
||||||
networkExist[nw.Name] = true
|
|
||||||
builder.networkConfig.EndpointsConfig[nw.Name] = &network.EndpointSettings{
|
|
||||||
Aliases: aliases,
|
|
||||||
NetworkID: nw.ID,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
var err error
|
||||||
|
networks, err = builder.client.NetworkListByNames(ctx, networkNames...)
|
||||||
for nw, found := range networkExist {
|
if err != nil {
|
||||||
if !found {
|
return "", err
|
||||||
return "", fmt.Errorf("Failed to add endpoint settings for network %v. It does not exist", nw)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
endpointSetting := &network.EndpointSettings{
|
||||||
|
NetworkID: networks[0].ID,
|
||||||
|
Aliases: builder.networks[networkNames[0]],
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.networkConfig.EndpointsConfig[networkNames[0]] = endpointSetting
|
||||||
|
|
||||||
|
networkNames = networkNames[1:]
|
||||||
|
networks = networks[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
// create container
|
// Container: Create
|
||||||
resp, err := builder.client.ContainerCreate(ctx, builder.containerConfig, builder.hostConfig, builder.networkConfig, builder.containerName)
|
resp, err := builder.client.ContainerCreate(
|
||||||
|
ctx,
|
||||||
|
builder.containerConfig,
|
||||||
|
builder.hostConfig,
|
||||||
|
builder.networkConfig,
|
||||||
|
builder.containerName,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("Failed to create container %v: %v", builder.containerName, err)
|
return "", fmt.Errorf("Unable to create container: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// start container
|
|
||||||
err = builder.client.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})
|
err = builder.client.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stopError := builder.client.ContainerStopByIDs(ctx, time.Second, resp.ID)
|
|
||||||
if stopError != nil {
|
shutdownErr := builder.client.ContainerStopByIDs(ctx, 1*time.Second, resp.ID)
|
||||||
return "", fmt.Errorf("Failed to start container %v: %v\nUnable to remove container %v. Manual cleanup necessary", builder.containerName, err, builder.containerName)
|
if shutdownErr != nil {
|
||||||
|
return "", fmt.Errorf("Unable to start container: %v\nUnable to remove container %s: %v\nManual cleanup necessary", err, resp.ID, shutdownErr)
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf("Failed to start container %v: %v", builder.containerName, err)
|
return "", fmt.Errorf("Unable to start container: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for healthy
|
// Network: Add more container networks
|
||||||
if builder.waitForHealthy {
|
for i, networkName := range networkNames {
|
||||||
|
endpointSetting := &network.EndpointSettings{
|
||||||
|
NetworkID: networks[i].ID,
|
||||||
|
Aliases: builder.networks[networkName],
|
||||||
|
}
|
||||||
|
err := builder.client.NetworkConnect(ctx, networks[i].ID, resp.ID, endpointSetting)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Unable to append container endpoint to network %v", networkName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if builder.waitForHealthy {
|
||||||
watcher := builder.client.GetWatcher()
|
watcher := builder.client.GetWatcher()
|
||||||
errorChannel := make(chan error, 1)
|
errors := make(chan error, 1)
|
||||||
doneChannel := make(chan struct{})
|
done := make(chan struct{})
|
||||||
err = watcher.AddListener(resp.ID, errorChannel, doneChannel)
|
err = watcher.AddListener(resp.ID, errors, done)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
containerRemoveError := builder.client.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{Force: true})
|
containerRemoveError := builder.client.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{Force: true})
|
||||||
if containerRemoveError != nil {
|
if containerRemoveError != nil {
|
||||||
return "", fmt.Errorf("Failed while watching status for container %v: %v\nUnable to remove container %v: %v", resp.ID, err, resp.ID, containerRemoveError)
|
return "", fmt.Errorf("error while watching for container status: %v - unable to remove container: %v", err, containerRemoveError)
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf("Failed while watching status for container %v: %v", resp.ID, err)
|
return "", fmt.Errorf("error while watching for container status: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case err := <-errorChannel:
|
case err := <-errors:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
containerRemoveError := builder.client.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{Force: true})
|
containerRemoveError := builder.client.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{Force: true})
|
||||||
if containerRemoveError != nil {
|
if containerRemoveError != nil {
|
||||||
return "", fmt.Errorf("Unable to remove container %v: %v", resp.ID, containerRemoveError)
|
return "", fmt.Errorf("%v - unable to remove container: %v", err, containerRemoveError)
|
||||||
}
|
}
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
case <-doneChannel:
|
case <-done:
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp.ID, nil
|
return resp.ID, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (builder *Builder) WaitForHealthy() *Builder {
|
func (builder *Builder) WaitForHealthy() *Builder {
|
||||||
builder.waitForHealthy = true
|
builder.waitForHealthy = true
|
||||||
return builder
|
return builder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithName set the name of the container
|
||||||
|
func (builder *Builder) WithName(containerName string) *Builder {
|
||||||
|
builder.containerName = containerName
|
||||||
|
return builder
|
||||||
|
}
|
||||||
|
@ -13,9 +13,11 @@ import (
|
|||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
"github.com/docker/docker/api/types/network"
|
"github.com/docker/docker/api/types/network"
|
||||||
|
"github.com/docker/docker/api/types/volume"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Client from the docker API with additional functions
|
||||||
type Client struct {
|
type Client struct {
|
||||||
*client.Client
|
*client.Client
|
||||||
watcher *Watcher
|
watcher *Watcher
|
||||||
@ -33,9 +35,9 @@ func (client *Client) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ContainerListByLabels returns only containers which match by given labels
|
// ContainerListByLabels returns only containers which match by given labels
|
||||||
func (client *Client) ContainerListByLabels(ctx context.Context, all bool, labels map[string]string) ([]types.Container, error) {
|
func (client *Client) ContainerListByLabels(ctx context.Context, all bool, containerLabels map[string]string) ([]types.Container, error) {
|
||||||
filterArgs := filters.NewArgs()
|
filterArgs := filters.NewArgs()
|
||||||
for key, value := range labels {
|
for key, value := range containerLabels {
|
||||||
filterArgs.Add("label", fmt.Sprintf("%v=%v", key, value))
|
filterArgs.Add("label", fmt.Sprintf("%v=%v", key, value))
|
||||||
}
|
}
|
||||||
containers, err := client.ContainerList(ctx, types.ContainerListOptions{
|
containers, err := client.ContainerList(ctx, types.ContainerListOptions{
|
||||||
@ -52,9 +54,9 @@ func (client *Client) ContainerListByLabels(ctx context.Context, all bool, label
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ContainerListByNames returns only containers which match by given labels
|
// ContainerListByNames returns only containers which match by given labels
|
||||||
func (client *Client) ContainerListByNames(ctx context.Context, all bool, names ...string) ([]types.Container, error) {
|
func (client *Client) ContainerListByNames(ctx context.Context, all bool, containerNames ...string) ([]types.Container, error) {
|
||||||
filterArgs := filters.NewArgs()
|
filterArgs := filters.NewArgs()
|
||||||
for _, name := range names {
|
for _, name := range containerNames {
|
||||||
filterArgs.Add("name", name)
|
filterArgs.Add("name", name)
|
||||||
}
|
}
|
||||||
containers, err := client.ContainerList(ctx, types.ContainerListOptions{
|
containers, err := client.ContainerList(ctx, types.ContainerListOptions{
|
||||||
@ -82,8 +84,8 @@ func (client *Client) ContainerRemoveByIDs(ctx context.Context, containerIDs ...
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ContainerRemoveByLabels deletes all containers which match by given labels
|
// ContainerRemoveByLabels deletes all containers which match by given labels
|
||||||
func (client *Client) ContainerRemoveByLabels(ctx context.Context, labels map[string]string) error {
|
func (client *Client) ContainerRemoveByLabels(ctx context.Context, containerLabels map[string]string) error {
|
||||||
containers, err := client.ContainerListByLabels(ctx, true, labels)
|
containers, err := client.ContainerListByLabels(ctx, true, containerLabels)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -97,8 +99,8 @@ func (client *Client) ContainerRemoveByLabels(ctx context.Context, labels map[st
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ContainerRemoveByNames deletes all containers which match by their names
|
// ContainerRemoveByNames deletes all containers which match by their names
|
||||||
func (client *Client) ContainerRemoveByNames(ctx context.Context, names ...string) error {
|
func (client *Client) ContainerRemoveByNames(ctx context.Context, containerNames ...string) error {
|
||||||
containers, err := client.ContainerListByNames(ctx, true, names...)
|
containers, err := client.ContainerListByNames(ctx, true, containerNames...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -123,8 +125,23 @@ func (client *Client) ContainerStopByIDs(ctx context.Context, timeout time.Durat
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ContainerStopByLabels shutdown containters which match by given labels
|
// ContainerStopByLabels shutdown containters which match by given labels
|
||||||
func (client *Client) ContainerStopByLabels(ctx context.Context, timeout time.Duration, labels map[string]string) error {
|
func (client *Client) ContainerStopByLabels(ctx context.Context, timeout time.Duration, containerLabels map[string]string) error {
|
||||||
containers, err := client.ContainerListByLabels(ctx, true, labels)
|
containers, err := client.ContainerListByLabels(ctx, true, containerLabels)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, container := range containers {
|
||||||
|
err := client.ContainerStop(ctx, container.ID, &timeout)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerStopByNames shutdown containters matching by their names
|
||||||
|
func (client *Client) ContainerStopByNames(ctx context.Context, timeout time.Duration, containerNames ...string) error {
|
||||||
|
containers, err := client.ContainerListByNames(ctx, true, containerNames...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -158,6 +175,96 @@ func (client *Client) GetWatcher() *Watcher {
|
|||||||
return client.watcher
|
return client.watcher
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NetworkListByLabels returns networks which match by given labels
|
||||||
|
func (client *Client) NetworkListByLabels(ctx context.Context, networkLabels map[string]string) ([]types.NetworkResource, error) {
|
||||||
|
args := filters.NewArgs()
|
||||||
|
for key, value := range networkLabels {
|
||||||
|
args.Add("label", fmt.Sprintf("%v=%v", key, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.NetworkList(ctx, types.NetworkListOptions{
|
||||||
|
Filters: args,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkListByNames returns networks which match by their names. If a
|
||||||
|
// network can not be found, the function returns an error
|
||||||
|
func (client *Client) NetworkListByNames(ctx context.Context, networkNames ...string) ([]types.NetworkResource, error) {
|
||||||
|
networks, err := client.NetworkList(ctx, types.NetworkListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
foundNetwork := make(map[string]bool, 0)
|
||||||
|
for _, networkName := range networkNames {
|
||||||
|
foundNetwork[networkName] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredNetworks := make([]types.NetworkResource, 0)
|
||||||
|
for _, networkName := range networkNames {
|
||||||
|
for _, network := range networks {
|
||||||
|
if network.Name == networkName {
|
||||||
|
filteredNetworks = append(filteredNetworks, network)
|
||||||
|
foundNetwork[networkName] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, networkName := range networkNames {
|
||||||
|
if !foundNetwork[networkName] {
|
||||||
|
return nil, fmt.Errorf("Network %v not found", networkName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filteredNetworks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkRemoveByLabels remove all networks which match by given labels
|
||||||
|
func (client *Client) NetworkRemoveByLabels(ctx context.Context, containerLabels map[string]string) error {
|
||||||
|
networks, err := client.NetworkListByLabels(ctx, containerLabels)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, network := range networks {
|
||||||
|
err := client.NetworkRemove(ctx, network.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkRemoveByNames remove all networks match by their names. If a
|
||||||
|
// network can not be found, the function returns an error
|
||||||
|
func (client *Client) NetworkRemoveByNames(ctx context.Context, networkNames ...string) error {
|
||||||
|
networks, err := client.NetworkListByNames(ctx, networkNames...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, network := range networks {
|
||||||
|
err := client.NetworkRemove(ctx, network.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkRemoveByIDs remove all networks match by their id
|
||||||
|
func (client *Client) NetworkRemoveByIDs(ctx context.Context, containerIDs ...string) error {
|
||||||
|
for _, containerID := range containerIDs {
|
||||||
|
err := client.NetworkRemove(ctx, containerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// NewBuilder returns a new builder for containers
|
// NewBuilder returns a new builder for containers
|
||||||
func (client *Client) NewBuilder(image string) *Builder {
|
func (client *Client) NewBuilder(image string) *Builder {
|
||||||
return &Builder{
|
return &Builder{
|
||||||
@ -165,8 +272,10 @@ func (client *Client) NewBuilder(image string) *Builder {
|
|||||||
containerConfig: &container.Config{
|
containerConfig: &container.Config{
|
||||||
Image: image,
|
Image: image,
|
||||||
},
|
},
|
||||||
hostConfig: new(container.HostConfig),
|
hostConfig: new(container.HostConfig),
|
||||||
networkConfig: new(network.NetworkingConfig),
|
networkConfig: &network.NetworkingConfig{
|
||||||
|
EndpointsConfig: make(map[string]*network.EndpointSettings, 0),
|
||||||
|
},
|
||||||
networks: make(map[string][]string, 0),
|
networks: make(map[string][]string, 0),
|
||||||
ports: make([]string, 0),
|
ports: make([]string, 0),
|
||||||
pull: false,
|
pull: false,
|
||||||
@ -206,6 +315,78 @@ func (client *Client) PullQuiet(ctx context.Context, image string) error {
|
|||||||
return client.Pull(ctx, image, ioutil.Discard)
|
return client.Pull(ctx, image, ioutil.Discard)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VolumeListByLabels returns volumes which match by given labels
|
||||||
|
func (client *Client) VolumeListByLabels(ctx context.Context, volumeLabels map[string]string) (volume.VolumesListOKBody, error) {
|
||||||
|
args := filters.NewArgs()
|
||||||
|
for key, value := range volumeLabels {
|
||||||
|
args.Add("label", fmt.Sprintf("%v=%v", key, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.VolumeList(ctx, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VolumeListByNames returns volumes which match by their names. If a
|
||||||
|
// volume can not be found, the function returns an error
|
||||||
|
func (client *Client) VolumeListByNames(ctx context.Context, volumeNames ...string) (volume.VolumesListOKBody, error) {
|
||||||
|
args := filters.NewArgs()
|
||||||
|
foundVolumes := make(map[string]bool, 0)
|
||||||
|
for _, volumeName := range volumeNames {
|
||||||
|
foundVolumes[volumeName] = false
|
||||||
|
args.Add("name", volumeName)
|
||||||
|
}
|
||||||
|
|
||||||
|
volumes, err := client.VolumeList(ctx, args)
|
||||||
|
if err != nil {
|
||||||
|
return volume.VolumesListOKBody{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, volume := range volumes.Volumes {
|
||||||
|
foundVolumes[volume.Name] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, volumeName := range volumeNames {
|
||||||
|
if foundVolumes[volumeName] != true {
|
||||||
|
return volume.VolumesListOKBody{}, fmt.Errorf("Volume %v not found", volumeName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return volumes, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// VolumeRemoveByLabels remove all volumes match by their labels
|
||||||
|
func (client *Client) VolumeRemoveByLabels(ctx context.Context, volumeLabels map[string]string) error {
|
||||||
|
volumes, err := client.VolumeListByLabels(ctx, volumeLabels)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, volume := range volumes.Volumes {
|
||||||
|
err := client.VolumeRemove(ctx, volume.Name, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// VolumeRemoveByNames remove all volumes match by their names. If a
|
||||||
|
// volume can not be found, the function returns an error
|
||||||
|
func (client *Client) VolumeRemoveByNames(ctx context.Context, volumeNames ...string) error {
|
||||||
|
volumes, err := client.VolumeListByNames(ctx, volumeNames...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, volume := range volumes.Volumes {
|
||||||
|
err := client.VolumeRemove(ctx, volume.Name, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// New returns a new dockerutil client
|
// New returns a new dockerutil client
|
||||||
func New() (*Client, error) {
|
func New() (*Client, error) {
|
||||||
dockerClient, err := client.NewEnvClient()
|
dockerClient, err := client.NewEnvClient()
|
||||||
|
443
pkg/testutils/dockerutils/client_test.go
Normal file
443
pkg/testutils/dockerutils/client_test.go
Normal file
@ -0,0 +1,443 @@
|
|||||||
|
package dockerutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/api/types/volume"
|
||||||
|
uuid "github.com/satori/go.uuid"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestContainerCRUD
|
||||||
|
// Test the following API functions:
|
||||||
|
// - ContainerListByLabels
|
||||||
|
// - ContainerListByNames
|
||||||
|
// - ContainerRemoveByNames
|
||||||
|
// - ContainerRemoveByLabels
|
||||||
|
// - ContainerRemoveByIDs
|
||||||
|
func TestContainerCRUD(t *testing.T) {
|
||||||
|
var (
|
||||||
|
ctx = context.Background()
|
||||||
|
require = require.New(t)
|
||||||
|
|
||||||
|
iterations = 5
|
||||||
|
|
||||||
|
cleanupLabels = map[string]string{
|
||||||
|
uuid.NewV4().String(): uuid.NewV4().String(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
dockerClient, err := New()
|
||||||
|
require.NoError(err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
dockerClient.ContainerRemoveByLabels(ctx, cleanupLabels)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create Containers
|
||||||
|
containerIDs := make([]string, 0)
|
||||||
|
containerNames := make([]string, 0)
|
||||||
|
for i := 0; i < iterations; i++ {
|
||||||
|
containerName := uuid.NewV4().String()
|
||||||
|
containerID, err := dockerClient.NewBuilder("nginx:alpine").
|
||||||
|
Labels(cleanupLabels).
|
||||||
|
Port("80").
|
||||||
|
Pull().
|
||||||
|
WithName(containerName).
|
||||||
|
Start(ctx)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
containerNames = append(containerNames, containerName)
|
||||||
|
containerIDs = append(containerIDs, containerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListByLabels
|
||||||
|
containers, err := dockerClient.ContainerListByLabels(ctx, true, cleanupLabels)
|
||||||
|
require.NoError(err)
|
||||||
|
require.Len(containers, iterations)
|
||||||
|
for _, container := range containers {
|
||||||
|
require.Contains(containerIDs, container.ID)
|
||||||
|
require.Contains(containerNames, strings.Split(container.Names[0], "/")[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListByNames
|
||||||
|
containers, err = dockerClient.ContainerListByNames(ctx, true, containerNames...)
|
||||||
|
require.NoError(err)
|
||||||
|
require.Len(containers, iterations)
|
||||||
|
for _, container := range containers {
|
||||||
|
require.Contains(containerIDs, container.ID)
|
||||||
|
require.Contains(containerNames, strings.Split(container.Names[0], "/")[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveByLabels
|
||||||
|
err = dockerClient.ContainerRemoveByLabels(ctx, cleanupLabels)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
containers, err = dockerClient.ContainerListByLabels(ctx, true, cleanupLabels)
|
||||||
|
require.NoError(err)
|
||||||
|
require.Len(containers, 0)
|
||||||
|
|
||||||
|
// Create
|
||||||
|
containerIDs = make([]string, 0)
|
||||||
|
containerNames = make([]string, 0)
|
||||||
|
for i := 0; i < iterations; i++ {
|
||||||
|
containerName := uuid.NewV4().String()
|
||||||
|
containerID, err := dockerClient.NewBuilder("nginx:alpine").
|
||||||
|
Labels(cleanupLabels).
|
||||||
|
Port("80").
|
||||||
|
Pull().
|
||||||
|
WithName(containerName).
|
||||||
|
Start(ctx)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
containerNames = append(containerNames, containerName)
|
||||||
|
containerIDs = append(containerIDs, containerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveByNames
|
||||||
|
err = dockerClient.ContainerRemoveByNames(ctx, containerNames...)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
containers, err = dockerClient.ContainerListByNames(ctx, true, containerNames...)
|
||||||
|
require.NoError(err)
|
||||||
|
require.Len(containers, 0)
|
||||||
|
|
||||||
|
// Create
|
||||||
|
containerIDs = make([]string, 0)
|
||||||
|
containerNames = make([]string, 0)
|
||||||
|
for i := 0; i < iterations; i++ {
|
||||||
|
containerName := uuid.NewV4().String()
|
||||||
|
containerID, err := dockerClient.NewBuilder("nginx:alpine").
|
||||||
|
Labels(cleanupLabels).
|
||||||
|
Port("80").
|
||||||
|
Pull().
|
||||||
|
WithName(containerName).
|
||||||
|
Start(ctx)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
containerNames = append(containerNames, containerName)
|
||||||
|
containerIDs = append(containerIDs, containerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveByID
|
||||||
|
err = dockerClient.ContainerRemoveByIDs(ctx, containerIDs...)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
containers, err = dockerClient.ContainerListByLabels(ctx, true, cleanupLabels)
|
||||||
|
require.NoError(err)
|
||||||
|
require.Len(containers, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestNetworkCRUD
|
||||||
|
// Test the following API functions:
|
||||||
|
// - NetworkListByLabels
|
||||||
|
// - NetworkListByNames
|
||||||
|
// - NetworkRemoveByLabels
|
||||||
|
// - NetworkRemoveByNames
|
||||||
|
// - NetworkRemoveByIDs
|
||||||
|
func TestNetworkCRUD(t *testing.T) {
|
||||||
|
var (
|
||||||
|
ctx = context.Background()
|
||||||
|
require = require.New(t)
|
||||||
|
|
||||||
|
iterations = 5
|
||||||
|
|
||||||
|
cleanupLabels = map[string]string{
|
||||||
|
uuid.NewV4().String(): uuid.NewV4().String(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
dockerClient, err := New()
|
||||||
|
require.NoError(err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
dockerClient.NetworkRemoveByLabels(ctx, cleanupLabels)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create Networks
|
||||||
|
networkIDs := make([]string, 0)
|
||||||
|
networkNames := make([]string, 0)
|
||||||
|
for i := 0; i < iterations; i++ {
|
||||||
|
networkName := uuid.NewV4().String()
|
||||||
|
resp, err := dockerClient.NetworkCreate(ctx, networkName, types.NetworkCreate{
|
||||||
|
Labels: cleanupLabels,
|
||||||
|
})
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
networkNames = append(networkNames, networkName)
|
||||||
|
networkIDs = append(networkIDs, resp.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListByLabels
|
||||||
|
networks, err := dockerClient.NetworkListByLabels(ctx, cleanupLabels)
|
||||||
|
require.NoError(err)
|
||||||
|
require.Len(networks, iterations)
|
||||||
|
for _, network := range networks {
|
||||||
|
require.Contains(networkIDs, network.ID)
|
||||||
|
require.Contains(networkNames, network.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListByLabels, network with label does not exist
|
||||||
|
networks, err = dockerClient.NetworkListByLabels(ctx, map[string]string{uuid.NewV4().String(): uuid.NewV4().String()})
|
||||||
|
require.NoError(err)
|
||||||
|
require.Len(networks, 0)
|
||||||
|
|
||||||
|
// ListByNames
|
||||||
|
networks, err = dockerClient.NetworkListByNames(ctx, networkNames...)
|
||||||
|
require.NoError(err)
|
||||||
|
require.Len(networks, iterations)
|
||||||
|
for _, network := range networks {
|
||||||
|
require.Contains(networkIDs, network.ID)
|
||||||
|
require.Contains(networkNames, network.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListByNames, network with names does not exist
|
||||||
|
networks, err = dockerClient.NetworkListByNames(ctx, uuid.NewV4().String(), uuid.NewV4().String())
|
||||||
|
require.Error(err)
|
||||||
|
require.Nil(networks)
|
||||||
|
|
||||||
|
// RemoveByLabels
|
||||||
|
err = dockerClient.NetworkRemoveByLabels(ctx, cleanupLabels)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
networks, err = dockerClient.NetworkListByLabels(ctx, cleanupLabels)
|
||||||
|
require.NoError(err)
|
||||||
|
require.Len(networks, 0)
|
||||||
|
|
||||||
|
// RemoveByLabels, label does not exists
|
||||||
|
err = dockerClient.NetworkRemoveByLabels(ctx, map[string]string{uuid.NewV4().String(): uuid.NewV4().String()})
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
// Create Networks
|
||||||
|
networkIDs = make([]string, 0)
|
||||||
|
networkNames = make([]string, 0)
|
||||||
|
for i := 0; i < iterations; i++ {
|
||||||
|
networkName := uuid.NewV4().String()
|
||||||
|
resp, err := dockerClient.NetworkCreate(ctx, networkName, types.NetworkCreate{
|
||||||
|
Labels: cleanupLabels,
|
||||||
|
})
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
networkNames = append(networkNames, networkName)
|
||||||
|
networkIDs = append(networkIDs, resp.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveByNames
|
||||||
|
err = dockerClient.NetworkRemoveByNames(ctx, networkNames...)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
networks, err = dockerClient.NetworkListByNames(ctx, networkNames...)
|
||||||
|
require.Error(err)
|
||||||
|
require.Nil(networks)
|
||||||
|
|
||||||
|
// RemoveByNames, name does not exists
|
||||||
|
err = dockerClient.NetworkRemoveByNames(ctx, uuid.NewV4().String())
|
||||||
|
require.Error(err)
|
||||||
|
|
||||||
|
// Create Networks
|
||||||
|
networkIDs = make([]string, 0)
|
||||||
|
networkNames = make([]string, 0)
|
||||||
|
for i := 0; i < iterations; i++ {
|
||||||
|
networkName := uuid.NewV4().String()
|
||||||
|
resp, err := dockerClient.NetworkCreate(ctx, networkName, types.NetworkCreate{
|
||||||
|
Labels: cleanupLabels,
|
||||||
|
})
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
networkNames = append(networkNames, networkName)
|
||||||
|
networkIDs = append(networkIDs, resp.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveByIDs
|
||||||
|
err = dockerClient.NetworkRemoveByIDs(ctx, networkIDs...)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
networks, err = dockerClient.NetworkListByNames(ctx, networkNames...)
|
||||||
|
require.Error(err)
|
||||||
|
require.Nil(networks)
|
||||||
|
|
||||||
|
// RemoveByID, id does not exists
|
||||||
|
err = dockerClient.NetworkRemoveByIDs(ctx, uuid.NewV4().String())
|
||||||
|
require.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVolumeCRUD(t *testing.T) {
|
||||||
|
var (
|
||||||
|
ctx = context.Background()
|
||||||
|
require = require.New(t)
|
||||||
|
|
||||||
|
iterations = 5
|
||||||
|
|
||||||
|
cleanupLabels = map[string]string{
|
||||||
|
uuid.NewV4().String(): uuid.NewV4().String(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
dockerClient, err := New()
|
||||||
|
require.NoError(err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
dockerClient.VolumeRemoveByLabels(ctx, cleanupLabels)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create Volumes
|
||||||
|
volumeNames := make([]string, 0)
|
||||||
|
for i := 0; i < iterations; i++ {
|
||||||
|
volumeName := uuid.NewV4().String()
|
||||||
|
volume, err := dockerClient.VolumeCreate(ctx, volume.VolumesCreateBody{
|
||||||
|
Name: volumeName,
|
||||||
|
Labels: cleanupLabels,
|
||||||
|
})
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
volumeNames = append(volumeNames, volume.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListByLabels
|
||||||
|
volumes, err := dockerClient.VolumeListByLabels(ctx, cleanupLabels)
|
||||||
|
require.NoError(err)
|
||||||
|
require.Len(volumes.Volumes, iterations)
|
||||||
|
for _, volume := range volumes.Volumes {
|
||||||
|
require.Contains(volumeNames, volume.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListByLabels, network with label does not exist
|
||||||
|
volumes, err = dockerClient.VolumeListByLabels(ctx, map[string]string{uuid.NewV4().String(): uuid.NewV4().String()})
|
||||||
|
require.NoError(err)
|
||||||
|
require.Len(volumes.Volumes, 0)
|
||||||
|
|
||||||
|
// ListByNames
|
||||||
|
volumes, err = dockerClient.VolumeListByNames(ctx, volumeNames...)
|
||||||
|
require.NoError(err)
|
||||||
|
require.Len(volumes.Volumes, iterations)
|
||||||
|
for _, volume := range volumes.Volumes {
|
||||||
|
require.Contains(volumeNames, volume.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListByNames, network with names does not exist
|
||||||
|
volumes, err = dockerClient.VolumeListByNames(ctx, uuid.NewV4().String(), uuid.NewV4().String())
|
||||||
|
require.Error(err)
|
||||||
|
require.Nil(volumes.Volumes)
|
||||||
|
|
||||||
|
// RemoveByLabels
|
||||||
|
err = dockerClient.VolumeRemoveByLabels(ctx, cleanupLabels)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
volumes, err = dockerClient.VolumeListByLabels(ctx, cleanupLabels)
|
||||||
|
require.NoError(err)
|
||||||
|
require.Len(volumes.Volumes, 0)
|
||||||
|
|
||||||
|
// RemoveByLabels, labels does not exists
|
||||||
|
err = dockerClient.NetworkRemoveByLabels(ctx, map[string]string{uuid.NewV4().String(): uuid.NewV4().String()})
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
// Create Volumes
|
||||||
|
volumeNames = make([]string, 0)
|
||||||
|
for i := 0; i < iterations; i++ {
|
||||||
|
volumeName := uuid.NewV4().String()
|
||||||
|
volume, err := dockerClient.VolumeCreate(ctx, volume.VolumesCreateBody{
|
||||||
|
Name: volumeName,
|
||||||
|
Labels: cleanupLabels,
|
||||||
|
})
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
volumeNames = append(volumeNames, volume.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveByNames
|
||||||
|
err = dockerClient.VolumeRemoveByNames(ctx, volumeNames...)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
volumes, err = dockerClient.VolumeListByNames(ctx, volumeNames...)
|
||||||
|
require.Error(err)
|
||||||
|
require.Nil(volumes.Volumes)
|
||||||
|
|
||||||
|
// RemoveByNames, name does not exists
|
||||||
|
err = dockerClient.NetworkRemoveByNames(ctx, uuid.NewV4().String())
|
||||||
|
require.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestContainerMultipleNetworks
|
||||||
|
// Test if a container can be accessed ofer multiple networks/ips.
|
||||||
|
func TestContainerMultipleNetworks(t *testing.T) {
|
||||||
|
var (
|
||||||
|
ctx = context.Background()
|
||||||
|
require = require.New(t)
|
||||||
|
|
||||||
|
iterations = 5
|
||||||
|
|
||||||
|
cleanupLabels = map[string]string{
|
||||||
|
uuid.NewV4().String(): uuid.NewV4().String(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
dockerClient, err := New()
|
||||||
|
require.NoError(err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
dockerClient.ContainerRemoveByLabels(ctx, cleanupLabels)
|
||||||
|
dockerClient.NetworkRemoveByLabels(ctx, cleanupLabels)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create Containers
|
||||||
|
containerIDs := make([]string, 0)
|
||||||
|
containerNames := make([]string, 0)
|
||||||
|
containersNetworks := make(map[string]map[string][]string, 0)
|
||||||
|
for i := 0; i < iterations; i++ {
|
||||||
|
containerName := uuid.NewV4().String()
|
||||||
|
|
||||||
|
containerNetworks := map[string][]string{
|
||||||
|
uuid.NewV4().String(): {
|
||||||
|
uuid.NewV4().String(),
|
||||||
|
uuid.NewV4().String(),
|
||||||
|
},
|
||||||
|
uuid.NewV4().String(): {
|
||||||
|
uuid.NewV4().String(),
|
||||||
|
uuid.NewV4().String(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
builder := dockerClient.NewBuilder("nginx:alpine").
|
||||||
|
Labels(cleanupLabels).
|
||||||
|
Port("80").
|
||||||
|
Pull().
|
||||||
|
WithName(containerName)
|
||||||
|
|
||||||
|
for networkName, aliasses := range containerNetworks {
|
||||||
|
_, err := dockerClient.NetworkCreate(ctx, networkName, types.NetworkCreate{
|
||||||
|
Labels: cleanupLabels,
|
||||||
|
})
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
builder.Network(networkName, aliasses...)
|
||||||
|
}
|
||||||
|
|
||||||
|
containerID, err := builder.Start(ctx)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
containerNames = append(containerNames, containerName)
|
||||||
|
containerIDs = append(containerIDs, containerID)
|
||||||
|
containersNetworks[containerID] = containerNetworks
|
||||||
|
}
|
||||||
|
|
||||||
|
for containerID, containerNetworks := range containersNetworks {
|
||||||
|
for networkName := range containerNetworks {
|
||||||
|
networks, err := dockerClient.NetworkListByNames(ctx, networkName)
|
||||||
|
require.NoError(err)
|
||||||
|
for _, network := range networks {
|
||||||
|
if _, present := network.Containers[containerID]; !present {
|
||||||
|
require.Fail("Container %v not found in network %v", containerID, network.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
networkIPParts := strings.Split(network.Containers[containerID].IPv4Address, "/")
|
||||||
|
|
||||||
|
url := fmt.Sprintf("http://%v", networkIPParts[0])
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
require.NoError(err)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
require.Equal(http.StatusOK, resp.StatusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user