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