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 }