github.com/armen/terraform@v0.5.2-0.20150529052519-caa8117a08f1/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 }