github.com/paulmey/terraform@v0.5.2-0.20150519145237-046e9b4c884d/builtin/providers/docker/resource_docker_image_funcs.go (about)

     1  package docker
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"regexp"
     7  
     8  	dc "github.com/fsouza/go-dockerclient"
     9  	"github.com/hashicorp/terraform/helper/schema"
    10  )
    11  
    12  func resourceDockerImageCreate(d *schema.ResourceData, meta interface{}) error {
    13  	client := meta.(*dc.Client)
    14  	apiImage, err := findImage(d, client)
    15  	if err != nil {
    16  		return fmt.Errorf("Unable to read Docker image into resource: %s", err)
    17  	}
    18  
    19  	d.SetId(apiImage.ID + d.Get("name").(string))
    20  	d.Set("latest", apiImage.ID)
    21  
    22  	return nil
    23  }
    24  
    25  func resourceDockerImageRead(d *schema.ResourceData, meta interface{}) error {
    26  	client := meta.(*dc.Client)
    27  	apiImage, err := findImage(d, client)
    28  	if err != nil {
    29  		return fmt.Errorf("Unable to read Docker image into resource: %s", err)
    30  	}
    31  
    32  	d.Set("latest", apiImage.ID)
    33  
    34  	return nil
    35  }
    36  
    37  func resourceDockerImageUpdate(d *schema.ResourceData, meta interface{}) error {
    38  	// We need to re-read in case switching parameters affects
    39  	// the value of "latest" or others
    40  
    41  	return resourceDockerImageRead(d, meta)
    42  }
    43  
    44  func resourceDockerImageDelete(d *schema.ResourceData, meta interface{}) error {
    45  	d.SetId("")
    46  	return nil
    47  }
    48  
    49  func fetchLocalImages(data *Data, client *dc.Client) error {
    50  	images, err := client.ListImages(dc.ListImagesOptions{All: false})
    51  	if err != nil {
    52  		return fmt.Errorf("Unable to list Docker images: %s", err)
    53  	}
    54  
    55  	if data.DockerImages == nil {
    56  		data.DockerImages = make(map[string]*dc.APIImages)
    57  	}
    58  
    59  	// Docker uses different nomenclatures in different places...sometimes a short
    60  	// ID, sometimes long, etc. So we store both in the map so we can always find
    61  	// the same image object. We store the tags, too.
    62  	for i, image := range images {
    63  		data.DockerImages[image.ID[:12]] = &images[i]
    64  		data.DockerImages[image.ID] = &images[i]
    65  		for _, repotag := range image.RepoTags {
    66  			data.DockerImages[repotag] = &images[i]
    67  		}
    68  	}
    69  
    70  	return nil
    71  }
    72  
    73  func pullImage(data *Data, client *dc.Client, image string) error {
    74  	// TODO: Test local registry handling. It should be working
    75  	// based on the code that was ported over
    76  
    77  	pullOpts := dc.PullImageOptions{}
    78  
    79  	// 
    80  	// Breaks apart an image string into host:port, repo, and tag components
    81  	regex := "^(?:(?P<host>(?:[\\w-]+(?:\\.[\\w-]+)+)(?::[\\d]+)?)/)?(?P<repo>[\\w.-]+(?:/[\\w.-]*)*)*(?::(?P<tag>[\\w.-]*))?"
    82  	r, _ := regexp.Compile(regex)
    83  
    84  	// Result is in form [[image, host, repo, tag]], so we get the head of the 
    85  	// outer list to pass the inner list to result
    86  	result := r.FindAllStringSubmatch(image, -1)[0]
    87  
    88  	// If the host is not an empty string, then the image is using a private registry
    89  	if (result[1] != "") {
    90  		pullOpts.Registry = result[1]
    91  		// The repository for a private registry should take the form of host/repo rather than just repo
    92  		pullOpts.Repository = result[1] + "/" + result[2]
    93  	} else if (result[2] != "") {
    94  		// Local registries, or the main docker registry will have an image named as just 'repo'
    95  		pullOpts.Repository = result[2]
    96  	}
    97  
    98  	// If there was a tag specified, then set it
    99  	if (result[3] != "") {
   100  		pullOpts.Tag = result[3]
   101  	}
   102  
   103  	if err := client.PullImage(pullOpts, dc.AuthConfiguration{}); err != nil {
   104  		return fmt.Errorf("Error pulling image %s: %s\n", image, err)
   105  	}
   106  
   107  	return fetchLocalImages(data, client)
   108  }
   109  
   110  func getImageTag(image string) string {
   111  	splitImageName := strings.Split(image, ":")
   112  	switch {
   113  
   114  	// It's in registry:port/repo:tag format
   115  	case len(splitImageName) == 3:
   116  		return splitImageName[2]
   117  
   118  	// It's either registry:port/repo or repo:tag with default registry
   119  	case len(splitImageName) == 2:
   120  		splitPortRepo := strings.Split(splitImageName[1], "/")
   121  		if len(splitPortRepo) == 2 {
   122  			return ""
   123  		} else {
   124  			return splitImageName[1]
   125  		}
   126  	}
   127  
   128  	return ""
   129  }
   130  
   131  func findImage(d *schema.ResourceData, client *dc.Client) (*dc.APIImages, error) {
   132  	var data Data
   133  	if err := fetchLocalImages(&data, client); err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	imageName := d.Get("name").(string)
   138  	if imageName == "" {
   139  		return nil, fmt.Errorf("Empty image name is not allowed")
   140  	}
   141  
   142  	searchLocal := func() *dc.APIImages {
   143  		if apiImage, ok := data.DockerImages[imageName]; ok {
   144  			return apiImage
   145  		}
   146  		if apiImage, ok := data.DockerImages[imageName+":latest"]; ok {
   147  			imageName = imageName + ":latest"
   148  			return apiImage
   149  		}
   150  		return nil
   151  	}
   152  
   153  	foundImage := searchLocal()
   154  
   155  	if d.Get("keep_updated").(bool) || foundImage == nil {
   156  		if err := pullImage(&data, client, imageName); err != nil {
   157  			return nil, fmt.Errorf("Unable to pull image %s: %s", imageName, err)
   158  		}
   159  	}
   160  
   161  	foundImage = searchLocal()
   162  	if foundImage != nil {
   163  		return foundImage, nil
   164  	}
   165  
   166  	return nil, fmt.Errorf("Unable to find or pull image %s", imageName)
   167  }