mirror of
https://github.com/kreuzwerker/terraform-provider-docker.git
synced 2026-02-16 00:58:22 -05:00
merge master
This commit is contained in:
commit
64e491f7b9
57 changed files with 694 additions and 7750 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -31,3 +31,6 @@ website/vendor
|
|||
!command/test-fixtures/**/.terraform/
|
||||
scripts/testing/auth
|
||||
scripts/testing/certs
|
||||
|
||||
# build outputs
|
||||
results
|
||||
|
|
|
|||
|
|
@ -25,10 +25,11 @@ install:
|
|||
- sudo service docker restart
|
||||
|
||||
script:
|
||||
- make test
|
||||
- make testacc
|
||||
- make vendor-status
|
||||
- make vet
|
||||
- make test
|
||||
- make testacc
|
||||
- make compile
|
||||
- make website-test
|
||||
|
||||
branches:
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
## 1.0.0 (Unreleased)
|
||||
## 1.0.1 (Unreleased)
|
||||
## 1.0.0 (July 03, 2018)
|
||||
|
||||
NOTES:
|
||||
* Update `go-dockerclient` to `bf3bc17bb` [#46](https://github.com/terraform-providers/terraform-provider-docker/pull/46)
|
||||
* The `links` property on `resource_docker_container` is now marked as deprecated [#47](https://github.com/terraform-providers/terraform-provider-docker/pull/47)
|
||||
|
||||
FEATURES:
|
||||
* Add `swarm` capabilities [GH-29] [#40](https://github.com/terraform-providers/terraform-provider-docker/pull/40) with fixes [#66](https://github.com/terraform-providers/terraform-provider-docker/pull/66) up to Docker `18.03.1` and API Version `1.37` [GH-64]
|
||||
* Add `swarm` capabilities ([#29](https://github.com/terraform-providers/terraform-provider-docker/issues/29)] [#40](https://github.com/terraform-providers/terraform-provider-docker/pull/40) with fixes [#66](https://github.com/terraform-providers/terraform-provider-docker/pull/66) up to Docker `18.03.1` and API Version `1.37` [[#64](https://github.com/terraform-providers/terraform-provider-docker/issues/64))
|
||||
* Add ability to upload executable files [#55](https://github.com/terraform-providers/terraform-provider-docker/pull/55)
|
||||
* Add support to attach devices to containers [GH-30] [#54](https://github.com/terraform-providers/terraform-provider-docker/pull/54)
|
||||
* Add support to attach devices to containers [[#30](https://github.com/terraform-providers/terraform-provider-docker/issues/30)] [#54](https://github.com/terraform-providers/terraform-provider-docker/pull/54)
|
||||
* Add Ulimits to containers [#35](https://github.com/terraform-providers/terraform-provider-docker/pull/35)
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
|
@ -18,7 +19,7 @@ IMPROVEMENTS:
|
|||
* Add prefix `library` only to official images in the path [#27](https://github.com/terraform-providers/terraform-provider-docker/pull/27)
|
||||
|
||||
BUG FIXES
|
||||
* Update documentation for private registries [GH-45]
|
||||
* Update documentation for private registries ([#45](https://github.com/terraform-providers/terraform-provider-docker/issues/45))
|
||||
|
||||
## 0.1.1 (November 21, 2017)
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ test: fmtcheck
|
|||
testacc: fmtcheck
|
||||
@sh -c "'$(CURDIR)/scripts/runAccTests.sh'"
|
||||
|
||||
compile: fmtcheck
|
||||
@sh -c "'$(CURDIR)/scripts/compile.sh'"
|
||||
|
||||
vet:
|
||||
@echo "go vet ."
|
||||
@go vet $$(go list ./... | grep -v vendor/) ; if [ $$? -eq 1 ]; then \
|
||||
|
|
|
|||
|
|
@ -5,12 +5,15 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
dc "github.com/fsouza/go-dockerclient"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
)
|
||||
|
||||
// DockerConfig is the structure that stores the configuration to talk to a
|
||||
const apiVersion = "1.37"
|
||||
|
||||
// Config is the structure that stores the configuration to talk to a
|
||||
// Docker API compatible host.
|
||||
type DockerConfig struct {
|
||||
type Config struct {
|
||||
Host string
|
||||
Ca string
|
||||
Cert string
|
||||
|
|
@ -18,8 +21,8 @@ type DockerConfig struct {
|
|||
CertPath string
|
||||
}
|
||||
|
||||
// NewClient() returns a new Docker client.
|
||||
func (c *DockerConfig) NewClient() (*dc.Client, error) {
|
||||
// NewClient returns a new Docker client.
|
||||
func (c *Config) NewClient() (*client.Client, error) {
|
||||
if c.Ca != "" || c.Cert != "" || c.Key != "" {
|
||||
if c.Ca == "" || c.Cert == "" || c.Key == "" {
|
||||
return nil, fmt.Errorf("ca_material, cert_material, and key_material must be specified")
|
||||
|
|
@ -29,7 +32,11 @@ func (c *DockerConfig) NewClient() (*dc.Client, error) {
|
|||
return nil, fmt.Errorf("cert_path must not be specified")
|
||||
}
|
||||
|
||||
return dc.NewTLSClientFromBytes(c.Host, []byte(c.Cert), []byte(c.Key), []byte(c.Ca))
|
||||
return client.NewClientWithOpts(
|
||||
client.WithHost(c.Host),
|
||||
client.WithTLSClientConfig(c.Ca, c.Cert, c.Key),
|
||||
client.WithVersion(apiVersion),
|
||||
)
|
||||
}
|
||||
|
||||
if c.CertPath != "" {
|
||||
|
|
@ -37,22 +44,29 @@ func (c *DockerConfig) NewClient() (*dc.Client, error) {
|
|||
ca := filepath.Join(c.CertPath, "ca.pem")
|
||||
cert := filepath.Join(c.CertPath, "cert.pem")
|
||||
key := filepath.Join(c.CertPath, "key.pem")
|
||||
return dc.NewTLSClient(c.Host, cert, key, ca)
|
||||
return client.NewClientWithOpts(
|
||||
client.WithHost(c.Host),
|
||||
client.WithTLSClientConfig(ca, cert, key),
|
||||
client.WithVersion(apiVersion),
|
||||
)
|
||||
}
|
||||
|
||||
// If there is no cert information, then just return the direct client
|
||||
return dc.NewClient(c.Host)
|
||||
return client.NewClientWithOpts(
|
||||
client.WithHost(c.Host),
|
||||
client.WithVersion(apiVersion),
|
||||
)
|
||||
}
|
||||
|
||||
// Data structure for holding data that we fetch from Docker.
|
||||
type Data struct {
|
||||
DockerImages map[string]*dc.APIImages
|
||||
DockerImages map[string]*types.ImageSummary
|
||||
}
|
||||
|
||||
// ProviderConfig for the custom registry provider
|
||||
type ProviderConfig struct {
|
||||
DockerClient *dc.Client
|
||||
AuthConfigs *dc.AuthConfigurations
|
||||
DockerClient *client.Client
|
||||
AuthConfigs *AuthConfigs
|
||||
}
|
||||
|
||||
// The registry address can be referenced in various places (registry auth, docker config file, image name)
|
||||
|
|
|
|||
|
|
@ -171,10 +171,10 @@ func getImageDigest(registry, image, tag, username, password string, fallback bo
|
|||
}
|
||||
|
||||
return digestResponse.Header.Get("Docker-Content-Digest"), nil
|
||||
} else {
|
||||
return "", fmt.Errorf("Bad credentials: " + resp.Status)
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("Bad credentials: " + resp.Status)
|
||||
|
||||
// Some unexpected status was given, return an error
|
||||
default:
|
||||
return "", fmt.Errorf("Got bad response from registry: " + resp.Status)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,18 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/user"
|
||||
"strings"
|
||||
|
||||
dc "github.com/fsouza/go-dockerclient"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
|
@ -107,7 +113,7 @@ func Provider() terraform.ResourceProvider {
|
|||
}
|
||||
|
||||
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
||||
config := DockerConfig{
|
||||
config := Config{
|
||||
Host: d.Get("host").(string),
|
||||
Ca: d.Get("ca_material").(string),
|
||||
Cert: d.Get("cert_material").(string),
|
||||
|
|
@ -120,12 +126,13 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
|||
return nil, fmt.Errorf("Error initializing Docker client: %s", err)
|
||||
}
|
||||
|
||||
err = client.Ping()
|
||||
ctx := context.Background()
|
||||
_, err = client.Ping(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error pinging Docker server: %s", err)
|
||||
}
|
||||
|
||||
authConfigs := &dc.AuthConfigurations{}
|
||||
authConfigs := &AuthConfigs{}
|
||||
|
||||
if v, ok := d.GetOk("registry_auth"); ok {
|
||||
authConfigs, err = providerSetToRegistryAuth(v.(*schema.Set))
|
||||
|
|
@ -143,15 +150,31 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
|||
return &providerConfig, nil
|
||||
}
|
||||
|
||||
// ErrCannotParseDockercfg is the error returned by NewAuthConfigurations when the dockercfg cannot be parsed.
|
||||
var ErrCannotParseDockercfg = errors.New("Failed to read authentication from dockercfg")
|
||||
|
||||
// AuthConfigs represents authentication options to use for the
|
||||
// PushImage method accommodating the new X-Registry-Config header
|
||||
type AuthConfigs struct {
|
||||
Configs map[string]types.AuthConfig `json:"configs"`
|
||||
}
|
||||
|
||||
// dockerConfig represents a registry authentation configuration from the
|
||||
// .dockercfg file.
|
||||
type dockerConfig struct {
|
||||
Auth string `json:"auth"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
// Take the given registry_auth schemas and return a map of registry auth configurations
|
||||
func providerSetToRegistryAuth(authSet *schema.Set) (*dc.AuthConfigurations, error) {
|
||||
authConfigs := dc.AuthConfigurations{
|
||||
Configs: make(map[string]dc.AuthConfiguration),
|
||||
func providerSetToRegistryAuth(authSet *schema.Set) (*AuthConfigs, error) {
|
||||
authConfigs := AuthConfigs{
|
||||
Configs: make(map[string]types.AuthConfig),
|
||||
}
|
||||
|
||||
for _, authInt := range authSet.List() {
|
||||
auth := authInt.(map[string]interface{})
|
||||
authConfig := dc.AuthConfiguration{}
|
||||
authConfig := types.AuthConfig{}
|
||||
authConfig.ServerAddress = normalizeRegistryAddress(auth["address"].(string))
|
||||
|
||||
// For each registry_auth block, generate an AuthConfiguration using either
|
||||
|
|
@ -174,7 +197,7 @@ func providerSetToRegistryAuth(authSet *schema.Set) (*dc.AuthConfigurations, err
|
|||
return nil, fmt.Errorf("Error opening docker registry config file: %v", err)
|
||||
}
|
||||
|
||||
auths, err := dc.NewAuthConfigurations(r)
|
||||
auths, err := newAuthConfigurations(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error parsing docker registry config json: %v", err)
|
||||
}
|
||||
|
|
@ -199,3 +222,68 @@ func providerSetToRegistryAuth(authSet *schema.Set) (*dc.AuthConfigurations, err
|
|||
|
||||
return &authConfigs, nil
|
||||
}
|
||||
|
||||
// newAuthConfigurations returns AuthConfigs from a JSON encoded string in the
|
||||
// same format as the .dockercfg file.
|
||||
func newAuthConfigurations(r io.Reader) (*AuthConfigs, error) {
|
||||
var auth *AuthConfigs
|
||||
confs, err := parseDockerConfig(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
auth, err = authConfigs(confs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return auth, nil
|
||||
}
|
||||
|
||||
// parseDockerConfig parses the docker config file for auths
|
||||
func parseDockerConfig(r io.Reader) (map[string]dockerConfig, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
buf.ReadFrom(r)
|
||||
byteData := buf.Bytes()
|
||||
|
||||
confsWrapper := struct {
|
||||
Auths map[string]dockerConfig `json:"auths"`
|
||||
}{}
|
||||
if err := json.Unmarshal(byteData, &confsWrapper); err == nil {
|
||||
if len(confsWrapper.Auths) > 0 {
|
||||
return confsWrapper.Auths, nil
|
||||
}
|
||||
}
|
||||
|
||||
var confs map[string]dockerConfig
|
||||
if err := json.Unmarshal(byteData, &confs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return confs, nil
|
||||
}
|
||||
|
||||
// authConfigs converts a dockerConfigs map to a AuthConfigs object.
|
||||
func authConfigs(confs map[string]dockerConfig) (*AuthConfigs, error) {
|
||||
c := &AuthConfigs{
|
||||
Configs: make(map[string]types.AuthConfig),
|
||||
}
|
||||
for reg, conf := range confs {
|
||||
if conf.Auth == "" {
|
||||
continue
|
||||
}
|
||||
data, err := base64.StdEncoding.DecodeString(conf.Auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userpass := strings.SplitN(string(data), ":", 2)
|
||||
if len(userpass) != 2 {
|
||||
return nil, ErrCannotParseDockercfg
|
||||
}
|
||||
c.Configs[reg] = types.AuthConfig{
|
||||
Email: conf.Email,
|
||||
Username: userpass[0],
|
||||
Password: userpass[1],
|
||||
ServerAddress: reg,
|
||||
Auth: conf.Auth,
|
||||
}
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ import (
|
|||
"encoding/base64"
|
||||
"log"
|
||||
|
||||
"context"
|
||||
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
dc "github.com/fsouza/go-dockerclient"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
|
|
@ -39,16 +40,14 @@ func resourceDockerConfigCreate(d *schema.ResourceData, meta interface{}) error
|
|||
client := meta.(*ProviderConfig).DockerClient
|
||||
data, _ := base64.StdEncoding.DecodeString(d.Get("data").(string))
|
||||
|
||||
createConfigOpts := dc.CreateConfigOptions{
|
||||
ConfigSpec: swarm.ConfigSpec{
|
||||
Annotations: swarm.Annotations{
|
||||
Name: d.Get("name").(string),
|
||||
},
|
||||
Data: data,
|
||||
configSpec := swarm.ConfigSpec{
|
||||
Annotations: swarm.Annotations{
|
||||
Name: d.Get("name").(string),
|
||||
},
|
||||
Data: data,
|
||||
}
|
||||
|
||||
config, err := client.CreateConfig(createConfigOpts)
|
||||
config, err := client.ConfigCreate(context.Background(), configSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -59,15 +58,12 @@ func resourceDockerConfigCreate(d *schema.ResourceData, meta interface{}) error
|
|||
|
||||
func resourceDockerConfigRead(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*ProviderConfig).DockerClient
|
||||
config, err := client.InspectConfig(d.Id())
|
||||
config, _, err := client.ConfigInspectWithRaw(context.Background(), d.Id())
|
||||
|
||||
if err != nil {
|
||||
if _, ok := err.(*dc.NoSuchConfig); ok {
|
||||
log.Printf("[WARN] Config (%s) not found, removing from state", d.Id())
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
log.Printf("[WARN] Config (%s) not found, removing from state", d.Id())
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
d.SetId(config.ID)
|
||||
return nil
|
||||
|
|
@ -75,9 +71,7 @@ func resourceDockerConfigRead(d *schema.ResourceData, meta interface{}) error {
|
|||
|
||||
func resourceDockerConfigDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*ProviderConfig).DockerClient
|
||||
err := client.RemoveConfig(dc.RemoveConfigOptions{
|
||||
ID: d.Id(),
|
||||
})
|
||||
err := client.ConfigRemove(context.Background(), d.Id())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
"context"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
|
@ -82,9 +83,9 @@ func testCheckDockerConfigDestroy(s *terraform.State) error {
|
|||
}
|
||||
|
||||
id := rs.Primary.Attributes["id"]
|
||||
config, err := client.InspectConfig(id)
|
||||
_, _, err := client.ConfigInspectWithRaw(context.Background(), id)
|
||||
|
||||
if err == nil || config != nil {
|
||||
if err == nil {
|
||||
return fmt.Errorf("Config with id '%s' still exists", id)
|
||||
}
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"regexp"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
|
|
@ -443,13 +439,3 @@ func resourceDockerContainer() *schema.Resource {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
func validateDockerContainerPath(v interface{}, k string) (ws []string, errors []error) {
|
||||
|
||||
value := v.(string)
|
||||
if !regexp.MustCompile(`^[a-zA-Z]:\\|^/`).MatchString(value) {
|
||||
errors = append(errors, fmt.Errorf("%q must be an absolute path", k))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,15 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
dc "github.com/fsouza/go-dockerclient"
|
||||
"context"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -33,27 +40,19 @@ func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) err
|
|||
image = image + ":latest"
|
||||
}
|
||||
|
||||
// The awesome, wonderful, splendiferous, sensical
|
||||
// Docker API now lets you specify a HostConfig in
|
||||
// CreateContainerOptions, but in my testing it still only
|
||||
// actually applies HostConfig options set in StartContainer.
|
||||
// How cool is that?
|
||||
createOpts := dc.CreateContainerOptions{
|
||||
Name: d.Get("name").(string),
|
||||
Config: &dc.Config{
|
||||
Image: image,
|
||||
Hostname: d.Get("hostname").(string),
|
||||
Domainname: d.Get("domainname").(string),
|
||||
},
|
||||
config := &container.Config{
|
||||
Image: image,
|
||||
Hostname: d.Get("hostname").(string),
|
||||
Domainname: d.Get("domainname").(string),
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("env"); ok {
|
||||
createOpts.Config.Env = stringSetToStringSlice(v.(*schema.Set))
|
||||
config.Env = stringSetToStringSlice(v.(*schema.Set))
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("command"); ok {
|
||||
createOpts.Config.Cmd = stringListToStringSlice(v.([]interface{}))
|
||||
for _, v := range createOpts.Config.Cmd {
|
||||
config.Cmd = stringListToStringSlice(v.([]interface{}))
|
||||
for _, v := range config.Cmd {
|
||||
if v == "" {
|
||||
return fmt.Errorf("values for command may not be empty")
|
||||
}
|
||||
|
|
@ -61,21 +60,21 @@ func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) err
|
|||
}
|
||||
|
||||
if v, ok := d.GetOk("entrypoint"); ok {
|
||||
createOpts.Config.Entrypoint = stringListToStringSlice(v.([]interface{}))
|
||||
config.Entrypoint = stringListToStringSlice(v.([]interface{}))
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("user"); ok {
|
||||
createOpts.Config.User = v.(string)
|
||||
config.User = v.(string)
|
||||
}
|
||||
|
||||
exposedPorts := map[dc.Port]struct{}{}
|
||||
portBindings := map[dc.Port][]dc.PortBinding{}
|
||||
exposedPorts := map[nat.Port]struct{}{}
|
||||
portBindings := map[nat.Port][]nat.PortBinding{}
|
||||
|
||||
if v, ok := d.GetOk("ports"); ok {
|
||||
exposedPorts, portBindings = portSetToDockerPorts(v.(*schema.Set))
|
||||
}
|
||||
if len(exposedPorts) != 0 {
|
||||
createOpts.Config.ExposedPorts = exposedPorts
|
||||
config.ExposedPorts = exposedPorts
|
||||
}
|
||||
|
||||
extraHosts := []string{}
|
||||
|
|
@ -83,7 +82,7 @@ func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) err
|
|||
extraHosts = extraHostsSetToDockerExtraHosts(v.(*schema.Set))
|
||||
}
|
||||
|
||||
extraUlimits := []dc.ULimit{}
|
||||
extraUlimits := []*units.Ulimit{}
|
||||
if v, ok := d.GetOk("ulimit"); ok {
|
||||
extraUlimits = ulimitsToDockerUlimits(v.(*schema.Set))
|
||||
}
|
||||
|
|
@ -98,21 +97,21 @@ func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) err
|
|||
}
|
||||
}
|
||||
if len(volumes) != 0 {
|
||||
createOpts.Config.Volumes = volumes
|
||||
config.Volumes = volumes
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("labels"); ok {
|
||||
createOpts.Config.Labels = mapTypeMapValsToString(v.(map[string]interface{}))
|
||||
config.Labels = mapTypeMapValsToString(v.(map[string]interface{}))
|
||||
}
|
||||
|
||||
hostConfig := &dc.HostConfig{
|
||||
hostConfig := &container.HostConfig{
|
||||
Privileged: d.Get("privileged").(bool),
|
||||
PublishAllPorts: d.Get("publish_all_ports").(bool),
|
||||
RestartPolicy: dc.RestartPolicy{
|
||||
RestartPolicy: container.RestartPolicy{
|
||||
Name: d.Get("restart").(string),
|
||||
MaximumRetryCount: d.Get("max_retry_count").(int),
|
||||
},
|
||||
LogConfig: dc.LogConfig{
|
||||
LogConfig: container.LogConfig{
|
||||
Type: d.Get("log_driver").(string),
|
||||
},
|
||||
}
|
||||
|
|
@ -182,36 +181,29 @@ func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) err
|
|||
hostConfig.LogConfig.Config = mapTypeMapValsToString(v.(map[string]interface{}))
|
||||
}
|
||||
|
||||
networkingConfig := &network.NetworkingConfig{}
|
||||
if v, ok := d.GetOk("network_mode"); ok {
|
||||
hostConfig.NetworkMode = v.(string)
|
||||
hostConfig.NetworkMode = container.NetworkMode(v.(string))
|
||||
}
|
||||
|
||||
createOpts.HostConfig = hostConfig
|
||||
var retContainer container.ContainerCreateCreatedBody
|
||||
|
||||
var retContainer *dc.Container
|
||||
if retContainer, err = client.CreateContainer(createOpts); err != nil {
|
||||
if retContainer, err = client.ContainerCreate(context.Background(), config, hostConfig, networkingConfig, d.Get("name").(string)); err != nil {
|
||||
return fmt.Errorf("Unable to create container: %s", err)
|
||||
}
|
||||
if retContainer == nil {
|
||||
return fmt.Errorf("Returned container is nil")
|
||||
}
|
||||
|
||||
d.SetId(retContainer.ID)
|
||||
|
||||
if v, ok := d.GetOk("networks"); ok {
|
||||
var connectionOpts dc.NetworkConnectionOptions
|
||||
endpointConfig := &network.EndpointSettings{}
|
||||
if v, ok := d.GetOk("network_alias"); ok {
|
||||
endpointConfig := &dc.EndpointConfig{}
|
||||
endpointConfig.Aliases = stringSetToStringSlice(v.(*schema.Set))
|
||||
connectionOpts = dc.NetworkConnectionOptions{Container: retContainer.ID, EndpointConfig: endpointConfig}
|
||||
} else {
|
||||
connectionOpts = dc.NetworkConnectionOptions{Container: retContainer.ID}
|
||||
}
|
||||
|
||||
for _, rawNetwork := range v.(*schema.Set).List() {
|
||||
network := rawNetwork.(string)
|
||||
if err := client.ConnectNetwork(network, connectionOpts); err != nil {
|
||||
return fmt.Errorf("Unable to connect to network '%s': %s", network, err)
|
||||
networkID := rawNetwork.(string)
|
||||
if err := client.NetworkConnect(context.Background(), networkID, retContainer.ID, endpointConfig); err != nil {
|
||||
return fmt.Errorf("Unable to connect to network '%s': %s", networkID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -246,19 +238,18 @@ func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) err
|
|||
return fmt.Errorf("Error creating tar archive: %s", err)
|
||||
}
|
||||
|
||||
uploadOpts := dc.UploadToContainerOptions{
|
||||
InputStream: bytes.NewReader(buf.Bytes()),
|
||||
Path: "/",
|
||||
}
|
||||
|
||||
if err := client.UploadToContainer(retContainer.ID, uploadOpts); err != nil {
|
||||
dstPath := "/"
|
||||
uploadContent := bytes.NewReader(buf.Bytes())
|
||||
options := types.CopyToContainerOptions{}
|
||||
if err := client.CopyToContainer(context.Background(), retContainer.ID, dstPath, uploadContent, options); err != nil {
|
||||
return fmt.Errorf("Unable to upload volume content: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
creationTime = time.Now()
|
||||
if err := client.StartContainer(retContainer.ID, nil); err != nil {
|
||||
options := types.ContainerStartOptions{}
|
||||
if err := client.ContainerStart(context.Background(), retContainer.ID, options); err != nil {
|
||||
return fmt.Errorf("Unable to start container: %s", err)
|
||||
}
|
||||
|
||||
|
|
@ -278,7 +269,7 @@ func resourceDockerContainerRead(d *schema.ResourceData, meta interface{}) error
|
|||
return nil
|
||||
}
|
||||
|
||||
var container *dc.Container
|
||||
var container types.ContainerJSON
|
||||
|
||||
// TODO fix this with statefunc
|
||||
loops := 1 // if it hasn't just been created, don't delay
|
||||
|
|
@ -288,7 +279,7 @@ func resourceDockerContainerRead(d *schema.ResourceData, meta interface{}) error
|
|||
sleepTime := 500 * time.Millisecond
|
||||
|
||||
for i := loops; i > 0; i-- {
|
||||
container, err = client.InspectContainer(apiContainer.ID)
|
||||
container, err = client.ContainerInspect(context.Background(), apiContainer.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error inspecting container %s: %s", apiContainer.ID, err)
|
||||
}
|
||||
|
|
@ -302,7 +293,11 @@ func resourceDockerContainerRead(d *schema.ResourceData, meta interface{}) error
|
|||
return resourceDockerContainerDelete(d, meta)
|
||||
}
|
||||
|
||||
if container.State.FinishedAt.After(creationTime) {
|
||||
finishTime, err := time.Parse(time.RFC3339, container.State.FinishedAt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Container finish time could not be parsed: %s", container.State.FinishedAt)
|
||||
}
|
||||
if finishTime.After(creationTime) {
|
||||
// It exited immediately, so error out so dependent containers
|
||||
// aren't started
|
||||
resourceDockerContainerDelete(d, meta)
|
||||
|
|
@ -338,19 +333,20 @@ func resourceDockerContainerDelete(d *schema.ResourceData, meta interface{}) err
|
|||
|
||||
// Stop the container before removing if destroy_grace_seconds is defined
|
||||
if d.Get("destroy_grace_seconds").(int) > 0 {
|
||||
var timeout = uint(d.Get("destroy_grace_seconds").(int))
|
||||
if err := client.StopContainer(d.Id(), timeout); err != nil {
|
||||
mapped := int32(d.Get("destroy_grace_seconds").(int))
|
||||
timeoutInSeconds := rand.Int31n(mapped)
|
||||
timeout := time.Duration(time.Duration(timeoutInSeconds) * time.Second)
|
||||
if err := client.ContainerStop(context.Background(), d.Id(), &timeout); err != nil {
|
||||
return fmt.Errorf("Error stopping container %s: %s", d.Id(), err)
|
||||
}
|
||||
}
|
||||
|
||||
removeOpts := dc.RemoveContainerOptions{
|
||||
ID: d.Id(),
|
||||
removeOpts := types.ContainerRemoveOptions{
|
||||
RemoveVolumes: true,
|
||||
Force: true,
|
||||
}
|
||||
|
||||
if err := client.RemoveContainer(removeOpts); err != nil {
|
||||
if err := client.ContainerRemove(context.Background(), d.Id(), removeOpts); err != nil {
|
||||
return fmt.Errorf("Error deleting container %s: %s", d.Id(), err)
|
||||
}
|
||||
|
||||
|
|
@ -398,8 +394,8 @@ func mapTypeMapValsToStringSlice(typeMap map[string]interface{}) []string {
|
|||
return mapped
|
||||
}
|
||||
|
||||
func fetchDockerContainer(ID string, client *dc.Client) (*dc.APIContainers, error) {
|
||||
apiContainers, err := client.ListContainers(dc.ListContainersOptions{All: true})
|
||||
func fetchDockerContainer(ID string, client *client.Client) (*types.Container, error) {
|
||||
apiContainers, err := client.ContainerList(context.Background(), types.ContainerListOptions{All: true})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error fetching container information from Docker: %s\n", err)
|
||||
|
|
@ -414,23 +410,23 @@ func fetchDockerContainer(ID string, client *dc.Client) (*dc.APIContainers, erro
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func portSetToDockerPorts(ports *schema.Set) (map[dc.Port]struct{}, map[dc.Port][]dc.PortBinding) {
|
||||
retExposedPorts := map[dc.Port]struct{}{}
|
||||
retPortBindings := map[dc.Port][]dc.PortBinding{}
|
||||
func portSetToDockerPorts(ports *schema.Set) (map[nat.Port]struct{}, map[nat.Port][]nat.PortBinding) {
|
||||
retExposedPorts := map[nat.Port]struct{}{}
|
||||
retPortBindings := map[nat.Port][]nat.PortBinding{}
|
||||
|
||||
for _, portInt := range ports.List() {
|
||||
port := portInt.(map[string]interface{})
|
||||
internal := port["internal"].(int)
|
||||
protocol := port["protocol"].(string)
|
||||
|
||||
exposedPort := dc.Port(strconv.Itoa(internal) + "/" + protocol)
|
||||
exposedPort := nat.Port(strconv.Itoa(internal) + "/" + protocol)
|
||||
retExposedPorts[exposedPort] = struct{}{}
|
||||
|
||||
external, extOk := port["external"].(int)
|
||||
ip, ipOk := port["ip"].(string)
|
||||
|
||||
if extOk {
|
||||
portBinding := dc.PortBinding{
|
||||
portBinding := nat.PortBinding{
|
||||
HostPort: strconv.Itoa(external),
|
||||
}
|
||||
if ipOk {
|
||||
|
|
@ -443,12 +439,12 @@ func portSetToDockerPorts(ports *schema.Set) (map[dc.Port]struct{}, map[dc.Port]
|
|||
return retExposedPorts, retPortBindings
|
||||
}
|
||||
|
||||
func ulimitsToDockerUlimits(extraUlimits *schema.Set) []dc.ULimit {
|
||||
retExtraUlimits := []dc.ULimit{}
|
||||
func ulimitsToDockerUlimits(extraUlimits *schema.Set) []*units.Ulimit {
|
||||
retExtraUlimits := []*units.Ulimit{}
|
||||
|
||||
for _, ulimitInt := range extraUlimits.List() {
|
||||
ulimits := ulimitInt.(map[string]interface{})
|
||||
u := dc.ULimit{
|
||||
u := &units.Ulimit{
|
||||
Name: ulimits["name"].(string),
|
||||
Soft: int64(ulimits["soft"].(int)),
|
||||
Hard: int64(ulimits["hard"].(int)),
|
||||
|
|
@ -508,8 +504,8 @@ func volumeSetToDockerVolumes(volumes *schema.Set) (map[string]struct{}, []strin
|
|||
return retVolumeMap, retHostConfigBinds, retVolumeFromContainers, nil
|
||||
}
|
||||
|
||||
func deviceSetToDockerDevices(devices *schema.Set) []dc.Device {
|
||||
retDevices := []dc.Device{}
|
||||
func deviceSetToDockerDevices(devices *schema.Set) []container.DeviceMapping {
|
||||
retDevices := []container.DeviceMapping{}
|
||||
for _, deviceInt := range devices.List() {
|
||||
deviceMap := deviceInt.(map[string]interface{})
|
||||
hostPath := deviceMap["host_path"].(string)
|
||||
|
|
@ -524,7 +520,7 @@ func deviceSetToDockerDevices(devices *schema.Set) []dc.Device {
|
|||
permissions = "rwm"
|
||||
}
|
||||
|
||||
device := dc.Device{
|
||||
device := container.DeviceMapping{
|
||||
PathOnHost: hostPath,
|
||||
PathInContainer: containerPath,
|
||||
CgroupPermissions: permissions,
|
||||
|
|
|
|||
|
|
@ -4,18 +4,18 @@ import (
|
|||
"archive/tar"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
dc "github.com/fsouza/go-dockerclient"
|
||||
"context"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccDockerContainer_basic(t *testing.T) {
|
||||
var c dc.Container
|
||||
var c types.ContainerJSON
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
|
|
@ -54,7 +54,7 @@ func TestAccDockerContainerPath_validation(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAccDockerContainer_volume(t *testing.T) {
|
||||
var c dc.Container
|
||||
var c types.ContainerJSON
|
||||
|
||||
testCheck := func(*terraform.State) error {
|
||||
if len(c.Mounts) != 1 {
|
||||
|
|
@ -96,7 +96,7 @@ func TestAccDockerContainer_volume(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAccDockerContainer_customized(t *testing.T) {
|
||||
var c dc.Container
|
||||
var c types.ContainerJSON
|
||||
|
||||
testCheck := func(*terraform.State) error {
|
||||
if len(c.Config.Entrypoint) < 3 ||
|
||||
|
|
@ -257,24 +257,18 @@ func TestAccDockerContainer_customized(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAccDockerContainer_upload(t *testing.T) {
|
||||
var c dc.Container
|
||||
var c types.ContainerJSON
|
||||
|
||||
testCheck := func(*terraform.State) error {
|
||||
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
opts := dc.DownloadFromContainerOptions{
|
||||
OutputStream: buf,
|
||||
Path: "/terraform/test.txt",
|
||||
}
|
||||
|
||||
if err := client.DownloadFromContainer(c.ID, opts); err != nil {
|
||||
srcPath := "/terraform/test.txt"
|
||||
r, _, err := client.CopyFromContainer(context.Background(), c.ID, srcPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to download a file from container: %s", err)
|
||||
}
|
||||
|
||||
r := bytes.NewReader(buf.Bytes())
|
||||
tr := tar.NewReader(r)
|
||||
|
||||
if header, err := tr.Next(); err != nil {
|
||||
return fmt.Errorf("Unable to read content of tar archive: %s", err)
|
||||
} else {
|
||||
|
|
@ -311,43 +305,32 @@ func TestAccDockerContainer_upload(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAccDockerContainer_device(t *testing.T) {
|
||||
var c dc.Container
|
||||
var c types.ContainerJSON
|
||||
|
||||
testCheck := func(*terraform.State) error {
|
||||
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
|
||||
|
||||
createExecOpts := dc.CreateExecOptions{
|
||||
Cmd: []string{"dd", "if=/dev/zero_test", "of=/tmp/test.txt", "count=10", "bs=1"},
|
||||
Container: c.ID,
|
||||
createExecOpts := types.ExecConfig{
|
||||
Cmd: []string{"dd", "if=/dev/zero_test", "of=/tmp/test.txt", "count=10", "bs=1"},
|
||||
}
|
||||
|
||||
exec, err := client.CreateExec(createExecOpts)
|
||||
exec, err := client.ContainerExecCreate(context.Background(), c.ID, createExecOpts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to create a exec instance on container: %s", err)
|
||||
}
|
||||
|
||||
startExecOpts := dc.StartExecOptions{
|
||||
OutputStream: os.Stdout,
|
||||
ErrorStream: os.Stdout,
|
||||
}
|
||||
|
||||
if err := client.StartExec(exec.ID, startExecOpts); err != nil {
|
||||
startExecOpts := types.ExecStartCheck{}
|
||||
if err := client.ContainerExecStart(context.Background(), exec.ID, startExecOpts); err != nil {
|
||||
return fmt.Errorf("Unable to run exec a instance on container: %s", err)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
downloadFileOpts := dc.DownloadFromContainerOptions{
|
||||
OutputStream: buf,
|
||||
Path: "/tmp/test.txt",
|
||||
}
|
||||
|
||||
if err := client.DownloadFromContainer(c.ID, downloadFileOpts); err != nil {
|
||||
srcPath := "/tmp/test.txt"
|
||||
out, _, err := client.CopyFromContainer(context.Background(), c.ID, srcPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to download a file from container: %s", err)
|
||||
}
|
||||
|
||||
r := bytes.NewReader(buf.Bytes())
|
||||
tr := tar.NewReader(r)
|
||||
|
||||
tr := tar.NewReader(out)
|
||||
if _, err := tr.Next(); err != nil {
|
||||
return fmt.Errorf("Unable to read content of tar archive: %s", err)
|
||||
}
|
||||
|
|
@ -383,7 +366,7 @@ func TestAccDockerContainer_device(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func testAccContainerRunning(n string, container *dc.Container) resource.TestCheckFunc {
|
||||
func testAccContainerRunning(n string, container *types.ContainerJSON) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
|
|
@ -395,18 +378,18 @@ func testAccContainerRunning(n string, container *dc.Container) resource.TestChe
|
|||
}
|
||||
|
||||
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
|
||||
containers, err := client.ListContainers(dc.ListContainersOptions{})
|
||||
containers, err := client.ContainerList(context.Background(), types.ContainerListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, c := range containers {
|
||||
if c.ID == rs.Primary.ID {
|
||||
inspected, err := client.InspectContainer(c.ID)
|
||||
inspected, err := client.ContainerInspect(context.Background(), c.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Container could not be inspected: %s", err)
|
||||
}
|
||||
*container = *inspected
|
||||
*container = inspected
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,16 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
dc "github.com/fsouza/go-dockerclient"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
|
|
@ -18,7 +24,7 @@ func resourceDockerImageCreate(d *schema.ResourceData, meta interface{}) error {
|
|||
d.SetId(apiImage.ID + d.Get("name").(string))
|
||||
d.Set("latest", apiImage.ID)
|
||||
|
||||
return nil
|
||||
return resourceDockerImageRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceDockerImageRead(d *schema.ResourceData, meta interface{}) error {
|
||||
|
|
@ -49,7 +55,7 @@ func resourceDockerImageUpdate(d *schema.ResourceData, meta interface{}) error {
|
|||
|
||||
d.Set("latest", apiImage.ID)
|
||||
|
||||
return nil
|
||||
return resourceDockerImageRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceDockerImageDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
|
|
@ -62,7 +68,7 @@ func resourceDockerImageDelete(d *schema.ResourceData, meta interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func searchLocalImages(data Data, imageName string) *dc.APIImages {
|
||||
func searchLocalImages(data Data, imageName string) *types.ImageSummary {
|
||||
if apiImage, ok := data.DockerImages[imageName]; ok {
|
||||
return apiImage
|
||||
}
|
||||
|
|
@ -73,7 +79,7 @@ func searchLocalImages(data Data, imageName string) *dc.APIImages {
|
|||
return nil
|
||||
}
|
||||
|
||||
func removeImage(d *schema.ResourceData, client *dc.Client) error {
|
||||
func removeImage(d *schema.ResourceData, client *client.Client) error {
|
||||
var data Data
|
||||
|
||||
if keepLocally := d.Get("keep_locally").(bool); keepLocally {
|
||||
|
|
@ -92,23 +98,24 @@ func removeImage(d *schema.ResourceData, client *dc.Client) error {
|
|||
foundImage := searchLocalImages(data, imageName)
|
||||
|
||||
if foundImage != nil {
|
||||
err := client.RemoveImage(foundImage.ID)
|
||||
imageDeleteResponseItems, err := client.ImageRemove(context.Background(), foundImage.ID, types.ImageRemoveOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("[INFO] Deleted image items: %v", imageDeleteResponseItems)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func fetchLocalImages(data *Data, client *dc.Client) error {
|
||||
images, err := client.ListImages(dc.ListImagesOptions{All: false})
|
||||
func fetchLocalImages(data *Data, client *client.Client) error {
|
||||
images, err := client.ImageList(context.Background(), types.ImageListOptions{All: false})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to list Docker images: %s", err)
|
||||
}
|
||||
|
||||
if data.DockerImages == nil {
|
||||
data.DockerImages = make(map[string]*dc.APIImages)
|
||||
data.DockerImages = make(map[string]*types.ImageSummary)
|
||||
}
|
||||
|
||||
// Docker uses different nomenclatures in different places...sometimes a short
|
||||
|
|
@ -125,11 +132,11 @@ func fetchLocalImages(data *Data, client *dc.Client) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func pullImage(data *Data, client *dc.Client, authConfig *dc.AuthConfigurations, image string) error {
|
||||
func pullImage(data *Data, client *client.Client, authConfig *AuthConfigs, image string) error {
|
||||
pullOpts := parseImageOptions(image)
|
||||
|
||||
// If a registry was specified in the image name, try to find auth for it
|
||||
auth := dc.AuthConfiguration{}
|
||||
auth := types.AuthConfig{}
|
||||
if pullOpts.Registry != "" {
|
||||
if authConfig, ok := authConfig.Configs[normalizeRegistryAddress(pullOpts.Registry)]; ok {
|
||||
auth = authConfig
|
||||
|
|
@ -141,16 +148,39 @@ func pullImage(data *Data, client *dc.Client, authConfig *dc.AuthConfigurations,
|
|||
}
|
||||
}
|
||||
|
||||
if err := client.PullImage(pullOpts, auth); err != nil {
|
||||
return fmt.Errorf("Error pulling image %s: %s\n", image, err)
|
||||
encodedJSON, err := json.Marshal(auth)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating auth config: %s", err)
|
||||
}
|
||||
|
||||
out, err := client.ImagePull(context.Background(), image, types.ImagePullOptions{
|
||||
RegistryAuth: base64.URLEncoding.EncodeToString(encodedJSON),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error pulling image %s: %s", image, err)
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
buf.ReadFrom(out)
|
||||
s := buf.String()
|
||||
log.Printf("[DEBUG] pulled image %v: %v", image, s)
|
||||
|
||||
return fetchLocalImages(data, client)
|
||||
}
|
||||
|
||||
func parseImageOptions(image string) dc.PullImageOptions {
|
||||
pullOpts := dc.PullImageOptions{}
|
||||
type internalPullImageOptions struct {
|
||||
Repository string `qs:"fromImage"`
|
||||
Tag string
|
||||
|
||||
// Only required for Docker Engine 1.9 or 1.10 w/ Remote API < 1.21
|
||||
// and Docker Engine < 1.9
|
||||
// This parameter was removed in Docker Engine 1.11
|
||||
Registry string
|
||||
}
|
||||
|
||||
func parseImageOptions(image string) internalPullImageOptions {
|
||||
pullOpts := internalPullImageOptions{}
|
||||
splitImageName := strings.Split(image, ":")
|
||||
switch len(splitImageName) {
|
||||
|
||||
|
|
@ -195,11 +225,11 @@ func parseImageOptions(image string) dc.PullImageOptions {
|
|||
return pullOpts
|
||||
}
|
||||
|
||||
func findImage(d *schema.ResourceData, client *dc.Client, authConfig *dc.AuthConfigurations) (*dc.APIImages, error) {
|
||||
func findImage(d *schema.ResourceData, client *client.Client, authConfig *AuthConfigs) (*types.ImageSummary, error) {
|
||||
var data Data
|
||||
if err := fetchLocalImages(&data, client); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//if err := fetchLocalImages(&data, client); err != nil {
|
||||
// return nil, err
|
||||
//} Is done in pullImage
|
||||
|
||||
imageName := d.Get("name").(string)
|
||||
if imageName == "" {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
dc "github.com/fsouza/go-dockerclient"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
|
@ -56,7 +56,7 @@ func TestAccDockerImage_destroy(t *testing.T) {
|
|||
}
|
||||
|
||||
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
|
||||
_, err := client.InspectImage(rs.Primary.Attributes["latest"])
|
||||
_, _, err := client.ImageInspectWithRaw(context.Background(), rs.Primary.Attributes["latest"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -132,11 +132,9 @@ func testAccDockerImageDestroy(s *terraform.State) error {
|
|||
}
|
||||
|
||||
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
|
||||
_, err := client.InspectImage(rs.Primary.Attributes["latest"])
|
||||
_, _, err := client.ImageInspectWithRaw(context.Background(), rs.Primary.Attributes["latest"])
|
||||
if err == nil {
|
||||
return fmt.Errorf("Image still exists")
|
||||
} else if err != dc.ErrNoSuchImage {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -3,16 +3,20 @@ package docker
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
dc "github.com/fsouza/go-dockerclient"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
func resourceDockerNetworkCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*ProviderConfig).DockerClient
|
||||
|
||||
createOpts := dc.CreateNetworkOptions{
|
||||
Name: d.Get("name").(string),
|
||||
}
|
||||
createOpts := types.NetworkCreate{}
|
||||
if v, ok := d.GetOk("check_duplicate"); ok {
|
||||
createOpts.CheckDuplicate = v.(bool)
|
||||
}
|
||||
|
|
@ -20,13 +24,13 @@ func resourceDockerNetworkCreate(d *schema.ResourceData, meta interface{}) error
|
|||
createOpts.Driver = v.(string)
|
||||
}
|
||||
if v, ok := d.GetOk("options"); ok {
|
||||
createOpts.Options = v.(map[string]interface{})
|
||||
createOpts.Options = mapTypeMapValsToString(v.(map[string]interface{}))
|
||||
}
|
||||
if v, ok := d.GetOk("internal"); ok {
|
||||
createOpts.Internal = v.(bool)
|
||||
}
|
||||
|
||||
ipamOpts := &dc.IPAMOptions{}
|
||||
ipamOpts := &network.IPAM{}
|
||||
ipamOptsSet := false
|
||||
if v, ok := d.GetOk("ipam_driver"); ok {
|
||||
ipamOpts.Driver = v.(string)
|
||||
|
|
@ -41,46 +45,34 @@ func resourceDockerNetworkCreate(d *schema.ResourceData, meta interface{}) error
|
|||
createOpts.IPAM = ipamOpts
|
||||
}
|
||||
|
||||
var err error
|
||||
var retNetwork *dc.Network
|
||||
if retNetwork, err = client.CreateNetwork(createOpts); err != nil {
|
||||
retNetwork := types.NetworkCreateResponse{}
|
||||
retNetwork, err := client.NetworkCreate(context.Background(), d.Get("name").(string), createOpts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to create network: %s", err)
|
||||
}
|
||||
if retNetwork == nil {
|
||||
return fmt.Errorf("Returned network is nil")
|
||||
}
|
||||
|
||||
d.SetId(retNetwork.ID)
|
||||
d.Set("name", retNetwork.Name)
|
||||
d.Set("scope", retNetwork.Scope)
|
||||
d.Set("driver", retNetwork.Driver)
|
||||
d.Set("options", retNetwork.Options)
|
||||
|
||||
// The 'internal' property is not send back when create network
|
||||
d.Set("internal", createOpts.Internal)
|
||||
|
||||
return nil
|
||||
return resourceDockerNetworkRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceDockerNetworkRead(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*ProviderConfig).DockerClient
|
||||
log.Printf("[INFO] Waiting for network: '%s' to expose all fields: max '%v seconds'", d.Id(), 30)
|
||||
|
||||
var err error
|
||||
var retNetwork *dc.Network
|
||||
if retNetwork, err = client.NetworkInfo(d.Id()); err != nil {
|
||||
if _, ok := err.(*dc.NoSuchNetwork); !ok {
|
||||
return fmt.Errorf("Unable to inspect network: %s", err)
|
||||
}
|
||||
}
|
||||
if retNetwork == nil {
|
||||
d.SetId("")
|
||||
return nil
|
||||
stateConf := &resource.StateChangeConf{
|
||||
Pending: []string{"pending"},
|
||||
Target: []string{"all_fields", "removed"},
|
||||
Refresh: resourceDockerNetworkReadRefreshFunc(d, meta),
|
||||
Timeout: 30 * time.Second,
|
||||
MinTimeout: 5 * time.Second,
|
||||
Delay: 2 * time.Second,
|
||||
}
|
||||
|
||||
d.Set("scope", retNetwork.Scope)
|
||||
d.Set("driver", retNetwork.Driver)
|
||||
d.Set("options", retNetwork.Options)
|
||||
d.Set("internal", retNetwork.Internal)
|
||||
// Wait, catching any errors
|
||||
_, err := stateConf.WaitForState()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -88,23 +80,21 @@ func resourceDockerNetworkRead(d *schema.ResourceData, meta interface{}) error {
|
|||
func resourceDockerNetworkDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*ProviderConfig).DockerClient
|
||||
|
||||
if err := client.RemoveNetwork(d.Id()); err != nil {
|
||||
if _, ok := err.(*dc.NoSuchNetwork); !ok {
|
||||
return fmt.Errorf("Error deleting network %s: %s", d.Id(), err)
|
||||
}
|
||||
if err := client.NetworkRemove(context.Background(), d.Id()); err != nil {
|
||||
return fmt.Errorf("Error deleting network %s: %s", d.Id(), err)
|
||||
}
|
||||
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
|
||||
func ipamConfigSetToIpamConfigs(ipamConfigSet *schema.Set) []dc.IPAMConfig {
|
||||
ipamConfigs := make([]dc.IPAMConfig, ipamConfigSet.Len())
|
||||
func ipamConfigSetToIpamConfigs(ipamConfigSet *schema.Set) []network.IPAMConfig {
|
||||
ipamConfigs := make([]network.IPAMConfig, ipamConfigSet.Len())
|
||||
|
||||
for i, ipamConfigInt := range ipamConfigSet.List() {
|
||||
ipamConfigRaw := ipamConfigInt.(map[string]interface{})
|
||||
|
||||
ipamConfig := dc.IPAMConfig{}
|
||||
ipamConfig := network.IPAMConfig{}
|
||||
ipamConfig.Subnet = ipamConfigRaw["subnet"].(string)
|
||||
ipamConfig.IPRange = ipamConfigRaw["ip_range"].(string)
|
||||
ipamConfig.Gateway = ipamConfigRaw["gateway"].(string)
|
||||
|
|
@ -120,3 +110,38 @@ func ipamConfigSetToIpamConfigs(ipamConfigSet *schema.Set) []dc.IPAMConfig {
|
|||
|
||||
return ipamConfigs
|
||||
}
|
||||
|
||||
func resourceDockerNetworkReadRefreshFunc(
|
||||
d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
|
||||
return func() (interface{}, string, error) {
|
||||
client := meta.(*ProviderConfig).DockerClient
|
||||
networkID := d.Id()
|
||||
|
||||
retNetwork, _, err := client.NetworkInspectWithRaw(context.Background(), networkID, types.NetworkInspectOptions{})
|
||||
if err != nil {
|
||||
log.Printf("[WARN] Network (%s) not found, removing from state", networkID)
|
||||
d.SetId("")
|
||||
return networkID, "removed", err
|
||||
}
|
||||
|
||||
jsonObj, _ := json.MarshalIndent(retNetwork, "", "\t")
|
||||
log.Printf("[DEBUG] Docker network inspect: %s", jsonObj)
|
||||
|
||||
d.Set("internal", retNetwork.Internal)
|
||||
d.Set("driver", retNetwork.Driver)
|
||||
d.Set("scope", retNetwork.Scope)
|
||||
if retNetwork.Scope == "overlay" {
|
||||
if retNetwork.Options != nil && len(retNetwork.Options) != 0 {
|
||||
d.Set("options", retNetwork.Options)
|
||||
} else {
|
||||
log.Printf("[DEBUG] options: %v not exposed", retNetwork.Options)
|
||||
return networkID, "pending", nil
|
||||
}
|
||||
} else {
|
||||
d.Set("options", retNetwork.Options)
|
||||
}
|
||||
|
||||
log.Println("[DEBUG] all network fields exposed")
|
||||
return networkID, "all_fields", nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,13 +4,14 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
dc "github.com/fsouza/go-dockerclient"
|
||||
"context"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccDockerNetwork_basic(t *testing.T) {
|
||||
var n dc.Network
|
||||
var n types.NetworkResource
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
|
@ -26,7 +27,7 @@ func TestAccDockerNetwork_basic(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func testAccNetwork(n string, network *dc.Network) resource.TestCheckFunc {
|
||||
func testAccNetwork(n string, network *types.NetworkResource) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
|
|
@ -38,18 +39,18 @@ func testAccNetwork(n string, network *dc.Network) resource.TestCheckFunc {
|
|||
}
|
||||
|
||||
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
|
||||
networks, err := client.ListNetworks()
|
||||
networks, err := client.NetworkList(context.Background(), types.NetworkListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, n := range networks {
|
||||
if n.ID == rs.Primary.ID {
|
||||
inspected, err := client.NetworkInfo(n.ID)
|
||||
inspected, err := client.NetworkInspect(context.Background(), n.ID, types.NetworkInspectOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Network could not be obtained: %s", err)
|
||||
}
|
||||
*network = *inspected
|
||||
*network = inspected
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
@ -65,7 +66,7 @@ resource "docker_network" "foo" {
|
|||
`
|
||||
|
||||
func TestAccDockerNetwork_internal(t *testing.T) {
|
||||
var n dc.Network
|
||||
var n types.NetworkResource
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
|
@ -82,7 +83,7 @@ func TestAccDockerNetwork_internal(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func testAccNetworkInternal(network *dc.Network, internal bool) resource.TestCheckFunc {
|
||||
func testAccNetworkInternal(network *types.NetworkResource, internal bool) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
if network.Internal != internal {
|
||||
return fmt.Errorf("Bad value for attribute 'internal': %t", network.Internal)
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import (
|
|||
"encoding/base64"
|
||||
"log"
|
||||
|
||||
"context"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
dc "github.com/fsouza/go-dockerclient"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
|
|
@ -39,16 +39,14 @@ func resourceDockerSecretCreate(d *schema.ResourceData, meta interface{}) error
|
|||
client := meta.(*ProviderConfig).DockerClient
|
||||
data, _ := base64.StdEncoding.DecodeString(d.Get("data").(string))
|
||||
|
||||
createSecretOpts := dc.CreateSecretOptions{
|
||||
SecretSpec: swarm.SecretSpec{
|
||||
Annotations: swarm.Annotations{
|
||||
Name: d.Get("name").(string),
|
||||
},
|
||||
Data: data,
|
||||
secretSpec := swarm.SecretSpec{
|
||||
Annotations: swarm.Annotations{
|
||||
Name: d.Get("name").(string),
|
||||
},
|
||||
Data: data,
|
||||
}
|
||||
|
||||
secret, err := client.CreateSecret(createSecretOpts)
|
||||
secret, err := client.SecretCreate(context.Background(), secretSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -60,15 +58,12 @@ func resourceDockerSecretCreate(d *schema.ResourceData, meta interface{}) error
|
|||
|
||||
func resourceDockerSecretRead(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*ProviderConfig).DockerClient
|
||||
secret, err := client.InspectSecret(d.Id())
|
||||
secret, _, err := client.SecretInspectWithRaw(context.Background(), d.Id())
|
||||
|
||||
if err != nil {
|
||||
if _, ok := err.(*dc.NoSuchSecret); ok {
|
||||
log.Printf("[WARN] Secret (%s) not found, removing from state", d.Id())
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
log.Printf("[WARN] Secret (%s) not found, removing from state", d.Id())
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
d.SetId(secret.ID)
|
||||
return nil
|
||||
|
|
@ -76,9 +71,7 @@ func resourceDockerSecretRead(d *schema.ResourceData, meta interface{}) error {
|
|||
|
||||
func resourceDockerSecretDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*ProviderConfig).DockerClient
|
||||
err := client.RemoveSecret(dc.RemoveSecretOptions{
|
||||
ID: d.Id(),
|
||||
})
|
||||
err := client.SecretRemove(context.Background(), d.Id())
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
"context"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
|
@ -29,7 +30,7 @@ func TestAccDockerSecret_basic(t *testing.T) {
|
|||
},
|
||||
})
|
||||
}
|
||||
func TestAccDockerSecret_basicUpdateble(t *testing.T) {
|
||||
func TestAccDockerSecret_basicUpdatable(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
|
|
@ -82,9 +83,9 @@ func testCheckDockerSecretDestroy(s *terraform.State) error {
|
|||
}
|
||||
|
||||
id := rs.Primary.Attributes["id"]
|
||||
secret, err := client.InspectSecret(id)
|
||||
_, _, err := client.SecretInspectWithRaw(context.Background(), id)
|
||||
|
||||
if err == nil || secret != nil {
|
||||
if err == nil {
|
||||
return fmt.Errorf("Secret with id '%s' still exists", id)
|
||||
}
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -10,10 +10,13 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"encoding/base64"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
dc "github.com/fsouza/go-dockerclient"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
|
@ -53,17 +56,20 @@ func resourceDockerServiceCreate(d *schema.ResourceData, meta interface{}) error
|
|||
return err
|
||||
}
|
||||
|
||||
createOpts := dc.CreateServiceOptions{
|
||||
ServiceSpec: serviceSpec,
|
||||
}
|
||||
|
||||
serviceOptions := types.ServiceCreateOptions{}
|
||||
auth := types.AuthConfig{}
|
||||
if v, ok := d.GetOk("auth"); ok {
|
||||
createOpts.Auth = authToServiceAuth(v.(map[string]interface{}))
|
||||
auth = authToServiceAuth(v.(map[string]interface{}))
|
||||
} else {
|
||||
createOpts.Auth = fromRegistryAuth(d.Get("task_spec.0.container_spec.0.image").(string), meta.(*ProviderConfig).AuthConfigs.Configs)
|
||||
auth = fromRegistryAuth(d.Get("task_spec.0.container_spec.0.image").(string), meta.(*ProviderConfig).AuthConfigs.Configs)
|
||||
}
|
||||
encodedJSON, err := json.Marshal(auth)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating auth config: %s", err)
|
||||
}
|
||||
serviceOptions.EncodedRegistryAuth = base64.URLEncoding.EncodeToString(encodedJSON)
|
||||
|
||||
service, err := client.CreateService(createOpts)
|
||||
service, err := client.ServiceCreate(context.Background(), serviceSpec, serviceOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -108,7 +114,7 @@ func resourceDockerServiceRead(d *schema.ResourceData, meta interface{}) error {
|
|||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
service, err := client.InspectService(apiService.ID)
|
||||
service, _, err := client.ServiceInspectWithRaw(context.Background(), apiService.ID, types.ServiceInspectOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error inspecting service %s: %s", apiService.ID, err)
|
||||
}
|
||||
|
|
@ -142,7 +148,7 @@ func resourceDockerServiceRead(d *schema.ResourceData, meta interface{}) error {
|
|||
func resourceDockerServiceUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*ProviderConfig).DockerClient
|
||||
|
||||
service, err := client.InspectService(d.Id())
|
||||
service, _, err := client.ServiceInspectWithRaw(context.Background(), d.Id(), types.ServiceInspectOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -152,20 +158,26 @@ func resourceDockerServiceUpdate(d *schema.ResourceData, meta interface{}) error
|
|||
return err
|
||||
}
|
||||
|
||||
updateOpts := dc.UpdateServiceOptions{
|
||||
ServiceSpec: serviceSpec,
|
||||
Version: service.Version.Index,
|
||||
}
|
||||
|
||||
updateOptions := types.ServiceUpdateOptions{}
|
||||
auth := types.AuthConfig{}
|
||||
if v, ok := d.GetOk("auth"); ok {
|
||||
updateOpts.Auth = authToServiceAuth(v.(map[string]interface{}))
|
||||
auth = authToServiceAuth(v.(map[string]interface{}))
|
||||
} else {
|
||||
updateOpts.Auth = fromRegistryAuth(d.Get("task_spec.0.container_spec.0.image").(string), meta.(*ProviderConfig).AuthConfigs.Configs)
|
||||
auth = fromRegistryAuth(d.Get("task_spec.0.container_spec.0.image").(string), meta.(*ProviderConfig).AuthConfigs.Configs)
|
||||
}
|
||||
encodedJSON, err := json.Marshal(auth)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating auth config: %s", err)
|
||||
}
|
||||
updateOptions.EncodedRegistryAuth = base64.URLEncoding.EncodeToString(encodedJSON)
|
||||
|
||||
if err = client.UpdateService(d.Id(), updateOpts); err != nil {
|
||||
updateResponse, err := client.ServiceUpdate(context.Background(), d.Id(), service.Version, serviceSpec, updateOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(updateResponse.Warnings) > 0 {
|
||||
log.Printf("[INFO] Warninig while updating Service '%s': %v", service.ID, updateResponse.Warnings)
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("converge_config"); ok {
|
||||
convergeConfig := createConvergeConfig(v.([]interface{}))
|
||||
|
|
@ -209,8 +221,8 @@ func resourceDockerServiceDelete(d *schema.ResourceData, meta interface{}) error
|
|||
// Helpers
|
||||
/////////////////
|
||||
// fetchDockerService fetches a service by its name or id
|
||||
func fetchDockerService(ID string, name string, client *dc.Client) (*swarm.Service, error) {
|
||||
apiServices, err := client.ListServices(dc.ListServicesOptions{})
|
||||
func fetchDockerService(ID string, name string, client *client.Client) (*swarm.Service, error) {
|
||||
apiServices, err := client.ServiceList(context.Background(), types.ServiceListOptions{})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error fetching service information from Docker: %s", err)
|
||||
|
|
@ -226,39 +238,34 @@ func fetchDockerService(ID string, name string, client *dc.Client) (*swarm.Servi
|
|||
}
|
||||
|
||||
// deleteService deletes the service with the given id
|
||||
func deleteService(serviceID string, d *schema.ResourceData, client *dc.Client) error {
|
||||
func deleteService(serviceID string, d *schema.ResourceData, client *client.Client) error {
|
||||
// get containerIDs of the running service because they do not exist after the service is deleted
|
||||
serviceContainerIds := make([]string, 0)
|
||||
if _, ok := d.GetOk("task_spec.0.container_spec.0.stop_grace_period"); ok {
|
||||
filter := make(map[string][]string)
|
||||
filter["service"] = []string{d.Get("name").(string)}
|
||||
tasks, err := client.ListTasks(dc.ListTasksOptions{
|
||||
Filters: filter,
|
||||
filters := filters.NewArgs()
|
||||
filters.Add("service", d.Get("name").(string))
|
||||
tasks, err := client.TaskList(context.Background(), types.TaskListOptions{
|
||||
Filters: filters,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, t := range tasks {
|
||||
task, _ := client.InspectTask(t.ID)
|
||||
log.Printf("[INFO] Found container ['%s'] for destroying: '%s'", task.Status.State, task.Status.ContainerStatus.ContainerID)
|
||||
if strings.TrimSpace(task.Status.ContainerStatus.ContainerID) != "" && task.Status.State != swarm.TaskStateShutdown {
|
||||
serviceContainerIds = append(serviceContainerIds, task.Status.ContainerStatus.ContainerID)
|
||||
task, _, _ := client.TaskInspectWithRaw(context.Background(), t.ID)
|
||||
containerID := ""
|
||||
if task.Status.ContainerStatus != nil {
|
||||
containerID = task.Status.ContainerStatus.ContainerID
|
||||
}
|
||||
log.Printf("[INFO] Found container ['%s'] for destroying: '%s'", task.Status.State, containerID)
|
||||
if strings.TrimSpace(containerID) != "" && task.Status.State != swarm.TaskStateShutdown {
|
||||
serviceContainerIds = append(serviceContainerIds, containerID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// delete the service
|
||||
log.Printf("[INFO] Deleting service: '%s'", serviceID)
|
||||
removeOpts := dc.RemoveServiceOptions{
|
||||
ID: serviceID,
|
||||
}
|
||||
|
||||
if err := client.RemoveService(removeOpts); err != nil {
|
||||
if _, ok := err.(*dc.NoSuchService); ok {
|
||||
log.Printf("[WARN] Service (%s) not found, removing from state", serviceID)
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
if err := client.ServiceRemove(context.Background(), serviceID); err != nil {
|
||||
return fmt.Errorf("Error deleting service %s: %s", serviceID, err)
|
||||
}
|
||||
|
||||
|
|
@ -269,17 +276,16 @@ func deleteService(serviceID string, d *schema.ResourceData, client *dc.Client)
|
|||
log.Printf("[INFO] Waiting for container: '%s' to exit: max %v", containerID, destroyGraceSeconds)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), destroyGraceSeconds)
|
||||
defer cancel()
|
||||
exitCode, _ := client.WaitContainerWithContext(containerID, ctx)
|
||||
exitCode, _ := client.ContainerWait(ctx, containerID, container.WaitConditionRemoved)
|
||||
log.Printf("[INFO] Container exited with code [%v]: '%s'", exitCode, containerID)
|
||||
|
||||
removeOpts := dc.RemoveContainerOptions{
|
||||
ID: containerID,
|
||||
removeOpts := types.ContainerRemoveOptions{
|
||||
RemoveVolumes: true,
|
||||
Force: true,
|
||||
}
|
||||
|
||||
log.Printf("[INFO] Removing container: '%s'", containerID)
|
||||
if err := client.RemoveContainer(removeOpts); err != nil {
|
||||
if err := client.ContainerRemove(context.Background(), containerID, removeOpts); err != nil {
|
||||
if !(strings.Contains(err.Error(), "No such container") || strings.Contains(err.Error(), "is already in progress")) {
|
||||
return fmt.Errorf("Error deleting container %s: %s", containerID, err)
|
||||
}
|
||||
|
|
@ -321,18 +327,17 @@ func resourceDockerServiceCreateRefreshFunc(
|
|||
updater = &replicatedConsoleLogUpdater{}
|
||||
}
|
||||
|
||||
filter := make(map[string][]string)
|
||||
filter["service"] = []string{serviceID}
|
||||
filter["desired-state"] = []string{"running"}
|
||||
filters := filters.NewArgs()
|
||||
filters.Add("service", serviceID)
|
||||
filters.Add("desired-state", "running")
|
||||
|
||||
getUpToDateTasks := func() ([]swarm.Task, error) {
|
||||
return client.ListTasks(dc.ListTasksOptions{
|
||||
Filters: filter,
|
||||
Context: ctx,
|
||||
return client.TaskList(ctx, types.TaskListOptions{
|
||||
Filters: filters,
|
||||
})
|
||||
}
|
||||
var service *swarm.Service
|
||||
service, err := client.InspectService(serviceID)
|
||||
|
||||
service, _, err := client.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
|
@ -347,7 +352,7 @@ func resourceDockerServiceCreateRefreshFunc(
|
|||
return nil, "", err
|
||||
}
|
||||
|
||||
serviceCreateStatus, err := updater.update(service, tasks, activeNodes, false)
|
||||
serviceCreateStatus, err := updater.update(&service, tasks, activeNodes, false)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
|
@ -377,18 +382,17 @@ func resourceDockerServiceUpdateRefreshFunc(
|
|||
}
|
||||
rollback = false
|
||||
|
||||
filter := make(map[string][]string)
|
||||
filter["service"] = []string{serviceID}
|
||||
filter["desired-state"] = []string{"running"}
|
||||
filters := filters.NewArgs()
|
||||
filters.Add("service", serviceID)
|
||||
filters.Add("desired-state", "running")
|
||||
|
||||
getUpToDateTasks := func() ([]swarm.Task, error) {
|
||||
return client.ListTasks(dc.ListTasksOptions{
|
||||
Filters: filter,
|
||||
Context: ctx,
|
||||
return client.TaskList(ctx, types.TaskListOptions{
|
||||
Filters: filters,
|
||||
})
|
||||
}
|
||||
var service *swarm.Service
|
||||
service, err := client.InspectService(serviceID)
|
||||
|
||||
service, _, err := client.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
|
@ -421,7 +425,7 @@ func resourceDockerServiceUpdateRefreshFunc(
|
|||
return nil, "", err
|
||||
}
|
||||
|
||||
isUpdateCompleted, err := updater.update(service, tasks, activeNodes, rollback)
|
||||
isUpdateCompleted, err := updater.update(&service, tasks, activeNodes, rollback)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
|
@ -438,8 +442,8 @@ func resourceDockerServiceUpdateRefreshFunc(
|
|||
}
|
||||
|
||||
// getActiveNodes gets the actives nodes withon a swarm
|
||||
func getActiveNodes(ctx context.Context, client *dc.Client) (map[string]struct{}, error) {
|
||||
nodes, err := client.ListNodes(dc.ListNodesOptions{Context: ctx})
|
||||
func getActiveNodes(ctx context.Context, client *client.Client) (map[string]struct{}, error) {
|
||||
nodes, err := client.NodeList(ctx, types.NodeListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -1265,20 +1269,20 @@ func createConvergeConfig(config []interface{}) *convergeConfig {
|
|||
}
|
||||
|
||||
// authToServiceAuth maps the auth to AuthConfiguration
|
||||
func authToServiceAuth(auth map[string]interface{}) dc.AuthConfiguration {
|
||||
func authToServiceAuth(auth map[string]interface{}) types.AuthConfig {
|
||||
if auth["username"] != nil && len(auth["username"].(string)) > 0 && auth["password"] != nil && len(auth["password"].(string)) > 0 {
|
||||
return dc.AuthConfiguration{
|
||||
return types.AuthConfig{
|
||||
Username: auth["username"].(string),
|
||||
Password: auth["password"].(string),
|
||||
ServerAddress: auth["server_address"].(string),
|
||||
}
|
||||
}
|
||||
|
||||
return dc.AuthConfiguration{}
|
||||
return types.AuthConfig{}
|
||||
}
|
||||
|
||||
// fromRegistryAuth extract the desired AuthConfiguration for the given image
|
||||
func fromRegistryAuth(image string, configs map[string]dc.AuthConfiguration) dc.AuthConfiguration {
|
||||
func fromRegistryAuth(image string, configs map[string]types.AuthConfig) types.AuthConfig {
|
||||
// Remove normalized prefixes to simlify substring
|
||||
image = strings.Replace(strings.Replace(image, "http://", "", 1), "https://", "", 1)
|
||||
// Get the registry with optional port
|
||||
|
|
@ -1291,7 +1295,7 @@ func fromRegistryAuth(image string, configs map[string]dc.AuthConfiguration) dc.
|
|||
}
|
||||
}
|
||||
|
||||
return dc.AuthConfiguration{}
|
||||
return types.AuthConfig{}
|
||||
}
|
||||
|
||||
// stringSetToPlacementPrefs maps a string set to PlacementPreference
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ import (
|
|||
"regexp"
|
||||
"testing"
|
||||
|
||||
dc "github.com/fsouza/go-dockerclient"
|
||||
"context"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
|
@ -16,8 +18,8 @@ import (
|
|||
// ----------------------------------------
|
||||
|
||||
func TestDockerSecretFromRegistryAuth_basic(t *testing.T) {
|
||||
authConfigs := make(map[string]dc.AuthConfiguration)
|
||||
authConfigs["https://repo.my-company.com:8787"] = dc.AuthConfiguration{
|
||||
authConfigs := make(map[string]types.AuthConfig)
|
||||
authConfigs["https://repo.my-company.com:8787"] = types.AuthConfig{
|
||||
Username: "myuser",
|
||||
Password: "mypass",
|
||||
Email: "",
|
||||
|
|
@ -32,14 +34,14 @@ func TestDockerSecretFromRegistryAuth_basic(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDockerSecretFromRegistryAuth_multiple(t *testing.T) {
|
||||
authConfigs := make(map[string]dc.AuthConfiguration)
|
||||
authConfigs["https://repo.my-company.com:8787"] = dc.AuthConfiguration{
|
||||
authConfigs := make(map[string]types.AuthConfig)
|
||||
authConfigs["https://repo.my-company.com:8787"] = types.AuthConfig{
|
||||
Username: "myuser",
|
||||
Password: "mypass",
|
||||
Email: "",
|
||||
ServerAddress: "repo.my-company.com:8787",
|
||||
}
|
||||
authConfigs["https://nexus.my-fancy-company.com"] = dc.AuthConfiguration{
|
||||
authConfigs["https://nexus.my-fancy-company.com"] = types.AuthConfig{
|
||||
Username: "myuser33",
|
||||
Password: "mypass123",
|
||||
Email: "test@example.com",
|
||||
|
|
@ -98,6 +100,7 @@ func TestAccDockerService_minimal(t *testing.T) {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDockerService_full(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
|
@ -113,7 +116,7 @@ func TestAccDockerService_full(t *testing.T) {
|
|||
name = "tftest-full-myconfig"
|
||||
data = "ewogICJwcmVmaXgiOiAiMTIzIgp9"
|
||||
}
|
||||
|
||||
|
||||
resource "docker_secret" "service_secret" {
|
||||
name = "tftest-mysecret"
|
||||
data = "ewogICJrZXkiOiAiUVdFUlRZIgp9"
|
||||
|
|
@ -126,27 +129,27 @@ func TestAccDockerService_full(t *testing.T) {
|
|||
|
||||
resource "docker_service" "foo" {
|
||||
name = "tftest-service-basic"
|
||||
|
||||
|
||||
task_spec {
|
||||
container_spec {
|
||||
image = "127.0.0.1:15000/tftest-service:v1"
|
||||
|
||||
|
||||
labels {
|
||||
foo = "bar"
|
||||
}
|
||||
|
||||
|
||||
command = ["ls"]
|
||||
args = ["-las"]
|
||||
hostname = "my-fancy-service"
|
||||
|
||||
|
||||
env {
|
||||
MYFOO = "BAR"
|
||||
}
|
||||
|
||||
|
||||
dir = "/root"
|
||||
user = "root"
|
||||
groups = ["docker", "foogroup"]
|
||||
|
||||
|
||||
privileges {
|
||||
se_linux_context {
|
||||
disable = true
|
||||
|
|
@ -156,9 +159,9 @@ func TestAccDockerService_full(t *testing.T) {
|
|||
level = "level-label"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
read_only = true
|
||||
|
||||
|
||||
mounts = [
|
||||
{
|
||||
target = "/mount/test"
|
||||
|
|
@ -178,28 +181,28 @@ func TestAccDockerService_full(t *testing.T) {
|
|||
}
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
stop_signal = "SIGTERM"
|
||||
stop_grace_period = "10s"
|
||||
|
||||
|
||||
healthcheck {
|
||||
test = ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||
interval = "5s"
|
||||
timeout = "2s"
|
||||
retries = 4
|
||||
}
|
||||
|
||||
|
||||
hosts {
|
||||
host = "testhost"
|
||||
ip = "10.0.1.0"
|
||||
}
|
||||
|
||||
|
||||
dns_config {
|
||||
nameservers = ["8.8.8.8"]
|
||||
search = ["example.org"]
|
||||
options = ["timeout:3"]
|
||||
}
|
||||
|
||||
|
||||
secrets = [
|
||||
{
|
||||
secret_id = "${docker_secret.service_secret.id}"
|
||||
|
|
@ -207,7 +210,7 @@ func TestAccDockerService_full(t *testing.T) {
|
|||
file_name = "/secrets.json"
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
configs = [
|
||||
{
|
||||
config_id = "${docker_config.service_config.id}"
|
||||
|
|
@ -216,51 +219,51 @@ func TestAccDockerService_full(t *testing.T) {
|
|||
},
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
resources {
|
||||
limits {
|
||||
nano_cpus = 1000000
|
||||
memory_bytes = 536870912
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
restart_policy {
|
||||
condition = "on-failure"
|
||||
delay = "3s"
|
||||
max_attempts = 4
|
||||
window = "10s"
|
||||
}
|
||||
|
||||
|
||||
placement {
|
||||
constraints = [
|
||||
"node.role==manager",
|
||||
]
|
||||
|
||||
|
||||
prefs = [
|
||||
"spread=node.role.manager",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
force_update = 0
|
||||
runtime = "container"
|
||||
networks = ["${docker_network.test_network.id}"]
|
||||
|
||||
|
||||
log_driver {
|
||||
name = "json-file"
|
||||
|
||||
|
||||
options {
|
||||
max-size = "10m"
|
||||
max-file = "3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mode {
|
||||
replicated {
|
||||
replicas = 2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
update_config {
|
||||
parallelism = 2
|
||||
delay = "10s"
|
||||
|
|
@ -269,7 +272,7 @@ func TestAccDockerService_full(t *testing.T) {
|
|||
max_failure_ratio = "0.1"
|
||||
order = "start-first"
|
||||
}
|
||||
|
||||
|
||||
rollback_config {
|
||||
parallelism = 2
|
||||
delay = "5ms"
|
||||
|
|
@ -278,10 +281,10 @@ func TestAccDockerService_full(t *testing.T) {
|
|||
max_failure_ratio = "0.9"
|
||||
order = "stop-first"
|
||||
}
|
||||
|
||||
|
||||
endpoint_spec {
|
||||
mode = "vip"
|
||||
|
||||
|
||||
ports {
|
||||
name = "random"
|
||||
protocol = "tcp"
|
||||
|
|
@ -291,7 +294,7 @@ func TestAccDockerService_full(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
`,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex),
|
||||
|
|
@ -338,12 +341,12 @@ func TestAccDockerService_full(t *testing.T) {
|
|||
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.secrets.#", "1"),
|
||||
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.resources.0.limits.0.nano_cpus", "1000000"),
|
||||
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.resources.0.limits.0.memory_bytes", "536870912"),
|
||||
// resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.restart_policy.condition", "on-failure"),
|
||||
// resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.restart_policy.delay", "3s"),
|
||||
// resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.restart_policy.max_attempts", "4"),
|
||||
// resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.restart_policy.window", "10s"),
|
||||
// resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.placement.0.constraints.4248571116", "node.role==manager"),
|
||||
// resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.placement.0.prefs.1751004438", "spread=node.role.manager"),
|
||||
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.restart_policy.condition", "on-failure"),
|
||||
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.restart_policy.delay", "3s"),
|
||||
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.restart_policy.max_attempts", "4"),
|
||||
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.restart_policy.window", "10s"),
|
||||
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.placement.0.constraints.4248571116", "node.role==manager"),
|
||||
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.placement.0.prefs.1751004438", "spread=node.role.manager"),
|
||||
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.force_update", "0"),
|
||||
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.networks.#", "1"),
|
||||
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.0.name", "json-file"),
|
||||
|
|
@ -503,6 +506,7 @@ func TestAccDockerService_GlobalAndReplicated(t *testing.T) {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDockerService_GlobalWithConvergeConfig(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
|
@ -692,7 +696,7 @@ func TestAccDockerService_updateConfigReplicasImageAndHealthIncreaseAndDecreaseR
|
|||
task_spec {
|
||||
container_spec {
|
||||
image = "127.0.0.1:15000/tftest-service:v1"
|
||||
|
||||
|
||||
configs = [
|
||||
{
|
||||
config_id = "${docker_config.service_config.id}"
|
||||
|
|
@ -700,7 +704,7 @@ func TestAccDockerService_updateConfigReplicasImageAndHealthIncreaseAndDecreaseR
|
|||
file_name = "/configs.json"
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
healthcheck {
|
||||
test = ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||
interval = "1s"
|
||||
|
|
@ -708,7 +712,7 @@ func TestAccDockerService_updateConfigReplicasImageAndHealthIncreaseAndDecreaseR
|
|||
start_period = "0s"
|
||||
retries = 2
|
||||
}
|
||||
|
||||
|
||||
stop_grace_period = "10s"
|
||||
}
|
||||
}
|
||||
|
|
@ -822,7 +826,7 @@ func TestAccDockerService_updateConfigReplicasImageAndHealthIncreaseAndDecreaseR
|
|||
target_port = "8080"
|
||||
published_port = "8082"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
|
@ -914,7 +918,7 @@ func TestAccDockerService_updateConfigReplicasImageAndHealthIncreaseAndDecreaseR
|
|||
target_port = "8080"
|
||||
published_port = "8082"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
|
@ -961,7 +965,7 @@ func TestAccDockerService_nonExistingPrivateImageConverge(t *testing.T) {
|
|||
container_spec = {
|
||||
image = "127.0.0.1:15000/idonoexist:latest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mode {
|
||||
replicated {
|
||||
|
|
@ -983,6 +987,7 @@ func TestAccDockerService_nonExistingPrivateImageConverge(t *testing.T) {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDockerService_nonExistingPublicImageConverge(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
|
@ -996,7 +1001,7 @@ func TestAccDockerService_nonExistingPublicImageConverge(t *testing.T) {
|
|||
container_spec = {
|
||||
image = "stovogel/blablabla:part5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mode {
|
||||
replicated {
|
||||
|
|
@ -1070,6 +1075,7 @@ func TestAccDockerService_basicConvergeAndStopGracefully(t *testing.T) {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDockerService_updateFailsAndRollbackConverge(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
|
@ -1082,7 +1088,7 @@ func TestAccDockerService_updateFailsAndRollbackConverge(t *testing.T) {
|
|||
task_spec {
|
||||
container_spec {
|
||||
image = "127.0.0.1:15000/tftest-service:v1"
|
||||
|
||||
|
||||
healthcheck {
|
||||
test = ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||
interval = "5s"
|
||||
|
|
@ -1212,6 +1218,7 @@ func TestAccDockerService_updateFailsAndRollbackConverge(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAccDockerService_updateNetworksConverge(t *testing.T) {
|
||||
// t.Skip("Skipped because response from daemon is not always consistent")
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
|
|
@ -1242,8 +1249,7 @@ func TestAccDockerService_updateNetworksConverge(t *testing.T) {
|
|||
replicas = 2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
endpoint_spec {
|
||||
mode = "vip"
|
||||
|
||||
|
|
@ -1253,8 +1259,8 @@ func TestAccDockerService_updateNetworksConverge(t *testing.T) {
|
|||
}
|
||||
|
||||
converge_config {
|
||||
delay = "7s"
|
||||
timeout = "3m"
|
||||
delay = "5s"
|
||||
timeout = "60s"
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1303,8 +1309,8 @@ func TestAccDockerService_updateNetworksConverge(t *testing.T) {
|
|||
}
|
||||
|
||||
converge_config {
|
||||
delay = "7s"
|
||||
timeout = "3m"
|
||||
delay = "5s"
|
||||
timeout = "60s"
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
|
@ -1356,8 +1362,8 @@ func TestAccDockerService_updateNetworksConverge(t *testing.T) {
|
|||
}
|
||||
|
||||
converge_config {
|
||||
delay = "7s"
|
||||
timeout = "3m"
|
||||
delay = "5s"
|
||||
timeout = "60s"
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
|
@ -1372,6 +1378,7 @@ func TestAccDockerService_updateNetworksConverge(t *testing.T) {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDockerService_updateMountsConverge(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
|
@ -1383,10 +1390,6 @@ func TestAccDockerService_updateMountsConverge(t *testing.T) {
|
|||
name = "tftest-volume"
|
||||
}
|
||||
|
||||
resource "docker_volume" "foo2" {
|
||||
name = "tftest-volume2"
|
||||
}
|
||||
|
||||
resource "docker_service" "foo" {
|
||||
name = "tftest-service-up-mounts"
|
||||
task_spec {
|
||||
|
|
@ -1415,10 +1418,9 @@ func TestAccDockerService_updateMountsConverge(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
converge_config {
|
||||
delay = "7s"
|
||||
timeout = "3m"
|
||||
delay = "5s"
|
||||
timeout = "60s"
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
|
@ -1482,8 +1484,8 @@ func TestAccDockerService_updateMountsConverge(t *testing.T) {
|
|||
}
|
||||
|
||||
converge_config {
|
||||
delay = "7s"
|
||||
timeout = "3m"
|
||||
delay = "5s"
|
||||
timeout = "60s"
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
|
@ -1498,6 +1500,7 @@ func TestAccDockerService_updateMountsConverge(t *testing.T) {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDockerService_updateHostsConverge(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
|
@ -1526,7 +1529,6 @@ func TestAccDockerService_updateHostsConverge(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
converge_config {
|
||||
delay = "7s"
|
||||
timeout = "3m"
|
||||
|
|
@ -1622,6 +1624,7 @@ func TestAccDockerService_updateHostsConverge(t *testing.T) {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDockerService_updateLoggingConverge(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
|
@ -1639,7 +1642,7 @@ func TestAccDockerService_updateLoggingConverge(t *testing.T) {
|
|||
|
||||
log_driver {
|
||||
name = "json-file"
|
||||
|
||||
|
||||
options {
|
||||
max-size = "10m"
|
||||
max-file = "3"
|
||||
|
|
@ -1681,7 +1684,7 @@ func TestAccDockerService_updateLoggingConverge(t *testing.T) {
|
|||
}
|
||||
log_driver {
|
||||
name = "json-file"
|
||||
|
||||
|
||||
options {
|
||||
max-size = "15m"
|
||||
max-file = "5"
|
||||
|
|
@ -1760,7 +1763,7 @@ func TestAccDockerService_updateHealthcheckConverge(t *testing.T) {
|
|||
container_spec {
|
||||
image = "127.0.0.1:15000/tftest-service:v1"
|
||||
stop_grace_period = "10s"
|
||||
|
||||
|
||||
healthcheck {
|
||||
test = ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||
interval = "1s"
|
||||
|
|
@ -1775,7 +1778,7 @@ func TestAccDockerService_updateHealthcheckConverge(t *testing.T) {
|
|||
replicas = 2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
update_config {
|
||||
parallelism = 1
|
||||
delay = "1s"
|
||||
|
|
@ -1843,7 +1846,7 @@ func TestAccDockerService_updateHealthcheckConverge(t *testing.T) {
|
|||
replicas = 2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
update_config {
|
||||
parallelism = 1
|
||||
delay = "1s"
|
||||
|
|
@ -1859,7 +1862,7 @@ func TestAccDockerService_updateHealthcheckConverge(t *testing.T) {
|
|||
published_port = "8080"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
converge_config {
|
||||
delay = "7s"
|
||||
timeout = "3m"
|
||||
|
|
@ -1922,7 +1925,7 @@ func TestAccDockerService_updateIncreaseReplicasConverge(t *testing.T) {
|
|||
replicas = 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
update_config {
|
||||
parallelism = 1
|
||||
delay = "1s"
|
||||
|
|
@ -1977,7 +1980,7 @@ func TestAccDockerService_updateIncreaseReplicasConverge(t *testing.T) {
|
|||
container_spec {
|
||||
image = "127.0.0.1:15000/tftest-service:v1"
|
||||
stop_grace_period = "10s"
|
||||
|
||||
|
||||
healthcheck {
|
||||
test = ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||
interval = "1s"
|
||||
|
|
@ -1992,7 +1995,7 @@ func TestAccDockerService_updateIncreaseReplicasConverge(t *testing.T) {
|
|||
replicas = 3
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
update_config {
|
||||
parallelism = 1
|
||||
delay = "1s"
|
||||
|
|
@ -2008,7 +2011,7 @@ func TestAccDockerService_updateIncreaseReplicasConverge(t *testing.T) {
|
|||
published_port = "8080"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
converge_config {
|
||||
delay = "7s"
|
||||
timeout = "3m"
|
||||
|
|
@ -2042,6 +2045,7 @@ func TestAccDockerService_updateIncreaseReplicasConverge(t *testing.T) {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDockerService_updateDecreaseReplicasConverge(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
|
@ -2055,7 +2059,7 @@ func TestAccDockerService_updateDecreaseReplicasConverge(t *testing.T) {
|
|||
container_spec {
|
||||
image = "127.0.0.1:15000/tftest-service:v1"
|
||||
stop_grace_period = "10s"
|
||||
|
||||
|
||||
healthcheck {
|
||||
test = ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||
interval = "1s"
|
||||
|
|
@ -2070,7 +2074,7 @@ func TestAccDockerService_updateDecreaseReplicasConverge(t *testing.T) {
|
|||
replicas = 5
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
update_config {
|
||||
parallelism = 1
|
||||
delay = "1s"
|
||||
|
|
@ -2139,7 +2143,7 @@ func TestAccDockerService_updateDecreaseReplicasConverge(t *testing.T) {
|
|||
replicas = 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
update_config {
|
||||
parallelism = 1
|
||||
delay = "1s"
|
||||
|
|
@ -2155,7 +2159,7 @@ func TestAccDockerService_updateDecreaseReplicasConverge(t *testing.T) {
|
|||
published_port = "8080"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
converge_config {
|
||||
delay = "7s"
|
||||
timeout = "3m"
|
||||
|
|
@ -2381,7 +2385,7 @@ func TestAccDockerService_updateConfigConverge(t *testing.T) {
|
|||
replicas = 2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
update_config {
|
||||
parallelism = 1
|
||||
delay = "1s"
|
||||
|
|
@ -2397,7 +2401,7 @@ func TestAccDockerService_updateConfigConverge(t *testing.T) {
|
|||
published_port = "8080"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
converge_config {
|
||||
delay = "7s"
|
||||
timeout = "30s"
|
||||
|
|
@ -2467,7 +2471,7 @@ func TestAccDockerService_updateConfigConverge(t *testing.T) {
|
|||
replicas = 2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
update_config {
|
||||
parallelism = 1
|
||||
delay = "1s"
|
||||
|
|
@ -2483,7 +2487,7 @@ func TestAccDockerService_updateConfigConverge(t *testing.T) {
|
|||
published_port = "8080"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
converge_config {
|
||||
delay = "7s"
|
||||
timeout = "30s"
|
||||
|
|
@ -2516,6 +2520,7 @@ func TestAccDockerService_updateConfigConverge(t *testing.T) {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDockerService_updateConfigAndSecretConverge(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
|
@ -2578,7 +2583,7 @@ func TestAccDockerService_updateConfigAndSecretConverge(t *testing.T) {
|
|||
replicas = 2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
update_config {
|
||||
parallelism = 1
|
||||
delay = "1s"
|
||||
|
|
@ -2595,7 +2600,6 @@ func TestAccDockerService_updateConfigAndSecretConverge(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
converge_config {
|
||||
delay = "7s"
|
||||
timeout = "3m"
|
||||
|
|
@ -2684,7 +2688,7 @@ func TestAccDockerService_updateConfigAndSecretConverge(t *testing.T) {
|
|||
replicas = 2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
update_config {
|
||||
parallelism = 1
|
||||
delay = "1s"
|
||||
|
|
@ -2701,7 +2705,6 @@ func TestAccDockerService_updateConfigAndSecretConverge(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
converge_config {
|
||||
delay = "7s"
|
||||
timeout = "3m"
|
||||
|
|
@ -2736,6 +2739,7 @@ func TestAccDockerService_updateConfigAndSecretConverge(t *testing.T) {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDockerService_updatePortConverge(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
|
@ -2852,7 +2856,7 @@ func TestAccDockerService_updatePortConverge(t *testing.T) {
|
|||
target_port = "8080"
|
||||
published_port = "8082"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
converge_config {
|
||||
|
|
@ -2889,6 +2893,7 @@ func TestAccDockerService_updatePortConverge(t *testing.T) {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDockerService_updateConfigReplicasImageAndHealthConverge(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
|
@ -3038,7 +3043,7 @@ func TestAccDockerService_updateConfigReplicasImageAndHealthConverge(t *testing.
|
|||
target_port = "8080"
|
||||
published_port = "8082"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
converge_config {
|
||||
|
|
@ -3075,6 +3080,7 @@ func TestAccDockerService_updateConfigReplicasImageAndHealthConverge(t *testing.
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDockerService_updateConfigAndDecreaseReplicasConverge(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
|
@ -3119,7 +3125,7 @@ func TestAccDockerService_updateConfigAndDecreaseReplicasConverge(t *testing.T)
|
|||
replicas = 5
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
update_config {
|
||||
parallelism = 1
|
||||
delay = "1s"
|
||||
|
|
@ -3204,7 +3210,7 @@ func TestAccDockerService_updateConfigAndDecreaseReplicasConverge(t *testing.T)
|
|||
replicas = 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
update_config {
|
||||
parallelism = 1
|
||||
delay = "1s"
|
||||
|
|
@ -3253,6 +3259,7 @@ func TestAccDockerService_updateConfigAndDecreaseReplicasConverge(t *testing.T)
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDockerService_updateConfigReplicasImageAndHealthIncreaseAndDecreaseReplicasConverge(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
|
@ -3402,7 +3409,7 @@ func TestAccDockerService_updateConfigReplicasImageAndHealthIncreaseAndDecreaseR
|
|||
target_port = "8080"
|
||||
published_port = "8082"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
converge_config {
|
||||
|
|
@ -3495,7 +3502,7 @@ func TestAccDockerService_updateConfigReplicasImageAndHealthIncreaseAndDecreaseR
|
|||
target_port = "8080"
|
||||
published_port = "8082"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
converge_config {
|
||||
|
|
@ -3579,10 +3586,10 @@ func TestAccDockerService_privateConverge(t *testing.T) {
|
|||
func isServiceRemoved(serviceName string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
|
||||
filter := make(map[string][]string)
|
||||
filter["name"] = []string{serviceName}
|
||||
services, err := client.ListServices(dc.ListServicesOptions{
|
||||
Filters: filter,
|
||||
filters := filters.NewArgs()
|
||||
filters.Add("name", serviceName)
|
||||
services, err := client.ServiceList(context.Background(), types.ServiceListOptions{
|
||||
Filters: filters,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error listing service for name %s: %v", serviceName, err)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
dc "github.com/fsouza/go-dockerclient"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
func resourceDockerVolume() *schema.Resource {
|
||||
|
|
@ -44,8 +46,10 @@ func resourceDockerVolume() *schema.Resource {
|
|||
|
||||
func resourceDockerVolumeCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*ProviderConfig).DockerClient
|
||||
ctx := context.Background()
|
||||
|
||||
createOpts := volume.VolumeCreateBody{}
|
||||
|
||||
createOpts := dc.CreateVolumeOptions{}
|
||||
if v, ok := d.GetOk("name"); ok {
|
||||
createOpts.Name = v.(string)
|
||||
}
|
||||
|
|
@ -57,34 +61,32 @@ func resourceDockerVolumeCreate(d *schema.ResourceData, meta interface{}) error
|
|||
}
|
||||
|
||||
var err error
|
||||
var retVolume *dc.Volume
|
||||
if retVolume, err = client.CreateVolume(createOpts); err != nil {
|
||||
var retVolume types.Volume
|
||||
retVolume, err = client.VolumeCreate(ctx, createOpts)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to create volume: %s", err)
|
||||
}
|
||||
if retVolume == nil {
|
||||
return fmt.Errorf("Returned volume is nil")
|
||||
}
|
||||
|
||||
d.SetId(retVolume.Name)
|
||||
d.Set("name", retVolume.Name)
|
||||
d.Set("driver", retVolume.Driver)
|
||||
d.Set("mountpoint", retVolume.Mountpoint)
|
||||
|
||||
return nil
|
||||
return resourceDockerVolumeRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceDockerVolumeRead(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*ProviderConfig).DockerClient
|
||||
ctx := context.Background()
|
||||
|
||||
var err error
|
||||
var retVolume *dc.Volume
|
||||
if retVolume, err = client.InspectVolume(d.Id()); err != nil && err != dc.ErrNoSuchVolume {
|
||||
var retVolume types.Volume
|
||||
retVolume, err = client.VolumeInspect(ctx, d.Id())
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to inspect volume: %s", err)
|
||||
}
|
||||
if retVolume == nil {
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
|
||||
d.Set("name", retVolume.Name)
|
||||
d.Set("driver", retVolume.Driver)
|
||||
|
|
@ -94,35 +96,42 @@ func resourceDockerVolumeRead(d *schema.ResourceData, meta interface{}) error {
|
|||
}
|
||||
|
||||
func resourceDockerVolumeDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*ProviderConfig).DockerClient
|
||||
log.Printf("[INFO] Waiting for volume: '%s' to get removed: max '%v seconds'", d.Id(), 30)
|
||||
|
||||
// TODO catch error if removal is already in progress + fix with statefunc
|
||||
if err := client.RemoveVolume(d.Id()); err != nil && err != dc.ErrNoSuchVolume {
|
||||
if err == dc.ErrVolumeInUse {
|
||||
loops := 20
|
||||
sleepTime := 1000 * time.Millisecond
|
||||
for i := loops; i > 0; i-- {
|
||||
if err = client.RemoveVolume(d.Id()); err != nil {
|
||||
if err == dc.ErrVolumeInUse {
|
||||
log.Printf("[INFO] Volume remove loop: %d of %d due to error: %s", loops-i+1, loops, err)
|
||||
time.Sleep(sleepTime)
|
||||
continue
|
||||
}
|
||||
if err == dc.ErrNoSuchVolume {
|
||||
log.Printf("[INFO] Volume successfully removed")
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
if !strings.Contains(err.Error(), "is already in progress") {
|
||||
// if it's not in use any more (so it's deleted successfully) and another error occurred
|
||||
return fmt.Errorf("Error deleting volume %s: %s", d.Id(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("Error deleting volume %s: %s after %d tries", d.Id(), err, loops)
|
||||
}
|
||||
stateConf := &resource.StateChangeConf{
|
||||
Pending: []string{"in_use"},
|
||||
Target: []string{"removed"},
|
||||
Refresh: resourceDockerVolumeRemoveRefreshFunc(d.Id(), meta),
|
||||
Timeout: 30 * time.Second,
|
||||
MinTimeout: 5 * time.Second,
|
||||
Delay: 2 * time.Second,
|
||||
}
|
||||
|
||||
// Wait, catching any errors
|
||||
_, err := stateConf.WaitForState()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceDockerVolumeRemoveRefreshFunc(
|
||||
volumeID string, meta interface{}) resource.StateRefreshFunc {
|
||||
return func() (interface{}, string, error) {
|
||||
client := meta.(*ProviderConfig).DockerClient
|
||||
forceDelete := true
|
||||
|
||||
if err := client.VolumeRemove(context.Background(), volumeID, forceDelete); err != nil {
|
||||
if strings.Contains(err.Error(), "volume is in use") { // store.IsInUse(err)
|
||||
log.Printf("[INFO] Volume with id '%v' is still in use", volumeID)
|
||||
return volumeID, "in_use", nil
|
||||
}
|
||||
log.Printf("[INFO] Removing volume with id '%v' caused an error: %v", volumeID, err)
|
||||
return nil, "", err
|
||||
}
|
||||
log.Printf("[INFO] Removing volume with id '%v' got removed", volumeID)
|
||||
return volumeID, "removed", nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
dc "github.com/fsouza/go-dockerclient"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAccDockerVolume_basic(t *testing.T) {
|
||||
var v dc.Volume
|
||||
var v types.Volume
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
|
@ -28,7 +28,7 @@ func TestAccDockerVolume_basic(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func checkDockerVolume(n string, volume *dc.Volume) resource.TestCheckFunc {
|
||||
func checkDockerVolume(n string, volume *types.Volume) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
|
|
@ -39,24 +39,16 @@ func checkDockerVolume(n string, volume *dc.Volume) resource.TestCheckFunc {
|
|||
return fmt.Errorf("No ID is set")
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
|
||||
volumes, err := client.ListVolumes(dc.ListVolumesOptions{})
|
||||
v, err := client.VolumeInspect(ctx, rs.Primary.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, v := range volumes {
|
||||
if v.Name == rs.Primary.ID {
|
||||
inspected, err := client.InspectVolume(v.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Volume could not be inspected: %s", err)
|
||||
}
|
||||
*volume = *inspected
|
||||
return nil
|
||||
}
|
||||
}
|
||||
*volume = v
|
||||
|
||||
return fmt.Errorf("Volume not found: %s", rs.Primary.ID)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -122,3 +122,13 @@ func validateStringIsBase64Encoded() schema.SchemaValidateFunc {
|
|||
return
|
||||
}
|
||||
}
|
||||
|
||||
func validateDockerContainerPath(v interface{}, k string) (ws []string, errors []error) {
|
||||
|
||||
value := v.(string)
|
||||
if !regexp.MustCompile(`^[a-zA-Z]:\\|^/`).MatchString(value) {
|
||||
errors = append(errors, fmt.Errorf("%q must be an absolute path", k))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
|||
30
scripts/compile.sh
Executable file
30
scripts/compile.sh
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
# Prerequisites
|
||||
if ! command -v gox > /dev/null; then
|
||||
go get -u github.com/mitchellh/gox
|
||||
fi
|
||||
|
||||
# setup environment
|
||||
PROVIDER_NAME="docker"
|
||||
TARGET_DIR="$(pwd)/results"
|
||||
XC_ARCH=${XC_ARCH:-"386 amd64 arm"}
|
||||
XC_OS=${XC_OS:=linux darwin windows freebsd openbsd solaris}
|
||||
XC_EXCLUDE_OSARCH="!darwin/arm !darwin/386 !solaris/amd64"
|
||||
LD_FLAGS="-s -w"
|
||||
export CGO_ENABLED=0
|
||||
|
||||
rm -rf "${TARGET_DIR}"
|
||||
mkdir -p "${TARGET_DIR}"
|
||||
|
||||
# Compile
|
||||
gox \
|
||||
-os="${XC_OS}" \
|
||||
-arch="${XC_ARCH}" \
|
||||
-osarch="${XC_EXCLUDE_OSARCH}" \
|
||||
-ldflags "${LD_FLAGS}" \
|
||||
-output "$TARGET_DIR/{{.OS}}_{{.Arch}}/terraform-provider-${PROVIDER_NAME}_v0.0.0_x4" \
|
||||
-verbose \
|
||||
-rebuild \
|
||||
.
|
||||
1
vendor/github.com/docker/docker/pkg/archive/README.md
generated
vendored
1
vendor/github.com/docker/docker/pkg/archive/README.md
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
This code provides helper functions for dealing with archive files.
|
||||
189
vendor/github.com/fsouza/go-dockerclient/AUTHORS
generated
vendored
189
vendor/github.com/fsouza/go-dockerclient/AUTHORS
generated
vendored
|
|
@ -1,189 +0,0 @@
|
|||
# This is the official list of go-dockerclient authors for copyright purposes.
|
||||
|
||||
Abhishek Chanda
|
||||
Adam Bell-Hanssen
|
||||
Adnan Khan
|
||||
Adrien Kohlbecker
|
||||
Aldrin Leal
|
||||
Alex Dadgar
|
||||
Alfonso Acosta
|
||||
André Carvalho
|
||||
Andreas Jaekle
|
||||
Andrew Snodgrass
|
||||
Andrews Medina
|
||||
Andrey Sibiryov
|
||||
Andy Goldstein
|
||||
Anirudh Aithal
|
||||
Antonio Murdaca
|
||||
Artem Sidorenko
|
||||
Arthur Rodrigues
|
||||
Ben Marini
|
||||
Ben McCann
|
||||
Ben Parees
|
||||
Benno van den Berg
|
||||
Bradley Cicenas
|
||||
Brendan Fosberry
|
||||
Brian Lalor
|
||||
Brian P. Hamachek
|
||||
Brian Palmer
|
||||
Bryan Boreham
|
||||
Burke Libbey
|
||||
Carlos Diaz-Padron
|
||||
Carson A
|
||||
Cássio Botaro
|
||||
Cesar Wong
|
||||
Cezar Sa Espinola
|
||||
Changping Chen
|
||||
Cheah Chu Yeow
|
||||
cheneydeng
|
||||
Chris Bednarski
|
||||
Chris Stavropoulos
|
||||
Christian Stewart
|
||||
Christophe Mourette
|
||||
Clint Armstrong
|
||||
CMGS
|
||||
Colin Hebert
|
||||
Craig Jellick
|
||||
Damien Lespiau
|
||||
Damon Wang
|
||||
Dan Williams
|
||||
Daniel, Dao Quang Minh
|
||||
Daniel Garcia
|
||||
Daniel Hiltgen
|
||||
Daniel Tsui
|
||||
Darren Shepherd
|
||||
Dave Choi
|
||||
David Huie
|
||||
Dawn Chen
|
||||
Denis Makogon
|
||||
Derek Petersen
|
||||
Dinesh Subhraveti
|
||||
Drew Wells
|
||||
Ed
|
||||
Elias G. Schneevoigt
|
||||
Erez Horev
|
||||
Eric Anderson
|
||||
Eric J. Holmes
|
||||
Eric Mountain
|
||||
Erwin van Eyk
|
||||
Ethan Mosbaugh
|
||||
Ewout Prangsma
|
||||
Fabio Rehm
|
||||
Fatih Arslan
|
||||
Felipe Oliveira
|
||||
Flavia Missi
|
||||
Florent Aide
|
||||
Francisco Souza
|
||||
Frank Groeneveld
|
||||
George Moura
|
||||
Grégoire Delattre
|
||||
Guilherme Rezende
|
||||
Guillermo Álvarez Fernández
|
||||
Harry Zhang
|
||||
He Simei
|
||||
Isaac Schnitzer
|
||||
Ivan Mikushin
|
||||
James Bardin
|
||||
James Nugent
|
||||
Jamie Snell
|
||||
Januar Wayong
|
||||
Jari Kolehmainen
|
||||
Jason Wilder
|
||||
Jawher Moussa
|
||||
Jean-Baptiste Dalido
|
||||
Jeff Mitchell
|
||||
Jeffrey Hulten
|
||||
Jen Andre
|
||||
Jérôme Laurens
|
||||
Jim Minter
|
||||
Johan Euphrosine
|
||||
Johannes Scheuermann
|
||||
John Hughes
|
||||
Jorge Marey
|
||||
Julian Einwag
|
||||
Kamil Domanski
|
||||
Karan Misra
|
||||
Ken Herner
|
||||
Kevin Lin
|
||||
Kevin Xu
|
||||
Kim, Hirokuni
|
||||
Kostas Lekkas
|
||||
Kyle Allan
|
||||
Liron Levin
|
||||
Lior Yankovich
|
||||
Liu Peng
|
||||
Lorenz Leutgeb
|
||||
Lucas Clemente
|
||||
Lucas Weiblen
|
||||
Lyon Hill
|
||||
Mantas Matelis
|
||||
Manuel Vogel
|
||||
Marguerite des Trois Maisons
|
||||
Mariusz Borsa
|
||||
Martin Sweeney
|
||||
Máximo Cuadros Ortiz
|
||||
Michael Schmatz
|
||||
Michal Fojtik
|
||||
Mike Dillon
|
||||
Mrunal Patel
|
||||
Nate Jones
|
||||
Nguyen Sy Thanh Son
|
||||
Nicholas Van Wiggeren
|
||||
Nick Ethier
|
||||
niko83
|
||||
Omeid Matten
|
||||
Orivej Desh
|
||||
Paul Bellamy
|
||||
Paul Morie
|
||||
Paul Weil
|
||||
Peter Edge
|
||||
Peter Jihoon Kim
|
||||
Peter Teich
|
||||
Phil Lu
|
||||
Philippe Lafoucrière
|
||||
Radek Simko
|
||||
Rafe Colton
|
||||
Raphaël Pinson
|
||||
Reed Allman
|
||||
RJ Catalano
|
||||
Rob Miller
|
||||
Robbert Klarenbeek
|
||||
Robert Williamson
|
||||
Roman Khlystik
|
||||
Russell Haering
|
||||
Salvador Gironès
|
||||
Sam Rijs
|
||||
Sami Wagiaalla
|
||||
Samuel Archambault
|
||||
Samuel Karp
|
||||
Sebastian Borza
|
||||
Seth Jennings
|
||||
Shane Xie
|
||||
Silas Sewell
|
||||
Simon Eskildsen
|
||||
Simon Menke
|
||||
Skolos
|
||||
Soulou
|
||||
Sridhar Ratnakumar
|
||||
Steven Jack
|
||||
Summer Mousa
|
||||
Sunjin Lee
|
||||
Sunny
|
||||
Swaroop Ramachandra
|
||||
Tarsis Azevedo
|
||||
Tim Schindler
|
||||
Timothy St. Clair
|
||||
Tobi Knaup
|
||||
Tom Wilkie
|
||||
Tonic
|
||||
ttyh061
|
||||
upccup
|
||||
Victor Marmol
|
||||
Vincenzo Prignano
|
||||
Vlad Alexandru Ionescu
|
||||
Weitao Zhou
|
||||
Wiliam Souza
|
||||
Ye Yin
|
||||
Yosuke Otosu
|
||||
Yu, Zou
|
||||
Yuriy Bogdanov
|
||||
6
vendor/github.com/fsouza/go-dockerclient/DOCKER-LICENSE
generated
vendored
6
vendor/github.com/fsouza/go-dockerclient/DOCKER-LICENSE
generated
vendored
|
|
@ -1,6 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
You can find the Docker license at the following link:
|
||||
https://raw.githubusercontent.com/docker/docker/master/LICENSE
|
||||
24
vendor/github.com/fsouza/go-dockerclient/Gopkg.toml
generated
vendored
24
vendor/github.com/fsouza/go-dockerclient/Gopkg.toml
generated
vendored
|
|
@ -1,24 +0,0 @@
|
|||
[[constraint]]
|
||||
name = "github.com/Microsoft/go-winio"
|
||||
version = "v0.4.5"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/docker/docker"
|
||||
branch = "master"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/docker/go-units"
|
||||
version = "v0.3.2"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/google/go-cmp"
|
||||
version = "v0.2.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/gorilla/mux"
|
||||
version = "v1.5.0"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/Nvveen/Gotty"
|
||||
source = "https://github.com/ijc25/Gotty.git"
|
||||
revision = "a8b993ba6abdb0e0c12b0125c603323a71c7790c"
|
||||
22
vendor/github.com/fsouza/go-dockerclient/LICENSE
generated
vendored
22
vendor/github.com/fsouza/go-dockerclient/LICENSE
generated
vendored
|
|
@ -1,22 +0,0 @@
|
|||
Copyright (c) 2013-2018, go-dockerclient authors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
34
vendor/github.com/fsouza/go-dockerclient/Makefile
generated
vendored
34
vendor/github.com/fsouza/go-dockerclient/Makefile
generated
vendored
|
|
@ -1,34 +0,0 @@
|
|||
.PHONY: \
|
||||
all \
|
||||
lint \
|
||||
vet \
|
||||
fmtcheck \
|
||||
pretest \
|
||||
test \
|
||||
integration
|
||||
|
||||
all: test
|
||||
|
||||
lint:
|
||||
@ go get -v golang.org/x/lint/golint
|
||||
[ -z "$$(golint . | grep -v 'type name will be used as docker.DockerInfo' | grep -v 'context.Context should be the first' | tee /dev/stderr)" ]
|
||||
|
||||
vet:
|
||||
go vet ./...
|
||||
|
||||
fmtcheck:
|
||||
[ -z "$$(gofmt -s -d *.go ./testing | tee /dev/stderr)" ]
|
||||
|
||||
testdeps:
|
||||
go get -u github.com/golang/dep/cmd/dep
|
||||
dep ensure -v
|
||||
|
||||
pretest: testdeps lint vet fmtcheck
|
||||
|
||||
gotest:
|
||||
go test -race ./...
|
||||
|
||||
test: pretest gotest
|
||||
|
||||
integration:
|
||||
go test -tags docker_integration -run TestIntegration -v
|
||||
133
vendor/github.com/fsouza/go-dockerclient/README.markdown
generated
vendored
133
vendor/github.com/fsouza/go-dockerclient/README.markdown
generated
vendored
|
|
@ -1,133 +0,0 @@
|
|||
# go-dockerclient
|
||||
|
||||
[](https://travis-ci.org/fsouza/go-dockerclient)
|
||||
[](https://ci.appveyor.com/project/fsouza/go-dockerclient)
|
||||
[](https://godoc.org/github.com/fsouza/go-dockerclient)
|
||||
|
||||
This package presents a client for the Docker remote API. It also provides
|
||||
support for the extensions in the [Swarm API](https://docs.docker.com/swarm/swarm-api/).
|
||||
|
||||
This package also provides support for docker's network API, which is a simple
|
||||
passthrough to the libnetwork remote API. Note that docker's network API is
|
||||
only available in docker 1.8 and above, and only enabled in docker if
|
||||
DOCKER_EXPERIMENTAL is defined during the docker build process.
|
||||
|
||||
For more details, check the [remote API
|
||||
documentation](http://docs.docker.com/engine/reference/api/docker_remote_api/).
|
||||
|
||||
## Example
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
)
|
||||
|
||||
func main() {
|
||||
endpoint := "unix:///var/run/docker.sock"
|
||||
client, err := docker.NewClient(endpoint)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
imgs, err := client.ListImages(docker.ListImagesOptions{All: false})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, img := range imgs {
|
||||
fmt.Println("ID: ", img.ID)
|
||||
fmt.Println("RepoTags: ", img.RepoTags)
|
||||
fmt.Println("Created: ", img.Created)
|
||||
fmt.Println("Size: ", img.Size)
|
||||
fmt.Println("VirtualSize: ", img.VirtualSize)
|
||||
fmt.Println("ParentId: ", img.ParentID)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Using with TLS
|
||||
|
||||
In order to instantiate the client for a TLS-enabled daemon, you should use
|
||||
NewTLSClient, passing the endpoint and path for key and certificates as
|
||||
parameters.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
)
|
||||
|
||||
func main() {
|
||||
endpoint := "tcp://[ip]:[port]"
|
||||
path := os.Getenv("DOCKER_CERT_PATH")
|
||||
ca := fmt.Sprintf("%s/ca.pem", path)
|
||||
cert := fmt.Sprintf("%s/cert.pem", path)
|
||||
key := fmt.Sprintf("%s/key.pem", path)
|
||||
client, _ := docker.NewTLSClient(endpoint, cert, key, ca)
|
||||
// use client
|
||||
}
|
||||
```
|
||||
|
||||
If using [docker-machine](https://docs.docker.com/machine/), or another
|
||||
application that exports environment variables `DOCKER_HOST`,
|
||||
`DOCKER_TLS_VERIFY`, `DOCKER_CERT_PATH`, you can use NewClientFromEnv.
|
||||
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
)
|
||||
|
||||
func main() {
|
||||
client, _ := docker.NewClientFromEnv()
|
||||
// use client
|
||||
}
|
||||
```
|
||||
|
||||
See the documentation for more details.
|
||||
|
||||
## Developing
|
||||
|
||||
All development commands can be seen in the [Makefile](Makefile).
|
||||
|
||||
Commited code must pass:
|
||||
|
||||
* [golint](https://github.com/golang/lint) (with some exceptions, see the Makefile).
|
||||
* [go vet](https://golang.org/cmd/vet/)
|
||||
* [gofmt](https://golang.org/cmd/gofmt)
|
||||
* [go test](https://golang.org/cmd/go/#hdr-Test_packages)
|
||||
|
||||
Running `make test` will check all of these. If your editor does not
|
||||
automatically call ``gofmt -s``, `make fmt` will format all go files in this
|
||||
repository.
|
||||
|
||||
## Vendoring
|
||||
|
||||
go-dockerclient uses [dep](https://github.com/golang/dep/) for vendoring. If
|
||||
you're using dep, you should be able to pick go-dockerclient releases and get
|
||||
the proper dependencies.
|
||||
|
||||
With other vendoring tools, users might need to specify go-dockerclient's
|
||||
dependencies manually.
|
||||
|
||||
## Using with Docker 1.9 and Go 1.4
|
||||
|
||||
There's a tag for using go-dockerclient with Docker 1.9 (which requires
|
||||
compiling go-dockerclient with Go 1.4), the tag name is ``docker-1.9/go-1.4``.
|
||||
|
||||
The instructions below can be used to get a version of go-dockerclient that compiles with Go 1.4:
|
||||
|
||||
```
|
||||
% git clone -b docker-1.9/go-1.4 https://github.com/fsouza/go-dockerclient.git $GOPATH/src/github.com/fsouza/go-dockerclient
|
||||
% git clone -b v1.9.1 https://github.com/docker/docker.git $GOPATH/src/github.com/docker/docker
|
||||
% go get github.com/fsouza/go-dockerclient
|
||||
```
|
||||
21
vendor/github.com/fsouza/go-dockerclient/appveyor.yml
generated
vendored
21
vendor/github.com/fsouza/go-dockerclient/appveyor.yml
generated
vendored
|
|
@ -1,21 +0,0 @@
|
|||
version: '{build}'
|
||||
platform: x64
|
||||
clone_depth: 2
|
||||
clone_folder: c:\gopath\src\github.com\fsouza\go-dockerclient
|
||||
environment:
|
||||
GOPATH: c:\gopath
|
||||
matrix:
|
||||
- GOVERSION: 1.9.4
|
||||
- GOVERSION: 1.10
|
||||
install:
|
||||
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
|
||||
- rmdir c:\go /s /q
|
||||
- appveyor DownloadFile https://storage.googleapis.com/golang/go%GOVERSION%.windows-amd64.zip
|
||||
- 7z x go%GOVERSION%.windows-amd64.zip -y -oC:\ > NUL
|
||||
build_script:
|
||||
- go get -u github.com/golang/dep/cmd/dep
|
||||
- dep ensure -v
|
||||
test_script:
|
||||
- for /f "" %%G in ('go list ./... ^| find /i /v "/vendor/"') do ( go test %%G & IF ERRORLEVEL == 1 EXIT 1)
|
||||
matrix:
|
||||
fast_finish: true
|
||||
185
vendor/github.com/fsouza/go-dockerclient/auth.go
generated
vendored
185
vendor/github.com/fsouza/go-dockerclient/auth.go
generated
vendored
|
|
@ -1,185 +0,0 @@
|
|||
// Copyright 2015 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ErrCannotParseDockercfg is the error returned by NewAuthConfigurations when the dockercfg cannot be parsed.
|
||||
var ErrCannotParseDockercfg = errors.New("Failed to read authentication from dockercfg")
|
||||
|
||||
// AuthConfiguration represents authentication options to use in the PushImage
|
||||
// method. It represents the authentication in the Docker index server.
|
||||
type AuthConfiguration struct {
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
ServerAddress string `json:"serveraddress,omitempty"`
|
||||
}
|
||||
|
||||
// AuthConfigurations represents authentication options to use for the
|
||||
// PushImage method accommodating the new X-Registry-Config header
|
||||
type AuthConfigurations struct {
|
||||
Configs map[string]AuthConfiguration `json:"configs"`
|
||||
}
|
||||
|
||||
// AuthConfigurations119 is used to serialize a set of AuthConfigurations
|
||||
// for Docker API >= 1.19.
|
||||
type AuthConfigurations119 map[string]AuthConfiguration
|
||||
|
||||
// dockerConfig represents a registry authentation configuration from the
|
||||
// .dockercfg file.
|
||||
type dockerConfig struct {
|
||||
Auth string `json:"auth"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
// NewAuthConfigurationsFromFile returns AuthConfigurations from a path containing JSON
|
||||
// in the same format as the .dockercfg file.
|
||||
func NewAuthConfigurationsFromFile(path string) (*AuthConfigurations, error) {
|
||||
r, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewAuthConfigurations(r)
|
||||
}
|
||||
|
||||
func cfgPaths(dockerConfigEnv string, homeEnv string) []string {
|
||||
var paths []string
|
||||
if dockerConfigEnv != "" {
|
||||
paths = append(paths, path.Join(dockerConfigEnv, "config.json"))
|
||||
}
|
||||
if homeEnv != "" {
|
||||
paths = append(paths, path.Join(homeEnv, ".docker", "config.json"))
|
||||
paths = append(paths, path.Join(homeEnv, ".dockercfg"))
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
// NewAuthConfigurationsFromDockerCfg returns AuthConfigurations from
|
||||
// system config files. The following files are checked in the order listed:
|
||||
// - $DOCKER_CONFIG/config.json if DOCKER_CONFIG set in the environment,
|
||||
// - $HOME/.docker/config.json
|
||||
// - $HOME/.dockercfg
|
||||
func NewAuthConfigurationsFromDockerCfg() (*AuthConfigurations, error) {
|
||||
err := fmt.Errorf("No docker configuration found")
|
||||
var auths *AuthConfigurations
|
||||
|
||||
pathsToTry := cfgPaths(os.Getenv("DOCKER_CONFIG"), os.Getenv("HOME"))
|
||||
for _, path := range pathsToTry {
|
||||
auths, err = NewAuthConfigurationsFromFile(path)
|
||||
if err == nil {
|
||||
return auths, nil
|
||||
}
|
||||
}
|
||||
return auths, err
|
||||
}
|
||||
|
||||
// NewAuthConfigurations returns AuthConfigurations from a JSON encoded string in the
|
||||
// same format as the .dockercfg file.
|
||||
func NewAuthConfigurations(r io.Reader) (*AuthConfigurations, error) {
|
||||
var auth *AuthConfigurations
|
||||
confs, err := parseDockerConfig(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
auth, err = authConfigs(confs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return auth, nil
|
||||
}
|
||||
|
||||
func parseDockerConfig(r io.Reader) (map[string]dockerConfig, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
buf.ReadFrom(r)
|
||||
byteData := buf.Bytes()
|
||||
|
||||
confsWrapper := struct {
|
||||
Auths map[string]dockerConfig `json:"auths"`
|
||||
}{}
|
||||
if err := json.Unmarshal(byteData, &confsWrapper); err == nil {
|
||||
if len(confsWrapper.Auths) > 0 {
|
||||
return confsWrapper.Auths, nil
|
||||
}
|
||||
}
|
||||
|
||||
var confs map[string]dockerConfig
|
||||
if err := json.Unmarshal(byteData, &confs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return confs, nil
|
||||
}
|
||||
|
||||
// authConfigs converts a dockerConfigs map to a AuthConfigurations object.
|
||||
func authConfigs(confs map[string]dockerConfig) (*AuthConfigurations, error) {
|
||||
c := &AuthConfigurations{
|
||||
Configs: make(map[string]AuthConfiguration),
|
||||
}
|
||||
for reg, conf := range confs {
|
||||
if conf.Auth == "" {
|
||||
continue
|
||||
}
|
||||
data, err := base64.StdEncoding.DecodeString(conf.Auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userpass := strings.SplitN(string(data), ":", 2)
|
||||
if len(userpass) != 2 {
|
||||
return nil, ErrCannotParseDockercfg
|
||||
}
|
||||
c.Configs[reg] = AuthConfiguration{
|
||||
Email: conf.Email,
|
||||
Username: userpass[0],
|
||||
Password: userpass[1],
|
||||
ServerAddress: reg,
|
||||
}
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// AuthStatus returns the authentication status for Docker API versions >= 1.23.
|
||||
type AuthStatus struct {
|
||||
Status string `json:"Status,omitempty" yaml:"Status,omitempty" toml:"Status,omitempty"`
|
||||
IdentityToken string `json:"IdentityToken,omitempty" yaml:"IdentityToken,omitempty" toml:"IdentityToken,omitempty"`
|
||||
}
|
||||
|
||||
// AuthCheck validates the given credentials. It returns nil if successful.
|
||||
//
|
||||
// For Docker API versions >= 1.23, the AuthStatus struct will be populated, otherwise it will be empty.`
|
||||
//
|
||||
// See https://goo.gl/6nsZkH for more details.
|
||||
func (c *Client) AuthCheck(conf *AuthConfiguration) (AuthStatus, error) {
|
||||
var authStatus AuthStatus
|
||||
if conf == nil {
|
||||
return authStatus, errors.New("conf is nil")
|
||||
}
|
||||
resp, err := c.do("POST", "/auth", doOptions{data: conf})
|
||||
if err != nil {
|
||||
return authStatus, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return authStatus, err
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return authStatus, nil
|
||||
}
|
||||
if err := json.Unmarshal(data, &authStatus); err != nil {
|
||||
return authStatus, err
|
||||
}
|
||||
return authStatus, nil
|
||||
}
|
||||
43
vendor/github.com/fsouza/go-dockerclient/change.go
generated
vendored
43
vendor/github.com/fsouza/go-dockerclient/change.go
generated
vendored
|
|
@ -1,43 +0,0 @@
|
|||
// Copyright 2014 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
import "fmt"
|
||||
|
||||
// ChangeType is a type for constants indicating the type of change
|
||||
// in a container
|
||||
type ChangeType int
|
||||
|
||||
const (
|
||||
// ChangeModify is the ChangeType for container modifications
|
||||
ChangeModify ChangeType = iota
|
||||
|
||||
// ChangeAdd is the ChangeType for additions to a container
|
||||
ChangeAdd
|
||||
|
||||
// ChangeDelete is the ChangeType for deletions from a container
|
||||
ChangeDelete
|
||||
)
|
||||
|
||||
// Change represents a change in a container.
|
||||
//
|
||||
// See https://goo.gl/Wo0JJp for more details.
|
||||
type Change struct {
|
||||
Path string
|
||||
Kind ChangeType
|
||||
}
|
||||
|
||||
func (change *Change) String() string {
|
||||
var kind string
|
||||
switch change.Kind {
|
||||
case ChangeModify:
|
||||
kind = "C"
|
||||
case ChangeAdd:
|
||||
kind = "A"
|
||||
case ChangeDelete:
|
||||
kind = "D"
|
||||
}
|
||||
return fmt.Sprintf("%s %s", kind, change.Path)
|
||||
}
|
||||
1092
vendor/github.com/fsouza/go-dockerclient/client.go
generated
vendored
1092
vendor/github.com/fsouza/go-dockerclient/client.go
generated
vendored
File diff suppressed because it is too large
Load diff
32
vendor/github.com/fsouza/go-dockerclient/client_unix.go
generated
vendored
32
vendor/github.com/fsouza/go-dockerclient/client_unix.go
generated
vendored
|
|
@ -1,32 +0,0 @@
|
|||
// Copyright 2016 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// initializeNativeClient initializes the native Unix domain socket client on
|
||||
// Unix-style operating systems
|
||||
func (c *Client) initializeNativeClient(trFunc func() *http.Transport) {
|
||||
if c.endpointURL.Scheme != unixProtocol {
|
||||
return
|
||||
}
|
||||
sockPath := c.endpointURL.Path
|
||||
|
||||
tr := trFunc()
|
||||
|
||||
tr.Dial = func(network, addr string) (net.Conn, error) {
|
||||
return c.Dialer.Dial(unixProtocol, sockPath)
|
||||
}
|
||||
tr.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return c.Dialer.Dial(unixProtocol, sockPath)
|
||||
}
|
||||
c.HTTPClient.Transport = tr
|
||||
}
|
||||
45
vendor/github.com/fsouza/go-dockerclient/client_windows.go
generated
vendored
45
vendor/github.com/fsouza/go-dockerclient/client_windows.go
generated
vendored
|
|
@ -1,45 +0,0 @@
|
|||
// Copyright 2016 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/Microsoft/go-winio"
|
||||
)
|
||||
|
||||
const namedPipeConnectTimeout = 2 * time.Second
|
||||
|
||||
type pipeDialer struct {
|
||||
dialFunc func(network, addr string) (net.Conn, error)
|
||||
}
|
||||
|
||||
func (p pipeDialer) Dial(network, address string) (net.Conn, error) {
|
||||
return p.dialFunc(network, address)
|
||||
}
|
||||
|
||||
// initializeNativeClient initializes the native Named Pipe client for Windows
|
||||
func (c *Client) initializeNativeClient(trFunc func() *http.Transport) {
|
||||
if c.endpointURL.Scheme != namedPipeProtocol {
|
||||
return
|
||||
}
|
||||
namedPipePath := c.endpointURL.Path
|
||||
dialFunc := func(network, addr string) (net.Conn, error) {
|
||||
timeout := namedPipeConnectTimeout
|
||||
return winio.DialPipe(namedPipePath, &timeout)
|
||||
}
|
||||
tr := trFunc()
|
||||
tr.Dial = dialFunc
|
||||
tr.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return dialFunc(network, addr)
|
||||
}
|
||||
c.Dialer = &pipeDialer{dialFunc}
|
||||
c.HTTPClient.Transport = tr
|
||||
}
|
||||
1622
vendor/github.com/fsouza/go-dockerclient/container.go
generated
vendored
1622
vendor/github.com/fsouza/go-dockerclient/container.go
generated
vendored
File diff suppressed because it is too large
Load diff
26
vendor/github.com/fsouza/go-dockerclient/distribution.go
generated
vendored
26
vendor/github.com/fsouza/go-dockerclient/distribution.go
generated
vendored
|
|
@ -1,26 +0,0 @@
|
|||
// Copyright 2017 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
)
|
||||
|
||||
// InspectDistribution returns image digest and platform information by contacting the registry
|
||||
func (c *Client) InspectDistribution(name string) (*registry.DistributionInspect, error) {
|
||||
path := "/distribution/" + name + "/json"
|
||||
resp, err := c.do("GET", path, doOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var distributionInspect registry.DistributionInspect
|
||||
if err := json.NewDecoder(resp.Body).Decode(&distributionInspect); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &distributionInspect, nil
|
||||
}
|
||||
172
vendor/github.com/fsouza/go-dockerclient/env.go
generated
vendored
172
vendor/github.com/fsouza/go-dockerclient/env.go
generated
vendored
|
|
@ -1,172 +0,0 @@
|
|||
// Copyright 2014 Docker authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the DOCKER-LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Env represents a list of key-pair represented in the form KEY=VALUE.
|
||||
type Env []string
|
||||
|
||||
// Get returns the string value of the given key.
|
||||
func (env *Env) Get(key string) (value string) {
|
||||
return env.Map()[key]
|
||||
}
|
||||
|
||||
// Exists checks whether the given key is defined in the internal Env
|
||||
// representation.
|
||||
func (env *Env) Exists(key string) bool {
|
||||
_, exists := env.Map()[key]
|
||||
return exists
|
||||
}
|
||||
|
||||
// GetBool returns a boolean representation of the given key. The key is false
|
||||
// whenever its value if 0, no, false, none or an empty string. Any other value
|
||||
// will be interpreted as true.
|
||||
func (env *Env) GetBool(key string) (value bool) {
|
||||
s := strings.ToLower(strings.Trim(env.Get(key), " \t"))
|
||||
if s == "" || s == "0" || s == "no" || s == "false" || s == "none" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// SetBool defines a boolean value to the given key.
|
||||
func (env *Env) SetBool(key string, value bool) {
|
||||
if value {
|
||||
env.Set(key, "1")
|
||||
} else {
|
||||
env.Set(key, "0")
|
||||
}
|
||||
}
|
||||
|
||||
// GetInt returns the value of the provided key, converted to int.
|
||||
//
|
||||
// It the value cannot be represented as an integer, it returns -1.
|
||||
func (env *Env) GetInt(key string) int {
|
||||
return int(env.GetInt64(key))
|
||||
}
|
||||
|
||||
// SetInt defines an integer value to the given key.
|
||||
func (env *Env) SetInt(key string, value int) {
|
||||
env.Set(key, strconv.Itoa(value))
|
||||
}
|
||||
|
||||
// GetInt64 returns the value of the provided key, converted to int64.
|
||||
//
|
||||
// It the value cannot be represented as an integer, it returns -1.
|
||||
func (env *Env) GetInt64(key string) int64 {
|
||||
s := strings.Trim(env.Get(key), " \t")
|
||||
val, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// SetInt64 defines an integer (64-bit wide) value to the given key.
|
||||
func (env *Env) SetInt64(key string, value int64) {
|
||||
env.Set(key, strconv.FormatInt(value, 10))
|
||||
}
|
||||
|
||||
// GetJSON unmarshals the value of the provided key in the provided iface.
|
||||
//
|
||||
// iface is a value that can be provided to the json.Unmarshal function.
|
||||
func (env *Env) GetJSON(key string, iface interface{}) error {
|
||||
sval := env.Get(key)
|
||||
if sval == "" {
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal([]byte(sval), iface)
|
||||
}
|
||||
|
||||
// SetJSON marshals the given value to JSON format and stores it using the
|
||||
// provided key.
|
||||
func (env *Env) SetJSON(key string, value interface{}) error {
|
||||
sval, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
env.Set(key, string(sval))
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetList returns a list of strings matching the provided key. It handles the
|
||||
// list as a JSON representation of a list of strings.
|
||||
//
|
||||
// If the given key matches to a single string, it will return a list
|
||||
// containing only the value that matches the key.
|
||||
func (env *Env) GetList(key string) []string {
|
||||
sval := env.Get(key)
|
||||
if sval == "" {
|
||||
return nil
|
||||
}
|
||||
var l []string
|
||||
if err := json.Unmarshal([]byte(sval), &l); err != nil {
|
||||
l = append(l, sval)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// SetList stores the given list in the provided key, after serializing it to
|
||||
// JSON format.
|
||||
func (env *Env) SetList(key string, value []string) error {
|
||||
return env.SetJSON(key, value)
|
||||
}
|
||||
|
||||
// Set defines the value of a key to the given string.
|
||||
func (env *Env) Set(key, value string) {
|
||||
*env = append(*env, key+"="+value)
|
||||
}
|
||||
|
||||
// Decode decodes `src` as a json dictionary, and adds each decoded key-value
|
||||
// pair to the environment.
|
||||
//
|
||||
// If `src` cannot be decoded as a json dictionary, an error is returned.
|
||||
func (env *Env) Decode(src io.Reader) error {
|
||||
m := make(map[string]interface{})
|
||||
if err := json.NewDecoder(src).Decode(&m); err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range m {
|
||||
env.SetAuto(k, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetAuto will try to define the Set* method to call based on the given value.
|
||||
func (env *Env) SetAuto(key string, value interface{}) {
|
||||
if fval, ok := value.(float64); ok {
|
||||
env.SetInt64(key, int64(fval))
|
||||
} else if sval, ok := value.(string); ok {
|
||||
env.Set(key, sval)
|
||||
} else if val, err := json.Marshal(value); err == nil {
|
||||
env.Set(key, string(val))
|
||||
} else {
|
||||
env.Set(key, fmt.Sprintf("%v", value))
|
||||
}
|
||||
}
|
||||
|
||||
// Map returns the map representation of the env.
|
||||
func (env *Env) Map() map[string]string {
|
||||
if len(*env) == 0 {
|
||||
return nil
|
||||
}
|
||||
m := make(map[string]string)
|
||||
for _, kv := range *env {
|
||||
parts := strings.SplitN(kv, "=", 2)
|
||||
if len(parts) == 1 {
|
||||
m[parts[0]] = ""
|
||||
} else {
|
||||
m[parts[0]] = parts[1]
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
395
vendor/github.com/fsouza/go-dockerclient/event.go
generated
vendored
395
vendor/github.com/fsouza/go-dockerclient/event.go
generated
vendored
|
|
@ -1,395 +0,0 @@
|
|||
// Copyright 2014 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// APIEvents represents events coming from the Docker API
|
||||
// The fields in the Docker API changed in API version 1.22, and
|
||||
// events for more than images and containers are now fired off.
|
||||
// To maintain forward and backward compatibility, go-dockerclient
|
||||
// replicates the event in both the new and old format as faithfully as possible.
|
||||
//
|
||||
// For events that only exist in 1.22 in later, `Status` is filled in as
|
||||
// `"Type:Action"` instead of just `Action` to allow for older clients to
|
||||
// differentiate and not break if they rely on the pre-1.22 Status types.
|
||||
//
|
||||
// The transformEvent method can be consulted for more information about how
|
||||
// events are translated from new/old API formats
|
||||
type APIEvents struct {
|
||||
// New API Fields in 1.22
|
||||
Action string `json:"action,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Actor APIActor `json:"actor,omitempty"`
|
||||
|
||||
// Old API fields for < 1.22
|
||||
Status string `json:"status,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
From string `json:"from,omitempty"`
|
||||
|
||||
// Fields in both
|
||||
Time int64 `json:"time,omitempty"`
|
||||
TimeNano int64 `json:"timeNano,omitempty"`
|
||||
}
|
||||
|
||||
// APIActor represents an actor that accomplishes something for an event
|
||||
type APIActor struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Attributes map[string]string `json:"attributes,omitempty"`
|
||||
}
|
||||
|
||||
type eventMonitoringState struct {
|
||||
// `sync/atomic` expects the first word in an allocated struct to be 64-bit
|
||||
// aligned on both ARM and x86-32. See https://goo.gl/zW7dgq for more details.
|
||||
lastSeen int64
|
||||
sync.RWMutex
|
||||
sync.WaitGroup
|
||||
enabled bool
|
||||
C chan *APIEvents
|
||||
errC chan error
|
||||
listeners []chan<- *APIEvents
|
||||
}
|
||||
|
||||
const (
|
||||
maxMonitorConnRetries = 5
|
||||
retryInitialWaitTime = 10.
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNoListeners is the error returned when no listeners are available
|
||||
// to receive an event.
|
||||
ErrNoListeners = errors.New("no listeners present to receive event")
|
||||
|
||||
// ErrListenerAlreadyExists is the error returned when the listerner already
|
||||
// exists.
|
||||
ErrListenerAlreadyExists = errors.New("listener already exists for docker events")
|
||||
|
||||
// ErrTLSNotSupported is the error returned when the client does not support
|
||||
// TLS (this applies to the Windows named pipe client).
|
||||
ErrTLSNotSupported = errors.New("tls not supported by this client")
|
||||
|
||||
// EOFEvent is sent when the event listener receives an EOF error.
|
||||
EOFEvent = &APIEvents{
|
||||
Type: "EOF",
|
||||
Status: "EOF",
|
||||
}
|
||||
)
|
||||
|
||||
// AddEventListener adds a new listener to container events in the Docker API.
|
||||
//
|
||||
// The parameter is a channel through which events will be sent.
|
||||
func (c *Client) AddEventListener(listener chan<- *APIEvents) error {
|
||||
var err error
|
||||
if !c.eventMonitor.isEnabled() {
|
||||
err = c.eventMonitor.enableEventMonitoring(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return c.eventMonitor.addListener(listener)
|
||||
}
|
||||
|
||||
// RemoveEventListener removes a listener from the monitor.
|
||||
func (c *Client) RemoveEventListener(listener chan *APIEvents) error {
|
||||
err := c.eventMonitor.removeListener(listener)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.eventMonitor.listernersCount() == 0 {
|
||||
c.eventMonitor.disableEventMonitoring()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (eventState *eventMonitoringState) addListener(listener chan<- *APIEvents) error {
|
||||
eventState.Lock()
|
||||
defer eventState.Unlock()
|
||||
if listenerExists(listener, &eventState.listeners) {
|
||||
return ErrListenerAlreadyExists
|
||||
}
|
||||
eventState.Add(1)
|
||||
eventState.listeners = append(eventState.listeners, listener)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (eventState *eventMonitoringState) removeListener(listener chan<- *APIEvents) error {
|
||||
eventState.Lock()
|
||||
defer eventState.Unlock()
|
||||
if listenerExists(listener, &eventState.listeners) {
|
||||
var newListeners []chan<- *APIEvents
|
||||
for _, l := range eventState.listeners {
|
||||
if l != listener {
|
||||
newListeners = append(newListeners, l)
|
||||
}
|
||||
}
|
||||
eventState.listeners = newListeners
|
||||
eventState.Add(-1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (eventState *eventMonitoringState) closeListeners() {
|
||||
for _, l := range eventState.listeners {
|
||||
close(l)
|
||||
eventState.Add(-1)
|
||||
}
|
||||
eventState.listeners = nil
|
||||
}
|
||||
|
||||
func (eventState *eventMonitoringState) listernersCount() int {
|
||||
eventState.RLock()
|
||||
defer eventState.RUnlock()
|
||||
return len(eventState.listeners)
|
||||
}
|
||||
|
||||
func listenerExists(a chan<- *APIEvents, list *[]chan<- *APIEvents) bool {
|
||||
for _, b := range *list {
|
||||
if b == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (eventState *eventMonitoringState) enableEventMonitoring(c *Client) error {
|
||||
eventState.Lock()
|
||||
defer eventState.Unlock()
|
||||
if !eventState.enabled {
|
||||
eventState.enabled = true
|
||||
atomic.StoreInt64(&eventState.lastSeen, 0)
|
||||
eventState.C = make(chan *APIEvents, 100)
|
||||
eventState.errC = make(chan error, 1)
|
||||
go eventState.monitorEvents(c)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (eventState *eventMonitoringState) disableEventMonitoring() error {
|
||||
eventState.Lock()
|
||||
defer eventState.Unlock()
|
||||
|
||||
eventState.closeListeners()
|
||||
|
||||
eventState.Wait()
|
||||
|
||||
if eventState.enabled {
|
||||
eventState.enabled = false
|
||||
close(eventState.C)
|
||||
close(eventState.errC)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (eventState *eventMonitoringState) monitorEvents(c *Client) {
|
||||
var err error
|
||||
for eventState.noListeners() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
if err = eventState.connectWithRetry(c); err != nil {
|
||||
// terminate if connect failed
|
||||
eventState.disableEventMonitoring()
|
||||
return
|
||||
}
|
||||
for eventState.isEnabled() {
|
||||
timeout := time.After(100 * time.Millisecond)
|
||||
select {
|
||||
case ev, ok := <-eventState.C:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if ev == EOFEvent {
|
||||
eventState.disableEventMonitoring()
|
||||
return
|
||||
}
|
||||
eventState.updateLastSeen(ev)
|
||||
eventState.sendEvent(ev)
|
||||
case err = <-eventState.errC:
|
||||
if err == ErrNoListeners {
|
||||
eventState.disableEventMonitoring()
|
||||
return
|
||||
} else if err != nil {
|
||||
defer func() { go eventState.monitorEvents(c) }()
|
||||
return
|
||||
}
|
||||
case <-timeout:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (eventState *eventMonitoringState) connectWithRetry(c *Client) error {
|
||||
var retries int
|
||||
eventState.RLock()
|
||||
eventChan := eventState.C
|
||||
errChan := eventState.errC
|
||||
eventState.RUnlock()
|
||||
err := c.eventHijack(atomic.LoadInt64(&eventState.lastSeen), eventChan, errChan)
|
||||
for ; err != nil && retries < maxMonitorConnRetries; retries++ {
|
||||
waitTime := int64(retryInitialWaitTime * math.Pow(2, float64(retries)))
|
||||
time.Sleep(time.Duration(waitTime) * time.Millisecond)
|
||||
eventState.RLock()
|
||||
eventChan = eventState.C
|
||||
errChan = eventState.errC
|
||||
eventState.RUnlock()
|
||||
err = c.eventHijack(atomic.LoadInt64(&eventState.lastSeen), eventChan, errChan)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (eventState *eventMonitoringState) noListeners() bool {
|
||||
eventState.RLock()
|
||||
defer eventState.RUnlock()
|
||||
return len(eventState.listeners) == 0
|
||||
}
|
||||
|
||||
func (eventState *eventMonitoringState) isEnabled() bool {
|
||||
eventState.RLock()
|
||||
defer eventState.RUnlock()
|
||||
return eventState.enabled
|
||||
}
|
||||
|
||||
func (eventState *eventMonitoringState) sendEvent(event *APIEvents) {
|
||||
eventState.RLock()
|
||||
defer eventState.RUnlock()
|
||||
eventState.Add(1)
|
||||
defer eventState.Done()
|
||||
if eventState.enabled {
|
||||
if len(eventState.listeners) == 0 {
|
||||
eventState.errC <- ErrNoListeners
|
||||
return
|
||||
}
|
||||
|
||||
for _, listener := range eventState.listeners {
|
||||
select {
|
||||
case listener <- event:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (eventState *eventMonitoringState) updateLastSeen(e *APIEvents) {
|
||||
eventState.Lock()
|
||||
defer eventState.Unlock()
|
||||
if atomic.LoadInt64(&eventState.lastSeen) < e.Time {
|
||||
atomic.StoreInt64(&eventState.lastSeen, e.Time)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) eventHijack(startTime int64, eventChan chan *APIEvents, errChan chan error) error {
|
||||
uri := "/events"
|
||||
if startTime != 0 {
|
||||
uri += fmt.Sprintf("?since=%d", startTime)
|
||||
}
|
||||
protocol := c.endpointURL.Scheme
|
||||
address := c.endpointURL.Path
|
||||
if protocol != "unix" && protocol != "npipe" {
|
||||
protocol = "tcp"
|
||||
address = c.endpointURL.Host
|
||||
}
|
||||
var dial net.Conn
|
||||
var err error
|
||||
if c.TLSConfig == nil {
|
||||
dial, err = c.Dialer.Dial(protocol, address)
|
||||
} else {
|
||||
netDialer, ok := c.Dialer.(*net.Dialer)
|
||||
if !ok {
|
||||
return ErrTLSNotSupported
|
||||
}
|
||||
dial, err = tlsDialWithDialer(netDialer, protocol, address, c.TLSConfig)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conn := httputil.NewClientConn(dial, nil)
|
||||
req, err := http.NewRequest("GET", uri, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res, err := conn.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func(res *http.Response, conn *httputil.ClientConn) {
|
||||
defer conn.Close()
|
||||
defer res.Body.Close()
|
||||
decoder := json.NewDecoder(res.Body)
|
||||
for {
|
||||
var event APIEvents
|
||||
if err = decoder.Decode(&event); err != nil {
|
||||
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||
c.eventMonitor.RLock()
|
||||
if c.eventMonitor.enabled && c.eventMonitor.C == eventChan {
|
||||
// Signal that we're exiting.
|
||||
eventChan <- EOFEvent
|
||||
}
|
||||
c.eventMonitor.RUnlock()
|
||||
break
|
||||
}
|
||||
errChan <- err
|
||||
}
|
||||
if event.Time == 0 {
|
||||
continue
|
||||
}
|
||||
transformEvent(&event)
|
||||
c.eventMonitor.RLock()
|
||||
if c.eventMonitor.enabled && c.eventMonitor.C == eventChan {
|
||||
eventChan <- &event
|
||||
}
|
||||
c.eventMonitor.RUnlock()
|
||||
}
|
||||
}(res, conn)
|
||||
return nil
|
||||
}
|
||||
|
||||
// transformEvent takes an event and determines what version it is from
|
||||
// then populates both versions of the event
|
||||
func transformEvent(event *APIEvents) {
|
||||
// if event version is <= 1.21 there will be no Action and no Type
|
||||
if event.Action == "" && event.Type == "" {
|
||||
event.Action = event.Status
|
||||
event.Actor.ID = event.ID
|
||||
event.Actor.Attributes = map[string]string{}
|
||||
switch event.Status {
|
||||
case "delete", "import", "pull", "push", "tag", "untag":
|
||||
event.Type = "image"
|
||||
default:
|
||||
event.Type = "container"
|
||||
if event.From != "" {
|
||||
event.Actor.Attributes["image"] = event.From
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if event.Status == "" {
|
||||
if event.Type == "image" || event.Type == "container" {
|
||||
event.Status = event.Action
|
||||
} else {
|
||||
// Because just the Status has been overloaded with different Types
|
||||
// if an event is not for an image or a container, we prepend the type
|
||||
// to avoid problems for people relying on actions being only for
|
||||
// images and containers
|
||||
event.Status = event.Type + ":" + event.Action
|
||||
}
|
||||
}
|
||||
if event.ID == "" {
|
||||
event.ID = event.Actor.ID
|
||||
}
|
||||
if event.From == "" {
|
||||
event.From = event.Actor.Attributes["image"]
|
||||
}
|
||||
}
|
||||
}
|
||||
213
vendor/github.com/fsouza/go-dockerclient/exec.go
generated
vendored
213
vendor/github.com/fsouza/go-dockerclient/exec.go
generated
vendored
|
|
@ -1,213 +0,0 @@
|
|||
// Copyright 2014 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Exec is the type representing a `docker exec` instance and containing the
|
||||
// instance ID
|
||||
type Exec struct {
|
||||
ID string `json:"Id,omitempty" yaml:"Id,omitempty"`
|
||||
}
|
||||
|
||||
// CreateExecOptions specify parameters to the CreateExecContainer function.
|
||||
//
|
||||
// See https://goo.gl/60TeBP for more details
|
||||
type CreateExecOptions struct {
|
||||
AttachStdin bool `json:"AttachStdin,omitempty" yaml:"AttachStdin,omitempty" toml:"AttachStdin,omitempty"`
|
||||
AttachStdout bool `json:"AttachStdout,omitempty" yaml:"AttachStdout,omitempty" toml:"AttachStdout,omitempty"`
|
||||
AttachStderr bool `json:"AttachStderr,omitempty" yaml:"AttachStderr,omitempty" toml:"AttachStderr,omitempty"`
|
||||
Tty bool `json:"Tty,omitempty" yaml:"Tty,omitempty" toml:"Tty,omitempty"`
|
||||
Env []string `json:"Env,omitempty" yaml:"Env,omitempty" toml:"Env,omitempty"`
|
||||
Cmd []string `json:"Cmd,omitempty" yaml:"Cmd,omitempty" toml:"Cmd,omitempty"`
|
||||
Container string `json:"Container,omitempty" yaml:"Container,omitempty" toml:"Container,omitempty"`
|
||||
User string `json:"User,omitempty" yaml:"User,omitempty" toml:"User,omitempty"`
|
||||
Context context.Context `json:"-"`
|
||||
Privileged bool `json:"Privileged,omitempty" yaml:"Privileged,omitempty" toml:"Privileged,omitempty"`
|
||||
}
|
||||
|
||||
// CreateExec sets up an exec instance in a running container `id`, returning the exec
|
||||
// instance, or an error in case of failure.
|
||||
//
|
||||
// See https://goo.gl/60TeBP for more details
|
||||
func (c *Client) CreateExec(opts CreateExecOptions) (*Exec, error) {
|
||||
if len(opts.Env) > 0 && c.serverAPIVersion.LessThan(apiVersion125) {
|
||||
return nil, errors.New("exec configuration Env is only supported in API#1.25 and above")
|
||||
}
|
||||
path := fmt.Sprintf("/containers/%s/exec", opts.Container)
|
||||
resp, err := c.do("POST", path, doOptions{data: opts, context: opts.Context})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, &NoSuchContainer{ID: opts.Container}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var exec Exec
|
||||
if err := json.NewDecoder(resp.Body).Decode(&exec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &exec, nil
|
||||
}
|
||||
|
||||
// StartExecOptions specify parameters to the StartExecContainer function.
|
||||
//
|
||||
// See https://goo.gl/1EeDWi for more details
|
||||
type StartExecOptions struct {
|
||||
InputStream io.Reader `qs:"-"`
|
||||
OutputStream io.Writer `qs:"-"`
|
||||
ErrorStream io.Writer `qs:"-"`
|
||||
|
||||
Detach bool `json:"Detach,omitempty" yaml:"Detach,omitempty" toml:"Detach,omitempty"`
|
||||
Tty bool `json:"Tty,omitempty" yaml:"Tty,omitempty" toml:"Tty,omitempty"`
|
||||
|
||||
// Use raw terminal? Usually true when the container contains a TTY.
|
||||
RawTerminal bool `qs:"-"`
|
||||
|
||||
// If set, after a successful connect, a sentinel will be sent and then the
|
||||
// client will block on receive before continuing.
|
||||
//
|
||||
// It must be an unbuffered channel. Using a buffered channel can lead
|
||||
// to unexpected behavior.
|
||||
Success chan struct{} `json:"-"`
|
||||
|
||||
Context context.Context `json:"-"`
|
||||
}
|
||||
|
||||
// StartExec starts a previously set up exec instance id. If opts.Detach is
|
||||
// true, it returns after starting the exec command. Otherwise, it sets up an
|
||||
// interactive session with the exec command.
|
||||
//
|
||||
// See https://goo.gl/1EeDWi for more details
|
||||
func (c *Client) StartExec(id string, opts StartExecOptions) error {
|
||||
cw, err := c.StartExecNonBlocking(id, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cw != nil {
|
||||
return cw.Wait()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StartExecNonBlocking starts a previously set up exec instance id. If opts.Detach is
|
||||
// true, it returns after starting the exec command. Otherwise, it sets up an
|
||||
// interactive session with the exec command.
|
||||
//
|
||||
// See https://goo.gl/1EeDWi for more details
|
||||
func (c *Client) StartExecNonBlocking(id string, opts StartExecOptions) (CloseWaiter, error) {
|
||||
if id == "" {
|
||||
return nil, &NoSuchExec{ID: id}
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/exec/%s/start", id)
|
||||
|
||||
if opts.Detach {
|
||||
resp, err := c.do("POST", path, doOptions{data: opts, context: opts.Context})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, &NoSuchExec{ID: id}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return c.hijack("POST", path, hijackOptions{
|
||||
success: opts.Success,
|
||||
setRawTerminal: opts.RawTerminal,
|
||||
in: opts.InputStream,
|
||||
stdout: opts.OutputStream,
|
||||
stderr: opts.ErrorStream,
|
||||
data: opts,
|
||||
})
|
||||
}
|
||||
|
||||
// ResizeExecTTY resizes the tty session used by the exec command id. This API
|
||||
// is valid only if Tty was specified as part of creating and starting the exec
|
||||
// command.
|
||||
//
|
||||
// See https://goo.gl/Mo5bxx for more details
|
||||
func (c *Client) ResizeExecTTY(id string, height, width int) error {
|
||||
params := make(url.Values)
|
||||
params.Set("h", strconv.Itoa(height))
|
||||
params.Set("w", strconv.Itoa(width))
|
||||
|
||||
path := fmt.Sprintf("/exec/%s/resize?%s", id, params.Encode())
|
||||
resp, err := c.do("POST", path, doOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExecProcessConfig is a type describing the command associated to a Exec
|
||||
// instance. It's used in the ExecInspect type.
|
||||
type ExecProcessConfig struct {
|
||||
User string `json:"user,omitempty" yaml:"user,omitempty" toml:"user,omitempty"`
|
||||
Privileged bool `json:"privileged,omitempty" yaml:"privileged,omitempty" toml:"privileged,omitempty"`
|
||||
Tty bool `json:"tty,omitempty" yaml:"tty,omitempty" toml:"tty,omitempty"`
|
||||
EntryPoint string `json:"entrypoint,omitempty" yaml:"entrypoint,omitempty" toml:"entrypoint,omitempty"`
|
||||
Arguments []string `json:"arguments,omitempty" yaml:"arguments,omitempty" toml:"arguments,omitempty"`
|
||||
}
|
||||
|
||||
// ExecInspect is a type with details about a exec instance, including the
|
||||
// exit code if the command has finished running. It's returned by a api
|
||||
// call to /exec/(id)/json
|
||||
//
|
||||
// See https://goo.gl/ctMUiW for more details
|
||||
type ExecInspect struct {
|
||||
ID string `json:"ID,omitempty" yaml:"ID,omitempty" toml:"ID,omitempty"`
|
||||
ExitCode int `json:"ExitCode,omitempty" yaml:"ExitCode,omitempty" toml:"ExitCode,omitempty"`
|
||||
Running bool `json:"Running,omitempty" yaml:"Running,omitempty" toml:"Running,omitempty"`
|
||||
OpenStdin bool `json:"OpenStdin,omitempty" yaml:"OpenStdin,omitempty" toml:"OpenStdin,omitempty"`
|
||||
OpenStderr bool `json:"OpenStderr,omitempty" yaml:"OpenStderr,omitempty" toml:"OpenStderr,omitempty"`
|
||||
OpenStdout bool `json:"OpenStdout,omitempty" yaml:"OpenStdout,omitempty" toml:"OpenStdout,omitempty"`
|
||||
ProcessConfig ExecProcessConfig `json:"ProcessConfig,omitempty" yaml:"ProcessConfig,omitempty" toml:"ProcessConfig,omitempty"`
|
||||
ContainerID string `json:"ContainerID,omitempty" yaml:"ContainerID,omitempty" toml:"ContainerID,omitempty"`
|
||||
DetachKeys string `json:"DetachKeys,omitempty" yaml:"DetachKeys,omitempty" toml:"DetachKeys,omitempty"`
|
||||
CanRemove bool `json:"CanRemove,omitempty" yaml:"CanRemove,omitempty" toml:"CanRemove,omitempty"`
|
||||
}
|
||||
|
||||
// InspectExec returns low-level information about the exec command id.
|
||||
//
|
||||
// See https://goo.gl/ctMUiW for more details
|
||||
func (c *Client) InspectExec(id string) (*ExecInspect, error) {
|
||||
path := fmt.Sprintf("/exec/%s/json", id)
|
||||
resp, err := c.do("GET", path, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, &NoSuchExec{ID: id}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var exec ExecInspect
|
||||
if err := json.NewDecoder(resp.Body).Decode(&exec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &exec, nil
|
||||
}
|
||||
|
||||
// NoSuchExec is the error returned when a given exec instance does not exist.
|
||||
type NoSuchExec struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
func (err *NoSuchExec) Error() string {
|
||||
return "No such exec instance: " + err.ID
|
||||
}
|
||||
719
vendor/github.com/fsouza/go-dockerclient/image.go
generated
vendored
719
vendor/github.com/fsouza/go-dockerclient/image.go
generated
vendored
|
|
@ -1,719 +0,0 @@
|
|||
// Copyright 2013 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// APIImages represent an image returned in the ListImages call.
|
||||
type APIImages struct {
|
||||
ID string `json:"Id" yaml:"Id" toml:"Id"`
|
||||
RepoTags []string `json:"RepoTags,omitempty" yaml:"RepoTags,omitempty" toml:"RepoTags,omitempty"`
|
||||
Created int64 `json:"Created,omitempty" yaml:"Created,omitempty" toml:"Created,omitempty"`
|
||||
Size int64 `json:"Size,omitempty" yaml:"Size,omitempty" toml:"Size,omitempty"`
|
||||
VirtualSize int64 `json:"VirtualSize,omitempty" yaml:"VirtualSize,omitempty" toml:"VirtualSize,omitempty"`
|
||||
ParentID string `json:"ParentId,omitempty" yaml:"ParentId,omitempty" toml:"ParentId,omitempty"`
|
||||
RepoDigests []string `json:"RepoDigests,omitempty" yaml:"RepoDigests,omitempty" toml:"RepoDigests,omitempty"`
|
||||
Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty" toml:"Labels,omitempty"`
|
||||
}
|
||||
|
||||
// RootFS represents the underlying layers used by an image
|
||||
type RootFS struct {
|
||||
Type string `json:"Type,omitempty" yaml:"Type,omitempty" toml:"Type,omitempty"`
|
||||
Layers []string `json:"Layers,omitempty" yaml:"Layers,omitempty" toml:"Layers,omitempty"`
|
||||
}
|
||||
|
||||
// Image is the type representing a docker image and its various properties
|
||||
type Image struct {
|
||||
ID string `json:"Id" yaml:"Id" toml:"Id"`
|
||||
RepoTags []string `json:"RepoTags,omitempty" yaml:"RepoTags,omitempty" toml:"RepoTags,omitempty"`
|
||||
Parent string `json:"Parent,omitempty" yaml:"Parent,omitempty" toml:"Parent,omitempty"`
|
||||
Comment string `json:"Comment,omitempty" yaml:"Comment,omitempty" toml:"Comment,omitempty"`
|
||||
Created time.Time `json:"Created,omitempty" yaml:"Created,omitempty" toml:"Created,omitempty"`
|
||||
Container string `json:"Container,omitempty" yaml:"Container,omitempty" toml:"Container,omitempty"`
|
||||
ContainerConfig Config `json:"ContainerConfig,omitempty" yaml:"ContainerConfig,omitempty" toml:"ContainerConfig,omitempty"`
|
||||
DockerVersion string `json:"DockerVersion,omitempty" yaml:"DockerVersion,omitempty" toml:"DockerVersion,omitempty"`
|
||||
Author string `json:"Author,omitempty" yaml:"Author,omitempty" toml:"Author,omitempty"`
|
||||
Config *Config `json:"Config,omitempty" yaml:"Config,omitempty" toml:"Config,omitempty"`
|
||||
Architecture string `json:"Architecture,omitempty" yaml:"Architecture,omitempty"`
|
||||
Size int64 `json:"Size,omitempty" yaml:"Size,omitempty" toml:"Size,omitempty"`
|
||||
VirtualSize int64 `json:"VirtualSize,omitempty" yaml:"VirtualSize,omitempty" toml:"VirtualSize,omitempty"`
|
||||
RepoDigests []string `json:"RepoDigests,omitempty" yaml:"RepoDigests,omitempty" toml:"RepoDigests,omitempty"`
|
||||
RootFS *RootFS `json:"RootFS,omitempty" yaml:"RootFS,omitempty" toml:"RootFS,omitempty"`
|
||||
OS string `json:"Os,omitempty" yaml:"Os,omitempty" toml:"Os,omitempty"`
|
||||
}
|
||||
|
||||
// ImagePre012 serves the same purpose as the Image type except that it is for
|
||||
// earlier versions of the Docker API (pre-012 to be specific)
|
||||
type ImagePre012 struct {
|
||||
ID string `json:"id"`
|
||||
Parent string `json:"parent,omitempty"`
|
||||
Comment string `json:"comment,omitempty"`
|
||||
Created time.Time `json:"created"`
|
||||
Container string `json:"container,omitempty"`
|
||||
ContainerConfig Config `json:"container_config,omitempty"`
|
||||
DockerVersion string `json:"docker_version,omitempty"`
|
||||
Author string `json:"author,omitempty"`
|
||||
Config *Config `json:"config,omitempty"`
|
||||
Architecture string `json:"architecture,omitempty"`
|
||||
Size int64 `json:"size,omitempty"`
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrNoSuchImage is the error returned when the image does not exist.
|
||||
ErrNoSuchImage = errors.New("no such image")
|
||||
|
||||
// ErrMissingRepo is the error returned when the remote repository is
|
||||
// missing.
|
||||
ErrMissingRepo = errors.New("missing remote repository e.g. 'github.com/user/repo'")
|
||||
|
||||
// ErrMissingOutputStream is the error returned when no output stream
|
||||
// is provided to some calls, like BuildImage.
|
||||
ErrMissingOutputStream = errors.New("missing output stream")
|
||||
|
||||
// ErrMultipleContexts is the error returned when both a ContextDir and
|
||||
// InputStream are provided in BuildImageOptions
|
||||
ErrMultipleContexts = errors.New("image build may not be provided BOTH context dir and input stream")
|
||||
|
||||
// ErrMustSpecifyNames is the error rreturned when the Names field on
|
||||
// ExportImagesOptions is nil or empty
|
||||
ErrMustSpecifyNames = errors.New("must specify at least one name to export")
|
||||
)
|
||||
|
||||
// ListImagesOptions specify parameters to the ListImages function.
|
||||
//
|
||||
// See https://goo.gl/BVzauZ for more details.
|
||||
type ListImagesOptions struct {
|
||||
Filters map[string][]string
|
||||
All bool
|
||||
Digests bool
|
||||
Filter string
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// ListImages returns the list of available images in the server.
|
||||
//
|
||||
// See https://goo.gl/BVzauZ for more details.
|
||||
func (c *Client) ListImages(opts ListImagesOptions) ([]APIImages, error) {
|
||||
path := "/images/json?" + queryString(opts)
|
||||
resp, err := c.do("GET", path, doOptions{context: opts.Context})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var images []APIImages
|
||||
if err := json.NewDecoder(resp.Body).Decode(&images); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return images, nil
|
||||
}
|
||||
|
||||
// ImageHistory represent a layer in an image's history returned by the
|
||||
// ImageHistory call.
|
||||
type ImageHistory struct {
|
||||
ID string `json:"Id" yaml:"Id" toml:"Id"`
|
||||
Tags []string `json:"Tags,omitempty" yaml:"Tags,omitempty" toml:"Tags,omitempty"`
|
||||
Created int64 `json:"Created,omitempty" yaml:"Created,omitempty" toml:"Tags,omitempty"`
|
||||
CreatedBy string `json:"CreatedBy,omitempty" yaml:"CreatedBy,omitempty" toml:"CreatedBy,omitempty"`
|
||||
Size int64 `json:"Size,omitempty" yaml:"Size,omitempty" toml:"Size,omitempty"`
|
||||
}
|
||||
|
||||
// ImageHistory returns the history of the image by its name or ID.
|
||||
//
|
||||
// See https://goo.gl/fYtxQa for more details.
|
||||
func (c *Client) ImageHistory(name string) ([]ImageHistory, error) {
|
||||
resp, err := c.do("GET", "/images/"+name+"/history", doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, ErrNoSuchImage
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var history []ImageHistory
|
||||
if err := json.NewDecoder(resp.Body).Decode(&history); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return history, nil
|
||||
}
|
||||
|
||||
// RemoveImage removes an image by its name or ID.
|
||||
//
|
||||
// See https://goo.gl/Vd2Pck for more details.
|
||||
func (c *Client) RemoveImage(name string) error {
|
||||
resp, err := c.do("DELETE", "/images/"+name, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return ErrNoSuchImage
|
||||
}
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveImageOptions present the set of options available for removing an image
|
||||
// from a registry.
|
||||
//
|
||||
// See https://goo.gl/Vd2Pck for more details.
|
||||
type RemoveImageOptions struct {
|
||||
Force bool `qs:"force"`
|
||||
NoPrune bool `qs:"noprune"`
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// RemoveImageExtended removes an image by its name or ID.
|
||||
// Extra params can be passed, see RemoveImageOptions
|
||||
//
|
||||
// See https://goo.gl/Vd2Pck for more details.
|
||||
func (c *Client) RemoveImageExtended(name string, opts RemoveImageOptions) error {
|
||||
uri := fmt.Sprintf("/images/%s?%s", name, queryString(&opts))
|
||||
resp, err := c.do("DELETE", uri, doOptions{context: opts.Context})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return ErrNoSuchImage
|
||||
}
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// InspectImage returns an image by its name or ID.
|
||||
//
|
||||
// See https://goo.gl/ncLTG8 for more details.
|
||||
func (c *Client) InspectImage(name string) (*Image, error) {
|
||||
resp, err := c.do("GET", "/images/"+name+"/json", doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, ErrNoSuchImage
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var image Image
|
||||
|
||||
// if the caller elected to skip checking the server's version, assume it's the latest
|
||||
if c.SkipServerVersionCheck || c.expectedAPIVersion.GreaterThanOrEqualTo(apiVersion112) {
|
||||
if err := json.NewDecoder(resp.Body).Decode(&image); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
var imagePre012 ImagePre012
|
||||
if err := json.NewDecoder(resp.Body).Decode(&imagePre012); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
image.ID = imagePre012.ID
|
||||
image.Parent = imagePre012.Parent
|
||||
image.Comment = imagePre012.Comment
|
||||
image.Created = imagePre012.Created
|
||||
image.Container = imagePre012.Container
|
||||
image.ContainerConfig = imagePre012.ContainerConfig
|
||||
image.DockerVersion = imagePre012.DockerVersion
|
||||
image.Author = imagePre012.Author
|
||||
image.Config = imagePre012.Config
|
||||
image.Architecture = imagePre012.Architecture
|
||||
image.Size = imagePre012.Size
|
||||
}
|
||||
|
||||
return &image, nil
|
||||
}
|
||||
|
||||
// PushImageOptions represents options to use in the PushImage method.
|
||||
//
|
||||
// See https://goo.gl/BZemGg for more details.
|
||||
type PushImageOptions struct {
|
||||
// Name of the image
|
||||
Name string
|
||||
|
||||
// Tag of the image
|
||||
Tag string
|
||||
|
||||
// Registry server to push the image
|
||||
Registry string
|
||||
|
||||
OutputStream io.Writer `qs:"-"`
|
||||
RawJSONStream bool `qs:"-"`
|
||||
InactivityTimeout time.Duration `qs:"-"`
|
||||
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// PushImage pushes an image to a remote registry, logging progress to w.
|
||||
//
|
||||
// An empty instance of AuthConfiguration may be used for unauthenticated
|
||||
// pushes.
|
||||
//
|
||||
// See https://goo.gl/BZemGg for more details.
|
||||
func (c *Client) PushImage(opts PushImageOptions, auth AuthConfiguration) error {
|
||||
if opts.Name == "" {
|
||||
return ErrNoSuchImage
|
||||
}
|
||||
headers, err := headersWithAuth(auth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
name := opts.Name
|
||||
opts.Name = ""
|
||||
path := "/images/" + name + "/push?" + queryString(&opts)
|
||||
return c.stream("POST", path, streamOptions{
|
||||
setRawTerminal: true,
|
||||
rawJSONStream: opts.RawJSONStream,
|
||||
headers: headers,
|
||||
stdout: opts.OutputStream,
|
||||
inactivityTimeout: opts.InactivityTimeout,
|
||||
context: opts.Context,
|
||||
})
|
||||
}
|
||||
|
||||
// PullImageOptions present the set of options available for pulling an image
|
||||
// from a registry.
|
||||
//
|
||||
// See https://goo.gl/qkoSsn for more details.
|
||||
type PullImageOptions struct {
|
||||
Repository string `qs:"fromImage"`
|
||||
Tag string
|
||||
|
||||
// Only required for Docker Engine 1.9 or 1.10 w/ Remote API < 1.21
|
||||
// and Docker Engine < 1.9
|
||||
// This parameter was removed in Docker Engine 1.11
|
||||
Registry string
|
||||
|
||||
OutputStream io.Writer `qs:"-"`
|
||||
RawJSONStream bool `qs:"-"`
|
||||
InactivityTimeout time.Duration `qs:"-"`
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// PullImage pulls an image from a remote registry, logging progress to
|
||||
// opts.OutputStream.
|
||||
//
|
||||
// See https://goo.gl/qkoSsn for more details.
|
||||
func (c *Client) PullImage(opts PullImageOptions, auth AuthConfiguration) error {
|
||||
if opts.Repository == "" {
|
||||
return ErrNoSuchImage
|
||||
}
|
||||
|
||||
headers, err := headersWithAuth(auth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if opts.Tag == "" && strings.Contains(opts.Repository, "@") {
|
||||
parts := strings.SplitN(opts.Repository, "@", 2)
|
||||
opts.Repository = parts[0]
|
||||
opts.Tag = parts[1]
|
||||
}
|
||||
return c.createImage(queryString(&opts), headers, nil, opts.OutputStream, opts.RawJSONStream, opts.InactivityTimeout, opts.Context)
|
||||
}
|
||||
|
||||
func (c *Client) createImage(qs string, headers map[string]string, in io.Reader, w io.Writer, rawJSONStream bool, timeout time.Duration, context context.Context) error {
|
||||
path := "/images/create?" + qs
|
||||
return c.stream("POST", path, streamOptions{
|
||||
setRawTerminal: true,
|
||||
headers: headers,
|
||||
in: in,
|
||||
stdout: w,
|
||||
rawJSONStream: rawJSONStream,
|
||||
inactivityTimeout: timeout,
|
||||
context: context,
|
||||
})
|
||||
}
|
||||
|
||||
// LoadImageOptions represents the options for LoadImage Docker API Call
|
||||
//
|
||||
// See https://goo.gl/rEsBV3 for more details.
|
||||
type LoadImageOptions struct {
|
||||
InputStream io.Reader
|
||||
OutputStream io.Writer
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// LoadImage imports a tarball docker image
|
||||
//
|
||||
// See https://goo.gl/rEsBV3 for more details.
|
||||
func (c *Client) LoadImage(opts LoadImageOptions) error {
|
||||
return c.stream("POST", "/images/load", streamOptions{
|
||||
setRawTerminal: true,
|
||||
in: opts.InputStream,
|
||||
stdout: opts.OutputStream,
|
||||
context: opts.Context,
|
||||
})
|
||||
}
|
||||
|
||||
// ExportImageOptions represent the options for ExportImage Docker API call.
|
||||
//
|
||||
// See https://goo.gl/AuySaA for more details.
|
||||
type ExportImageOptions struct {
|
||||
Name string
|
||||
OutputStream io.Writer
|
||||
InactivityTimeout time.Duration
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// ExportImage exports an image (as a tar file) into the stream.
|
||||
//
|
||||
// See https://goo.gl/AuySaA for more details.
|
||||
func (c *Client) ExportImage(opts ExportImageOptions) error {
|
||||
return c.stream("GET", fmt.Sprintf("/images/%s/get", opts.Name), streamOptions{
|
||||
setRawTerminal: true,
|
||||
stdout: opts.OutputStream,
|
||||
inactivityTimeout: opts.InactivityTimeout,
|
||||
context: opts.Context,
|
||||
})
|
||||
}
|
||||
|
||||
// ExportImagesOptions represent the options for ExportImages Docker API call
|
||||
//
|
||||
// See https://goo.gl/N9XlDn for more details.
|
||||
type ExportImagesOptions struct {
|
||||
Names []string
|
||||
OutputStream io.Writer `qs:"-"`
|
||||
InactivityTimeout time.Duration `qs:"-"`
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// ExportImages exports one or more images (as a tar file) into the stream
|
||||
//
|
||||
// See https://goo.gl/N9XlDn for more details.
|
||||
func (c *Client) ExportImages(opts ExportImagesOptions) error {
|
||||
if opts.Names == nil || len(opts.Names) == 0 {
|
||||
return ErrMustSpecifyNames
|
||||
}
|
||||
return c.stream("GET", "/images/get?"+queryString(&opts), streamOptions{
|
||||
setRawTerminal: true,
|
||||
stdout: opts.OutputStream,
|
||||
inactivityTimeout: opts.InactivityTimeout,
|
||||
})
|
||||
}
|
||||
|
||||
// ImportImageOptions present the set of informations available for importing
|
||||
// an image from a source file or the stdin.
|
||||
//
|
||||
// See https://goo.gl/qkoSsn for more details.
|
||||
type ImportImageOptions struct {
|
||||
Repository string `qs:"repo"`
|
||||
Source string `qs:"fromSrc"`
|
||||
Tag string `qs:"tag"`
|
||||
|
||||
InputStream io.Reader `qs:"-"`
|
||||
OutputStream io.Writer `qs:"-"`
|
||||
RawJSONStream bool `qs:"-"`
|
||||
InactivityTimeout time.Duration `qs:"-"`
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// ImportImage imports an image from a url, a file or stdin
|
||||
//
|
||||
// See https://goo.gl/qkoSsn for more details.
|
||||
func (c *Client) ImportImage(opts ImportImageOptions) error {
|
||||
if opts.Repository == "" {
|
||||
return ErrNoSuchImage
|
||||
}
|
||||
if opts.Source != "-" {
|
||||
opts.InputStream = nil
|
||||
}
|
||||
if opts.Source != "-" && !isURL(opts.Source) {
|
||||
f, err := os.Open(opts.Source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
opts.InputStream = f
|
||||
opts.Source = "-"
|
||||
}
|
||||
return c.createImage(queryString(&opts), nil, opts.InputStream, opts.OutputStream, opts.RawJSONStream, opts.InactivityTimeout, opts.Context)
|
||||
}
|
||||
|
||||
// BuildImageOptions present the set of informations available for building an
|
||||
// image from a tarfile with a Dockerfile in it.
|
||||
//
|
||||
// For more details about the Docker building process, see
|
||||
// https://goo.gl/4nYHwV.
|
||||
type BuildImageOptions struct {
|
||||
Name string `qs:"t"`
|
||||
Dockerfile string `qs:"dockerfile"`
|
||||
NoCache bool `qs:"nocache"`
|
||||
CacheFrom []string `qs:"-"`
|
||||
SuppressOutput bool `qs:"q"`
|
||||
Pull bool `qs:"pull"`
|
||||
RmTmpContainer bool `qs:"rm"`
|
||||
ForceRmTmpContainer bool `qs:"forcerm"`
|
||||
RawJSONStream bool `qs:"-"`
|
||||
Memory int64 `qs:"memory"`
|
||||
Memswap int64 `qs:"memswap"`
|
||||
CPUShares int64 `qs:"cpushares"`
|
||||
CPUQuota int64 `qs:"cpuquota"`
|
||||
CPUPeriod int64 `qs:"cpuperiod"`
|
||||
CPUSetCPUs string `qs:"cpusetcpus"`
|
||||
Labels map[string]string `qs:"labels"`
|
||||
InputStream io.Reader `qs:"-"`
|
||||
OutputStream io.Writer `qs:"-"`
|
||||
Remote string `qs:"remote"`
|
||||
Auth AuthConfiguration `qs:"-"` // for older docker X-Registry-Auth header
|
||||
AuthConfigs AuthConfigurations `qs:"-"` // for newer docker X-Registry-Config header
|
||||
ContextDir string `qs:"-"`
|
||||
Ulimits []ULimit `qs:"-"`
|
||||
BuildArgs []BuildArg `qs:"-"`
|
||||
NetworkMode string `qs:"networkmode"`
|
||||
InactivityTimeout time.Duration `qs:"-"`
|
||||
CgroupParent string `qs:"cgroupparent"`
|
||||
SecurityOpt []string `qs:"securityopt"`
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// BuildArg represents arguments that can be passed to the image when building
|
||||
// it from a Dockerfile.
|
||||
//
|
||||
// For more details about the Docker building process, see
|
||||
// https://goo.gl/4nYHwV.
|
||||
type BuildArg struct {
|
||||
Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"`
|
||||
Value string `json:"Value,omitempty" yaml:"Value,omitempty" toml:"Value,omitempty"`
|
||||
}
|
||||
|
||||
// BuildImage builds an image from a tarball's url or a Dockerfile in the input
|
||||
// stream.
|
||||
//
|
||||
// See https://goo.gl/4nYHwV for more details.
|
||||
func (c *Client) BuildImage(opts BuildImageOptions) error {
|
||||
if opts.OutputStream == nil {
|
||||
return ErrMissingOutputStream
|
||||
}
|
||||
headers, err := headersWithAuth(opts.Auth, c.versionedAuthConfigs(opts.AuthConfigs))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.Remote != "" && opts.Name == "" {
|
||||
opts.Name = opts.Remote
|
||||
}
|
||||
if opts.InputStream != nil || opts.ContextDir != "" {
|
||||
headers["Content-Type"] = "application/tar"
|
||||
} else if opts.Remote == "" {
|
||||
return ErrMissingRepo
|
||||
}
|
||||
if opts.ContextDir != "" {
|
||||
if opts.InputStream != nil {
|
||||
return ErrMultipleContexts
|
||||
}
|
||||
var err error
|
||||
if opts.InputStream, err = createTarStream(opts.ContextDir, opts.Dockerfile); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
qs := queryString(&opts)
|
||||
|
||||
if c.serverAPIVersion.GreaterThanOrEqualTo(apiVersion125) && len(opts.CacheFrom) > 0 {
|
||||
if b, err := json.Marshal(opts.CacheFrom); err == nil {
|
||||
item := url.Values(map[string][]string{})
|
||||
item.Add("cachefrom", string(b))
|
||||
qs = fmt.Sprintf("%s&%s", qs, item.Encode())
|
||||
}
|
||||
}
|
||||
|
||||
if len(opts.Ulimits) > 0 {
|
||||
if b, err := json.Marshal(opts.Ulimits); err == nil {
|
||||
item := url.Values(map[string][]string{})
|
||||
item.Add("ulimits", string(b))
|
||||
qs = fmt.Sprintf("%s&%s", qs, item.Encode())
|
||||
}
|
||||
}
|
||||
|
||||
if len(opts.BuildArgs) > 0 {
|
||||
v := make(map[string]string)
|
||||
for _, arg := range opts.BuildArgs {
|
||||
v[arg.Name] = arg.Value
|
||||
}
|
||||
if b, err := json.Marshal(v); err == nil {
|
||||
item := url.Values(map[string][]string{})
|
||||
item.Add("buildargs", string(b))
|
||||
qs = fmt.Sprintf("%s&%s", qs, item.Encode())
|
||||
}
|
||||
}
|
||||
|
||||
return c.stream("POST", fmt.Sprintf("/build?%s", qs), streamOptions{
|
||||
setRawTerminal: true,
|
||||
rawJSONStream: opts.RawJSONStream,
|
||||
headers: headers,
|
||||
in: opts.InputStream,
|
||||
stdout: opts.OutputStream,
|
||||
inactivityTimeout: opts.InactivityTimeout,
|
||||
context: opts.Context,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Client) versionedAuthConfigs(authConfigs AuthConfigurations) interface{} {
|
||||
if c.serverAPIVersion == nil {
|
||||
c.checkAPIVersion()
|
||||
}
|
||||
if c.serverAPIVersion != nil && c.serverAPIVersion.GreaterThanOrEqualTo(apiVersion119) {
|
||||
return AuthConfigurations119(authConfigs.Configs)
|
||||
}
|
||||
return authConfigs
|
||||
}
|
||||
|
||||
// TagImageOptions present the set of options to tag an image.
|
||||
//
|
||||
// See https://goo.gl/prHrvo for more details.
|
||||
type TagImageOptions struct {
|
||||
Repo string
|
||||
Tag string
|
||||
Force bool
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// TagImage adds a tag to the image identified by the given name.
|
||||
//
|
||||
// See https://goo.gl/prHrvo for more details.
|
||||
func (c *Client) TagImage(name string, opts TagImageOptions) error {
|
||||
if name == "" {
|
||||
return ErrNoSuchImage
|
||||
}
|
||||
resp, err := c.do("POST", "/images/"+name+"/tag?"+queryString(&opts), doOptions{
|
||||
context: opts.Context,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
return ErrNoSuchImage
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func isURL(u string) bool {
|
||||
p, err := url.Parse(u)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return p.Scheme == "http" || p.Scheme == "https"
|
||||
}
|
||||
|
||||
func headersWithAuth(auths ...interface{}) (map[string]string, error) {
|
||||
var headers = make(map[string]string)
|
||||
|
||||
for _, auth := range auths {
|
||||
switch auth.(type) {
|
||||
case AuthConfiguration:
|
||||
var buf bytes.Buffer
|
||||
if err := json.NewEncoder(&buf).Encode(auth); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
headers["X-Registry-Auth"] = base64.URLEncoding.EncodeToString(buf.Bytes())
|
||||
case AuthConfigurations, AuthConfigurations119:
|
||||
var buf bytes.Buffer
|
||||
if err := json.NewEncoder(&buf).Encode(auth); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
headers["X-Registry-Config"] = base64.URLEncoding.EncodeToString(buf.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
return headers, nil
|
||||
}
|
||||
|
||||
// APIImageSearch reflect the result of a search on the Docker Hub.
|
||||
//
|
||||
// See https://goo.gl/KLO9IZ for more details.
|
||||
type APIImageSearch struct {
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty" toml:"description,omitempty"`
|
||||
IsOfficial bool `json:"is_official,omitempty" yaml:"is_official,omitempty" toml:"is_official,omitempty"`
|
||||
IsAutomated bool `json:"is_automated,omitempty" yaml:"is_automated,omitempty" toml:"is_automated,omitempty"`
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty" toml:"name,omitempty"`
|
||||
StarCount int `json:"star_count,omitempty" yaml:"star_count,omitempty" toml:"star_count,omitempty"`
|
||||
}
|
||||
|
||||
// SearchImages search the docker hub with a specific given term.
|
||||
//
|
||||
// See https://goo.gl/KLO9IZ for more details.
|
||||
func (c *Client) SearchImages(term string) ([]APIImageSearch, error) {
|
||||
resp, err := c.do("GET", "/images/search?term="+term, doOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var searchResult []APIImageSearch
|
||||
if err := json.NewDecoder(resp.Body).Decode(&searchResult); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return searchResult, nil
|
||||
}
|
||||
|
||||
// SearchImagesEx search the docker hub with a specific given term and authentication.
|
||||
//
|
||||
// See https://goo.gl/KLO9IZ for more details.
|
||||
func (c *Client) SearchImagesEx(term string, auth AuthConfiguration) ([]APIImageSearch, error) {
|
||||
headers, err := headersWithAuth(auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.do("GET", "/images/search?term="+term, doOptions{
|
||||
headers: headers,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
var searchResult []APIImageSearch
|
||||
if err := json.NewDecoder(resp.Body).Decode(&searchResult); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return searchResult, nil
|
||||
}
|
||||
|
||||
// PruneImagesOptions specify parameters to the PruneImages function.
|
||||
//
|
||||
// See https://goo.gl/qfZlbZ for more details.
|
||||
type PruneImagesOptions struct {
|
||||
Filters map[string][]string
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// PruneImagesResults specify results from the PruneImages function.
|
||||
//
|
||||
// See https://goo.gl/qfZlbZ for more details.
|
||||
type PruneImagesResults struct {
|
||||
ImagesDeleted []struct{ Untagged, Deleted string }
|
||||
SpaceReclaimed int64
|
||||
}
|
||||
|
||||
// PruneImages deletes images which are unused.
|
||||
//
|
||||
// See https://goo.gl/qfZlbZ for more details.
|
||||
func (c *Client) PruneImages(opts PruneImagesOptions) (*PruneImagesResults, error) {
|
||||
path := "/images/prune?" + queryString(opts)
|
||||
resp, err := c.do("POST", path, doOptions{context: opts.Context})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var results PruneImagesResults
|
||||
if err := json.NewDecoder(resp.Body).Decode(&results); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &results, nil
|
||||
}
|
||||
182
vendor/github.com/fsouza/go-dockerclient/misc.go
generated
vendored
182
vendor/github.com/fsouza/go-dockerclient/misc.go
generated
vendored
|
|
@ -1,182 +0,0 @@
|
|||
// Copyright 2013 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
)
|
||||
|
||||
// Version returns version information about the docker server.
|
||||
//
|
||||
// See https://goo.gl/mU7yje for more details.
|
||||
func (c *Client) Version() (*Env, error) {
|
||||
resp, err := c.do("GET", "/version", doOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var env Env
|
||||
if err := env.Decode(resp.Body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &env, nil
|
||||
}
|
||||
|
||||
// DockerInfo contains information about the Docker server
|
||||
//
|
||||
// See https://goo.gl/bHUoz9 for more details.
|
||||
type DockerInfo struct {
|
||||
ID string
|
||||
Containers int
|
||||
ContainersRunning int
|
||||
ContainersPaused int
|
||||
ContainersStopped int
|
||||
Images int
|
||||
Driver string
|
||||
DriverStatus [][2]string
|
||||
SystemStatus [][2]string
|
||||
Plugins PluginsInfo
|
||||
MemoryLimit bool
|
||||
SwapLimit bool
|
||||
KernelMemory bool
|
||||
CPUCfsPeriod bool `json:"CpuCfsPeriod"`
|
||||
CPUCfsQuota bool `json:"CpuCfsQuota"`
|
||||
CPUShares bool
|
||||
CPUSet bool
|
||||
IPv4Forwarding bool
|
||||
BridgeNfIptables bool
|
||||
BridgeNfIP6tables bool `json:"BridgeNfIp6tables"`
|
||||
Debug bool
|
||||
OomKillDisable bool
|
||||
ExperimentalBuild bool
|
||||
NFd int
|
||||
NGoroutines int
|
||||
SystemTime string
|
||||
ExecutionDriver string
|
||||
LoggingDriver string
|
||||
CgroupDriver string
|
||||
NEventsListener int
|
||||
KernelVersion string
|
||||
OperatingSystem string
|
||||
OSType string
|
||||
Architecture string
|
||||
IndexServerAddress string
|
||||
RegistryConfig *ServiceConfig
|
||||
SecurityOptions []string
|
||||
NCPU int
|
||||
MemTotal int64
|
||||
DockerRootDir string
|
||||
HTTPProxy string `json:"HttpProxy"`
|
||||
HTTPSProxy string `json:"HttpsProxy"`
|
||||
NoProxy string
|
||||
Name string
|
||||
Labels []string
|
||||
ServerVersion string
|
||||
ClusterStore string
|
||||
ClusterAdvertise string
|
||||
Isolation string
|
||||
InitBinary string
|
||||
DefaultRuntime string
|
||||
LiveRestoreEnabled bool
|
||||
Swarm swarm.Info
|
||||
}
|
||||
|
||||
// PluginsInfo is a struct with the plugins registered with the docker daemon
|
||||
//
|
||||
// for more information, see: https://goo.gl/bHUoz9
|
||||
type PluginsInfo struct {
|
||||
// List of Volume plugins registered
|
||||
Volume []string
|
||||
// List of Network plugins registered
|
||||
Network []string
|
||||
// List of Authorization plugins registered
|
||||
Authorization []string
|
||||
}
|
||||
|
||||
// ServiceConfig stores daemon registry services configuration.
|
||||
//
|
||||
// for more information, see: https://goo.gl/7iFFDz
|
||||
type ServiceConfig struct {
|
||||
InsecureRegistryCIDRs []*NetIPNet
|
||||
IndexConfigs map[string]*IndexInfo
|
||||
Mirrors []string
|
||||
}
|
||||
|
||||
// NetIPNet is the net.IPNet type, which can be marshalled and
|
||||
// unmarshalled to JSON.
|
||||
//
|
||||
// for more information, see: https://goo.gl/7iFFDz
|
||||
type NetIPNet net.IPNet
|
||||
|
||||
// MarshalJSON returns the JSON representation of the IPNet.
|
||||
//
|
||||
func (ipnet *NetIPNet) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal((*net.IPNet)(ipnet).String())
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets the IPNet from a byte array of JSON.
|
||||
//
|
||||
func (ipnet *NetIPNet) UnmarshalJSON(b []byte) (err error) {
|
||||
var ipnetStr string
|
||||
if err = json.Unmarshal(b, &ipnetStr); err == nil {
|
||||
var cidr *net.IPNet
|
||||
if _, cidr, err = net.ParseCIDR(ipnetStr); err == nil {
|
||||
*ipnet = NetIPNet(*cidr)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// IndexInfo contains information about a registry.
|
||||
//
|
||||
// for more information, see: https://goo.gl/7iFFDz
|
||||
type IndexInfo struct {
|
||||
Name string
|
||||
Mirrors []string
|
||||
Secure bool
|
||||
Official bool
|
||||
}
|
||||
|
||||
// Info returns system-wide information about the Docker server.
|
||||
//
|
||||
// See https://goo.gl/ElTHi2 for more details.
|
||||
func (c *Client) Info() (*DockerInfo, error) {
|
||||
resp, err := c.do("GET", "/info", doOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var info DockerInfo
|
||||
if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
// ParseRepositoryTag gets the name of the repository and returns it splitted
|
||||
// in two parts: the repository and the tag. It ignores the digest when it is
|
||||
// present.
|
||||
//
|
||||
// Some examples:
|
||||
//
|
||||
// localhost.localdomain:5000/samalba/hipache:latest -> localhost.localdomain:5000/samalba/hipache, latest
|
||||
// localhost.localdomain:5000/samalba/hipache -> localhost.localdomain:5000/samalba/hipache, ""
|
||||
// busybox:latest@sha256:4a731fb46adc5cefe3ae374a8b6020fc1b6ad667a279647766e9a3cd89f6fa92 -> busybox, latest
|
||||
func ParseRepositoryTag(repoTag string) (repository string, tag string) {
|
||||
parts := strings.SplitN(repoTag, "@", 2)
|
||||
repoTag = parts[0]
|
||||
n := strings.LastIndex(repoTag, ":")
|
||||
if n < 0 {
|
||||
return repoTag, ""
|
||||
}
|
||||
if tag := repoTag[n+1:]; !strings.Contains(tag, "/") {
|
||||
return repoTag[:n], tag
|
||||
}
|
||||
return repoTag, ""
|
||||
}
|
||||
321
vendor/github.com/fsouza/go-dockerclient/network.go
generated
vendored
321
vendor/github.com/fsouza/go-dockerclient/network.go
generated
vendored
|
|
@ -1,321 +0,0 @@
|
|||
// Copyright 2015 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ErrNetworkAlreadyExists is the error returned by CreateNetwork when the
|
||||
// network already exists.
|
||||
var ErrNetworkAlreadyExists = errors.New("network already exists")
|
||||
|
||||
// Network represents a network.
|
||||
//
|
||||
// See https://goo.gl/6GugX3 for more details.
|
||||
type Network struct {
|
||||
Name string
|
||||
ID string `json:"Id"`
|
||||
Scope string
|
||||
Driver string
|
||||
IPAM IPAMOptions
|
||||
Containers map[string]Endpoint
|
||||
Options map[string]string
|
||||
Internal bool
|
||||
EnableIPv6 bool `json:"EnableIPv6"`
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
// Endpoint contains network resources allocated and used for a container in a network
|
||||
//
|
||||
// See https://goo.gl/6GugX3 for more details.
|
||||
type Endpoint struct {
|
||||
Name string
|
||||
ID string `json:"EndpointID"`
|
||||
MacAddress string
|
||||
IPv4Address string
|
||||
IPv6Address string
|
||||
}
|
||||
|
||||
// ListNetworks returns all networks.
|
||||
//
|
||||
// See https://goo.gl/6GugX3 for more details.
|
||||
func (c *Client) ListNetworks() ([]Network, error) {
|
||||
resp, err := c.do("GET", "/networks", doOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var networks []Network
|
||||
if err := json.NewDecoder(resp.Body).Decode(&networks); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return networks, nil
|
||||
}
|
||||
|
||||
// NetworkFilterOpts is an aggregation of key=value that Docker
|
||||
// uses to filter networks
|
||||
type NetworkFilterOpts map[string]map[string]bool
|
||||
|
||||
// FilteredListNetworks returns all networks with the filters applied
|
||||
//
|
||||
// See goo.gl/zd2mx4 for more details.
|
||||
func (c *Client) FilteredListNetworks(opts NetworkFilterOpts) ([]Network, error) {
|
||||
params, err := json.Marshal(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path := "/networks?filters=" + string(params)
|
||||
resp, err := c.do("GET", path, doOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var networks []Network
|
||||
if err := json.NewDecoder(resp.Body).Decode(&networks); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return networks, nil
|
||||
}
|
||||
|
||||
// NetworkInfo returns information about a network by its ID.
|
||||
//
|
||||
// See https://goo.gl/6GugX3 for more details.
|
||||
func (c *Client) NetworkInfo(id string) (*Network, error) {
|
||||
path := "/networks/" + id
|
||||
resp, err := c.do("GET", path, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, &NoSuchNetwork{ID: id}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var network Network
|
||||
if err := json.NewDecoder(resp.Body).Decode(&network); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &network, nil
|
||||
}
|
||||
|
||||
// CreateNetworkOptions specify parameters to the CreateNetwork function and
|
||||
// (for now) is the expected body of the "create network" http request message
|
||||
//
|
||||
// See https://goo.gl/6GugX3 for more details.
|
||||
type CreateNetworkOptions struct {
|
||||
Name string `json:"Name" yaml:"Name" toml:"Name"`
|
||||
Driver string `json:"Driver" yaml:"Driver" toml:"Driver"`
|
||||
IPAM *IPAMOptions `json:"IPAM,omitempty" yaml:"IPAM" toml:"IPAM"`
|
||||
Options map[string]interface{} `json:"Options" yaml:"Options" toml:"Options"`
|
||||
Labels map[string]string `json:"Labels" yaml:"Labels" toml:"Labels"`
|
||||
CheckDuplicate bool `json:"CheckDuplicate" yaml:"CheckDuplicate" toml:"CheckDuplicate"`
|
||||
Internal bool `json:"Internal" yaml:"Internal" toml:"Internal"`
|
||||
EnableIPv6 bool `json:"EnableIPv6" yaml:"EnableIPv6" toml:"EnableIPv6"`
|
||||
Context context.Context `json:"-"`
|
||||
}
|
||||
|
||||
// IPAMOptions controls IP Address Management when creating a network
|
||||
//
|
||||
// See https://goo.gl/T8kRVH for more details.
|
||||
type IPAMOptions struct {
|
||||
Driver string `json:"Driver" yaml:"Driver" toml:"Driver"`
|
||||
Config []IPAMConfig `json:"Config" yaml:"Config" toml:"Config"`
|
||||
Options map[string]string `json:"Options" yaml:"Options" toml:"Options"`
|
||||
}
|
||||
|
||||
// IPAMConfig represents IPAM configurations
|
||||
//
|
||||
// See https://goo.gl/T8kRVH for more details.
|
||||
type IPAMConfig struct {
|
||||
Subnet string `json:",omitempty"`
|
||||
IPRange string `json:",omitempty"`
|
||||
Gateway string `json:",omitempty"`
|
||||
AuxAddress map[string]string `json:"AuxiliaryAddresses,omitempty"`
|
||||
}
|
||||
|
||||
// CreateNetwork creates a new network, returning the network instance,
|
||||
// or an error in case of failure.
|
||||
//
|
||||
// See https://goo.gl/6GugX3 for more details.
|
||||
func (c *Client) CreateNetwork(opts CreateNetworkOptions) (*Network, error) {
|
||||
resp, err := c.do(
|
||||
"POST",
|
||||
"/networks/create",
|
||||
doOptions{
|
||||
data: opts,
|
||||
context: opts.Context,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
type createNetworkResponse struct {
|
||||
ID string
|
||||
}
|
||||
var (
|
||||
network Network
|
||||
cnr createNetworkResponse
|
||||
)
|
||||
if err := json.NewDecoder(resp.Body).Decode(&cnr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
network.Name = opts.Name
|
||||
network.ID = cnr.ID
|
||||
network.Driver = opts.Driver
|
||||
|
||||
return &network, nil
|
||||
}
|
||||
|
||||
// RemoveNetwork removes a network or returns an error in case of failure.
|
||||
//
|
||||
// See https://goo.gl/6GugX3 for more details.
|
||||
func (c *Client) RemoveNetwork(id string) error {
|
||||
resp, err := c.do("DELETE", "/networks/"+id, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return &NoSuchNetwork{ID: id}
|
||||
}
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// NetworkConnectionOptions specify parameters to the ConnectNetwork and
|
||||
// DisconnectNetwork function.
|
||||
//
|
||||
// See https://goo.gl/RV7BJU for more details.
|
||||
type NetworkConnectionOptions struct {
|
||||
Container string
|
||||
|
||||
// EndpointConfig is only applicable to the ConnectNetwork call
|
||||
EndpointConfig *EndpointConfig `json:"EndpointConfig,omitempty"`
|
||||
|
||||
// Force is only applicable to the DisconnectNetwork call
|
||||
Force bool
|
||||
|
||||
Context context.Context `json:"-"`
|
||||
}
|
||||
|
||||
// EndpointConfig stores network endpoint details
|
||||
//
|
||||
// See https://goo.gl/RV7BJU for more details.
|
||||
type EndpointConfig struct {
|
||||
IPAMConfig *EndpointIPAMConfig `json:"IPAMConfig,omitempty" yaml:"IPAMConfig,omitempty" toml:"IPAMConfig,omitempty"`
|
||||
Links []string `json:"Links,omitempty" yaml:"Links,omitempty" toml:"Links,omitempty"`
|
||||
Aliases []string `json:"Aliases,omitempty" yaml:"Aliases,omitempty" toml:"Aliases,omitempty"`
|
||||
NetworkID string `json:"NetworkID,omitempty" yaml:"NetworkID,omitempty" toml:"NetworkID,omitempty"`
|
||||
EndpointID string `json:"EndpointID,omitempty" yaml:"EndpointID,omitempty" toml:"EndpointID,omitempty"`
|
||||
Gateway string `json:"Gateway,omitempty" yaml:"Gateway,omitempty" toml:"Gateway,omitempty"`
|
||||
IPAddress string `json:"IPAddress,omitempty" yaml:"IPAddress,omitempty" toml:"IPAddress,omitempty"`
|
||||
IPPrefixLen int `json:"IPPrefixLen,omitempty" yaml:"IPPrefixLen,omitempty" toml:"IPPrefixLen,omitempty"`
|
||||
IPv6Gateway string `json:"IPv6Gateway,omitempty" yaml:"IPv6Gateway,omitempty" toml:"IPv6Gateway,omitempty"`
|
||||
GlobalIPv6Address string `json:"GlobalIPv6Address,omitempty" yaml:"GlobalIPv6Address,omitempty" toml:"GlobalIPv6Address,omitempty"`
|
||||
GlobalIPv6PrefixLen int `json:"GlobalIPv6PrefixLen,omitempty" yaml:"GlobalIPv6PrefixLen,omitempty" toml:"GlobalIPv6PrefixLen,omitempty"`
|
||||
MacAddress string `json:"MacAddress,omitempty" yaml:"MacAddress,omitempty" toml:"MacAddress,omitempty"`
|
||||
}
|
||||
|
||||
// EndpointIPAMConfig represents IPAM configurations for an
|
||||
// endpoint
|
||||
//
|
||||
// See https://goo.gl/RV7BJU for more details.
|
||||
type EndpointIPAMConfig struct {
|
||||
IPv4Address string `json:",omitempty"`
|
||||
IPv6Address string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// ConnectNetwork adds a container to a network or returns an error in case of
|
||||
// failure.
|
||||
//
|
||||
// See https://goo.gl/6GugX3 for more details.
|
||||
func (c *Client) ConnectNetwork(id string, opts NetworkConnectionOptions) error {
|
||||
resp, err := c.do("POST", "/networks/"+id+"/connect", doOptions{
|
||||
data: opts,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return &NoSuchNetworkOrContainer{NetworkID: id, ContainerID: opts.Container}
|
||||
}
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// DisconnectNetwork removes a container from a network or returns an error in
|
||||
// case of failure.
|
||||
//
|
||||
// See https://goo.gl/6GugX3 for more details.
|
||||
func (c *Client) DisconnectNetwork(id string, opts NetworkConnectionOptions) error {
|
||||
resp, err := c.do("POST", "/networks/"+id+"/disconnect", doOptions{data: opts})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return &NoSuchNetworkOrContainer{NetworkID: id, ContainerID: opts.Container}
|
||||
}
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// PruneNetworksOptions specify parameters to the PruneNetworks function.
|
||||
//
|
||||
// See https://goo.gl/kX0S9h for more details.
|
||||
type PruneNetworksOptions struct {
|
||||
Filters map[string][]string
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// PruneNetworksResults specify results from the PruneNetworks function.
|
||||
//
|
||||
// See https://goo.gl/kX0S9h for more details.
|
||||
type PruneNetworksResults struct {
|
||||
NetworksDeleted []string
|
||||
}
|
||||
|
||||
// PruneNetworks deletes networks which are unused.
|
||||
//
|
||||
// See https://goo.gl/kX0S9h for more details.
|
||||
func (c *Client) PruneNetworks(opts PruneNetworksOptions) (*PruneNetworksResults, error) {
|
||||
path := "/networks/prune?" + queryString(opts)
|
||||
resp, err := c.do("POST", path, doOptions{context: opts.Context})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var results PruneNetworksResults
|
||||
if err := json.NewDecoder(resp.Body).Decode(&results); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &results, nil
|
||||
}
|
||||
|
||||
// NoSuchNetwork is the error returned when a given network does not exist.
|
||||
type NoSuchNetwork struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
func (err *NoSuchNetwork) Error() string {
|
||||
return fmt.Sprintf("No such network: %s", err.ID)
|
||||
}
|
||||
|
||||
// NoSuchNetworkOrContainer is the error returned when a given network or
|
||||
// container does not exist.
|
||||
type NoSuchNetworkOrContainer struct {
|
||||
NetworkID string
|
||||
ContainerID string
|
||||
}
|
||||
|
||||
func (err *NoSuchNetworkOrContainer) Error() string {
|
||||
return fmt.Sprintf("No such network (%s) or container (%s)", err.NetworkID, err.ContainerID)
|
||||
}
|
||||
391
vendor/github.com/fsouza/go-dockerclient/plugin.go
generated
vendored
391
vendor/github.com/fsouza/go-dockerclient/plugin.go
generated
vendored
|
|
@ -1,391 +0,0 @@
|
|||
// Copyright 2018 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// PluginPrivilege represents a privilege for a plugin.
|
||||
type PluginPrivilege struct {
|
||||
Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"`
|
||||
Description string `json:"Description,omitempty" yaml:"Description,omitempty" toml:"Description,omitempty"`
|
||||
Value []string `json:"Value,omitempty" yaml:"Value,omitempty" toml:"Value,omitempty"`
|
||||
}
|
||||
|
||||
// InstallPluginOptions specify parameters to the InstallPlugins function.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
type InstallPluginOptions struct {
|
||||
Remote string
|
||||
Name string
|
||||
Plugins []PluginPrivilege `qs:"-"`
|
||||
|
||||
Auth AuthConfiguration
|
||||
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// InstallPlugins installs a plugin or returns an error in case of failure.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
func (c *Client) InstallPlugins(opts InstallPluginOptions) error {
|
||||
path := "/plugins/pull?" + queryString(opts)
|
||||
resp, err := c.do("POST", path, doOptions{
|
||||
data: opts.Plugins,
|
||||
context: opts.Context,
|
||||
})
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PluginSettings stores plugin settings.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
type PluginSettings struct {
|
||||
Env []string `json:"Env,omitempty" yaml:"Env,omitempty" toml:"Env,omitempty"`
|
||||
Args []string `json:"Args,omitempty" yaml:"Args,omitempty" toml:"Args,omitempty"`
|
||||
Devices []string `json:"Devices,omitempty" yaml:"Devices,omitempty" toml:"Devices,omitempty"`
|
||||
}
|
||||
|
||||
// PluginInterface stores plugin interface.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
type PluginInterface struct {
|
||||
Types []string `json:"Types,omitempty" yaml:"Types,omitempty" toml:"Types,omitempty"`
|
||||
Socket string `json:"Socket,omitempty" yaml:"Socket,omitempty" toml:"Socket,omitempty"`
|
||||
}
|
||||
|
||||
// PluginNetwork stores plugin network type.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
type PluginNetwork struct {
|
||||
Type string `json:"Type,omitempty" yaml:"Type,omitempty" toml:"Type,omitempty"`
|
||||
}
|
||||
|
||||
// PluginLinux stores plugin linux setting.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
type PluginLinux struct {
|
||||
Capabilities []string `json:"Capabilities,omitempty" yaml:"Capabilities,omitempty" toml:"Capabilities,omitempty"`
|
||||
AllowAllDevices bool `json:"AllowAllDevices,omitempty" yaml:"AllowAllDevices,omitempty" toml:"AllowAllDevices,omitempty"`
|
||||
Devices []PluginLinuxDevices `json:"Devices,omitempty" yaml:"Devices,omitempty" toml:"Devices,omitempty"`
|
||||
}
|
||||
|
||||
// PluginLinuxDevices stores plugin linux device setting.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
type PluginLinuxDevices struct {
|
||||
Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"`
|
||||
Description string `json:"Documentation,omitempty" yaml:"Documentation,omitempty" toml:"Documentation,omitempty"`
|
||||
Settable []string `json:"Settable,omitempty" yaml:"Settable,omitempty" toml:"Settable,omitempty"`
|
||||
Path string `json:"Path,omitempty" yaml:"Path,omitempty" toml:"Path,omitempty"`
|
||||
}
|
||||
|
||||
// PluginEnv stores plugin environment.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
type PluginEnv struct {
|
||||
Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"`
|
||||
Description string `json:"Description,omitempty" yaml:"Description,omitempty" toml:"Description,omitempty"`
|
||||
Settable []string `json:"Settable,omitempty" yaml:"Settable,omitempty" toml:"Settable,omitempty"`
|
||||
Value string `json:"Value,omitempty" yaml:"Value,omitempty" toml:"Value,omitempty"`
|
||||
}
|
||||
|
||||
// PluginArgs stores plugin arguments.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
type PluginArgs struct {
|
||||
Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"`
|
||||
Description string `json:"Description,omitempty" yaml:"Description,omitempty" toml:"Description,omitempty"`
|
||||
Settable []string `json:"Settable,omitempty" yaml:"Settable,omitempty" toml:"Settable,omitempty"`
|
||||
Value []string `json:"Value,omitempty" yaml:"Value,omitempty" toml:"Value,omitempty"`
|
||||
}
|
||||
|
||||
// PluginUser stores plugin user.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
type PluginUser struct {
|
||||
UID int32 `json:"UID,omitempty" yaml:"UID,omitempty" toml:"UID,omitempty"`
|
||||
GID int32 `json:"GID,omitempty" yaml:"GID,omitempty" toml:"GID,omitempty"`
|
||||
}
|
||||
|
||||
// PluginConfig stores plugin config.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
type PluginConfig struct {
|
||||
Description string `json:"Description,omitempty" yaml:"Description,omitempty" toml:"Description,omitempty"`
|
||||
Documentation string
|
||||
Interface PluginInterface `json:"Interface,omitempty" yaml:"Interface,omitempty" toml:"Interface,omitempty"`
|
||||
Entrypoint []string `json:"Entrypoint,omitempty" yaml:"Entrypoint,omitempty" toml:"Entrypoint,omitempty"`
|
||||
WorkDir string `json:"WorkDir,omitempty" yaml:"WorkDir,omitempty" toml:"WorkDir,omitempty"`
|
||||
User PluginUser `json:"User,omitempty" yaml:"User,omitempty" toml:"User,omitempty"`
|
||||
Network PluginNetwork `json:"Network,omitempty" yaml:"Network,omitempty" toml:"Network,omitempty"`
|
||||
Linux PluginLinux `json:"Linux,omitempty" yaml:"Linux,omitempty" toml:"Linux,omitempty"`
|
||||
PropagatedMount string `json:"PropagatedMount,omitempty" yaml:"PropagatedMount,omitempty" toml:"PropagatedMount,omitempty"`
|
||||
Mounts []Mount `json:"Mounts,omitempty" yaml:"Mounts,omitempty" toml:"Mounts,omitempty"`
|
||||
Env []PluginEnv `json:"Env,omitempty" yaml:"Env,omitempty" toml:"Env,omitempty"`
|
||||
Args PluginArgs `json:"Args,omitempty" yaml:"Args,omitempty" toml:"Args,omitempty"`
|
||||
}
|
||||
|
||||
// PluginDetail specify results from the ListPlugins function.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
type PluginDetail struct {
|
||||
ID string `json:"Id,omitempty" yaml:"Id,omitempty" toml:"Id,omitempty"`
|
||||
Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"`
|
||||
Tag string `json:"Tag,omitempty" yaml:"Tag,omitempty" toml:"Tag,omitempty"`
|
||||
Active bool `json:"Active,omitempty" yaml:"Active,omitempty" toml:"Active,omitempty"`
|
||||
Settings PluginSettings `json:"Settings,omitempty" yaml:"Settings,omitempty" toml:"Settings,omitempty"`
|
||||
Config PluginConfig `json:"Config,omitempty" yaml:"Config,omitempty" toml:"Config,omitempty"`
|
||||
}
|
||||
|
||||
// ListPlugins returns pluginDetails or an error.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
func (c *Client) ListPlugins(ctx context.Context) ([]PluginDetail, error) {
|
||||
resp, err := c.do("GET", "/plugins", doOptions{
|
||||
context: ctx,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
pluginDetails := make([]PluginDetail, 0)
|
||||
if err := json.NewDecoder(resp.Body).Decode(&pluginDetails); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pluginDetails, nil
|
||||
}
|
||||
|
||||
// GetPluginPrivileges returns pulginPrivileges or an error.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
func (c *Client) GetPluginPrivileges(name string, ctx context.Context) ([]PluginPrivilege, error) {
|
||||
resp, err := c.do("GET", "/plugins/privileges?remote="+name, doOptions{
|
||||
context: ctx,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var pluginPrivileges []PluginPrivilege
|
||||
if err := json.NewDecoder(resp.Body).Decode(&pluginPrivileges); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pluginPrivileges, nil
|
||||
}
|
||||
|
||||
// InspectPlugins returns a pluginDetail or an error.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
func (c *Client) InspectPlugins(name string, ctx context.Context) (*PluginDetail, error) {
|
||||
resp, err := c.do("GET", "/plugins/"+name+"/json", doOptions{
|
||||
context: ctx,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, &NoSuchPlugin{ID: name}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
var pluginDetail PluginDetail
|
||||
if err := json.NewDecoder(resp.Body).Decode(&pluginDetail); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pluginDetail, nil
|
||||
}
|
||||
|
||||
// RemovePluginOptions specify parameters to the RemovePlugin function.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
type RemovePluginOptions struct {
|
||||
// The Name of the plugin.
|
||||
Name string `qs:"-"`
|
||||
|
||||
Force bool `qs:"force"`
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// RemovePlugin returns a PluginDetail or an error.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
func (c *Client) RemovePlugin(opts RemovePluginOptions) (*PluginDetail, error) {
|
||||
path := "/plugins/" + opts.Name + "?" + queryString(opts)
|
||||
resp, err := c.do("DELETE", path, doOptions{context: opts.Context})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, &NoSuchPlugin{ID: opts.Name}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
var pluginDetail PluginDetail
|
||||
if err := json.NewDecoder(resp.Body).Decode(&pluginDetail); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pluginDetail, nil
|
||||
}
|
||||
|
||||
// EnablePluginOptions specify parameters to the EnablePlugin function.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
type EnablePluginOptions struct {
|
||||
// The Name of the plugin.
|
||||
Name string `qs:"-"`
|
||||
Timeout int64 `qs:"timeout"`
|
||||
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// EnablePlugin enables plugin that opts point or returns an error.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
func (c *Client) EnablePlugin(opts EnablePluginOptions) error {
|
||||
path := "/plugins/" + opts.Name + "/enable?" + queryString(opts)
|
||||
resp, err := c.do("POST", path, doOptions{context: opts.Context})
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// DisablePluginOptions specify parameters to the DisablePlugin function.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
type DisablePluginOptions struct {
|
||||
// The Name of the plugin.
|
||||
Name string `qs:"-"`
|
||||
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// DisablePlugin disables plugin that opts point or returns an error.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
func (c *Client) DisablePlugin(opts DisablePluginOptions) error {
|
||||
path := "/plugins/" + opts.Name + "/disable"
|
||||
resp, err := c.do("POST", path, doOptions{context: opts.Context})
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreatePluginOptions specify parameters to the CreatePlugin function.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
type CreatePluginOptions struct {
|
||||
// The Name of the plugin.
|
||||
Name string `qs:"name"`
|
||||
// Path to tar containing plugin
|
||||
Path string `qs:"-"`
|
||||
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// CreatePlugin creates plugin that opts point or returns an error.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
func (c *Client) CreatePlugin(opts CreatePluginOptions) (string, error) {
|
||||
path := "/plugins/create?" + queryString(opts)
|
||||
resp, err := c.do("POST", path, doOptions{
|
||||
data: opts.Path,
|
||||
context: opts.Context})
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
containerNameBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(containerNameBytes), nil
|
||||
}
|
||||
|
||||
// PushPluginOptions specify parameters to PushPlugin function.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
type PushPluginOptions struct {
|
||||
// The Name of the plugin.
|
||||
Name string
|
||||
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// PushPlugin pushes plugin that opts point or returns an error.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
func (c *Client) PushPlugin(opts PushPluginOptions) error {
|
||||
path := "/plugins/" + opts.Name + "/push"
|
||||
resp, err := c.do("POST", path, doOptions{context: opts.Context})
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigurePluginOptions specify parameters to the ConfigurePlugin
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
type ConfigurePluginOptions struct {
|
||||
// The Name of the plugin.
|
||||
Name string `qs:"name"`
|
||||
Envs []string
|
||||
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// ConfigurePlugin configures plugin that opts point or returns an error.
|
||||
//
|
||||
// See https://goo.gl/C4t7Tz for more details.
|
||||
func (c *Client) ConfigurePlugin(opts ConfigurePluginOptions) error {
|
||||
path := "/plugins/" + opts.Name + "/set"
|
||||
resp, err := c.do("POST", path, doOptions{
|
||||
data: opts.Envs,
|
||||
context: opts.Context,
|
||||
})
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return &NoSuchPlugin{ID: opts.Name}
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NoSuchPlugin is the error returned when a given plugin does not exist.
|
||||
type NoSuchPlugin struct {
|
||||
ID string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (err *NoSuchPlugin) Error() string {
|
||||
if err.Err != nil {
|
||||
return err.Err.Error()
|
||||
}
|
||||
return "No such plugin: " + err.ID
|
||||
}
|
||||
49
vendor/github.com/fsouza/go-dockerclient/signal.go
generated
vendored
49
vendor/github.com/fsouza/go-dockerclient/signal.go
generated
vendored
|
|
@ -1,49 +0,0 @@
|
|||
// Copyright 2014 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
// Signal represents a signal that can be send to the container on
|
||||
// KillContainer call.
|
||||
type Signal int
|
||||
|
||||
// These values represent all signals available on Linux, where containers will
|
||||
// be running.
|
||||
const (
|
||||
SIGABRT = Signal(0x6)
|
||||
SIGALRM = Signal(0xe)
|
||||
SIGBUS = Signal(0x7)
|
||||
SIGCHLD = Signal(0x11)
|
||||
SIGCLD = Signal(0x11)
|
||||
SIGCONT = Signal(0x12)
|
||||
SIGFPE = Signal(0x8)
|
||||
SIGHUP = Signal(0x1)
|
||||
SIGILL = Signal(0x4)
|
||||
SIGINT = Signal(0x2)
|
||||
SIGIO = Signal(0x1d)
|
||||
SIGIOT = Signal(0x6)
|
||||
SIGKILL = Signal(0x9)
|
||||
SIGPIPE = Signal(0xd)
|
||||
SIGPOLL = Signal(0x1d)
|
||||
SIGPROF = Signal(0x1b)
|
||||
SIGPWR = Signal(0x1e)
|
||||
SIGQUIT = Signal(0x3)
|
||||
SIGSEGV = Signal(0xb)
|
||||
SIGSTKFLT = Signal(0x10)
|
||||
SIGSTOP = Signal(0x13)
|
||||
SIGSYS = Signal(0x1f)
|
||||
SIGTERM = Signal(0xf)
|
||||
SIGTRAP = Signal(0x5)
|
||||
SIGTSTP = Signal(0x14)
|
||||
SIGTTIN = Signal(0x15)
|
||||
SIGTTOU = Signal(0x16)
|
||||
SIGUNUSED = Signal(0x1f)
|
||||
SIGURG = Signal(0x17)
|
||||
SIGUSR1 = Signal(0xa)
|
||||
SIGUSR2 = Signal(0xc)
|
||||
SIGVTALRM = Signal(0x1a)
|
||||
SIGWINCH = Signal(0x1c)
|
||||
SIGXCPU = Signal(0x18)
|
||||
SIGXFSZ = Signal(0x19)
|
||||
)
|
||||
156
vendor/github.com/fsouza/go-dockerclient/swarm.go
generated
vendored
156
vendor/github.com/fsouza/go-dockerclient/swarm.go
generated
vendored
|
|
@ -1,156 +0,0 @@
|
|||
// Copyright 2016 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNodeAlreadyInSwarm is the error returned by InitSwarm and JoinSwarm
|
||||
// when the node is already part of a Swarm.
|
||||
ErrNodeAlreadyInSwarm = errors.New("node already in a Swarm")
|
||||
|
||||
// ErrNodeNotInSwarm is the error returned by LeaveSwarm and UpdateSwarm
|
||||
// when the node is not part of a Swarm.
|
||||
ErrNodeNotInSwarm = errors.New("node is not in a Swarm")
|
||||
)
|
||||
|
||||
// InitSwarmOptions specify parameters to the InitSwarm function.
|
||||
// See https://goo.gl/hzkgWu for more details.
|
||||
type InitSwarmOptions struct {
|
||||
swarm.InitRequest
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// InitSwarm initializes a new Swarm and returns the node ID.
|
||||
// See https://goo.gl/ZWyG1M for more details.
|
||||
func (c *Client) InitSwarm(opts InitSwarmOptions) (string, error) {
|
||||
path := "/swarm/init"
|
||||
resp, err := c.do("POST", path, doOptions{
|
||||
data: opts.InitRequest,
|
||||
forceJSON: true,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) {
|
||||
return "", ErrNodeAlreadyInSwarm
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var response string
|
||||
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// JoinSwarmOptions specify parameters to the JoinSwarm function.
|
||||
// See https://goo.gl/TdhJWU for more details.
|
||||
type JoinSwarmOptions struct {
|
||||
swarm.JoinRequest
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// JoinSwarm joins an existing Swarm.
|
||||
// See https://goo.gl/N59IP1 for more details.
|
||||
func (c *Client) JoinSwarm(opts JoinSwarmOptions) error {
|
||||
path := "/swarm/join"
|
||||
resp, err := c.do("POST", path, doOptions{
|
||||
data: opts.JoinRequest,
|
||||
forceJSON: true,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) {
|
||||
return ErrNodeAlreadyInSwarm
|
||||
}
|
||||
}
|
||||
resp.Body.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
// LeaveSwarmOptions specify parameters to the LeaveSwarm function.
|
||||
// See https://goo.gl/UWDlLg for more details.
|
||||
type LeaveSwarmOptions struct {
|
||||
Force bool
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// LeaveSwarm leaves a Swarm.
|
||||
// See https://goo.gl/FTX1aD for more details.
|
||||
func (c *Client) LeaveSwarm(opts LeaveSwarmOptions) error {
|
||||
params := make(url.Values)
|
||||
params.Set("force", strconv.FormatBool(opts.Force))
|
||||
path := "/swarm/leave?" + params.Encode()
|
||||
resp, err := c.do("POST", path, doOptions{
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) {
|
||||
return ErrNodeNotInSwarm
|
||||
}
|
||||
}
|
||||
resp.Body.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateSwarmOptions specify parameters to the UpdateSwarm function.
|
||||
// See https://goo.gl/vFbq36 for more details.
|
||||
type UpdateSwarmOptions struct {
|
||||
Version int
|
||||
RotateWorkerToken bool
|
||||
RotateManagerToken bool
|
||||
Swarm swarm.Spec
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// UpdateSwarm updates a Swarm.
|
||||
// See https://goo.gl/iJFnsw for more details.
|
||||
func (c *Client) UpdateSwarm(opts UpdateSwarmOptions) error {
|
||||
params := make(url.Values)
|
||||
params.Set("version", strconv.Itoa(opts.Version))
|
||||
params.Set("rotateWorkerToken", strconv.FormatBool(opts.RotateWorkerToken))
|
||||
params.Set("rotateManagerToken", strconv.FormatBool(opts.RotateManagerToken))
|
||||
path := "/swarm/update?" + params.Encode()
|
||||
resp, err := c.do("POST", path, doOptions{
|
||||
data: opts.Swarm,
|
||||
forceJSON: true,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) {
|
||||
return ErrNodeNotInSwarm
|
||||
}
|
||||
}
|
||||
resp.Body.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
// InspectSwarm inspects a Swarm.
|
||||
// See https://goo.gl/MFwgX9 for more details.
|
||||
func (c *Client) InspectSwarm(ctx context.Context) (swarm.Swarm, error) {
|
||||
response := swarm.Swarm{}
|
||||
resp, err := c.do("GET", "/swarm", doOptions{
|
||||
context: ctx,
|
||||
})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) {
|
||||
return response, ErrNodeNotInSwarm
|
||||
}
|
||||
return response, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||
return response, err
|
||||
}
|
||||
171
vendor/github.com/fsouza/go-dockerclient/swarm_configs.go
generated
vendored
171
vendor/github.com/fsouza/go-dockerclient/swarm_configs.go
generated
vendored
|
|
@ -1,171 +0,0 @@
|
|||
// Copyright 2017 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
)
|
||||
|
||||
// NoSuchConfig is the error returned when a given config does not exist.
|
||||
type NoSuchConfig struct {
|
||||
ID string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (err *NoSuchConfig) Error() string {
|
||||
if err.Err != nil {
|
||||
return err.Err.Error()
|
||||
}
|
||||
return "No such config: " + err.ID
|
||||
}
|
||||
|
||||
// CreateConfigOptions specify parameters to the CreateConfig function.
|
||||
//
|
||||
// See https://goo.gl/KrVjHz for more details.
|
||||
type CreateConfigOptions struct {
|
||||
Auth AuthConfiguration `qs:"-"`
|
||||
swarm.ConfigSpec
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// CreateConfig creates a new config, returning the config instance
|
||||
// or an error in case of failure.
|
||||
//
|
||||
// See https://goo.gl/KrVjHz for more details.
|
||||
func (c *Client) CreateConfig(opts CreateConfigOptions) (*swarm.Config, error) {
|
||||
headers, err := headersWithAuth(opts.Auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path := "/configs/create?" + queryString(opts)
|
||||
resp, err := c.do("POST", path, doOptions{
|
||||
headers: headers,
|
||||
data: opts.ConfigSpec,
|
||||
forceJSON: true,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var config swarm.Config
|
||||
if err := json.NewDecoder(resp.Body).Decode(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// RemoveConfigOptions encapsulates options to remove a config.
|
||||
//
|
||||
// See https://goo.gl/Tqrtya for more details.
|
||||
type RemoveConfigOptions struct {
|
||||
ID string `qs:"-"`
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// RemoveConfig removes a config, returning an error in case of failure.
|
||||
//
|
||||
// See https://goo.gl/Tqrtya for more details.
|
||||
func (c *Client) RemoveConfig(opts RemoveConfigOptions) error {
|
||||
path := "/configs/" + opts.ID
|
||||
resp, err := c.do("DELETE", path, doOptions{context: opts.Context})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return &NoSuchConfig{ID: opts.ID}
|
||||
}
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateConfigOptions specify parameters to the UpdateConfig function.
|
||||
//
|
||||
// See https://goo.gl/wu3MmS for more details.
|
||||
type UpdateConfigOptions struct {
|
||||
Auth AuthConfiguration `qs:"-"`
|
||||
swarm.ConfigSpec
|
||||
Context context.Context
|
||||
Version uint64
|
||||
}
|
||||
|
||||
// UpdateConfig updates the config at ID with the options
|
||||
//
|
||||
// Only label can be updated
|
||||
// https://docs.docker.com/engine/api/v1.33/#operation/ConfigUpdate
|
||||
// See https://goo.gl/wu3MmS for more details.
|
||||
func (c *Client) UpdateConfig(id string, opts UpdateConfigOptions) error {
|
||||
headers, err := headersWithAuth(opts.Auth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params := make(url.Values)
|
||||
params.Set("version", strconv.FormatUint(opts.Version, 10))
|
||||
resp, err := c.do("POST", "/configs/"+id+"/update?"+params.Encode(), doOptions{
|
||||
headers: headers,
|
||||
data: opts.ConfigSpec,
|
||||
forceJSON: true,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return &NoSuchConfig{ID: id}
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// InspectConfig returns information about a config by its ID.
|
||||
//
|
||||
// See https://goo.gl/dHmr75 for more details.
|
||||
func (c *Client) InspectConfig(id string) (*swarm.Config, error) {
|
||||
path := "/configs/" + id
|
||||
resp, err := c.do("GET", path, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, &NoSuchConfig{ID: id}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var config swarm.Config
|
||||
if err := json.NewDecoder(resp.Body).Decode(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// ListConfigsOptions specify parameters to the ListConfigs function.
|
||||
//
|
||||
// See https://goo.gl/DwvNMd for more details.
|
||||
type ListConfigsOptions struct {
|
||||
Filters map[string][]string
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// ListConfigs returns a slice of configs matching the given criteria.
|
||||
//
|
||||
// See https://goo.gl/DwvNMd for more details.
|
||||
func (c *Client) ListConfigs(opts ListConfigsOptions) ([]swarm.Config, error) {
|
||||
path := "/configs?" + queryString(opts)
|
||||
resp, err := c.do("GET", path, doOptions{context: opts.Context})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var configs []swarm.Config
|
||||
if err := json.NewDecoder(resp.Body).Decode(&configs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return configs, nil
|
||||
}
|
||||
130
vendor/github.com/fsouza/go-dockerclient/swarm_node.go
generated
vendored
130
vendor/github.com/fsouza/go-dockerclient/swarm_node.go
generated
vendored
|
|
@ -1,130 +0,0 @@
|
|||
// Copyright 2016 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
)
|
||||
|
||||
// NoSuchNode is the error returned when a given node does not exist.
|
||||
type NoSuchNode struct {
|
||||
ID string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (err *NoSuchNode) Error() string {
|
||||
if err.Err != nil {
|
||||
return err.Err.Error()
|
||||
}
|
||||
return "No such node: " + err.ID
|
||||
}
|
||||
|
||||
// ListNodesOptions specify parameters to the ListNodes function.
|
||||
//
|
||||
// See http://goo.gl/3K4GwU for more details.
|
||||
type ListNodesOptions struct {
|
||||
Filters map[string][]string
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// ListNodes returns a slice of nodes matching the given criteria.
|
||||
//
|
||||
// See http://goo.gl/3K4GwU for more details.
|
||||
func (c *Client) ListNodes(opts ListNodesOptions) ([]swarm.Node, error) {
|
||||
path := "/nodes?" + queryString(opts)
|
||||
resp, err := c.do("GET", path, doOptions{context: opts.Context})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var nodes []swarm.Node
|
||||
if err := json.NewDecoder(resp.Body).Decode(&nodes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
// InspectNode returns information about a node by its ID.
|
||||
//
|
||||
// See http://goo.gl/WjkTOk for more details.
|
||||
func (c *Client) InspectNode(id string) (*swarm.Node, error) {
|
||||
resp, err := c.do("GET", "/nodes/"+id, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, &NoSuchNode{ID: id}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var node swarm.Node
|
||||
if err := json.NewDecoder(resp.Body).Decode(&node); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &node, nil
|
||||
}
|
||||
|
||||
// UpdateNodeOptions specify parameters to the NodeUpdate function.
|
||||
//
|
||||
// See http://goo.gl/VPBFgA for more details.
|
||||
type UpdateNodeOptions struct {
|
||||
swarm.NodeSpec
|
||||
Version uint64
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// UpdateNode updates a node.
|
||||
//
|
||||
// See http://goo.gl/VPBFgA for more details.
|
||||
func (c *Client) UpdateNode(id string, opts UpdateNodeOptions) error {
|
||||
params := make(url.Values)
|
||||
params.Set("version", strconv.FormatUint(opts.Version, 10))
|
||||
path := "/nodes/" + id + "/update?" + params.Encode()
|
||||
resp, err := c.do("POST", path, doOptions{
|
||||
context: opts.Context,
|
||||
forceJSON: true,
|
||||
data: opts.NodeSpec,
|
||||
})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return &NoSuchNode{ID: id}
|
||||
}
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveNodeOptions specify parameters to the RemoveNode function.
|
||||
//
|
||||
// See http://goo.gl/0SNvYg for more details.
|
||||
type RemoveNodeOptions struct {
|
||||
ID string
|
||||
Force bool
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// RemoveNode removes a node.
|
||||
//
|
||||
// See http://goo.gl/0SNvYg for more details.
|
||||
func (c *Client) RemoveNode(opts RemoveNodeOptions) error {
|
||||
params := make(url.Values)
|
||||
params.Set("force", strconv.FormatBool(opts.Force))
|
||||
path := "/nodes/" + opts.ID + "?" + params.Encode()
|
||||
resp, err := c.do("DELETE", path, doOptions{context: opts.Context})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return &NoSuchNode{ID: opts.ID}
|
||||
}
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
171
vendor/github.com/fsouza/go-dockerclient/swarm_secrets.go
generated
vendored
171
vendor/github.com/fsouza/go-dockerclient/swarm_secrets.go
generated
vendored
|
|
@ -1,171 +0,0 @@
|
|||
// Copyright 2016 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
)
|
||||
|
||||
// NoSuchSecret is the error returned when a given secret does not exist.
|
||||
type NoSuchSecret struct {
|
||||
ID string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (err *NoSuchSecret) Error() string {
|
||||
if err.Err != nil {
|
||||
return err.Err.Error()
|
||||
}
|
||||
return "No such secret: " + err.ID
|
||||
}
|
||||
|
||||
// CreateSecretOptions specify parameters to the CreateSecret function.
|
||||
//
|
||||
// See https://goo.gl/KrVjHz for more details.
|
||||
type CreateSecretOptions struct {
|
||||
Auth AuthConfiguration `qs:"-"`
|
||||
swarm.SecretSpec
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// CreateSecret creates a new secret, returning the secret instance
|
||||
// or an error in case of failure.
|
||||
//
|
||||
// See https://goo.gl/KrVjHz for more details.
|
||||
func (c *Client) CreateSecret(opts CreateSecretOptions) (*swarm.Secret, error) {
|
||||
headers, err := headersWithAuth(opts.Auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path := "/secrets/create?" + queryString(opts)
|
||||
resp, err := c.do("POST", path, doOptions{
|
||||
headers: headers,
|
||||
data: opts.SecretSpec,
|
||||
forceJSON: true,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var secret swarm.Secret
|
||||
if err := json.NewDecoder(resp.Body).Decode(&secret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &secret, nil
|
||||
}
|
||||
|
||||
// RemoveSecretOptions encapsulates options to remove a secret.
|
||||
//
|
||||
// See https://goo.gl/Tqrtya for more details.
|
||||
type RemoveSecretOptions struct {
|
||||
ID string `qs:"-"`
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// RemoveSecret removes a secret, returning an error in case of failure.
|
||||
//
|
||||
// See https://goo.gl/Tqrtya for more details.
|
||||
func (c *Client) RemoveSecret(opts RemoveSecretOptions) error {
|
||||
path := "/secrets/" + opts.ID
|
||||
resp, err := c.do("DELETE", path, doOptions{context: opts.Context})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return &NoSuchSecret{ID: opts.ID}
|
||||
}
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateSecretOptions specify parameters to the UpdateSecret function.
|
||||
//
|
||||
// Only label can be updated
|
||||
// See https://docs.docker.com/engine/api/v1.33/#operation/SecretUpdate
|
||||
// See https://goo.gl/wu3MmS for more details.
|
||||
type UpdateSecretOptions struct {
|
||||
Auth AuthConfiguration `qs:"-"`
|
||||
swarm.SecretSpec
|
||||
Context context.Context
|
||||
Version uint64
|
||||
}
|
||||
|
||||
// UpdateSecret updates the secret at ID with the options
|
||||
//
|
||||
// See https://goo.gl/wu3MmS for more details.
|
||||
func (c *Client) UpdateSecret(id string, opts UpdateSecretOptions) error {
|
||||
headers, err := headersWithAuth(opts.Auth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params := make(url.Values)
|
||||
params.Set("version", strconv.FormatUint(opts.Version, 10))
|
||||
resp, err := c.do("POST", "/secrets/"+id+"/update?"+params.Encode(), doOptions{
|
||||
headers: headers,
|
||||
data: opts.SecretSpec,
|
||||
forceJSON: true,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return &NoSuchSecret{ID: id}
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// InspectSecret returns information about a secret by its ID.
|
||||
//
|
||||
// See https://goo.gl/dHmr75 for more details.
|
||||
func (c *Client) InspectSecret(id string) (*swarm.Secret, error) {
|
||||
path := "/secrets/" + id
|
||||
resp, err := c.do("GET", path, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, &NoSuchSecret{ID: id}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var secret swarm.Secret
|
||||
if err := json.NewDecoder(resp.Body).Decode(&secret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &secret, nil
|
||||
}
|
||||
|
||||
// ListSecretsOptions specify parameters to the ListSecrets function.
|
||||
//
|
||||
// See https://goo.gl/DwvNMd for more details.
|
||||
type ListSecretsOptions struct {
|
||||
Filters map[string][]string
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// ListSecrets returns a slice of secrets matching the given criteria.
|
||||
//
|
||||
// See https://goo.gl/DwvNMd for more details.
|
||||
func (c *Client) ListSecrets(opts ListSecretsOptions) ([]swarm.Secret, error) {
|
||||
path := "/secrets?" + queryString(opts)
|
||||
resp, err := c.do("GET", path, doOptions{context: opts.Context})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var secrets []swarm.Secret
|
||||
if err := json.NewDecoder(resp.Body).Decode(&secrets); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return secrets, nil
|
||||
}
|
||||
213
vendor/github.com/fsouza/go-dockerclient/swarm_service.go
generated
vendored
213
vendor/github.com/fsouza/go-dockerclient/swarm_service.go
generated
vendored
|
|
@ -1,213 +0,0 @@
|
|||
// Copyright 2016 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
)
|
||||
|
||||
// NoSuchService is the error returned when a given service does not exist.
|
||||
type NoSuchService struct {
|
||||
ID string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (err *NoSuchService) Error() string {
|
||||
if err.Err != nil {
|
||||
return err.Err.Error()
|
||||
}
|
||||
return "No such service: " + err.ID
|
||||
}
|
||||
|
||||
// CreateServiceOptions specify parameters to the CreateService function.
|
||||
//
|
||||
// See https://goo.gl/KrVjHz for more details.
|
||||
type CreateServiceOptions struct {
|
||||
Auth AuthConfiguration `qs:"-"`
|
||||
swarm.ServiceSpec
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// CreateService creates a new service, returning the service instance
|
||||
// or an error in case of failure.
|
||||
//
|
||||
// See https://goo.gl/KrVjHz for more details.
|
||||
func (c *Client) CreateService(opts CreateServiceOptions) (*swarm.Service, error) {
|
||||
headers, err := headersWithAuth(opts.Auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path := "/services/create?" + queryString(opts)
|
||||
resp, err := c.do("POST", path, doOptions{
|
||||
headers: headers,
|
||||
data: opts.ServiceSpec,
|
||||
forceJSON: true,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var service swarm.Service
|
||||
if err := json.NewDecoder(resp.Body).Decode(&service); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &service, nil
|
||||
}
|
||||
|
||||
// RemoveServiceOptions encapsulates options to remove a service.
|
||||
//
|
||||
// See https://goo.gl/Tqrtya for more details.
|
||||
type RemoveServiceOptions struct {
|
||||
ID string `qs:"-"`
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// RemoveService removes a service, returning an error in case of failure.
|
||||
//
|
||||
// See https://goo.gl/Tqrtya for more details.
|
||||
func (c *Client) RemoveService(opts RemoveServiceOptions) error {
|
||||
path := "/services/" + opts.ID
|
||||
resp, err := c.do("DELETE", path, doOptions{context: opts.Context})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return &NoSuchService{ID: opts.ID}
|
||||
}
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateServiceOptions specify parameters to the UpdateService function.
|
||||
//
|
||||
// See https://goo.gl/wu3MmS for more details.
|
||||
type UpdateServiceOptions struct {
|
||||
Auth AuthConfiguration `qs:"-"`
|
||||
swarm.ServiceSpec `qs:"-"`
|
||||
Context context.Context
|
||||
Version uint64
|
||||
Rollback string
|
||||
}
|
||||
|
||||
// UpdateService updates the service at ID with the options
|
||||
//
|
||||
// See https://goo.gl/wu3MmS for more details.
|
||||
func (c *Client) UpdateService(id string, opts UpdateServiceOptions) error {
|
||||
headers, err := headersWithAuth(opts.Auth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := c.do("POST", "/services/"+id+"/update?"+queryString(opts), doOptions{
|
||||
headers: headers,
|
||||
data: opts.ServiceSpec,
|
||||
forceJSON: true,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return &NoSuchService{ID: id}
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// InspectService returns information about a service by its ID.
|
||||
//
|
||||
// See https://goo.gl/dHmr75 for more details.
|
||||
func (c *Client) InspectService(id string) (*swarm.Service, error) {
|
||||
path := "/services/" + id
|
||||
resp, err := c.do("GET", path, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, &NoSuchService{ID: id}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var service swarm.Service
|
||||
if err := json.NewDecoder(resp.Body).Decode(&service); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &service, nil
|
||||
}
|
||||
|
||||
// ListServicesOptions specify parameters to the ListServices function.
|
||||
//
|
||||
// See https://goo.gl/DwvNMd for more details.
|
||||
type ListServicesOptions struct {
|
||||
Filters map[string][]string
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// ListServices returns a slice of services matching the given criteria.
|
||||
//
|
||||
// See https://goo.gl/DwvNMd for more details.
|
||||
func (c *Client) ListServices(opts ListServicesOptions) ([]swarm.Service, error) {
|
||||
path := "/services?" + queryString(opts)
|
||||
resp, err := c.do("GET", path, doOptions{context: opts.Context})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var services []swarm.Service
|
||||
if err := json.NewDecoder(resp.Body).Decode(&services); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return services, nil
|
||||
}
|
||||
|
||||
// LogsServiceOptions represents the set of options used when getting logs from a
|
||||
// service.
|
||||
type LogsServiceOptions struct {
|
||||
Context context.Context
|
||||
Service string `qs:"-"`
|
||||
OutputStream io.Writer `qs:"-"`
|
||||
ErrorStream io.Writer `qs:"-"`
|
||||
InactivityTimeout time.Duration `qs:"-"`
|
||||
Tail string
|
||||
|
||||
// Use raw terminal? Usually true when the container contains a TTY.
|
||||
RawTerminal bool `qs:"-"`
|
||||
Since int64
|
||||
Follow bool
|
||||
Stdout bool
|
||||
Stderr bool
|
||||
Timestamps bool
|
||||
Details bool
|
||||
}
|
||||
|
||||
// GetServiceLogs gets stdout and stderr logs from the specified service.
|
||||
//
|
||||
// When LogsServiceOptions.RawTerminal is set to false, go-dockerclient will multiplex
|
||||
// the streams and send the containers stdout to LogsServiceOptions.OutputStream, and
|
||||
// stderr to LogsServiceOptions.ErrorStream.
|
||||
//
|
||||
// When LogsServiceOptions.RawTerminal is true, callers will get the raw stream on
|
||||
// LogsServiceOptions.OutputStream.
|
||||
func (c *Client) GetServiceLogs(opts LogsServiceOptions) error {
|
||||
if opts.Service == "" {
|
||||
return &NoSuchService{ID: opts.Service}
|
||||
}
|
||||
if opts.Tail == "" {
|
||||
opts.Tail = "all"
|
||||
}
|
||||
path := "/services/" + opts.Service + "/logs?" + queryString(opts)
|
||||
return c.stream("GET", path, streamOptions{
|
||||
setRawTerminal: opts.RawTerminal,
|
||||
stdout: opts.OutputStream,
|
||||
stderr: opts.ErrorStream,
|
||||
inactivityTimeout: opts.InactivityTimeout,
|
||||
context: opts.Context,
|
||||
})
|
||||
}
|
||||
70
vendor/github.com/fsouza/go-dockerclient/swarm_task.go
generated
vendored
70
vendor/github.com/fsouza/go-dockerclient/swarm_task.go
generated
vendored
|
|
@ -1,70 +0,0 @@
|
|||
// Copyright 2016 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
)
|
||||
|
||||
// NoSuchTask is the error returned when a given task does not exist.
|
||||
type NoSuchTask struct {
|
||||
ID string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (err *NoSuchTask) Error() string {
|
||||
if err.Err != nil {
|
||||
return err.Err.Error()
|
||||
}
|
||||
return "No such task: " + err.ID
|
||||
}
|
||||
|
||||
// ListTasksOptions specify parameters to the ListTasks function.
|
||||
//
|
||||
// See http://goo.gl/rByLzw for more details.
|
||||
type ListTasksOptions struct {
|
||||
Filters map[string][]string
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// ListTasks returns a slice of tasks matching the given criteria.
|
||||
//
|
||||
// See http://goo.gl/rByLzw for more details.
|
||||
func (c *Client) ListTasks(opts ListTasksOptions) ([]swarm.Task, error) {
|
||||
path := "/tasks?" + queryString(opts)
|
||||
resp, err := c.do("GET", path, doOptions{context: opts.Context})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var tasks []swarm.Task
|
||||
if err := json.NewDecoder(resp.Body).Decode(&tasks); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tasks, nil
|
||||
}
|
||||
|
||||
// InspectTask returns information about a task by its ID.
|
||||
//
|
||||
// See http://goo.gl/kyziuq for more details.
|
||||
func (c *Client) InspectTask(id string) (*swarm.Task, error) {
|
||||
resp, err := c.do("GET", "/tasks/"+id, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, &NoSuchTask{ID: id}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var task swarm.Task
|
||||
if err := json.NewDecoder(resp.Body).Decode(&task); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &task, nil
|
||||
}
|
||||
122
vendor/github.com/fsouza/go-dockerclient/tar.go
generated
vendored
122
vendor/github.com/fsouza/go-dockerclient/tar.go
generated
vendored
|
|
@ -1,122 +0,0 @@
|
|||
// Copyright 2014 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/fileutils"
|
||||
)
|
||||
|
||||
func createTarStream(srcPath, dockerfilePath string) (io.ReadCloser, error) {
|
||||
srcPath, err := filepath.Abs(srcPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
excludes, err := parseDockerignore(srcPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
includes := []string{"."}
|
||||
|
||||
// If .dockerignore mentions .dockerignore or the Dockerfile
|
||||
// then make sure we send both files over to the daemon
|
||||
// because Dockerfile is, obviously, needed no matter what, and
|
||||
// .dockerignore is needed to know if either one needs to be
|
||||
// removed. The deamon will remove them for us, if needed, after it
|
||||
// parses the Dockerfile.
|
||||
//
|
||||
// https://github.com/docker/docker/issues/8330
|
||||
//
|
||||
forceIncludeFiles := []string{".dockerignore", dockerfilePath}
|
||||
|
||||
for _, includeFile := range forceIncludeFiles {
|
||||
if includeFile == "" {
|
||||
continue
|
||||
}
|
||||
keepThem, err := fileutils.Matches(includeFile, excludes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot match .dockerfile: '%s', error: %s", includeFile, err)
|
||||
}
|
||||
if keepThem {
|
||||
includes = append(includes, includeFile)
|
||||
}
|
||||
}
|
||||
|
||||
if err := validateContextDirectory(srcPath, excludes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tarOpts := &archive.TarOptions{
|
||||
ExcludePatterns: excludes,
|
||||
IncludeFiles: includes,
|
||||
Compression: archive.Uncompressed,
|
||||
NoLchown: true,
|
||||
}
|
||||
return archive.TarWithOptions(srcPath, tarOpts)
|
||||
}
|
||||
|
||||
// validateContextDirectory checks if all the contents of the directory
|
||||
// can be read and returns an error if some files can't be read.
|
||||
// Symlinks which point to non-existing files don't trigger an error
|
||||
func validateContextDirectory(srcPath string, excludes []string) error {
|
||||
return filepath.Walk(filepath.Join(srcPath, "."), func(filePath string, f os.FileInfo, err error) error {
|
||||
// skip this directory/file if it's not in the path, it won't get added to the context
|
||||
if relFilePath, relErr := filepath.Rel(srcPath, filePath); relErr != nil {
|
||||
return relErr
|
||||
} else if skip, matchErr := fileutils.Matches(relFilePath, excludes); matchErr != nil {
|
||||
return matchErr
|
||||
} else if skip {
|
||||
if f.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if os.IsPermission(err) {
|
||||
return fmt.Errorf("can't stat '%s'", filePath)
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// skip checking if symlinks point to non-existing files, such symlinks can be useful
|
||||
// also skip named pipes, because they hanging on open
|
||||
if f.Mode()&(os.ModeSymlink|os.ModeNamedPipe) != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !f.IsDir() {
|
||||
currentFile, err := os.Open(filePath)
|
||||
if err != nil && os.IsPermission(err) {
|
||||
return fmt.Errorf("no permission to read from '%s'", filePath)
|
||||
}
|
||||
currentFile.Close()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func parseDockerignore(root string) ([]string, error) {
|
||||
var excludes []string
|
||||
ignore, err := ioutil.ReadFile(path.Join(root, ".dockerignore"))
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return excludes, fmt.Errorf("error reading .dockerignore: '%s'", err)
|
||||
}
|
||||
excludes = strings.Split(string(ignore), "\n")
|
||||
|
||||
return excludes, nil
|
||||
}
|
||||
118
vendor/github.com/fsouza/go-dockerclient/tls.go
generated
vendored
118
vendor/github.com/fsouza/go-dockerclient/tls.go
generated
vendored
|
|
@ -1,118 +0,0 @@
|
|||
// Copyright 2014 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
//
|
||||
// The content is borrowed from Docker's own source code to provide a simple
|
||||
// tls based dialer
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type tlsClientCon struct {
|
||||
*tls.Conn
|
||||
rawConn net.Conn
|
||||
}
|
||||
|
||||
func (c *tlsClientCon) CloseWrite() error {
|
||||
// Go standard tls.Conn doesn't provide the CloseWrite() method so we do it
|
||||
// on its underlying connection.
|
||||
if cwc, ok := c.rawConn.(interface {
|
||||
CloseWrite() error
|
||||
}); ok {
|
||||
return cwc.CloseWrite()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func tlsDialWithDialer(dialer *net.Dialer, network, addr string, config *tls.Config) (net.Conn, error) {
|
||||
// We want the Timeout and Deadline values from dialer to cover the
|
||||
// whole process: TCP connection and TLS handshake. This means that we
|
||||
// also need to start our own timers now.
|
||||
timeout := dialer.Timeout
|
||||
|
||||
if !dialer.Deadline.IsZero() {
|
||||
deadlineTimeout := dialer.Deadline.Sub(time.Now())
|
||||
if timeout == 0 || deadlineTimeout < timeout {
|
||||
timeout = deadlineTimeout
|
||||
}
|
||||
}
|
||||
|
||||
var errChannel chan error
|
||||
|
||||
if timeout != 0 {
|
||||
errChannel = make(chan error, 2)
|
||||
time.AfterFunc(timeout, func() {
|
||||
errChannel <- errors.New("")
|
||||
})
|
||||
}
|
||||
|
||||
rawConn, err := dialer.Dial(network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
colonPos := strings.LastIndex(addr, ":")
|
||||
if colonPos == -1 {
|
||||
colonPos = len(addr)
|
||||
}
|
||||
hostname := addr[:colonPos]
|
||||
|
||||
// If no ServerName is set, infer the ServerName
|
||||
// from the hostname we're connecting to.
|
||||
if config.ServerName == "" {
|
||||
// Make a copy to avoid polluting argument or default.
|
||||
config = copyTLSConfig(config)
|
||||
config.ServerName = hostname
|
||||
}
|
||||
|
||||
conn := tls.Client(rawConn, config)
|
||||
|
||||
if timeout == 0 {
|
||||
err = conn.Handshake()
|
||||
} else {
|
||||
go func() {
|
||||
errChannel <- conn.Handshake()
|
||||
}()
|
||||
|
||||
err = <-errChannel
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
rawConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// This is Docker difference with standard's crypto/tls package: returned a
|
||||
// wrapper which holds both the TLS and raw connections.
|
||||
return &tlsClientCon{conn, rawConn}, nil
|
||||
}
|
||||
|
||||
// this exists to silent an error message in go vet
|
||||
func copyTLSConfig(cfg *tls.Config) *tls.Config {
|
||||
return &tls.Config{
|
||||
Certificates: cfg.Certificates,
|
||||
CipherSuites: cfg.CipherSuites,
|
||||
ClientAuth: cfg.ClientAuth,
|
||||
ClientCAs: cfg.ClientCAs,
|
||||
ClientSessionCache: cfg.ClientSessionCache,
|
||||
CurvePreferences: cfg.CurvePreferences,
|
||||
InsecureSkipVerify: cfg.InsecureSkipVerify,
|
||||
MaxVersion: cfg.MaxVersion,
|
||||
MinVersion: cfg.MinVersion,
|
||||
NameToCertificate: cfg.NameToCertificate,
|
||||
NextProtos: cfg.NextProtos,
|
||||
PreferServerCipherSuites: cfg.PreferServerCipherSuites,
|
||||
Rand: cfg.Rand,
|
||||
RootCAs: cfg.RootCAs,
|
||||
ServerName: cfg.ServerName,
|
||||
SessionTicketKey: cfg.SessionTicketKey,
|
||||
SessionTicketsDisabled: cfg.SessionTicketsDisabled,
|
||||
}
|
||||
}
|
||||
190
vendor/github.com/fsouza/go-dockerclient/volume.go
generated
vendored
190
vendor/github.com/fsouza/go-dockerclient/volume.go
generated
vendored
|
|
@ -1,190 +0,0 @@
|
|||
// Copyright 2015 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNoSuchVolume is the error returned when the volume does not exist.
|
||||
ErrNoSuchVolume = errors.New("no such volume")
|
||||
|
||||
// ErrVolumeInUse is the error returned when the volume requested to be removed is still in use.
|
||||
ErrVolumeInUse = errors.New("volume in use and cannot be removed")
|
||||
)
|
||||
|
||||
// Volume represents a volume.
|
||||
//
|
||||
// See https://goo.gl/3wgTsd for more details.
|
||||
type Volume struct {
|
||||
Name string `json:"Name" yaml:"Name" toml:"Name"`
|
||||
Driver string `json:"Driver,omitempty" yaml:"Driver,omitempty" toml:"Driver,omitempty"`
|
||||
Mountpoint string `json:"Mountpoint,omitempty" yaml:"Mountpoint,omitempty" toml:"Mountpoint,omitempty"`
|
||||
Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty" toml:"Labels,omitempty"`
|
||||
Options map[string]string `json:"Options,omitempty" yaml:"Options,omitempty" toml:"Options,omitempty"`
|
||||
}
|
||||
|
||||
// ListVolumesOptions specify parameters to the ListVolumes function.
|
||||
//
|
||||
// See https://goo.gl/3wgTsd for more details.
|
||||
type ListVolumesOptions struct {
|
||||
Filters map[string][]string
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// ListVolumes returns a list of available volumes in the server.
|
||||
//
|
||||
// See https://goo.gl/3wgTsd for more details.
|
||||
func (c *Client) ListVolumes(opts ListVolumesOptions) ([]Volume, error) {
|
||||
resp, err := c.do("GET", "/volumes?"+queryString(opts), doOptions{
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
m := make(map[string]interface{})
|
||||
if err = json.NewDecoder(resp.Body).Decode(&m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var volumes []Volume
|
||||
volumesJSON, ok := m["Volumes"]
|
||||
if !ok {
|
||||
return volumes, nil
|
||||
}
|
||||
data, err := json.Marshal(volumesJSON)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.Unmarshal(data, &volumes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return volumes, nil
|
||||
}
|
||||
|
||||
// CreateVolumeOptions specify parameters to the CreateVolume function.
|
||||
//
|
||||
// See https://goo.gl/qEhmEC for more details.
|
||||
type CreateVolumeOptions struct {
|
||||
Name string
|
||||
Driver string
|
||||
DriverOpts map[string]string
|
||||
Context context.Context `json:"-"`
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
// CreateVolume creates a volume on the server.
|
||||
//
|
||||
// See https://goo.gl/qEhmEC for more details.
|
||||
func (c *Client) CreateVolume(opts CreateVolumeOptions) (*Volume, error) {
|
||||
resp, err := c.do("POST", "/volumes/create", doOptions{
|
||||
data: opts,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var volume Volume
|
||||
if err := json.NewDecoder(resp.Body).Decode(&volume); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &volume, nil
|
||||
}
|
||||
|
||||
// InspectVolume returns a volume by its name.
|
||||
//
|
||||
// See https://goo.gl/GMjsMc for more details.
|
||||
func (c *Client) InspectVolume(name string) (*Volume, error) {
|
||||
resp, err := c.do("GET", "/volumes/"+name, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, ErrNoSuchVolume
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var volume Volume
|
||||
if err := json.NewDecoder(resp.Body).Decode(&volume); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &volume, nil
|
||||
}
|
||||
|
||||
// RemoveVolume removes a volume by its name.
|
||||
//
|
||||
// Deprecated: Use RemoveVolumeWithOptions instead.
|
||||
func (c *Client) RemoveVolume(name string) error {
|
||||
return c.RemoveVolumeWithOptions(RemoveVolumeOptions{Name: name})
|
||||
}
|
||||
|
||||
// RemoveVolumeOptions specify parameters to the RemoveVolumeWithOptions
|
||||
// function.
|
||||
//
|
||||
// See https://goo.gl/nvd6qj for more details.
|
||||
type RemoveVolumeOptions struct {
|
||||
Context context.Context
|
||||
Name string `qs:"-"`
|
||||
Force bool
|
||||
}
|
||||
|
||||
// RemoveVolumeWithOptions removes a volume by its name and takes extra
|
||||
// parameters.
|
||||
//
|
||||
// See https://goo.gl/nvd6qj for more details.
|
||||
func (c *Client) RemoveVolumeWithOptions(opts RemoveVolumeOptions) error {
|
||||
path := "/volumes/" + opts.Name
|
||||
resp, err := c.do("DELETE", path+"?"+queryString(opts), doOptions{context: opts.Context})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok {
|
||||
if e.Status == http.StatusNotFound {
|
||||
return ErrNoSuchVolume
|
||||
}
|
||||
if e.Status == http.StatusConflict {
|
||||
return ErrVolumeInUse
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// PruneVolumesOptions specify parameters to the PruneVolumes function.
|
||||
//
|
||||
// See https://goo.gl/f9XDem for more details.
|
||||
type PruneVolumesOptions struct {
|
||||
Filters map[string][]string
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// PruneVolumesResults specify results from the PruneVolumes function.
|
||||
//
|
||||
// See https://goo.gl/f9XDem for more details.
|
||||
type PruneVolumesResults struct {
|
||||
VolumesDeleted []string
|
||||
SpaceReclaimed int64
|
||||
}
|
||||
|
||||
// PruneVolumes deletes volumes which are unused.
|
||||
//
|
||||
// See https://goo.gl/f9XDem for more details.
|
||||
func (c *Client) PruneVolumes(opts PruneVolumesOptions) (*PruneVolumesResults, error) {
|
||||
path := "/volumes/prune?" + queryString(opts)
|
||||
resp, err := c.do("POST", path, doOptions{context: opts.Context})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var results PruneVolumesResults
|
||||
if err := json.NewDecoder(resp.Body).Decode(&results); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &results, nil
|
||||
}
|
||||
32
vendor/vendor.json
vendored
32
vendor/vendor.json
vendored
|
|
@ -351,6 +351,7 @@
|
|||
"path": "github.com/docker/docker/api/types/swarm/runtime",
|
||||
"revision": "65bd038fc5e47ed37d2702cbdd6ce484d320380b",
|
||||
"revisionTime": "2018-05-30T01:28:07Z"
|
||||
<<<<<<< HEAD
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "V8OZpLS8i1HfO4lAzUzEw2BgnfQ=",
|
||||
|
|
@ -359,10 +360,21 @@
|
|||
"revisionTime": "2018-05-30T01:28:07Z"
|
||||
},
|
||||
{
|
||||
=======
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "V8OZpLS8i1HfO4lAzUzEw2BgnfQ=",
|
||||
"path": "github.com/docker/docker/api/types/time",
|
||||
"revision": "65bd038fc5e47ed37d2702cbdd6ce484d320380b",
|
||||
"revisionTime": "2018-05-30T01:28:07Z"
|
||||
},
|
||||
{
|
||||
>>>>>>> d96333876b7267be943761aa5586c6f65aed1533
|
||||
"checksumSHA1": "MZsgRjJJ0D/gAsXfKiEys+op6dE=",
|
||||
"path": "github.com/docker/docker/api/types/versions",
|
||||
"revision": "65bd038fc5e47ed37d2702cbdd6ce484d320380b",
|
||||
"revisionTime": "2018-05-30T01:28:07Z"
|
||||
<<<<<<< HEAD
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "BjgxujsDKAzTeQR97H7ojww7BjM=",
|
||||
|
|
@ -379,12 +391,29 @@
|
|||
{
|
||||
"checksumSHA1": "u6EOrZRfhdjr4up14b2JJ7MMMaY=",
|
||||
"path": "github.com/docker/docker/opts",
|
||||
=======
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "BjgxujsDKAzTeQR97H7ojww7BjM=",
|
||||
"path": "github.com/docker/docker/api/types/volume",
|
||||
"revision": "65bd038fc5e47ed37d2702cbdd6ce484d320380b",
|
||||
"revisionTime": "2018-05-30T01:28:07Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "6CnC2OQhh5uzaYoftbN+VKDMyyE=",
|
||||
"path": "github.com/docker/docker/client",
|
||||
>>>>>>> d96333876b7267be943761aa5586c6f65aed1533
|
||||
"revision": "65bd038fc5e47ed37d2702cbdd6ce484d320380b",
|
||||
"revisionTime": "2018-05-30T01:28:07Z"
|
||||
},
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
"checksumSHA1": "XcUgUwnG9cMKIvwl8UGuUsXc0CA=",
|
||||
"path": "github.com/docker/docker/pkg/archive",
|
||||
=======
|
||||
"checksumSHA1": "u6EOrZRfhdjr4up14b2JJ7MMMaY=",
|
||||
"path": "github.com/docker/docker/opts",
|
||||
>>>>>>> d96333876b7267be943761aa5586c6f65aed1533
|
||||
"revision": "65bd038fc5e47ed37d2702cbdd6ce484d320380b",
|
||||
"revisionTime": "2018-05-30T01:28:07Z"
|
||||
},
|
||||
|
|
@ -489,12 +518,15 @@
|
|||
"path": "github.com/docker/libnetwork/ipamutils",
|
||||
"revision": "a355c4bd0900178ebd6f435ee55d0ab4f712f506",
|
||||
"revisionTime": "2018-05-29T16:43:54Z"
|
||||
<<<<<<< HEAD
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "LxmlsftDto7ZuwfNmJRdKPah1t8=",
|
||||
"path": "github.com/fsouza/go-dockerclient",
|
||||
"revision": "bf3bc17bb026cbe5e9203374f9a1ce37e521a364",
|
||||
"revisionTime": "2018-03-03T17:22:32Z"
|
||||
=======
|
||||
>>>>>>> d96333876b7267be943761aa5586c6f65aed1533
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "1K+xrZ1PBez190iGt5OnMtGdih4=",
|
||||
|
|
|
|||
Loading…
Reference in a new issue