github.com/andresvia/terraform@v0.6.15-0.20160412045437-d51c75946785/builtin/providers/docker/resource_docker_image_funcs.go (about) 1 package docker 2 3 import ( 4 "fmt" 5 "strings" 6 7 dc "github.com/fsouza/go-dockerclient" 8 "github.com/hashicorp/terraform/helper/schema" 9 ) 10 11 func resourceDockerImageCreate(d *schema.ResourceData, meta interface{}) error { 12 client := meta.(*dc.Client) 13 apiImage, err := findImage(d, client) 14 if err != nil { 15 return fmt.Errorf("Unable to read Docker image into resource: %s", err) 16 } 17 18 d.SetId(apiImage.ID + d.Get("name").(string)) 19 d.Set("latest", apiImage.ID) 20 21 return nil 22 } 23 24 func resourceDockerImageRead(d *schema.ResourceData, meta interface{}) error { 25 client := meta.(*dc.Client) 26 apiImage, err := findImage(d, client) 27 if err != nil { 28 return fmt.Errorf("Unable to read Docker image into resource: %s", err) 29 } 30 31 d.Set("latest", apiImage.ID) 32 33 return nil 34 } 35 36 func resourceDockerImageUpdate(d *schema.ResourceData, meta interface{}) error { 37 // We need to re-read in case switching parameters affects 38 // the value of "latest" or others 39 40 return resourceDockerImageRead(d, meta) 41 } 42 43 func resourceDockerImageDelete(d *schema.ResourceData, meta interface{}) error { 44 client := meta.(*dc.Client) 45 err := removeImage(d, client) 46 if err != nil { 47 return fmt.Errorf("Unable to remove Docker image: %s", err) 48 } 49 d.SetId("") 50 return nil 51 } 52 53 func searchLocalImages(data Data, imageName string) *dc.APIImages { 54 if apiImage, ok := data.DockerImages[imageName]; ok { 55 return apiImage 56 } 57 if apiImage, ok := data.DockerImages[imageName+":latest"]; ok { 58 imageName = imageName + ":latest" 59 return apiImage 60 } 61 return nil 62 } 63 64 func removeImage(d *schema.ResourceData, client *dc.Client) error { 65 var data Data 66 if err := fetchLocalImages(&data, client); err != nil { 67 return err 68 } 69 70 imageName := d.Get("name").(string) 71 if imageName == "" { 72 return fmt.Errorf("Empty image name is not allowed") 73 } 74 75 foundImage := searchLocalImages(data, imageName) 76 77 if foundImage != nil { 78 err := client.RemoveImage(foundImage.ID) 79 if err != nil { 80 return err 81 } 82 } 83 84 return nil 85 } 86 87 func fetchLocalImages(data *Data, client *dc.Client) error { 88 images, err := client.ListImages(dc.ListImagesOptions{All: false}) 89 if err != nil { 90 return fmt.Errorf("Unable to list Docker images: %s", err) 91 } 92 93 if data.DockerImages == nil { 94 data.DockerImages = make(map[string]*dc.APIImages) 95 } 96 97 // Docker uses different nomenclatures in different places...sometimes a short 98 // ID, sometimes long, etc. So we store both in the map so we can always find 99 // the same image object. We store the tags, too. 100 for i, image := range images { 101 data.DockerImages[image.ID[:12]] = &images[i] 102 data.DockerImages[image.ID] = &images[i] 103 for _, repotag := range image.RepoTags { 104 data.DockerImages[repotag] = &images[i] 105 } 106 } 107 108 return nil 109 } 110 111 func pullImage(data *Data, client *dc.Client, image string) error { 112 // TODO: Test local registry handling. It should be working 113 // based on the code that was ported over 114 115 pullOpts := dc.PullImageOptions{} 116 117 splitImageName := strings.Split(image, ":") 118 switch len(splitImageName) { 119 120 // It's in registry:port/username/repo:tag or registry:port/repo:tag format 121 case 3: 122 splitPortRepo := strings.Split(splitImageName[1], "/") 123 pullOpts.Registry = splitImageName[0] + ":" + splitPortRepo[0] 124 pullOpts.Tag = splitImageName[2] 125 pullOpts.Repository = pullOpts.Registry + "/" + strings.Join(splitPortRepo[1:], "/") 126 127 // It's either registry:port/username/repo, registry:port/repo, 128 // or repo:tag with default registry 129 case 2: 130 splitPortRepo := strings.Split(splitImageName[1], "/") 131 switch len(splitPortRepo) { 132 // repo:tag 133 case 1: 134 pullOpts.Repository = splitImageName[0] 135 pullOpts.Tag = splitImageName[1] 136 137 // registry:port/username/repo or registry:port/repo 138 default: 139 pullOpts.Registry = splitImageName[0] + ":" + splitPortRepo[0] 140 pullOpts.Repository = pullOpts.Registry + "/" + strings.Join(splitPortRepo[1:], "/") 141 pullOpts.Tag = "latest" 142 } 143 144 // Plain username/repo or repo 145 default: 146 pullOpts.Repository = image 147 } 148 149 if err := client.PullImage(pullOpts, dc.AuthConfiguration{}); err != nil { 150 return fmt.Errorf("Error pulling image %s: %s\n", image, err) 151 } 152 153 return fetchLocalImages(data, client) 154 } 155 156 func getImageTag(image string) string { 157 splitImageName := strings.Split(image, ":") 158 switch { 159 160 // It's in registry:port/repo:tag format 161 case len(splitImageName) == 3: 162 return splitImageName[2] 163 164 // It's either registry:port/repo or repo:tag with default registry 165 case len(splitImageName) == 2: 166 splitPortRepo := strings.Split(splitImageName[1], "/") 167 if len(splitPortRepo) == 2 { 168 return "" 169 } else { 170 return splitImageName[1] 171 } 172 } 173 174 return "" 175 } 176 177 func findImage(d *schema.ResourceData, client *dc.Client) (*dc.APIImages, error) { 178 var data Data 179 if err := fetchLocalImages(&data, client); err != nil { 180 return nil, err 181 } 182 183 imageName := d.Get("name").(string) 184 if imageName == "" { 185 return nil, fmt.Errorf("Empty image name is not allowed") 186 } 187 188 foundImage := searchLocalImages(data, imageName) 189 190 if d.Get("keep_updated").(bool) || foundImage == nil { 191 if err := pullImage(&data, client, imageName); err != nil { 192 return nil, fmt.Errorf("Unable to pull image %s: %s", imageName, err) 193 } 194 } 195 196 foundImage = searchLocalImages(data, imageName) 197 if foundImage != nil { 198 return foundImage, nil 199 } 200 201 return nil, fmt.Errorf("Unable to find or pull image %s", imageName) 202 }