github.com/gabrielperezs/terraform@v0.7.0-rc2.0.20160715084931-f7da2612946f/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 67 if keepLocally := d.Get("keep_locally").(bool); keepLocally { 68 return nil 69 } 70 71 if err := fetchLocalImages(&data, client); err != nil { 72 return err 73 } 74 75 imageName := d.Get("name").(string) 76 if imageName == "" { 77 return fmt.Errorf("Empty image name is not allowed") 78 } 79 80 foundImage := searchLocalImages(data, imageName) 81 82 if foundImage != nil { 83 err := client.RemoveImage(foundImage.ID) 84 if err != nil { 85 return err 86 } 87 } 88 89 return nil 90 } 91 92 func fetchLocalImages(data *Data, client *dc.Client) error { 93 images, err := client.ListImages(dc.ListImagesOptions{All: false}) 94 if err != nil { 95 return fmt.Errorf("Unable to list Docker images: %s", err) 96 } 97 98 if data.DockerImages == nil { 99 data.DockerImages = make(map[string]*dc.APIImages) 100 } 101 102 // Docker uses different nomenclatures in different places...sometimes a short 103 // ID, sometimes long, etc. So we store both in the map so we can always find 104 // the same image object. We store the tags, too. 105 for i, image := range images { 106 data.DockerImages[image.ID[:12]] = &images[i] 107 data.DockerImages[image.ID] = &images[i] 108 for _, repotag := range image.RepoTags { 109 data.DockerImages[repotag] = &images[i] 110 } 111 } 112 113 return nil 114 } 115 116 func pullImage(data *Data, client *dc.Client, image string) error { 117 // TODO: Test local registry handling. It should be working 118 // based on the code that was ported over 119 120 pullOpts := dc.PullImageOptions{} 121 122 splitImageName := strings.Split(image, ":") 123 switch len(splitImageName) { 124 125 // It's in registry:port/username/repo:tag or registry:port/repo:tag format 126 case 3: 127 splitPortRepo := strings.Split(splitImageName[1], "/") 128 pullOpts.Registry = splitImageName[0] + ":" + splitPortRepo[0] 129 pullOpts.Tag = splitImageName[2] 130 pullOpts.Repository = pullOpts.Registry + "/" + strings.Join(splitPortRepo[1:], "/") 131 132 // It's either registry:port/username/repo, registry:port/repo, 133 // or repo:tag with default registry 134 case 2: 135 splitPortRepo := strings.Split(splitImageName[1], "/") 136 switch len(splitPortRepo) { 137 // repo:tag 138 case 1: 139 pullOpts.Repository = splitImageName[0] 140 pullOpts.Tag = splitImageName[1] 141 142 // registry:port/username/repo or registry:port/repo 143 default: 144 pullOpts.Registry = splitImageName[0] + ":" + splitPortRepo[0] 145 pullOpts.Repository = pullOpts.Registry + "/" + strings.Join(splitPortRepo[1:], "/") 146 pullOpts.Tag = "latest" 147 } 148 149 // Plain username/repo or repo 150 default: 151 pullOpts.Repository = image 152 } 153 154 if err := client.PullImage(pullOpts, dc.AuthConfiguration{}); err != nil { 155 return fmt.Errorf("Error pulling image %s: %s\n", image, err) 156 } 157 158 return fetchLocalImages(data, client) 159 } 160 161 func getImageTag(image string) string { 162 splitImageName := strings.Split(image, ":") 163 switch { 164 165 // It's in registry:port/repo:tag format 166 case len(splitImageName) == 3: 167 return splitImageName[2] 168 169 // It's either registry:port/repo or repo:tag with default registry 170 case len(splitImageName) == 2: 171 splitPortRepo := strings.Split(splitImageName[1], "/") 172 if len(splitPortRepo) == 2 { 173 return "" 174 } else { 175 return splitImageName[1] 176 } 177 } 178 179 return "" 180 } 181 182 func findImage(d *schema.ResourceData, client *dc.Client) (*dc.APIImages, error) { 183 var data Data 184 if err := fetchLocalImages(&data, client); err != nil { 185 return nil, err 186 } 187 188 imageName := d.Get("name").(string) 189 if imageName == "" { 190 return nil, fmt.Errorf("Empty image name is not allowed") 191 } 192 193 foundImage := searchLocalImages(data, imageName) 194 195 if d.Get("keep_updated").(bool) || foundImage == nil { 196 if err := pullImage(&data, client, imageName); err != nil { 197 return nil, fmt.Errorf("Unable to pull image %s: %s", imageName, err) 198 } 199 } 200 201 foundImage = searchLocalImages(data, imageName) 202 if foundImage != nil { 203 return foundImage, nil 204 } 205 206 return nil, fmt.Errorf("Unable to find or pull image %s", imageName) 207 }