github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/google/image.go (about)

     1  package google
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  	"strings"
     7  
     8  	"google.golang.org/api/googleapi"
     9  )
    10  
    11  const (
    12  	resolveImageProjectRegex = "[-_a-zA-Z0-9]*"
    13  	resolveImageFamilyRegex  = "[-_a-zA-Z0-9]*"
    14  	resolveImageImageRegex   = "[-_a-zA-Z0-9]*"
    15  )
    16  
    17  var (
    18  	resolveImageProjectImage           = regexp.MustCompile(fmt.Sprintf("^projects/(%s)/global/images/(%s)$", resolveImageProjectRegex, resolveImageImageRegex))
    19  	resolveImageProjectFamily          = regexp.MustCompile(fmt.Sprintf("^projects/(%s)/global/images/family/(%s)$", resolveImageProjectRegex, resolveImageFamilyRegex))
    20  	resolveImageGlobalImage            = regexp.MustCompile(fmt.Sprintf("^global/images/(%s)$", resolveImageImageRegex))
    21  	resolveImageGlobalFamily           = regexp.MustCompile(fmt.Sprintf("^global/images/family/(%s)$", resolveImageFamilyRegex))
    22  	resolveImageFamilyFamily           = regexp.MustCompile(fmt.Sprintf("^family/(%s)$", resolveImageFamilyRegex))
    23  	resolveImageProjectImageShorthand  = regexp.MustCompile(fmt.Sprintf("^(%s)/(%s)$", resolveImageProjectRegex, resolveImageImageRegex))
    24  	resolveImageProjectFamilyShorthand = regexp.MustCompile(fmt.Sprintf("^(%s)/(%s)$", resolveImageProjectRegex, resolveImageFamilyRegex))
    25  	resolveImageFamily                 = regexp.MustCompile(fmt.Sprintf("^(%s)$", resolveImageFamilyRegex))
    26  	resolveImageImage                  = regexp.MustCompile(fmt.Sprintf("^(%s)$", resolveImageImageRegex))
    27  	resolveImageLink                   = regexp.MustCompile(fmt.Sprintf("^https://www.googleapis.com/compute/v1/projects/(%s)/global/images/(%s)", resolveImageProjectRegex, resolveImageImageRegex))
    28  )
    29  
    30  func resolveImageImageExists(c *Config, project, name string) (bool, error) {
    31  	if _, err := c.clientCompute.Images.Get(project, name).Do(); err == nil {
    32  		return true, nil
    33  	} else if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
    34  		return false, nil
    35  	} else {
    36  		return false, fmt.Errorf("Error checking if image %s exists: %s", name, err)
    37  	}
    38  }
    39  
    40  func resolveImageFamilyExists(c *Config, project, name string) (bool, error) {
    41  	if _, err := c.clientCompute.Images.GetFromFamily(project, name).Do(); err == nil {
    42  		return true, nil
    43  	} else if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
    44  		return false, nil
    45  	} else {
    46  		return false, fmt.Errorf("Error checking if family %s exists: %s", name, err)
    47  	}
    48  }
    49  
    50  func sanityTestRegexMatches(expected int, got []string, regexType, name string) error {
    51  	if len(got)-1 != expected { // subtract one, index zero is the entire matched expression
    52  		return fmt.Errorf("Expected %d %s regex matches, got %d for %s", expected, regexType, len(got)-1, name)
    53  	}
    54  	return nil
    55  }
    56  
    57  // If the given name is a URL, return it.
    58  // If it's in the form projects/{project}/global/images/{image}, return it
    59  // If it's in the form projects/{project}/global/images/family/{family}, return it
    60  // If it's in the form global/images/{image}, return it
    61  // If it's in the form global/images/family/{family}, return it
    62  // If it's in the form family/{family}, check if it's a family in the current project. If it is, return it as global/images/family/{family}.
    63  //    If not, check if it could be a GCP-provided family, and if it exists. If it does, return it as projects/{project}/global/images/family/{family}.
    64  // If it's in the form {project}/{family-or-image}, check if it's an image in the named project. If it is, return it as projects/{project}/global/images/{image}.
    65  //    If not, check if it's a family in the named project. If it is, return it as projects/{project}/global/images/family/{family}.
    66  // If it's in the form {family-or-image}, check if it's an image in the current project. If it is, return it as global/images/{image}.
    67  //    If not, check if it could be a GCP-provided image, and if it exists. If it does, return it as projects/{project}/global/images/{image}.
    68  //    If not, check if it's a family in the current project. If it is, return it as global/images/family/{family}.
    69  //    If not, check if it could be a GCP-provided family, and if it exists. If it does, return it as projects/{project}/global/images/family/{family}
    70  func resolveImage(c *Config, name string) (string, error) {
    71  	// built-in projects to look for images/families containing the string
    72  	// on the left in
    73  	imageMap := map[string]string{
    74  		"centos":   "centos-cloud",
    75  		"coreos":   "coreos-cloud",
    76  		"debian":   "debian-cloud",
    77  		"opensuse": "opensuse-cloud",
    78  		"rhel":     "rhel-cloud",
    79  		"sles":     "suse-cloud",
    80  		"ubuntu":   "ubuntu-os-cloud",
    81  		"windows":  "windows-cloud",
    82  	}
    83  	var builtInProject string
    84  	for k, v := range imageMap {
    85  		if strings.Contains(name, k) {
    86  			builtInProject = v
    87  			break
    88  		}
    89  	}
    90  	switch {
    91  	case resolveImageLink.MatchString(name): // https://www.googleapis.com/compute/v1/projects/xyz/global/images/xyz
    92  		return name, nil
    93  	case resolveImageProjectImage.MatchString(name): // projects/xyz/global/images/xyz
    94  		res := resolveImageProjectImage.FindStringSubmatch(name)
    95  		if err := sanityTestRegexMatches(2, res, "project image", name); err != nil {
    96  			return "", err
    97  		}
    98  		return fmt.Sprintf("projects/%s/global/images/%s", res[1], res[2]), nil
    99  	case resolveImageProjectFamily.MatchString(name): // projects/xyz/global/images/family/xyz
   100  		res := resolveImageProjectFamily.FindStringSubmatch(name)
   101  		if err := sanityTestRegexMatches(2, res, "project family", name); err != nil {
   102  			return "", err
   103  		}
   104  		return fmt.Sprintf("projects/%s/global/images/family/%s", res[1], res[2]), nil
   105  	case resolveImageGlobalImage.MatchString(name): // global/images/xyz
   106  		res := resolveImageGlobalImage.FindStringSubmatch(name)
   107  		if err := sanityTestRegexMatches(1, res, "global image", name); err != nil {
   108  			return "", err
   109  		}
   110  		return fmt.Sprintf("global/images/%s", res[1]), nil
   111  	case resolveImageGlobalFamily.MatchString(name): // global/images/family/xyz
   112  		res := resolveImageGlobalFamily.FindStringSubmatch(name)
   113  		if err := sanityTestRegexMatches(1, res, "global family", name); err != nil {
   114  			return "", err
   115  		}
   116  		return fmt.Sprintf("global/images/family/%s", res[1]), nil
   117  	case resolveImageFamilyFamily.MatchString(name): // family/xyz
   118  		res := resolveImageFamilyFamily.FindStringSubmatch(name)
   119  		if err := sanityTestRegexMatches(1, res, "family family", name); err != nil {
   120  			return "", err
   121  		}
   122  		if ok, err := resolveImageFamilyExists(c, c.Project, res[1]); err != nil {
   123  			return "", err
   124  		} else if ok {
   125  			return fmt.Sprintf("global/images/family/%s", res[1]), nil
   126  		}
   127  		if builtInProject != "" {
   128  			if ok, err := resolveImageFamilyExists(c, builtInProject, res[1]); err != nil {
   129  				return "", err
   130  			} else if ok {
   131  				return fmt.Sprintf("projects/%s/global/images/family/%s", builtInProject, res[1]), nil
   132  			}
   133  		}
   134  	case resolveImageProjectImageShorthand.MatchString(name): // xyz/xyz
   135  		res := resolveImageProjectImageShorthand.FindStringSubmatch(name)
   136  		if err := sanityTestRegexMatches(2, res, "project image shorthand", name); err != nil {
   137  			return "", err
   138  		}
   139  		if ok, err := resolveImageImageExists(c, res[1], res[2]); err != nil {
   140  			return "", err
   141  		} else if ok {
   142  			return fmt.Sprintf("projects/%s/global/images/%s", res[1], res[2]), nil
   143  		}
   144  		fallthrough // check if it's a family
   145  	case resolveImageProjectFamilyShorthand.MatchString(name): // xyz/xyz
   146  		res := resolveImageProjectFamilyShorthand.FindStringSubmatch(name)
   147  		if err := sanityTestRegexMatches(2, res, "project family shorthand", name); err != nil {
   148  			return "", err
   149  		}
   150  		if ok, err := resolveImageFamilyExists(c, res[1], res[2]); err != nil {
   151  			return "", err
   152  		} else if ok {
   153  			return fmt.Sprintf("projects/%s/global/images/family/%s", res[1], res[2]), nil
   154  		}
   155  	case resolveImageImage.MatchString(name): // xyz
   156  		res := resolveImageImage.FindStringSubmatch(name)
   157  		if err := sanityTestRegexMatches(1, res, "image", name); err != nil {
   158  			return "", err
   159  		}
   160  		if ok, err := resolveImageImageExists(c, c.Project, res[1]); err != nil {
   161  			return "", err
   162  		} else if ok {
   163  			return fmt.Sprintf("global/images/%s", res[1]), nil
   164  		}
   165  		if builtInProject != "" {
   166  			// check the images GCP provides
   167  			if ok, err := resolveImageImageExists(c, builtInProject, res[1]); err != nil {
   168  				return "", err
   169  			} else if ok {
   170  				return fmt.Sprintf("projects/%s/global/images/%s", builtInProject, res[1]), nil
   171  			}
   172  		}
   173  		fallthrough // check if the name is a family, instead of an image
   174  	case resolveImageFamily.MatchString(name): // xyz
   175  		res := resolveImageFamily.FindStringSubmatch(name)
   176  		if err := sanityTestRegexMatches(1, res, "family", name); err != nil {
   177  			return "", err
   178  		}
   179  		if ok, err := resolveImageFamilyExists(c, c.Project, res[1]); err != nil {
   180  			return "", err
   181  		} else if ok {
   182  			return fmt.Sprintf("global/images/family/%s", res[1]), nil
   183  		}
   184  		if builtInProject != "" {
   185  			// check the families GCP provides
   186  			if ok, err := resolveImageFamilyExists(c, builtInProject, res[1]); err != nil {
   187  				return "", err
   188  			} else if ok {
   189  				return fmt.Sprintf("projects/%s/global/images/family/%s", builtInProject, res[1]), nil
   190  			}
   191  		}
   192  	}
   193  	return "", fmt.Errorf("Could not find image or family %s", name)
   194  }