Merge pull request #2327 from jefferai/f-delay-on-link

Fix two serious problems when using links in Docker containers
This commit is contained in:
Paul Hinze 2015-06-29 14:36:38 -05:00
commit b26df75b50
1 changed files with 53 additions and 18 deletions

View File

@ -5,11 +5,16 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
"time"
dc "github.com/fsouza/go-dockerclient" dc "github.com/fsouza/go-dockerclient"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
) )
var (
creationTime time.Time
)
func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) error { func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) error {
var err error var err error
client := meta.(*dc.Client) client := meta.(*dc.Client)
@ -23,9 +28,8 @@ func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) err
if _, ok := data.DockerImages[image]; !ok { if _, ok := data.DockerImages[image]; !ok {
if _, ok := data.DockerImages[image+":latest"]; !ok { if _, ok := data.DockerImages[image+":latest"]; !ok {
return fmt.Errorf("Unable to find image %s", image) return fmt.Errorf("Unable to find image %s", image)
} else {
image = image + ":latest"
} }
image = image + ":latest"
} }
// The awesome, wonderful, splendiferous, sensical // The awesome, wonderful, splendiferous, sensical
@ -108,6 +112,7 @@ func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) err
hostConfig.Links = stringSetToStringSlice(v.(*schema.Set)) hostConfig.Links = stringSetToStringSlice(v.(*schema.Set))
} }
creationTime = time.Now()
if err := client.StartContainer(retContainer.ID, hostConfig); err != nil { if err := client.StartContainer(retContainer.ID, hostConfig); err != nil {
return fmt.Errorf("Unable to start container: %s", err) return fmt.Errorf("Unable to start container: %s", err)
} }
@ -122,21 +127,49 @@ func resourceDockerContainerRead(d *schema.ResourceData, meta interface{}) error
if err != nil { if err != nil {
return err return err
} }
if apiContainer == nil { if apiContainer == nil {
// This container doesn't exist anymore // This container doesn't exist anymore
d.SetId("") d.SetId("")
return nil return nil
} }
container, err := client.InspectContainer(apiContainer.ID) var container *dc.Container
if err != nil {
return fmt.Errorf("Error inspecting container %s: %s", apiContainer.ID, err) loops := 1 // if it hasn't just been created, don't delay
if !creationTime.IsZero() {
loops = 30 // with 500ms spacing, 15 seconds; ought to be plenty
}
sleepTime := 500 * time.Millisecond
for i := loops; i > 0; i-- {
container, err = client.InspectContainer(apiContainer.ID)
if err != nil {
return fmt.Errorf("Error inspecting container %s: %s", apiContainer.ID, err)
}
if container.State.Running ||
(!container.State.Running && !d.Get("must_run").(bool)) {
break
}
if creationTime.IsZero() { // We didn't just create it, so don't wait around
return resourceDockerContainerDelete(d, meta)
}
if container.State.FinishedAt.After(creationTime) {
// It exited immediately, so error out so dependent containers
// aren't started
resourceDockerContainerDelete(d, meta)
return fmt.Errorf("Container %s exited after creation, error was: %s", apiContainer.ID, container.State.Error)
}
time.Sleep(sleepTime)
} }
if d.Get("must_run").(bool) && !container.State.Running { // Handle the case of the for loop above running its course
return resourceDockerContainerDelete(d, meta) if !container.State.Running && d.Get("must_run").(bool) {
resourceDockerContainerDelete(d, meta)
return fmt.Errorf("Container %s failed to be in running state", apiContainer.ID)
} }
// Read Network Settings // Read Network Settings
@ -201,15 +234,17 @@ func fetchDockerContainer(name string, client *dc.Client) (*dc.APIContainers, er
// Sometimes the Docker API prefixes container names with / // Sometimes the Docker API prefixes container names with /
// like it does in these commands. But if there's no // like it does in these commands. But if there's no
// set name, it just uses the ID without a /...ugh. // set name, it just uses the ID without a /...ugh.
var dockerContainerName string switch len(apiContainer.Names) {
if len(apiContainer.Names) > 0 { case 0:
dockerContainerName = strings.TrimLeft(apiContainer.Names[0], "/") if apiContainer.ID == name {
} else { return &apiContainer, nil
dockerContainerName = apiContainer.ID }
} default:
for _, containerName := range apiContainer.Names {
if dockerContainerName == name { if strings.TrimLeft(containerName, "/") == name {
return &apiContainer, nil return &apiContainer, nil
}
}
} }
} }