mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-27 17:06:27 -06:00
f7512ca29f
Docker's API is huge and only a small subset is currently implemented, but this is expected to grow over time. Currently it's enough to satisfy the use cases of probably 95% of Docker users. I'm preparing this initial pull request as a preview step for feedback. My ideal scenario would be to develop this within a branch in the main repository; the more eyes and testing and pitching in on the code, the better (this would avoid a merge request-to-the-merge-request scenario, as I figure this will be built up over the longer term, even before a merge into master). Unit tests do not exist yet. Right now I've just been focused on getting initial functionality ported over. I've been testing each option extensively via the Docker inspect capabilities. This code (C)2014-2015 Akamai Technologies, Inc. <opensource@akamai.com>
178 lines
4.4 KiB
Go
178 lines
4.4 KiB
Go
package docker
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
dc "github.com/fsouza/go-dockerclient"
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
)
|
|
|
|
func resourceDockerImageCreate(d *schema.ResourceData, meta interface{}) error {
|
|
config := meta.(*Config)
|
|
|
|
apiImage, err := findImage(d, config)
|
|
if err != nil {
|
|
return fmt.Errorf("Unable to read Docker image into resource: %s", err)
|
|
}
|
|
|
|
d.SetId(apiImage.ID + d.Get("name").(string))
|
|
d.Set("latest", apiImage.ID)
|
|
|
|
return nil
|
|
}
|
|
|
|
func resourceDockerImageRead(d *schema.ResourceData, meta interface{}) error {
|
|
config := meta.(*Config)
|
|
|
|
apiImage, err := findImage(d, config)
|
|
if err != nil {
|
|
return fmt.Errorf("Unable to read Docker image into resource: %s", err)
|
|
}
|
|
|
|
d.Set("latest", apiImage.ID)
|
|
|
|
return nil
|
|
}
|
|
|
|
func resourceDockerImageUpdate(d *schema.ResourceData, meta interface{}) error {
|
|
// We need to re-read in case switching parameters affects
|
|
// the value of "latest" or others
|
|
|
|
return resourceDockerImageRead(d, meta)
|
|
}
|
|
|
|
func resourceDockerImageDelete(d *schema.ResourceData, meta interface{}) error {
|
|
d.SetId("")
|
|
return nil
|
|
}
|
|
|
|
func fetchLocalImages(data *Data, client *dc.Client) error {
|
|
images, err := client.ListImages(dc.ListImagesOptions{All: false})
|
|
if err != nil {
|
|
return fmt.Errorf("Unable to list Docker images: %s", err)
|
|
}
|
|
|
|
// Docker uses different nomenclatures in different places...sometimes a short
|
|
// ID, sometimes long, etc. So we store both in the map so we can always find
|
|
// the same image object. We store the tags, too.
|
|
for i, image := range images {
|
|
data.DockerImages[image.ID[:12]] = &images[i]
|
|
data.DockerImages[image.ID] = &images[i]
|
|
for _, repotag := range image.RepoTags {
|
|
data.DockerImages[repotag] = &images[i]
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func pullImage(data *Data, client *dc.Client, image string) error {
|
|
// TODO: Test local registry handling. It should be working
|
|
// based on the code that was ported over
|
|
|
|
pullOpts := dc.PullImageOptions{}
|
|
|
|
splitImageName := strings.Split(image, ":")
|
|
switch {
|
|
|
|
// It's in registry:port/repo:tag format
|
|
case len(splitImageName) == 3:
|
|
splitPortRepo := strings.Split(splitImageName[1], "/")
|
|
pullOpts.Registry = splitImageName[0] + ":" + splitPortRepo[0]
|
|
pullOpts.Repository = splitPortRepo[1]
|
|
pullOpts.Tag = splitImageName[2]
|
|
|
|
// It's either registry:port/repo or repo:tag with default registry
|
|
case len(splitImageName) == 2:
|
|
splitPortRepo := strings.Split(splitImageName[1], "/")
|
|
switch len(splitPortRepo) {
|
|
|
|
// registry:port/repo
|
|
case 2:
|
|
pullOpts.Registry = splitImageName[0] + ":" + splitPortRepo[0]
|
|
pullOpts.Repository = splitPortRepo[1]
|
|
pullOpts.Tag = "latest"
|
|
|
|
// repo:tag
|
|
case 1:
|
|
pullOpts.Repository = splitImageName[0]
|
|
pullOpts.Tag = splitImageName[1]
|
|
}
|
|
|
|
default:
|
|
pullOpts.Repository = image
|
|
}
|
|
|
|
if err := client.PullImage(pullOpts, dc.AuthConfiguration{}); err != nil {
|
|
return fmt.Errorf("Error pulling image %s: %s\n", image, err)
|
|
}
|
|
|
|
return fetchLocalImages(data, client)
|
|
}
|
|
|
|
func getImageTag(image string) string {
|
|
splitImageName := strings.Split(image, ":")
|
|
switch {
|
|
|
|
// It's in registry:port/repo:tag format
|
|
case len(splitImageName) == 3:
|
|
return splitImageName[2]
|
|
|
|
// It's either registry:port/repo or repo:tag with default registry
|
|
case len(splitImageName) == 2:
|
|
splitPortRepo := strings.Split(splitImageName[1], "/")
|
|
if len(splitPortRepo) == 2 {
|
|
return ""
|
|
} else {
|
|
return splitImageName[1]
|
|
}
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func findImage(d *schema.ResourceData, config *Config) (*dc.APIImages, error) {
|
|
client, err := config.NewClient()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Unable to connect to Docker: %s", err)
|
|
}
|
|
|
|
data := config.NewData()
|
|
|
|
if err := fetchLocalImages(data, client); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
imageName := d.Get("name").(string)
|
|
if imageName == "" {
|
|
return nil, fmt.Errorf("Empty image name is not allowed")
|
|
}
|
|
|
|
searchLocal := func() *dc.APIImages {
|
|
if apiImage, ok := data.DockerImages[imageName]; ok {
|
|
return apiImage
|
|
}
|
|
if apiImage, ok := data.DockerImages[imageName+":latest"]; ok {
|
|
imageName = imageName + ":latest"
|
|
return apiImage
|
|
}
|
|
return nil
|
|
}
|
|
|
|
foundImage := searchLocal()
|
|
|
|
if d.Get("keep_updated").(bool) || foundImage == nil {
|
|
if err := pullImage(data, client, imageName); err != nil {
|
|
return nil, fmt.Errorf("Unable to pull image %s: %s", imageName, err)
|
|
}
|
|
}
|
|
|
|
foundImage = searchLocal()
|
|
if foundImage != nil {
|
|
return foundImage, nil
|
|
}
|
|
|
|
return nil, fmt.Errorf("Unable to find or pull image %s", imageName)
|
|
}
|