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  }