Merge pull request #7456 from hashicorp/do_5770

introduce the clean_resource_name to clean image/var names
This commit is contained in:
Megan Marsh 2019-04-05 09:12:20 -07:00 committed by GitHub
commit 3e72e65a6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 187 additions and 20 deletions

View File

@ -101,7 +101,7 @@ func (c *AMIConfig) Prepare(accessConfig *AccessConfig, ctx *interpolate.Context
errs = append(errs, fmt.Errorf("AMIName should only contain "+
"alphanumeric characters, parentheses (()), square brackets ([]), spaces "+
"( ), periods (.), slashes (/), dashes (-), single quotes ('), at-signs "+
"(@), or underscores(_). You can use the `clean_ami_name` template "+
"(@), or underscores(_). You can use the `clean_resource_name` template "+
"filter to automatically clean your ami name."))
}

View File

@ -3,6 +3,8 @@ package common
import (
"bytes"
"text/template"
packertpl "github.com/hashicorp/packer/common/template"
)
func isalphanumeric(b byte) bool {
@ -36,5 +38,6 @@ func templateCleanAMIName(s string) string {
}
var TemplateFuncs = template.FuncMap{
"clean_ami_name": templateCleanAMIName,
"clean_resource_name": templateCleanAMIName,
"clean_ami_name": packertpl.DeprecatedTemplateFunc("clean_ami_name", "clean_resource_name", templateCleanAMIName),
}

View File

@ -3,6 +3,8 @@ package arm
import (
"bytes"
"text/template"
packertpl "github.com/hashicorp/packer/common/template"
)
func isValidByteValue(b byte) bool {
@ -39,5 +41,6 @@ func templateCleanImageName(s string) string {
}
var TemplateFuncs = template.FuncMap{
"clean_image_name": templateCleanImageName,
"clean_resource_name": templateCleanImageName,
"clean_image_name": packertpl.DeprecatedTemplateFunc("clean_image_name", "clean_resource_name", templateCleanImageName),
}

View File

@ -3,6 +3,8 @@ package googlecompute
import (
"strings"
"text/template"
packertpl "github.com/hashicorp/packer/common/template"
)
func isalphanumeric(b byte) bool {
@ -34,5 +36,6 @@ func templateCleanImageName(s string) string {
}
var TemplateFuncs = template.FuncMap{
"clean_image_name": templateCleanImageName,
"clean_resource_name": templateCleanImageName,
"clean_image_name": packertpl.DeprecatedTemplateFunc("clean_image_name", "clean_resource_name", templateCleanImageName),
}

19
common/template/funcs.go Normal file
View File

@ -0,0 +1,19 @@
package template
import (
"log"
"sync"
)
// DeprecatedTemplateFunc wraps a template func to warn users that it's
// deprecated. The deprecation warning is called only once.
func DeprecatedTemplateFunc(funcName, useInstead string, deprecated func(string) string) func(string) string {
once := sync.Once{}
return func(in string) string {
once.Do(func() {
log.Printf("[WARN]: the `%s` template func is deprecated, please use %s instead",
funcName, useInstead)
})
return deprecated(in)
}
}

View File

@ -42,6 +42,7 @@ func init() {
"hyperv-vmxc-typo": new(FixerHypervVmxcTypo),
"hyperv-cpu-and-ram": new(FizerHypervCPUandRAM),
"vmware-compaction": new(FixerVMwareCompaction),
"clean-image-name": new(FixerCleanImageName),
}
FixerOrder = []string{
@ -65,5 +66,6 @@ func init() {
"powershell-escapes",
"vmware-compaction",
"hyperv-cpu-and-ram",
"clean-image-name",
}
}

View File

@ -0,0 +1,62 @@
package fix
import (
"fmt"
"regexp"
"github.com/mitchellh/mapstructure"
)
// FixerCleanImageName is a Fixer that replaces the "clean_(image|ami)_name" template
// calls with "clean_resource_name"
type FixerCleanImageName struct{}
func (FixerCleanImageName) Fix(input map[string]interface{}) (map[string]interface{}, error) {
// Our template type we'll use for this fixer only
type template struct {
Builders []map[string]interface{}
}
// Decode the input into our structure, if we can
var tpl template
if err := mapstructure.Decode(input, &tpl); err != nil {
return nil, err
}
re := regexp.MustCompile(`clean_(image|ami)_name`)
// Go through each builder and replace CreateTime if we can
for _, builder := range tpl.Builders {
for key, value := range builder {
switch v := value.(type) {
case string:
changed := re.ReplaceAllString(v, "clean_resource_name")
builder[key] = changed
case map[string]string:
for k := range v {
v[k] = re.ReplaceAllString(v[k], "clean_resource_name")
}
builder[key] = v
case map[string]interface{}:
for k := range v {
if s, ok := v[k].(string); ok {
v[k] = re.ReplaceAllString(s, "clean_resource_name")
}
}
builder[key] = v
default:
if key == "image_labels" {
panic(fmt.Sprintf("value: %#v", value))
}
}
}
}
input["builders"] = tpl.Builders
return input, nil
}
func (FixerCleanImageName) Synopsis() string {
return `Replaces /clean_(image|ami)_name/ in builder configs with "clean_resource_name"`
}

View File

@ -0,0 +1,52 @@
package fix
import (
"testing"
"github.com/google/go-cmp/cmp"
)
func TestFixerCleanImageName_Impl(t *testing.T) {
var raw interface{}
raw = new(FixerCleanImageName)
if _, ok := raw.(Fixer); !ok {
t.Fatalf("must be a Fixer")
}
}
func TestFixerCleanImageName_Fix(t *testing.T) {
var f FixerCleanImageName
input := map[string]interface{}{
"builders": []interface{}{
map[string]interface{}{
"type": "foo",
"ami_name": "heyo clean_image_name",
"image_labels": map[string]interface{}{
"name": "test-packer-{{packer_version | clean_image_name}}",
},
},
},
}
expected := map[string]interface{}{
"builders": []map[string]interface{}{
{
"type": "foo",
"ami_name": "heyo clean_resource_name",
"image_labels": map[string]interface{}{
"name": "test-packer-{{packer_version | clean_resource_name}}",
},
},
},
}
output, err := f.Fix(input)
if err != nil {
t.Fatalf("err: %s", err)
}
if diff := cmp.Diff(expected, output); diff != "" {
t.Fatalf("unexpected output: %s", diff)
}
}

View File

@ -27,10 +27,10 @@ The syntax of templates uses the following conventions:
Functions perform operations on and within strings, for example the
`{{timestamp}}` function can be used in any string to generate the current
timestamp. This is useful for configurations that require unique keys, such as
AMI names. By setting the AMI name to something like
`My Packer AMI {{timestamp}}`, the AMI name will be unique down to the second.
If you need greater than one second granularity, you should use `{{uuid}}`, for
example when you have multiple builders in the same template.
AMI names. By setting the AMI name to something like `My Packer AMI
{{timestamp}}`, the AMI name will be unique down to the second. If you need
greater than one second granularity, you should use `{{uuid}}`, for example
when you have multiple builders in the same template.
Here is a full list of the available functions for reference.
@ -54,18 +54,41 @@ Here is a full list of the available functions for reference.
- `upper` - Uppercases the string.
- `user` - Specifies a user variable.
- `packer_version` - Returns Packer version.
- `clean_resource_name` - Image names can only contain certain characters and
have a maximum length, eg 63 on GCE & 80 on Azure. `clean_resource_name`
will convert upper cases to lower cases and replace illegal characters with
a "-" character. Example:
`"mybuild-{{isotime | clean_image_name}}"` will become
`mybuild-2017-10-18t02-06-30z`.
Note: Valid Azure image names must match the regex
`^[^_\\W][\\w-._)]{0,79}$`
Note: Valid GCE image names must match the regex
`(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)`
This engine does not guarantee that the final image name will match the
regex; it will not truncate your name if it exceeds the maximum number of
allowed characters, and it will not validate that the beginning and end of
the engine's output are valid. For example, `"image_name": {{isotime |
clean_resource_name}}"` will cause your build to fail because the image
name will start with a number, which is why in the above example we prepend
the isotime with "mybuild".
#### Specific to Amazon builders:
- `clean_ami_name` - AMI names can only contain certain characters. This
function will replace illegal characters with a '-" character. Example
usage since ":" is not a legal AMI name is: `{{isotime | clean_ami_name}}`.
- `clean_ami_name` - DEPRECATED use `clean_resource_name` instead - AMI names
can only contain certain characters. This function will replace illegal
characters with a '-" character. Example usage since ":" is not a legal AMI
name is: `{{isotime | clean_ami_name}}`.
#### Specific to Google Compute builders:
- `clean_image_name` - GCE image names can only contain certain characters
and the maximum length is 63. This function will convert upper cases to
lower cases and replace illegal characters with a "-" character. Example:
- `clean_image_name` - DEPRECATED use `clean_resource_name` instead - GCE
image names can only contain certain characters and the maximum length is
63. This function will convert upper cases to lower cases and replace
illegal characters with a "-" character. Example:
`"mybuild-{{isotime | clean_image_name}}"` will become
`mybuild-2017-10-18t02-06-30z`.
@ -82,9 +105,10 @@ Here is a full list of the available functions for reference.
#### Specific to Azure builders:
- `clean_image_name` - Azure managed image names can only contain certain
characters and the maximum length is 80. This function will replace illegal
characters with a "-" character. Example:
- `clean_image_name` - DEPRECATED use `clean_resource_name` instead - Azure
managed image names can only contain certain characters and the maximum
length is 80. This function will replace illegal characters with a "-"
character. Example:
`"mybuild-{{isotime | clean_image_name}}"` will become
`mybuild-2017-10-18t02-06-30z`.
@ -96,9 +120,8 @@ Here is a full list of the available functions for reference.
regex; it will not truncate your name if it exceeds 80 characters, and it
will not validate that the beginning and end of the engine's output are
valid. It will truncate invalid characters from the end of the name when
converting illegal characters. For example,
`"managed_image_name: "My-Name::"` will be converted to
`"managed_image_name: "My-Name"`
converting illegal characters. For example, `"managed_image_name:
"My-Name::"` will be converted to `"managed_image_name: "My-Name"`
## Template variables