packer-cn/command/meta.go
Adrien Delorme 5ba134ac5b
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 docs
2020-08-25 10:51:43 +02:00

105 lines
2.7 KiB
Go

package command
import (
"bufio"
"flag"
"io"
"os"
kvflag "github.com/hashicorp/packer/helper/flag-kv"
"github.com/hashicorp/packer/helper/wrappedstreams"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template"
)
// FlagSetFlags is an enum to define what flags are present in the
// default FlagSet returned by Meta.FlagSet
type FlagSetFlags uint
const (
FlagSetNone FlagSetFlags = 0
FlagSetBuildFilter FlagSetFlags = 1 << iota
FlagSetVars
)
// Meta contains the meta-options and functionality that nearly every
// Packer command inherits.
type Meta struct {
CoreConfig *packer.CoreConfig
Ui packer.Ui
Version string
}
// Core returns the core for the given template given the configured
// CoreConfig and user variables on this Meta.
func (m *Meta) Core(tpl *template.Template, cla *MetaArgs) (*packer.Core, error) {
// Copy the config so we don't modify it
config := *m.CoreConfig
config.Template = tpl
fj := &kvflag.FlagJSON{}
// First populate fj with contents from var files
for _, file := range cla.VarFiles {
err := fj.Set(file)
if err != nil {
return nil, err
}
}
// Now read fj values back into flagvars and set as config.Variables. Only
// add to flagVars if the key doesn't already exist, because flagVars comes
// from the command line and should not be overridden by variable files.
if cla.Vars == nil {
cla.Vars = map[string]string{}
}
for k, v := range *fj {
if _, exists := cla.Vars[k]; !exists {
cla.Vars[k] = v
}
}
config.Variables = cla.Vars
core := packer.NewCore(&config)
return core, nil
}
// FlagSet returns a FlagSet with the common flags that every
// command implements. The exact behavior of FlagSet can be configured
// using the flags as the second parameter, for example to disable
// build settings on the commands that don't handle builds.
func (m *Meta) FlagSet(n string, _ FlagSetFlags) *flag.FlagSet {
f := flag.NewFlagSet(n, flag.ContinueOnError)
// Create an io.Writer that writes to our Ui properly for errors.
// This is kind of a hack, but it does the job. Basically: create
// a pipe, use a scanner to break it into lines, and output each line
// to the UI. Do this forever.
errR, errW := io.Pipe()
errScanner := bufio.NewScanner(errR)
go func() {
for errScanner.Scan() {
m.Ui.Error(errScanner.Text())
}
}()
f.SetOutput(errW)
return f
}
// ValidateFlags should be called after parsing flags to validate the
// given flags
func (m *Meta) ValidateFlags() error {
// TODO
return nil
}
// StdinPiped returns true if the input is piped.
func (m *Meta) StdinPiped() bool {
fi, err := wrappedstreams.Stdin().Stat()
if err != nil {
// If there is an error, let's just say its not piped
return false
}
return fi.Mode()&os.ModeNamedPipe != 0
}