This add : * discovery of `packer-plugin-*` binaries from the known folders and ask them to describe themselves * tests For testing: in go we create a bash script that in turn calls back to Go. I could not make the tests to work on windows and then would like to postpone testing this for when we know more about the finite layout of this feature. That is mainly: how things are going to work with init, versioning and such.
172 lines
4.2 KiB
Go
172 lines
4.2 KiB
Go
package plugin
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"sort"
|
|
|
|
packersdk "github.com/hashicorp/packer/packer-plugin-sdk/packer"
|
|
pluginVersion "github.com/hashicorp/packer/packer-plugin-sdk/version"
|
|
"github.com/hashicorp/packer/version"
|
|
)
|
|
|
|
// Set is a plugin set. It's API is meant to be very close to what is returned
|
|
// by plugin.Server
|
|
// It can describe itself or run a single plugin using the CLI arguments.
|
|
type Set struct {
|
|
version string
|
|
sdkVersion string
|
|
Builders map[string]packersdk.Builder
|
|
PostProcessors map[string]packersdk.PostProcessor
|
|
Provisioners map[string]packersdk.Provisioner
|
|
}
|
|
|
|
// SetDescription describes a Set.
|
|
type SetDescription struct {
|
|
Version string `json:"version"`
|
|
SDKVersion string `json:"sdk_version"`
|
|
Builders []string `json:"builders"`
|
|
PostProcessors []string `json:"post_processors"`
|
|
Provisioners []string `json:"provisioners"`
|
|
}
|
|
|
|
////
|
|
// Setup
|
|
////
|
|
|
|
func NewSet() *Set {
|
|
return &Set{
|
|
version: version.String(),
|
|
sdkVersion: version.String(), // TODO: Set me after the split
|
|
Builders: map[string]packersdk.Builder{},
|
|
PostProcessors: map[string]packersdk.PostProcessor{},
|
|
Provisioners: map[string]packersdk.Provisioner{},
|
|
}
|
|
}
|
|
|
|
func (i *Set) SetVersion(version *pluginVersion.PluginVersion) {
|
|
i.version = version.String()
|
|
}
|
|
|
|
func (i *Set) RegisterBuilder(name string, builder packersdk.Builder) {
|
|
if _, found := i.Builders[name]; found {
|
|
panic(fmt.Errorf("registering duplicate %s builder", name))
|
|
}
|
|
i.Builders[name] = builder
|
|
}
|
|
|
|
func (i *Set) RegisterPostProcessor(name string, postProcessor packersdk.PostProcessor) {
|
|
if _, found := i.PostProcessors[name]; found {
|
|
panic(fmt.Errorf("registering duplicate %s post-processor", name))
|
|
}
|
|
i.PostProcessors[name] = postProcessor
|
|
}
|
|
|
|
func (i *Set) RegisterProvisioner(name string, provisioner packersdk.Provisioner) {
|
|
if _, found := i.Provisioners[name]; found {
|
|
panic(fmt.Errorf("registering duplicate %s provisioner", name))
|
|
}
|
|
i.Provisioners[name] = provisioner
|
|
}
|
|
|
|
// Run takes the os Args and runs a packer plugin command from it.
|
|
// * "describe" command makes the plugin set describe itself.
|
|
// * "start builder builder-name" starts the builder "builder-name"
|
|
// * "start post-processor example" starts the post-processor "example"
|
|
func (i *Set) Run() error {
|
|
args := os.Args[1:]
|
|
return i.RunCommand(args...)
|
|
}
|
|
|
|
func (i *Set) RunCommand(args ...string) error {
|
|
if len(args) < 1 {
|
|
return fmt.Errorf("needs at least one argument")
|
|
}
|
|
|
|
switch args[0] {
|
|
case "describe":
|
|
return i.jsonDescribe(os.Stdout)
|
|
case "start":
|
|
args = args[1:]
|
|
if len(args) != 2 {
|
|
return fmt.Errorf("start takes two arguments, for example 'start builder example-builder'. Found: %v", args)
|
|
}
|
|
return i.start(args[0], args[1])
|
|
default:
|
|
return fmt.Errorf("Unknown command: %q", args[0])
|
|
}
|
|
}
|
|
|
|
func (i *Set) start(kind, name string) error {
|
|
server, err := Server()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Printf("[TRACE] starting %s %s", kind, name)
|
|
|
|
switch kind {
|
|
case "builder":
|
|
err = server.RegisterBuilder(i.Builders[name])
|
|
case "post-processor":
|
|
err = server.RegisterPostProcessor(i.PostProcessors[name])
|
|
case "provisioners":
|
|
err = server.RegisterProvisioner(i.Provisioners[name])
|
|
default:
|
|
err = fmt.Errorf("Unknown plugin type: %s", kind)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
server.Serve()
|
|
return nil
|
|
}
|
|
|
|
////
|
|
// Describe
|
|
////
|
|
|
|
func (i *Set) description() SetDescription {
|
|
return SetDescription{
|
|
Version: i.version,
|
|
SDKVersion: i.sdkVersion,
|
|
Builders: i.buildersDescription(),
|
|
PostProcessors: i.postProcessorsDescription(),
|
|
Provisioners: i.provisionersDescription(),
|
|
}
|
|
}
|
|
|
|
func (i *Set) jsonDescribe(out io.Writer) error {
|
|
return json.NewEncoder(out).Encode(i.description())
|
|
}
|
|
|
|
func (i *Set) buildersDescription() []string {
|
|
out := []string{}
|
|
for key := range i.Builders {
|
|
out = append(out, key)
|
|
}
|
|
sort.Strings(out)
|
|
return out
|
|
}
|
|
|
|
func (i *Set) postProcessorsDescription() []string {
|
|
out := []string{}
|
|
for key := range i.PostProcessors {
|
|
out = append(out, key)
|
|
}
|
|
sort.Strings(out)
|
|
return out
|
|
}
|
|
|
|
func (i *Set) provisionersDescription() []string {
|
|
out := []string{}
|
|
for key := range i.Provisioners {
|
|
out = append(out, key)
|
|
}
|
|
sort.Strings(out)
|
|
return out
|
|
}
|