Skip to content

Commit

Permalink
Initial commit. This adds the initial bits of a Docker provider.
Browse files Browse the repository at this point in the history
Docker's API is huge and only a small subset is currently implemented,
but this is expected to grow over time. Currently it's enough to
satisfy the use cases of probably 95% of Docker users.

I'm preparing this initial pull request as a preview step for feedback.
My ideal scenario would be to develop this within a branch in the main
repository; the more eyes and testing and pitching in on the code, the
better (this would avoid a merge request-to-the-merge-request scenario,
as I figure this will be built up over the longer term, even before
a merge into master).

Unit tests do not exist yet. Right now I've just been focused on getting
initial functionality ported over. I've been testing each option
extensively via the Docker inspect capabilities.

This code (C)2014-2015 Akamai Technologies, Inc. <opensource@akamai.com>
  • Loading branch information
Jeff Mitchell committed Mar 10, 2015
1 parent d2c6ae0 commit f7512ca
Show file tree
Hide file tree
Showing 10 changed files with 786 additions and 3 deletions.
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ If you have never worked with Go before, you will have to complete the
following steps in order to be able to compile and test Terraform (or
use the Vagrantfile in this repo to stand up a dev VM).

1. Install Go. Make sure the Go version is at least Go 1.2. Terraform will not work with anything less than
Go 1.2. On a Mac, you can `brew install go` to install Go 1.2.
1. Install Go. Make sure the Go version is at least Go 1.4. Terraform will not work with anything less than
Go 1.4. On a Mac, you can `brew install go` to install Go 1.4.

2. Set and export the `GOPATH` environment variable and update your `PATH`.
For example, you can add to your `.bash_profile`.
Expand Down
12 changes: 12 additions & 0 deletions builtin/bins/provider-docker/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package main

import (
"github.com/hashicorp/terraform/builtin/providers/docker"
"github.com/hashicorp/terraform/plugin"
)

func main() {
plugin.Serve(&plugin.ServeOpts{
ProviderFunc: docker.Provider,
})
}
1 change: 1 addition & 0 deletions builtin/bins/provider-docker/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package main
24 changes: 24 additions & 0 deletions builtin/providers/docker/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package docker

import dc "github.com/fsouza/go-dockerclient"

type Config struct {
DockerHost string
SkipPull bool
}

type Data struct {
DockerImages map[string]*dc.APIImages
}

// NewClient() returns a new Docker client.
func (c *Config) NewClient() (*dc.Client, error) {
return dc.NewClient(c.DockerHost)
}

// NewData() returns a new data struct.
func (c *Config) NewData() *Data {
return &Data{
DockerImages: map[string]*dc.APIImages{},
}
}
34 changes: 34 additions & 0 deletions builtin/providers/docker/provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package docker

import (
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)

func Provider() terraform.ResourceProvider {
return &schema.Provider{
Schema: map[string]*schema.Schema{
"docker_host": &schema.Schema{
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("DOCKER_HOST", "unix:/run/docker.sock"),
Description: "The Docker daemon endpoint",
},
},

ResourcesMap: map[string]*schema.Resource{
"docker_container": resourceDockerContainer(),
"docker_image": resourceDockerImage(),
},

ConfigureFunc: providerConfigure,
}
}

func providerConfigure(d *schema.ResourceData) (interface{}, error) {
config := Config{
DockerHost: d.Get("docker_host").(string),
}

return &config, nil
}
222 changes: 222 additions & 0 deletions builtin/providers/docker/resource_docker_container.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
package docker

import (
"bytes"
"fmt"

"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema"
)

func resourceDockerContainer() *schema.Resource {
return &schema.Resource{
Create: resourceDockerContainerCreate,
Read: resourceDockerContainerRead,
Update: resourceDockerContainerUpdate,
Delete: resourceDockerContainerDelete,

Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

// Indicates whether the container must be running.
//
// An assumption is made that configured containers
// should be running; if not, they should not be in
// the configuration. Therefore a stopped container
// should be started. Set to false to have the
// provider leave the container alone.
//
// Actively-debugged containers are likely to be
// stopped and started manually, and Docker has
// some provisions for restarting containers that
// stop. The utility here comes from the fact that
// this will delete and re-create the container
// following the principle that the containers
// should be pristine when started.
"must_run": &schema.Schema{
Type: schema.TypeBool,
Default: true,
Optional: true,
},

// ForceNew is not true for image because we need to
// sane this against Docker image IDs, as each image
// can have multiple names/tags attached do it.
"image": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"hostname": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

"domainname": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

"command": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},

"dns": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: stringSetHash,
},

"publish_all_ports": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
ForceNew: true,
},

"volumes": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
ForceNew: true,
Elem: getVolumesElem(),
Set: resourceDockerVolumesHash,
},

"ports": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
ForceNew: true,
Elem: getPortsElem(),
Set: resourceDockerPortsHash,
},

"env": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: stringSetHash,
},
},
}
}

func getVolumesElem() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
"from_container": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

"container_path": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

"host_path": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

"read_only": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
ForceNew: true,
},
},
}
}

func getPortsElem() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
"internal": &schema.Schema{
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},

"external": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},

"ip": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

"protocol": &schema.Schema{
Type: schema.TypeString,
Default: "tcp",
Optional: true,
ForceNew: true,
},
},
}
}

func resourceDockerPortsHash(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})

buf.WriteString(fmt.Sprintf("%v-", m["internal"].(int)))

if v, ok := m["external"]; ok {
buf.WriteString(fmt.Sprintf("%v-", v.(int)))
}

if v, ok := m["ip"]; ok {
buf.WriteString(fmt.Sprintf("%v-", v.(string)))
}

if v, ok := m["protocol"]; ok {
buf.WriteString(fmt.Sprintf("%v-", v.(string)))
}

return hashcode.String(buf.String())
}

func resourceDockerVolumesHash(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})

if v, ok := m["from_container"]; ok {
buf.WriteString(fmt.Sprintf("%v-", v.(string)))
}

if v, ok := m["container_path"]; ok {
buf.WriteString(fmt.Sprintf("%v-", v.(string)))
}

if v, ok := m["host_path"]; ok {
buf.WriteString(fmt.Sprintf("%v-", v.(string)))
}

if v, ok := m["read_only"]; ok {
buf.WriteString(fmt.Sprintf("%v-", v.(bool)))
}

return hashcode.String(buf.String())
}

func stringSetHash(v interface{}) int {
return hashcode.String(v.(string))
}
Loading

0 comments on commit f7512ca

Please sign in to comment.