mirror of https://github.com/hashicorp/packer
JSON to HCL2 (minimal best-effort) transpiler (#9659)
hcl2_upgrade transforms a JSON build-file in a HCL2 build-file. This starts a validated Packer core and from that core we generate an HCL 'block' per plugin/configuration. So for a builder, a provisioner, a post-processor or a variable. The contents of each block is just transformed as is and basically all fields are HCL2-ified. A generated field can be valid in JSON but invalid on HCL2; for example JSON templating (in mapstructure) allows to set arrays of strings - like `x = ["a", "b"]` - with single strings - like `x="a"` -, HCL does not allow this. Since JSON does not make the distinction between variables and locals, everything will be a variable. So variables that use other variables will not work. hcl2_upgrade tries to transform go templating interpolation calls to HCL2 calls when possible, leaving the go templating calls like they are in case it cannot. Work: * transpiler * tests * update hcl v2 library so that output looks great. * update docspull/9824/head
parent
9f2cb0d560
commit
5ba134ac5b
@ -0,0 +1,391 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
texttemplate "text/template"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hclwrite"
|
||||
hcl2shim "github.com/hashicorp/packer/hcl2template/shim"
|
||||
"github.com/hashicorp/packer/template"
|
||||
"github.com/posener/complete"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
type HCL2UpgradeCommand struct {
|
||||
Meta
|
||||
}
|
||||
|
||||
func (c *HCL2UpgradeCommand) Run(args []string) int {
|
||||
ctx, cleanup := handleTermInterrupt(c.Ui)
|
||||
defer cleanup()
|
||||
|
||||
cfg, ret := c.ParseArgs(args)
|
||||
if ret != 0 {
|
||||
return ret
|
||||
}
|
||||
|
||||
return c.RunContext(ctx, cfg)
|
||||
}
|
||||
|
||||
func (c *HCL2UpgradeCommand) ParseArgs(args []string) (*HCL2UpgradeArgs, int) {
|
||||
var cfg HCL2UpgradeArgs
|
||||
flags := c.Meta.FlagSet("hcl2_upgrade", FlagSetNone)
|
||||
flags.Usage = func() { c.Ui.Say(c.Help()) }
|
||||
cfg.AddFlagSets(flags)
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return &cfg, 1
|
||||
}
|
||||
args = flags.Args()
|
||||
if len(args) != 1 {
|
||||
flags.Usage()
|
||||
return &cfg, 1
|
||||
}
|
||||
cfg.Path = args[0]
|
||||
if cfg.OutputFile == "" {
|
||||
cfg.OutputFile = cfg.Path + ".pkr.hcl"
|
||||
}
|
||||
return &cfg, 0
|
||||
}
|
||||
|
||||
const (
|
||||
hcl2UpgradeFileHeader = `# This file was autogenerate by the BETA 'packer hcl2_upgrade' command. We
|
||||
# recommend double checking that everything is correct before going forward. We
|
||||
# also recommend treating this file as disposable. The HCL2 blocks in this
|
||||
# file can be moved to other files. For example, the variable blocks could be
|
||||
# moved to their own 'variables.pkr.hcl' file, etc. Those files need to be
|
||||
# suffixed with '.pkr.hcl' to be visible to Packer. To use multiple files at
|
||||
# once they also need to be in the same folder. 'packer inspect folder/'
|
||||
# will describe to you what is in that folder.
|
||||
|
||||
# All generated input variables will be of string type as this how Packer JSON
|
||||
# views them; you can later on change their type. Read the variables type
|
||||
# constraints documentation
|
||||
# https://www.packer.io/docs/from-1.5/variables#type-constraints for more info.
|
||||
`
|
||||
|
||||
sourcesHeader = `
|
||||
# source blocks are generated from your builders; a source can be referenced in
|
||||
# build blocks. A build block runs provisioner and post-processors onto a
|
||||
# source. Read the documentation for source blocks here:
|
||||
# https://www.packer.io/docs/from-1.5/blocks/source`
|
||||
|
||||
buildHeader = `
|
||||
# a build block invokes sources and runs provisionning steps on them. The
|
||||
# documentation for build blocks can be found here:
|
||||
# https://www.packer.io/docs/from-1.5/blocks/build
|
||||
build {
|
||||
`
|
||||
)
|
||||
|
||||
func (c *HCL2UpgradeCommand) RunContext(buildCtx context.Context, cla *HCL2UpgradeArgs) int {
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
var output io.Writer
|
||||
if err := os.MkdirAll(filepath.Dir(cla.OutputFile), 0); err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to create output directory: %v", err))
|
||||
return 1
|
||||
}
|
||||
if f, err := os.Create(cla.OutputFile); err == nil {
|
||||
output = f
|
||||
defer f.Close()
|
||||
} else {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to create output file: %v", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
if _, err := output.Write([]byte(hcl2UpgradeFileHeader)); err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to write to file: %v", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
hdl, ret := c.GetConfigFromJSON(&cla.MetaArgs)
|
||||
if ret != 0 {
|
||||
return ret
|
||||
}
|
||||
|
||||
core := hdl.(*CoreWrapper).Core
|
||||
if err := core.Initialize(); err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Initialization error, continuing: %v", err))
|
||||
}
|
||||
tpl := core.Template
|
||||
|
||||
// Output variables section
|
||||
|
||||
variables := []*template.Variable{}
|
||||
{
|
||||
// sort variables to avoid map's randomness
|
||||
|
||||
for _, variable := range tpl.Variables {
|
||||
variables = append(variables, variable)
|
||||
}
|
||||
sort.Slice(variables, func(i, j int) bool {
|
||||
return variables[i].Key < variables[j].Key
|
||||
})
|
||||
}
|
||||
|
||||
for _, variable := range variables {
|
||||
variablesContent := hclwrite.NewEmptyFile()
|
||||
variablesBody := variablesContent.Body()
|
||||
|
||||
variableBody := variablesBody.AppendNewBlock("variable", []string{variable.Key}).Body()
|
||||
variableBody.SetAttributeRaw("type", hclwrite.Tokens{&hclwrite.Token{Bytes: []byte("string")}})
|
||||
|
||||
if variable.Default != "" || !variable.Required {
|
||||
variableBody.SetAttributeValue("default", hcl2shim.HCL2ValueFromConfigValue(variable.Default))
|
||||
}
|
||||
if isSensitiveVariable(variable.Key, tpl.SensitiveVariables) {
|
||||
variableBody.SetAttributeValue("sensitive", cty.BoolVal(true))
|
||||
}
|
||||
variablesBody.AppendNewline()
|
||||
out.Write(transposeTemplatingCalls(variablesContent.Bytes()))
|
||||
}
|
||||
|
||||
fmt.Fprintln(out, `# "timestamp" template function replacement`)
|
||||
fmt.Fprintln(out, `locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") }`)
|
||||
|
||||
// Output sources section
|
||||
|
||||
builders := []*template.Builder{}
|
||||
{
|
||||
// sort builders to avoid map's randomnes
|
||||
for _, builder := range tpl.Builders {
|
||||
builders = append(builders, builder)
|
||||
}
|
||||
sort.Slice(builders, func(i, j int) bool {
|
||||
return builders[i].Type+builders[i].Name < builders[j].Type+builders[j].Name
|
||||
})
|
||||
}
|
||||
|
||||
out.Write([]byte(sourcesHeader))
|
||||
|
||||
for i, builderCfg := range builders {
|
||||
sourcesContent := hclwrite.NewEmptyFile()
|
||||
body := sourcesContent.Body()
|
||||
|
||||
body.AppendNewline()
|
||||
if !c.Meta.CoreConfig.Components.BuilderStore.Has(builderCfg.Type) {
|
||||
c.Ui.Error(fmt.Sprintf("unknown builder type: %q\n", builderCfg.Type))
|
||||
return 1
|
||||
}
|
||||
if builderCfg.Name == "" || builderCfg.Name == builderCfg.Type {
|
||||
builderCfg.Name = fmt.Sprintf("autogenerated_%d", i+1)
|
||||
}
|
||||
sourceBody := body.AppendNewBlock("source", []string{builderCfg.Type, builderCfg.Name}).Body()
|
||||
|
||||
jsonBodyToHCL2Body(sourceBody, builderCfg.Config)
|
||||
|
||||
_, _ = out.Write(transposeTemplatingCalls(sourcesContent.Bytes()))
|
||||
}
|
||||
|
||||
// Output build section
|
||||
out.Write([]byte(buildHeader))
|
||||
|
||||
buildContent := hclwrite.NewEmptyFile()
|
||||
buildBody := buildContent.Body()
|
||||
if tpl.Description != "" {
|
||||
buildBody.SetAttributeValue("description", cty.StringVal(tpl.Description))
|
||||
buildBody.AppendNewline()
|
||||
}
|
||||
|
||||
sourceNames := []string{}
|
||||
for _, builder := range builders {
|
||||
sourceNames = append(sourceNames, fmt.Sprintf("source.%s.%s", builder.Type, builder.Name))
|
||||
}
|
||||
buildBody.SetAttributeValue("sources", hcl2shim.HCL2ValueFromConfigValue(sourceNames))
|
||||
buildBody.AppendNewline()
|
||||
_, _ = buildContent.WriteTo(out)
|
||||
|
||||
for _, provisioner := range tpl.Provisioners {
|
||||
provisionerContent := hclwrite.NewEmptyFile()
|
||||
body := provisionerContent.Body()
|
||||
|
||||
buildBody.AppendNewline()
|
||||
block := body.AppendNewBlock("provisioner", []string{provisioner.Type})
|
||||
cfg := provisioner.Config
|
||||
if len(provisioner.Except) > 0 {
|
||||
cfg["except"] = provisioner.Except
|
||||
}
|
||||
if len(provisioner.Only) > 0 {
|
||||
cfg["only"] = provisioner.Only
|
||||
}
|
||||
if provisioner.MaxRetries != "" {
|
||||
cfg["max_retries"] = provisioner.MaxRetries
|
||||
}
|
||||
if provisioner.Timeout > 0 {
|
||||
cfg["timeout"] = provisioner.Timeout.String()
|
||||
}
|
||||
jsonBodyToHCL2Body(block.Body(), cfg)
|
||||
|
||||
out.Write(transposeTemplatingCalls(provisionerContent.Bytes()))
|
||||
}
|
||||
for _, pps := range tpl.PostProcessors {
|
||||
postProcessorContent := hclwrite.NewEmptyFile()
|
||||
body := postProcessorContent.Body()
|
||||
|
||||
switch len(pps) {
|
||||
case 0:
|
||||
continue
|
||||
case 1:
|
||||
default:
|
||||
body = body.AppendNewBlock("post-processors", nil).Body()
|
||||
}
|
||||
for _, pp := range pps {
|
||||
ppBody := body.AppendNewBlock("post-processor", []string{pp.Type}).Body()
|
||||
if pp.KeepInputArtifact != nil {
|
||||
ppBody.SetAttributeValue("keep_input_artifact", cty.BoolVal(*pp.KeepInputArtifact))
|
||||
}
|
||||
cfg := pp.Config
|
||||
if len(pp.Except) > 0 {
|
||||
cfg["except"] = pp.Except
|
||||
}
|
||||
if len(pp.Only) > 0 {
|
||||
cfg["only"] = pp.Only
|
||||
}
|
||||
if pp.Name != "" && pp.Name != pp.Type {
|
||||
cfg["name"] = pp.Name
|
||||
}
|
||||
jsonBodyToHCL2Body(ppBody, cfg)
|
||||
}
|
||||
|
||||
_, _ = out.Write(transposeTemplatingCalls(postProcessorContent.Bytes()))
|
||||
}
|
||||
|
||||
_, _ = out.Write([]byte("}\n"))
|
||||
|
||||
_, _ = output.Write(hclwrite.Format(out.Bytes()))
|
||||
|
||||
c.Ui.Say(fmt.Sprintf("Successfully created %s ", cla.OutputFile))
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// transposeTemplatingCalls executes parts of blocks as go template files and replaces
|
||||
// their result with their hcl2 variant. If something goes wrong the template
|
||||
// containing the go template string is returned.
|
||||
func transposeTemplatingCalls(s []byte) []byte {
|
||||
fallbackReturn := func(err error) []byte {
|
||||
return append([]byte(fmt.Sprintf("\n#could not parse template for following block: %q\n", err)), s...)
|
||||
}
|
||||
funcMap := texttemplate.FuncMap{
|
||||
"timestamp": func() string {
|
||||
return "${local.timestamp}"
|
||||
},
|
||||
"isotime": func() string {
|
||||
return "${local.timestamp}"
|
||||
},
|
||||
"user": func(in string) string {
|
||||
return fmt.Sprintf("${var.%s}", in)
|
||||
},
|
||||
"env": func(in string) string {
|
||||
return fmt.Sprintf("${var.%s}", in)
|
||||
},
|
||||
"build": func(a string) string {
|
||||
return fmt.Sprintf("${build.%s}", a)
|
||||
},
|
||||
}
|
||||
|
||||
tpl, err := texttemplate.New("generated").
|
||||
Funcs(funcMap).
|
||||
Parse(string(s))
|
||||
|
||||
if err != nil {
|
||||
return fallbackReturn(err)
|
||||
}
|
||||
|
||||
str := &bytes.Buffer{}
|
||||
v := struct {
|
||||
HTTPIP string
|
||||
HTTPPort string
|
||||
}{
|
||||
HTTPIP: "{{ .HTTPIP }}",
|
||||
HTTPPort: "{{ .HTTPPort }}",
|
||||
}
|
||||
if err := tpl.Execute(str, v); err != nil {
|
||||
return fallbackReturn(err)
|
||||
}
|
||||
|
||||
return str.Bytes()
|
||||
}
|
||||
|
||||
func jsonBodyToHCL2Body(out *hclwrite.Body, kvs map[string]interface{}) {
|
||||
ks := []string{}
|
||||
for k := range kvs {
|
||||
ks = append(ks, k)
|
||||
}
|
||||
sort.Strings(ks)
|
||||
|
||||
for _, k := range ks {
|
||||
value := kvs[k]
|
||||
|
||||
switch value := value.(type) {
|
||||
case map[string]interface{}:
|
||||
var first interface{}
|
||||
for _, elem := range value {
|
||||
first = elem
|
||||
}
|
||||
|
||||
switch first.(type) {
|
||||
case string, int, float64:
|
||||
out.SetAttributeValue(k, hcl2shim.HCL2ValueFromConfigValue(value))
|
||||
default:
|
||||
nestedBlockBody := out.AppendNewBlock(k, nil).Body()
|
||||
jsonBodyToHCL2Body(nestedBlockBody, value)
|
||||
}
|
||||
case []interface{}:
|
||||
if len(value) == 0 {
|
||||
continue
|
||||
}
|
||||
switch value[0].(type) {
|
||||
case map[string]interface{}:
|
||||
for i := range value {
|
||||
value := value[i].(map[string]interface{})
|
||||
nestedBlockBody := out.AppendNewBlock(k, nil).Body()
|
||||
jsonBodyToHCL2Body(nestedBlockBody, value)
|
||||
}
|
||||
continue
|
||||
default:
|
||||
}
|
||||
out.SetAttributeValue(k, hcl2shim.HCL2ValueFromConfigValue(value))
|
||||
default:
|
||||
out.SetAttributeValue(k, hcl2shim.HCL2ValueFromConfigValue(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isSensitiveVariable(key string, vars []*template.Variable) bool {
|
||||
for _, v := range vars {
|
||||
if v.Key == key {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (*HCL2UpgradeCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: packer hcl2_upgrade -output-file=JSON_TEMPLATE.pkr.hcl JSON_TEMPLATE...
|
||||
|
||||
Will transform your JSON template to a HCL2 configuration.
|
||||
`
|
||||
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
||||
func (*HCL2UpgradeCommand) Synopsis() string {
|
||||
return "build image(s) from template"
|
||||
}
|
||||
|
||||
func (*HCL2UpgradeCommand) AutocompleteArgs() complete.Predictor {
|
||||
return complete.PredictNothing
|
||||
}
|
||||
|
||||
func (*HCL2UpgradeCommand) AutocompleteFlags() complete.Flags {
|
||||
return complete.Flags{}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func Test_hcl2_upgrade(t *testing.T) {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("Getwd: %v", err)
|
||||
}
|
||||
_ = cwd
|
||||
|
||||
tc := []struct {
|
||||
folder string
|
||||
}{
|
||||
{"hcl2_upgrade_basic"},
|
||||
}
|
||||
|
||||
for _, tc := range tc {
|
||||
t.Run(tc.folder, func(t *testing.T) {
|
||||
inputPath := filepath.Join(testFixture(tc.folder, "input.json"))
|
||||
outputPath := inputPath + ".pkr.hcl"
|
||||
expectedPath := filepath.Join(testFixture(tc.folder, "expected.pkr.hcl"))
|
||||
p := helperCommand(t, "hcl2_upgrade", inputPath)
|
||||
bs, err := p.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("%v %s", err, bs)
|
||||
}
|
||||
expected := mustBytes(ioutil.ReadFile(expectedPath))
|
||||
actual := mustBytes(ioutil.ReadFile(outputPath))
|
||||
|
||||
if diff := cmp.Diff(expected, actual); diff != "" {
|
||||
t.Fatalf("unexpected output: %s", diff)
|
||||
}
|
||||
os.Remove(outputPath)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func mustBytes(b []byte, e error) []byte {
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
return b
|
||||
}
|
||||
@ -0,0 +1,115 @@
|
||||
# This file was autogenerate by the BETA 'packer hcl2_upgrade' command. We
|
||||
# recommend double checking that everything is correct before going forward. We
|
||||
# also recommend treating this file as disposable. The HCL2 blocks in this
|
||||
# file can be moved to other files. For example, the variable blocks could be
|
||||
# moved to their own 'variables.pkr.hcl' file, etc. Those files need to be
|
||||
# suffixed with '.pkr.hcl' to be visible to Packer. To use multiple files at
|
||||
# once they also need to be in the same folder. 'packer inspect folder/'
|
||||
# will describe to you what is in that folder.
|
||||
|
||||
# All generated input variables will be of string type as this how Packer JSON
|
||||
# views them; you can later on change their type. Read the variables type
|
||||
# constraints documentation
|
||||
# https://www.packer.io/docs/from-1.5/variables#type-constraints for more info.
|
||||
variable "aws_access_key" {
|
||||
type = string
|
||||
default = ""
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "aws_region" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "aws_secret_key" {
|
||||
type = string
|
||||
default = ""
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
# "timestamp" template function replacement
|
||||
locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") }
|
||||
|
||||
# source blocks are generated from your builders; a source can be referenced in
|
||||
# build blocks. A build block runs provisioner and post-processors onto a
|
||||
# source. Read the documentation for source blocks here:
|
||||
# https://www.packer.io/docs/from-1.5/blocks/source
|
||||
source "amazon-ebs" "autogenerated_1" {
|
||||
access_key = "${var.aws_access_key}"
|
||||
ami_description = "Ubuntu 16.04 LTS - expand root partition"
|
||||
ami_name = "ubuntu-16-04-test-${local.timestamp}"
|
||||
encrypt_boot = true
|
||||
launch_block_device_mappings {
|
||||
delete_on_termination = true
|
||||
device_name = "/dev/sda1"
|
||||
volume_size = 48
|
||||
volume_type = "gp2"
|
||||
}
|
||||
region = "${var.aws_region}"
|
||||
secret_key = "${var.aws_secret_key}"
|
||||
source_ami_filter {
|
||||
filters = {
|
||||
name = "ubuntu/images/*/ubuntu-xenial-16.04-amd64-server-*"
|
||||
root-device-type = "ebs"
|
||||
virtualization-type = "hvm"
|
||||
}
|
||||
most_recent = true
|
||||
owners = ["099720109477"]
|
||||
}
|
||||
spot_instance_types = ["t2.small", "t2.medium", "t2.large"]
|
||||
spot_price = "0.0075"
|
||||
ssh_interface = "session_manager"
|
||||
ssh_username = "ubuntu"
|
||||
temporary_iam_instance_profile_policy_document {
|
||||
Statement {
|
||||
Action = ["*"]
|
||||
Effect = "Allow"
|
||||
Resource = ["*"]
|
||||
}
|
||||
Version = "2012-10-17"
|
||||
}
|
||||
}
|
||||
|
||||
# a build block invokes sources and runs provisionning steps on them. The
|
||||
# documentation for build blocks can be found here:
|
||||
# https://www.packer.io/docs/from-1.5/blocks/build
|
||||
build {
|
||||
sources = ["source.amazon-ebs.autogenerated_1"]
|
||||
|
||||
provisioner "shell" {
|
||||
inline = ["echo ${var.secret_account}", "echo ${build.ID}", "echo ${build.SSHPrivateKey}", "sleep 100000"]
|
||||
max_retries = "5"
|
||||
only = ["amazon-ebs"]
|
||||
timeout = "5s"
|
||||
}
|
||||
provisioner "shell-local" {
|
||||
except = ["amazon-ebs"]
|
||||
inline = ["sleep 100000"]
|
||||
timeout = "5s"
|
||||
}
|
||||
post-processor "amazon-import" {
|
||||
format = "vmdk"
|
||||
license_type = "BYOL"
|
||||
region = "eu-west-3"
|
||||
s3_bucket_name = "hashicorp.adrien"
|
||||
tags = {
|
||||
Description = "packer amazon-import ${local.timestamp}"
|
||||
}
|
||||
}
|
||||
post-processors {
|
||||
post-processor "artifice" {
|
||||
keep_input_artifact = true
|
||||
files = ["path/something.ova"]
|
||||
name = "very_special_artifice_post-processor"
|
||||
only = ["amazon-ebs"]
|
||||
}
|
||||
post-processor "amazon-import" {
|
||||
except = ["amazon-ebs"]
|
||||
license_type = "BYOL"
|
||||
s3_bucket_name = "hashicorp.adrien"
|
||||
tags = {
|
||||
Description = "packer amazon-import ${local.timestamp}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,126 @@
|
||||
{
|
||||
"variables": {
|
||||
"aws_region": null,
|
||||
"aws_secret_key": "",
|
||||
"aws_access_key": ""
|
||||
},
|
||||
"sensitive-variables": [
|
||||
"aws_secret_key",
|
||||
"aws_access_key",
|
||||
"potato"
|
||||
],
|
||||
"builders": [
|
||||
{
|
||||
"type": "amazon-ebs",
|
||||
"region": "{{ user `aws_region` }}",
|
||||
"secret_key": "{{ user `aws_secret_key` }}",
|
||||
"access_key": "{{ user `aws_access_key` }}",
|
||||
"ami_name": "ubuntu-16-04-test-{{ timestamp }}",
|
||||
"ami_description": "Ubuntu 16.04 LTS - expand root partition",
|
||||
"source_ami_filter": {
|
||||
"filters": {
|
||||
"virtualization-type": "hvm",
|
||||
"name": "ubuntu/images/*/ubuntu-xenial-16.04-amd64-server-*",
|
||||
"root-device-type": "ebs"
|
||||
},
|
||||
"owners": [
|
||||
"099720109477"
|
||||
],
|
||||
"most_recent": true
|
||||
},
|
||||
"launch_block_device_mappings": [
|
||||
{
|
||||
"delete_on_termination": true,
|
||||
"device_name": "/dev/sda1",
|
||||
"volume_type": "gp2",
|
||||
"volume_size": 48
|
||||
}
|
||||
],
|
||||
"spot_price": "0.0075",
|
||||
"spot_instance_types": [
|
||||
"t2.small",
|
||||
"t2.medium",
|
||||
"t2.large"
|
||||
],
|
||||
"encrypt_boot": true,
|
||||
"ssh_username": "ubuntu",
|
||||
"temporary_iam_instance_profile_policy_document": {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"*"
|
||||
],
|
||||
"Resource": ["*"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ssh_interface": "session_manager"
|
||||
}
|
||||
],
|
||||
"provisioners": [
|
||||
{
|
||||
"type": "shell",
|
||||
"only": [
|
||||
"amazon-ebs"
|
||||
],
|
||||
"max_retries": 5,
|
||||
"timeout": "5s",
|
||||
"inline": [
|
||||
"echo {{ user `secret_account` }}",
|
||||
"echo {{ build `ID` }}",
|
||||
"echo {{ build `SSHPrivateKey` }}",
|
||||
"sleep 100000"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "shell-local",
|
||||
"except": [
|
||||
"amazon-ebs"
|
||||
],
|
||||
"timeout": "5s",
|
||||
"inline": [
|
||||
"sleep 100000"
|
||||
]
|
||||
}
|
||||
],
|
||||
"post-processors": [
|
||||
[
|
||||
{
|
||||
"type": "amazon-import",
|
||||
"region": "eu-west-3",
|
||||
"s3_bucket_name": "hashicorp.adrien",
|
||||
"license_type": "BYOL",
|
||||
"format": "vmdk",
|
||||
"tags": {
|
||||
"Description": "packer amazon-import {{timestamp}}"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"only": [
|
||||
"amazon-ebs"
|
||||
],
|
||||
"files": [
|
||||
"path/something.ova"
|
||||
],
|
||||
"keep_input_artifact": true,
|
||||
"name": "very_special_artifice_post-processor",
|
||||
"type": "artifice"
|
||||
},
|
||||
{
|
||||
"except": [
|
||||
"amazon-ebs"
|
||||
],
|
||||
"type": "amazon-import",
|
||||
"s3_bucket_name": "hashicorp.adrien",
|
||||
"license_type": "BYOL",
|
||||
"tags": {
|
||||
"Description": "packer amazon-import {{timestamp}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
@ -1,95 +0,0 @@
|
||||
Copyright (c) 2017 Martin Atkins
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
---------
|
||||
|
||||
Unicode table generation programs are under a separate copyright and license:
|
||||
|
||||
Copyright (c) 2014 Couchbase, Inc.
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
|
||||
except in compliance with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software distributed under the
|
||||
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
either express or implied. See the License for the specific language governing permissions
|
||||
and limitations under the License.
|
||||
|
||||
---------
|
||||
|
||||
Grapheme break data is provided as part of the Unicode character database,
|
||||
copright 2016 Unicode, Inc, which is provided with the following license:
|
||||
|
||||
Unicode Data Files include all data files under the directories
|
||||
http://www.unicode.org/Public/, http://www.unicode.org/reports/,
|
||||
http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and
|
||||
http://www.unicode.org/utility/trac/browser/.
|
||||
|
||||
Unicode Data Files do not include PDF online code charts under the
|
||||
directory http://www.unicode.org/Public/.
|
||||
|
||||
Software includes any source code published in the Unicode Standard
|
||||
or under the directories
|
||||
http://www.unicode.org/Public/, http://www.unicode.org/reports/,
|
||||
http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and
|
||||
http://www.unicode.org/utility/trac/browser/.
|
||||
|
||||
NOTICE TO USER: Carefully read the following legal agreement.
|
||||
BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S
|
||||
DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"),
|
||||
YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE
|
||||
TERMS AND CONDITIONS OF THIS AGREEMENT.
|
||||
IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE
|
||||
THE DATA FILES OR SOFTWARE.
|
||||
|
||||
COPYRIGHT AND PERMISSION NOTICE
|
||||
|
||||
Copyright © 1991-2017 Unicode, Inc. All rights reserved.
|
||||
Distributed under the Terms of Use in http://www.unicode.org/copyright.html.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Unicode data files and any associated documentation
|
||||
(the "Data Files") or Unicode software and any associated documentation
|
||||
(the "Software") to deal in the Data Files or Software
|
||||
without restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, and/or sell copies of
|
||||
the Data Files or Software, and to permit persons to whom the Data Files
|
||||
or Software are furnished to do so, provided that either
|
||||
(a) this copyright and permission notice appear with all copies
|
||||
of the Data Files or Software, or
|
||||
(b) this copyright and permission notice appear in associated
|
||||
Documentation.
|
||||
|
||||
THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
|
||||
NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
|
||||
DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||
DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THE DATA FILES OR SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name of a copyright holder
|
||||
shall not be used in advertising or otherwise to promote the sale,
|
||||
use or other dealings in these Data Files or Software without prior
|
||||
written authorization of the copyright holder.
|
||||
@ -1,30 +0,0 @@
|
||||
package textseg
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// AllTokens is a utility that uses a bufio.SplitFunc to produce a slice of
|
||||
// all of the recognized tokens in the given buffer.
|
||||
func AllTokens(buf []byte, splitFunc bufio.SplitFunc) ([][]byte, error) {
|
||||
scanner := bufio.NewScanner(bytes.NewReader(buf))
|
||||
scanner.Split(splitFunc)
|
||||
var ret [][]byte
|
||||
for scanner.Scan() {
|
||||
ret = append(ret, scanner.Bytes())
|
||||
}
|
||||
return ret, scanner.Err()
|
||||
}
|
||||
|
||||
// TokenCount is a utility that uses a bufio.SplitFunc to count the number of
|
||||
// recognized tokens in the given buffer.
|
||||
func TokenCount(buf []byte, splitFunc bufio.SplitFunc) (int, error) {
|
||||
scanner := bufio.NewScanner(bytes.NewReader(buf))
|
||||
scanner.Split(splitFunc)
|
||||
var ret int
|
||||
for scanner.Scan() {
|
||||
ret++
|
||||
}
|
||||
return ret, scanner.Err()
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
package textseg
|
||||
|
||||
//go:generate go run make_tables.go -output tables.go
|
||||
//go:generate go run make_test_tables.go -output tables_test.go
|
||||
//go:generate ruby unicode2ragel.rb --url=http://www.unicode.org/Public/9.0.0/ucd/auxiliary/GraphemeBreakProperty.txt -m GraphemeCluster -p "Prepend,CR,LF,Control,Extend,Regional_Indicator,SpacingMark,L,V,T,LV,LVT,E_Base,E_Modifier,ZWJ,Glue_After_Zwj,E_Base_GAZ" -o grapheme_clusters_table.rl
|
||||
//go:generate ragel -Z grapheme_clusters.rl
|
||||
//go:generate gofmt -w grapheme_clusters.go
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,132 +0,0 @@
|
||||
package textseg
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Generated from grapheme_clusters.rl. DO NOT EDIT
|
||||
%%{
|
||||
# (except you are actually in grapheme_clusters.rl here, so edit away!)
|
||||
|
||||
machine graphclust;
|
||||
write data;
|
||||
}%%
|
||||
|
||||
var Error = errors.New("invalid UTF8 text")
|
||||
|
||||
// ScanGraphemeClusters is a split function for bufio.Scanner that splits
|
||||
// on grapheme cluster boundaries.
|
||||
func ScanGraphemeClusters(data []byte, atEOF bool) (int, []byte, error) {
|
||||
if len(data) == 0 {
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
// Ragel state
|
||||
cs := 0 // Current State
|
||||
p := 0 // "Pointer" into data
|
||||
pe := len(data) // End-of-data "pointer"
|
||||
ts := 0
|
||||
te := 0
|
||||
act := 0
|
||||
eof := pe
|
||||
|
||||
// Make Go compiler happy
|
||||
_ = ts
|
||||
_ = te
|
||||
_ = act
|
||||
_ = eof
|
||||
|
||||
startPos := 0
|
||||
endPos := 0
|
||||
|
||||
%%{
|
||||
include GraphemeCluster "grapheme_clusters_table.rl";
|
||||
|
||||
action start {
|
||||
startPos = p
|
||||
}
|
||||
|
||||
action end {
|
||||
endPos = p
|
||||
}
|
||||
|
||||
action emit {
|
||||
return endPos+1, data[startPos:endPos+1], nil
|
||||
}
|
||||
|
||||
ZWJGlue = ZWJ (Glue_After_Zwj | E_Base_GAZ Extend* E_Modifier?)?;
|
||||
AnyExtender = Extend | ZWJGlue | SpacingMark;
|
||||
Extension = AnyExtender*;
|
||||
ReplacementChar = (0xEF 0xBF 0xBD);
|
||||
|
||||
CRLFSeq = CR LF;
|
||||
ControlSeq = Control | ReplacementChar;
|
||||
HangulSeq = (
|
||||
L+ (((LV? V+ | LVT) T*)?|LV?) |
|
||||
LV V* T* |
|
||||
V+ T* |
|
||||
LVT T* |
|
||||
T+
|
||||
) Extension;
|
||||
EmojiSeq = (E_Base | E_Base_GAZ) Extend* E_Modifier? Extension;
|
||||
ZWJSeq = ZWJGlue Extension;
|
||||
EmojiFlagSeq = Regional_Indicator Regional_Indicator? Extension;
|
||||
|
||||
UTF8Cont = 0x80 .. 0xBF;
|
||||
AnyUTF8 = (
|
||||
0x00..0x7F |
|
||||
0xC0..0xDF . UTF8Cont |
|
||||
0xE0..0xEF . UTF8Cont . UTF8Cont |
|
||||
0xF0..0xF7 . UTF8Cont . UTF8Cont . UTF8Cont
|
||||
);
|
||||
|
||||
# OtherSeq is any character that isn't at the start of one of the extended sequences above, followed by extension
|
||||
OtherSeq = (AnyUTF8 - (CR|LF|Control|ReplacementChar|L|LV|V|LVT|T|E_Base|E_Base_GAZ|ZWJ|Regional_Indicator|Prepend)) Extension;
|
||||
|
||||
# PrependSeq is prepend followed by any of the other patterns above, except control characters which explicitly break
|
||||
PrependSeq = Prepend+ (HangulSeq|EmojiSeq|ZWJSeq|EmojiFlagSeq|OtherSeq)?;
|
||||
|
||||
CRLFTok = CRLFSeq >start @end;
|
||||
ControlTok = ControlSeq >start @end;
|
||||
HangulTok = HangulSeq >start @end;
|
||||
EmojiTok = EmojiSeq >start @end;
|
||||
ZWJTok = ZWJSeq >start @end;
|
||||
EmojiFlagTok = EmojiFlagSeq >start @end;
|
||||
OtherTok = OtherSeq >start @end;
|
||||
PrependTok = PrependSeq >start @end;
|
||||
|
||||
main := |*
|
||||
CRLFTok => emit;
|
||||
ControlTok => emit;
|
||||
HangulTok => emit;
|
||||
EmojiTok => emit;
|
||||
ZWJTok => emit;
|
||||
EmojiFlagTok => emit;
|
||||
PrependTok => emit;
|
||||
OtherTok => emit;
|
||||
|
||||
# any single valid UTF-8 character would also be valid per spec,
|
||||
# but we'll handle that separately after the loop so we can deal
|
||||
# with requesting more bytes if we're not at EOF.
|
||||
*|;
|
||||
|
||||
write init;
|
||||
write exec;
|
||||
}%%
|
||||
|
||||
// If we fall out here then we were unable to complete a sequence.
|
||||
// If we weren't able to complete a sequence then either we've
|
||||
// reached the end of a partial buffer (so there's more data to come)
|
||||
// or we have an isolated symbol that would normally be part of a
|
||||
// grapheme cluster but has appeared in isolation here.
|
||||
|
||||
if !atEOF {
|
||||
// Request more
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
// Just take the first UTF-8 sequence and return that.
|
||||
_, seqLen := utf8.DecodeRune(data)
|
||||
return seqLen, data[:seqLen], nil
|
||||
}
|
||||
1583
vendor/github.com/apparentlymart/go-textseg/textseg/grapheme_clusters_table.rl
generated
vendored
1583
vendor/github.com/apparentlymart/go-textseg/textseg/grapheme_clusters_table.rl
generated
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,335 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
#
|
||||
# This scripted has been updated to accept more command-line arguments:
|
||||
#
|
||||
# -u, --url URL to process
|
||||
# -m, --machine Machine name
|
||||
# -p, --properties Properties to add to the machine
|
||||
# -o, --output Write output to file
|
||||
#
|
||||
# Updated by: Marty Schoch <marty.schoch@gmail.com>
|
||||
#
|
||||
# This script uses the unicode spec to generate a Ragel state machine
|
||||
# that recognizes unicode alphanumeric characters. It generates 5
|
||||
# character classes: uupper, ulower, ualpha, udigit, and ualnum.
|
||||
# Currently supported encodings are UTF-8 [default] and UCS-4.
|
||||
#
|
||||
# Usage: unicode2ragel.rb [options]
|
||||
# -e, --encoding [ucs4 | utf8] Data encoding
|
||||
# -h, --help Show this message
|
||||
#
|
||||
# This script was originally written as part of the Ferret search
|
||||
# engine library.
|
||||
#
|
||||
# Author: Rakan El-Khalil <rakan@well.com>
|
||||
|
||||
require 'optparse'
|
||||
require 'open-uri'
|
||||
|
||||
ENCODINGS = [ :utf8, :ucs4 ]
|
||||
ALPHTYPES = { :utf8 => "byte", :ucs4 => "rune" }
|
||||
DEFAULT_CHART_URL = "http://www.unicode.org/Public/5.1.0/ucd/DerivedCoreProperties.txt"
|
||||
DEFAULT_MACHINE_NAME= "WChar"
|
||||
|
||||
###
|
||||
# Display vars & default option
|
||||
|
||||
TOTAL_WIDTH = 80
|
||||
RANGE_WIDTH = 23
|
||||
@encoding = :utf8
|
||||
@chart_url = DEFAULT_CHART_URL
|
||||
machine_name = DEFAULT_MACHINE_NAME
|
||||
properties = []
|
||||
@output = $stdout
|
||||
|
||||
###
|
||||
# Option parsing
|
||||
|
||||
cli_opts = OptionParser.new do |opts|
|
||||
opts.on("-e", "--encoding [ucs4 | utf8]", "Data encoding") do |o|
|
||||
@encoding = o.downcase.to_sym
|
||||
end
|
||||
opts.on("-h", "--help", "Show this message") do
|
||||
puts opts
|
||||
exit
|
||||
end
|
||||
opts.on("-u", "--url URL", "URL to process") do |o|
|
||||
@chart_url = o
|
||||
end
|
||||
opts.on("-m", "--machine MACHINE_NAME", "Machine name") do |o|
|
||||
machine_name = o
|
||||
end
|
||||
opts.on("-p", "--properties x,y,z", Array, "Properties to add to machine") do |o|
|
||||
properties = o
|
||||
end
|
||||
opts.on("-o", "--output FILE", "output file") do |o|
|
||||
@output = File.new(o, "w+")
|
||||
end
|
||||
end
|
||||
|
||||
cli_opts.parse(ARGV)
|
||||
unless ENCODINGS.member? @encoding
|
||||
puts "Invalid encoding: #{@encoding}"
|
||||
puts cli_opts
|
||||
exit
|
||||
end
|
||||
|
||||
##
|
||||
# Downloads the document at url and yields every alpha line's hex
|
||||
# range and description.
|
||||
|
||||
def each_alpha( url, property )
|
||||
open( url ) do |file|
|
||||
file.each_line do |line|
|
||||
next if line =~ /^#/;
|
||||
next if line !~ /; #{property} #/;
|
||||
|
||||
range, description = line.split(/;/)
|
||||
range.strip!
|
||||
description.gsub!(/.*#/, '').strip!
|
||||
|
||||
if range =~ /\.\./
|
||||
start, stop = range.split '..'
|
||||
else start = stop = range
|
||||
end
|
||||
|
||||
yield start.hex .. stop.hex, description
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
###
|
||||
# Formats to hex at minimum width
|
||||
|
||||
def to_hex( n )
|
||||
r = "%0X" % n
|
||||
r = "0#{r}" unless (r.length % 2).zero?
|
||||
r
|
||||
end
|
||||
|
||||
###
|
||||
# UCS4 is just a straight hex conversion of the unicode codepoint.
|
||||
|
||||
def to_ucs4( range )
|
||||
rangestr = "0x" + to_hex(range.begin)
|
||||
rangestr << "..0x" + to_hex(range.end) if range.begin != range.end
|
||||
[ rangestr ]
|
||||
end
|
||||
|
||||
##
|
||||
# 0x00 - 0x7f -> 0zzzzzzz[7]
|
||||
# 0x80 - 0x7ff -> 110yyyyy[5] 10zzzzzz[6]
|
||||
# 0x800 - 0xffff -> 1110xxxx[4] 10yyyyyy[6] 10zzzzzz[6]
|
||||
# 0x010000 - 0x10ffff -> 11110www[3] 10xxxxxx[6] 10yyyyyy[6] 10zzzzzz[6]
|
||||
|
||||
UTF8_BOUNDARIES = [0x7f, 0x7ff, 0xffff, 0x10ffff]
|
||||
|
||||
def to_utf8_enc( n )
|
||||
r = 0
|
||||
if n <= 0x7f
|
||||
r = n
|
||||
elsif n <= 0x7ff
|
||||
y = 0xc0 | (n >> 6)
|
||||
z = 0x80 | (n & 0x3f)
|
||||
r = y << 8 | z
|
||||
elsif n <= 0xffff
|
||||
x = 0xe0 | (n >> 12)
|
||||
y = 0x80 | (n >> 6) & 0x3f
|
||||
z = 0x80 | n & 0x3f
|
||||
r = x << 16 | y << 8 | z
|
||||
elsif n <= 0x10ffff
|
||||
w = 0xf0 | (n >> 18)
|
||||
x = 0x80 | (n >> 12) & 0x3f
|
||||
y = 0x80 | (n >> 6) & 0x3f
|
||||
z = 0x80 | n & 0x3f
|
||||
r = w << 24 | x << 16 | y << 8 | z
|
||||
end
|
||||
|
||||
to_hex(r)
|
||||
end
|
||||
|
||||
def from_utf8_enc( n )
|
||||
n = n.hex
|
||||
r = 0
|
||||
if n <= 0x7f
|
||||
r = n
|
||||
elsif n <= 0xdfff
|
||||
y = (n >> 8) & 0x1f
|
||||
z = n & 0x3f
|
||||
r = y << 6 | z
|
||||
elsif n <= 0xefffff
|
||||
x = (n >> 16) & 0x0f
|
||||
y = (n >> 8) & 0x3f
|
||||
z = n & 0x3f
|
||||
r = x << 10 | y << 6 | z
|
||||
elsif n <= 0xf7ffffff
|
||||
w = (n >> 24) & 0x07
|
||||
x = (n >> 16) & 0x3f
|
||||
y = (n >> 8) & 0x3f
|
||||
z = n & 0x3f
|
||||
r = w << 18 | x << 12 | y << 6 | z
|
||||
end
|
||||
r
|
||||
end
|
||||
|
||||
###
|
||||
# Given a range, splits it up into ranges that can be continuously
|
||||
# encoded into utf8. Eg: 0x00 .. 0xff => [0x00..0x7f, 0x80..0xff]
|
||||
# This is not strictly needed since the current [5.1] unicode standard
|
||||
# doesn't have ranges that straddle utf8 boundaries. This is included
|
||||
# for completeness as there is no telling if that will ever change.
|
||||
|
||||
def utf8_ranges( range )
|
||||
ranges = []
|
||||
UTF8_BOUNDARIES.each do |max|
|
||||
if range.begin <= max
|
||||
if range.end <= max
|
||||
ranges << range
|
||||
return ranges
|
||||
end
|
||||
|
||||
ranges << (range.begin .. max)
|
||||
range = (max + 1) .. range.end
|
||||
end
|
||||
end
|
||||
ranges
|
||||
end
|
||||
|
||||
def build_range( start, stop )
|
||||
size = start.size/2
|
||||
left = size - 1
|
||||
return [""] if size < 1
|
||||
|
||||
a = start[0..1]
|
||||
b = stop[0..1]
|
||||
|
||||
###
|
||||
# Shared prefix
|
||||
|
||||
if a == b
|
||||
return build_range(start[2..-1], stop[2..-1]).map do |elt|
|
||||
"0x#{a} " + elt
|
||||
end
|
||||
end
|
||||
|
||||
###
|
||||
# Unshared prefix, end of run
|
||||
|
||||
return ["0x#{a}..0x#{b} "] if left.zero?
|
||||
|
||||
###
|
||||
# Unshared prefix, not end of run
|
||||
# Range can be 0x123456..0x56789A
|
||||
# Which is equivalent to:
|
||||
# 0x123456 .. 0x12FFFF
|
||||
# 0x130000 .. 0x55FFFF
|
||||
# 0x560000 .. 0x56789A
|
||||
|
||||
ret = []
|
||||
ret << build_range(start, a + "FF" * left)
|
||||
|
||||
###
|
||||
# Only generate middle range if need be.
|
||||
|
||||
if a.hex+1 != b.hex
|
||||
max = to_hex(b.hex - 1)
|
||||
max = "FF" if b == "FF"
|
||||
ret << "0x#{to_hex(a.hex+1)}..0x#{max} " + "0x00..0xFF " * left
|
||||
end
|
||||
|
||||
###
|
||||
# Don't generate last range if it is covered by first range
|
||||
|
||||
ret << build_range(b + "00" * left, stop) unless b == "FF"
|
||||
ret.flatten!
|
||||
end
|
||||
|
||||
def to_utf8( range )
|
||||
utf8_ranges( range ).map do |r|
|
||||
begin_enc = to_utf8_enc(r.begin)
|
||||
end_enc = to_utf8_enc(r.end)
|
||||
build_range begin_enc, end_enc
|
||||
end.flatten!
|
||||
end
|
||||
|
||||
##
|
||||
# Perform a 3-way comparison of the number of codepoints advertised by
|
||||
# the unicode spec for the given range, the originally parsed range,
|
||||
# and the resulting utf8 encoded range.
|
||||
|
||||
def count_codepoints( code )
|
||||
code.split(' ').inject(1) do |acc, elt|
|
||||
if elt =~ /0x(.+)\.\.0x(.+)/
|
||||
if @encoding == :utf8
|
||||
acc * (from_utf8_enc($2) - from_utf8_enc($1) + 1)
|
||||
else
|
||||
acc * ($2.hex - $1.hex + 1)
|
||||
end
|
||||
else
|
||||
acc
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def is_valid?( range, desc, codes )
|
||||
spec_count = 1
|
||||
spec_count = $1.to_i if desc =~ /\[(\d+)\]/
|
||||
range_count = range.end - range.begin + 1
|
||||
|
||||
sum = codes.inject(0) { |acc, elt| acc + count_codepoints(elt) }
|
||||
sum == spec_count and sum == range_count
|
||||
end
|
||||
|
||||
##
|
||||
# Generate the state maching to stdout
|
||||
|
||||
def generate_machine( name, property )
|
||||
pipe = " "
|
||||
@output.puts " #{name} = "
|
||||
each_alpha( @chart_url, property ) do |range, desc|
|
||||
|
||||
codes = (@encoding == :ucs4) ? to_ucs4(range) : to_utf8(range)
|
||||
|
||||
#raise "Invalid encoding of range #{range}: #{codes.inspect}" unless
|
||||
# is_valid? range, desc, codes
|
||||
|
||||
range_width = codes.map { |a| a.size }.max
|
||||
range_width = RANGE_WIDTH if range_width < RANGE_WIDTH
|
||||
|
||||
desc_width = TOTAL_WIDTH - RANGE_WIDTH - 11
|
||||
desc_width -= (range_width - RANGE_WIDTH) if range_width > RANGE_WIDTH
|
||||
|
||||
if desc.size > desc_width
|
||||
desc = desc[0..desc_width - 4] + "..."
|
||||
end
|
||||
|
||||
codes.each_with_index do |r, idx|
|
||||
desc = "" unless idx.zero?
|
||||
code = "%-#{range_width}s" % r
|
||||
@output.puts " #{pipe} #{code} ##{desc}"
|
||||
pipe = "|"
|
||||
end
|
||||
end
|
||||
@output.puts " ;"
|
||||
@output.puts ""
|
||||
end
|
||||
|
||||
@output.puts <<EOF
|
||||
# The following Ragel file was autogenerated with #{$0}
|
||||
# from: #{@chart_url}
|
||||
#
|
||||
# It defines #{properties}.
|
||||
#
|
||||
# To use this, make sure that your alphtype is set to #{ALPHTYPES[@encoding]},
|
||||
# and that your input is in #{@encoding}.
|
||||
|
||||
%%{
|
||||
machine #{machine_name};
|
||||
|
||||
EOF
|
||||
|
||||
properties.each { |x| generate_machine( x, x ) }
|
||||
|
||||
@output.puts <<EOF
|
||||
}%%
|
||||
EOF
|
||||
@ -1,19 +0,0 @@
|
||||
package textseg
|
||||
|
||||
import "unicode/utf8"
|
||||
|
||||
// ScanGraphemeClusters is a split function for bufio.Scanner that splits
|
||||
// on UTF8 sequence boundaries.
|
||||
//
|
||||
// This is included largely for completeness, since this behavior is already
|
||||
// built in to Go when ranging over a string.
|
||||
func ScanUTF8Sequences(data []byte, atEOF bool) (int, []byte, error) {
|
||||
if len(data) == 0 {
|
||||
return 0, nil, nil
|
||||
}
|
||||
r, seqLen := utf8.DecodeRune(data)
|
||||
if r == utf8.RuneError && !atEOF {
|
||||
return 0, nil, nil
|
||||
}
|
||||
return seqLen, data[:seqLen], nil
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
---
|
||||
description: |
|
||||
The `packer hcl2_upgrade` Packer command is used to transpile a JSON
|
||||
configuration template to it's formatted HCL2 counterpart. The command will
|
||||
return a zero exit status on success, and a non-zero exit status on failure.
|
||||
layout: docs
|
||||
page_title: packer hcl2_upgrade - Commands
|
||||
sidebar_title: <tt>hcl2_upgrade</tt>
|
||||
---
|
||||
|
||||
-> **Note:** This command is Beta, and currently being improved upon; do not
|
||||
hesitate [opening a new
|
||||
issue](https://github.com/hashicorp/packer/issues/new/choose) if you find
|
||||
something wrong.
|
||||
|
||||
# `hcl2_upgrade` Command
|
||||
|
||||
The `packer hcl2_upgrade` Packer command is used to transpile a JSON
|
||||
configuration template to it's formatted HCL2 counterpart. The command will
|
||||
return a zero exit status on success, and a non-zero exit status on failure.
|
||||
|
||||
Example usage:
|
||||
|
||||
```shell-session
|
||||
$ packer hcl2_upgrade my-template.json
|
||||
|
||||
Successfully created my-template.json.pkr.hcl
|
||||
```
|
||||
|
||||
## User variables using other user variables
|
||||
|
||||
Packer JSON recently started allowing using user variables from variables. In
|
||||
HCL2, input variables cannot use functions nor other variables and are
|
||||
virtually static, local variables must be used instead to craft more dynamic
|
||||
variables. For that reason `hcl2_upgrade` cannot decide for you what local
|
||||
variables to create and the `hcl2_upgrade` command will simply output all seen
|
||||
variables as an input variable, it is now up to you to create a local variable.
|
||||
|
||||
Here is an example of a local variable using a string input variables:
|
||||
|
||||
```hcl
|
||||
variable "foo" {
|
||||
default = "Hello,"
|
||||
}
|
||||
|
||||
variable "bar" {
|
||||
default = "World!"
|
||||
}
|
||||
|
||||
locals {
|
||||
baz = "${var.foo} ${var.bar}"
|
||||
}
|
||||
```
|
||||
|
||||
## Go template functions
|
||||
|
||||
`hcl2_upgrade` will do its best to transform your go _template calls_ to HCL2,
|
||||
here is the list of calls that should get transformed:
|
||||
- ```{{ user `my_var` }}``` becomes ```${var.my_var}```.
|
||||
- ```{{ env `my_var` }}``` becomes ```${var.my_var}```. Packer HCL2 supports
|
||||
environment variables through input variables. See
|
||||
[docs](http://packer.io/docs/from-1.5/variables#environment-variables)
|
||||
for more info.
|
||||
- ```{{ timestamp }}``` becomes ```${local.timestamp}```, the local variable
|
||||
will be created for all generated files.
|
||||
- ```{{ build `ID` }}``` becomes ```${build.ID}```.
|
||||
|
||||
The rest of the calls should remain go template calls for now, this will be
|
||||
improved over time.
|
||||
|
||||
-> **Note**: The `hcl2_upgrade` command does its best to transform template
|
||||
calls to their JSON counterpart, but it might fail. In that case the
|
||||
`hcl2_upgrade` command will simply output the local HCL2 block without
|
||||
transformation and with the error message in a comment. We are currently
|
||||
working on improving this part of the transformer.
|
||||
|
||||
## Options
|
||||
|
||||
- `-output-file` - File where to put the hcl2 generated config. Defaults to
|
||||
JSON_TEMPLATE.pkr.hcl
|
||||
Loading…
Reference in new issue