terraform-provider-docker/docker/provider.go
Manuel Vogel dc824c1030
Feat/swarm 4 new resources (#40)
Adds docker swarm features to the provider for the Docker Engine 17.09.1 and API Version 1.32. 

The spec is close to the API. By default, the swarm services are fire and forget. A converging config implements the features of the docker cli to ensure a service and all its replicas are up and running. Furthermore, service can have configs, secrets, networks, mounts and be added to a network.
2018-05-16 18:00:04 +02:00

201 lines
6 KiB
Go

package docker
import (
"fmt"
"os"
"os/user"
"strings"
dc "github.com/fsouza/go-dockerclient"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)
// Provider creates the Docker provider
func Provider() terraform.ResourceProvider {
return &schema.Provider{
Schema: map[string]*schema.Schema{
"host": &schema.Schema{
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("DOCKER_HOST", "unix:///var/run/docker.sock"),
Description: "The Docker daemon address",
},
"ca_material": &schema.Schema{
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("DOCKER_CA_MATERIAL", ""),
Description: "PEM-encoded content of Docker host CA certificate",
},
"cert_material": &schema.Schema{
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("DOCKER_CERT_MATERIAL", ""),
Description: "PEM-encoded content of Docker client certificate",
},
"key_material": &schema.Schema{
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("DOCKER_KEY_MATERIAL", ""),
Description: "PEM-encoded content of Docker client private key",
},
"cert_path": &schema.Schema{
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("DOCKER_CERT_PATH", ""),
Description: "Path to directory with Docker TLS config",
},
"registry_auth": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"address": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: "Address of the registry",
},
"username": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ConflictsWith: []string{"registry_auth.config_file"},
DefaultFunc: schema.EnvDefaultFunc("DOCKER_REGISTRY_USER", ""),
Description: "Username for the registry",
},
"password": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Sensitive: true,
ConflictsWith: []string{"registry_auth.config_file"},
DefaultFunc: schema.EnvDefaultFunc("DOCKER_REGISTRY_PASS", ""),
Description: "Password for the registry",
},
"config_file": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ConflictsWith: []string{"registry_auth.username", "registry_auth.password"},
DefaultFunc: schema.EnvDefaultFunc("DOCKER_CONFIG", "~/.docker/config.json"),
Description: "Path to docker json file for registry auth",
},
},
},
},
},
ResourcesMap: map[string]*schema.Resource{
"docker_container": resourceDockerContainer(),
"docker_image": resourceDockerImage(),
"docker_network": resourceDockerNetwork(),
"docker_volume": resourceDockerVolume(),
"docker_config": resourceDockerConfig(),
"docker_secret": resourceDockerSecret(),
"docker_service": resourceDockerService(),
},
DataSourcesMap: map[string]*schema.Resource{
"docker_registry_image": dataSourceDockerRegistryImage(),
},
ConfigureFunc: providerConfigure,
}
}
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
config := DockerConfig{
Host: d.Get("host").(string),
Ca: d.Get("ca_material").(string),
Cert: d.Get("cert_material").(string),
Key: d.Get("key_material").(string),
CertPath: d.Get("cert_path").(string),
}
client, err := config.NewClient()
if err != nil {
return nil, fmt.Errorf("Error initializing Docker client: %s", err)
}
err = client.Ping()
if err != nil {
return nil, fmt.Errorf("Error pinging Docker server: %s", err)
}
authConfigs := &dc.AuthConfigurations{}
if v, ok := d.GetOk("registry_auth"); ok {
authConfigs, err = providerSetToRegistryAuth(v.(*schema.Set))
if err != nil {
return nil, fmt.Errorf("Error loading registry auth config: %s", err)
}
}
providerConfig := ProviderConfig{
DockerClient: client,
AuthConfigs: authConfigs,
}
return &providerConfig, nil
}
// 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),
}
for _, authInt := range authSet.List() {
auth := authInt.(map[string]interface{})
authConfig := dc.AuthConfiguration{}
authConfig.ServerAddress = normalizeRegistryAddress(auth["address"].(string))
// For each registry_auth block, generate an AuthConfiguration using either
// username/password or the given config file
if username, ok := auth["username"]; ok && username.(string) != "" {
authConfig.Username = auth["username"].(string)
authConfig.Password = auth["password"].(string)
} else if configFile, ok := auth["config_file"]; ok && configFile.(string) != "" {
filePath := configFile.(string)
if strings.HasPrefix(filePath, "~/") {
usr, err := user.Current()
if err != nil {
return nil, err
}
filePath = strings.Replace(filePath, "~", usr.HomeDir, 1)
}
r, err := os.Open(filePath)
if err != nil {
return nil, fmt.Errorf("Error opening docker registry config file: %v", err)
}
auths, err := dc.NewAuthConfigurations(r)
if err != nil {
return nil, fmt.Errorf("Error parsing docker registry config json: %v", err)
}
foundRegistry := false
for registry, authFileConfig := range auths.Configs {
if authConfig.ServerAddress == normalizeRegistryAddress(registry) {
authConfig.Username = authFileConfig.Username
authConfig.Password = authFileConfig.Password
foundRegistry = true
}
}
if !foundRegistry {
return nil, fmt.Errorf("Couldn't find registry config for '%s' in file: %s",
authConfig.ServerAddress, filePath)
}
}
authConfigs.Configs[authConfig.ServerAddress] = authConfig
}
return &authConfigs, nil
}