From 125a2f1f761af101e4cd2659c7148e6ca6c223cc Mon Sep 17 00:00:00 2001 From: Marc Falzon Date: Fri, 7 Aug 2020 16:15:09 +0200 Subject: [PATCH] Remove "exoscale-import" post-processor This change removes the `exoscale-import` post-processor from the upstream Packer repository, following extraction as a standalone plugin in a dedicated repository (https://github.com/exoscale/packer-post-processor-exoscale-import) --- CODEOWNERS | 1 - command/plugin.go | 2 - post-processor/exoscale-import/artifact.go | 31 -- .../exoscale-import/post-processor.go | 270 -------------- .../post-processor.hcl2spec.go | 69 ---- .../exoscale-import/version/version.go | 13 - website/data/docs-navigation.js | 335 ++++++++++++++++++ 7 files changed, 335 insertions(+), 386 deletions(-) delete mode 100644 post-processor/exoscale-import/artifact.go delete mode 100644 post-processor/exoscale-import/post-processor.go delete mode 100644 post-processor/exoscale-import/post-processor.hcl2spec.go delete mode 100644 post-processor/exoscale-import/version/version.go create mode 100644 website/data/docs-navigation.js diff --git a/CODEOWNERS b/CODEOWNERS index e11e01cab..5fd90ba79 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -87,7 +87,6 @@ /post-processor/alicloud-import/ dongxiao.zzh@alibaba-inc.com /post-processor/checksum/ v.tolstov@selfip.ru -/post-processor/exoscale-import/ @falzm @mcorbin /post-processor/googlecompute-export/ crunkleton@google.com /post-processor/yandex-export/ @GennadySpb /post-processor/yandex-import/ @GennadySpb diff --git a/command/plugin.go b/command/plugin.go index 2e039ed6c..b5c08b185 100644 --- a/command/plugin.go +++ b/command/plugin.go @@ -72,7 +72,6 @@ import ( checksumpostprocessor "github.com/hashicorp/packer/post-processor/checksum" compresspostprocessor "github.com/hashicorp/packer/post-processor/compress" digitaloceanimportpostprocessor "github.com/hashicorp/packer/post-processor/digitalocean-import" - exoscaleimportpostprocessor "github.com/hashicorp/packer/post-processor/exoscale-import" googlecomputeexportpostprocessor "github.com/hashicorp/packer/post-processor/googlecompute-export" googlecomputeimportpostprocessor "github.com/hashicorp/packer/post-processor/googlecompute-import" manifestpostprocessor "github.com/hashicorp/packer/post-processor/manifest" @@ -190,7 +189,6 @@ var PostProcessors = map[string]packersdk.PostProcessor{ "checksum": new(checksumpostprocessor.PostProcessor), "compress": new(compresspostprocessor.PostProcessor), "digitalocean-import": new(digitaloceanimportpostprocessor.PostProcessor), - "exoscale-import": new(exoscaleimportpostprocessor.PostProcessor), "googlecompute-export": new(googlecomputeexportpostprocessor.PostProcessor), "googlecompute-import": new(googlecomputeimportpostprocessor.PostProcessor), "manifest": new(manifestpostprocessor.PostProcessor), diff --git a/post-processor/exoscale-import/artifact.go b/post-processor/exoscale-import/artifact.go deleted file mode 100644 index cd3f9797b..000000000 --- a/post-processor/exoscale-import/artifact.go +++ /dev/null @@ -1,31 +0,0 @@ -package exoscaleimport - -const BuilderId = "packer.post-processor.exoscale-import" - -type Artifact struct { - id string -} - -func (a *Artifact) BuilderId() string { - return BuilderId -} - -func (a *Artifact) Id() string { - return a.id -} - -func (a *Artifact) Files() []string { - return nil -} - -func (a *Artifact) String() string { - return a.id -} - -func (a *Artifact) State(name string) interface{} { - return nil -} - -func (a *Artifact) Destroy() error { - return nil -} diff --git a/post-processor/exoscale-import/post-processor.go b/post-processor/exoscale-import/post-processor.go deleted file mode 100644 index 1045bf906..000000000 --- a/post-processor/exoscale-import/post-processor.go +++ /dev/null @@ -1,270 +0,0 @@ -//go:generate mapstructure-to-hcl2 -type Config - -package exoscaleimport - -import ( - "context" - "crypto/md5" - "encoding/base64" - "fmt" - "io" - "os" - "path/filepath" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/s3" - "github.com/aws/aws-sdk-go/service/s3/s3manager" - "github.com/exoscale/egoscale" - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/hashicorp/packer-plugin-sdk/common" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer-plugin-sdk/template/config" - "github.com/hashicorp/packer/builder/file" - "github.com/hashicorp/packer/builder/qemu" - "github.com/hashicorp/packer/post-processor/artifice" - "github.com/hashicorp/packer/post-processor/exoscale-import/version" -) - -var ( - defaultTemplateZone = "ch-gva-2" - defaultAPIEndpoint = "https://api.exoscale.com/compute" - defaultSOSEndpoint = "https://sos-" + defaultTemplateZone + ".exo.io" -) - -type Config struct { - common.PackerConfig `mapstructure:",squash"` - SkipClean bool `mapstructure:"skip_clean"` - - SOSEndpoint string `mapstructure:"sos_endpoint"` - APIEndpoint string `mapstructure:"api_endpoint"` - APIKey string `mapstructure:"api_key"` - APISecret string `mapstructure:"api_secret"` - ImageBucket string `mapstructure:"image_bucket"` - TemplateZone string `mapstructure:"template_zone"` - TemplateName string `mapstructure:"template_name"` - TemplateDescription string `mapstructure:"template_description"` - TemplateUsername string `mapstructure:"template_username"` - TemplateDisablePassword bool `mapstructure:"template_disable_password"` - TemplateDisableSSHKey bool `mapstructure:"template_disable_sshkey"` -} - -func init() { - egoscale.UserAgent = "Packer-Exoscale/" + version.ExoscaleImportPluginVersion.FormattedVersion() + " " + egoscale.UserAgent -} - -type PostProcessor struct { - config Config -} - -func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } - -func (p *PostProcessor) Configure(raws ...interface{}) error { - p.config.TemplateZone = defaultTemplateZone - p.config.APIEndpoint = defaultAPIEndpoint - p.config.SOSEndpoint = defaultSOSEndpoint - - if err := config.Decode(&p.config, nil, raws...); err != nil { - return err - } - - if p.config.APIKey == "" { - p.config.APIKey = os.Getenv("EXOSCALE_API_KEY") - } - - if p.config.APISecret == "" { - p.config.APISecret = os.Getenv("EXOSCALE_API_SECRET") - } - - requiredArgs := map[string]*string{ - "api_key": &p.config.APIKey, - "api_secret": &p.config.APISecret, - "api_endpoint": &p.config.APIEndpoint, - "sos_endpoint": &p.config.SOSEndpoint, - "image_bucket": &p.config.ImageBucket, - "template_zone": &p.config.TemplateZone, - "template_name": &p.config.TemplateName, - "template_description": &p.config.TemplateDescription, - } - - errs := new(packersdk.MultiError) - for k, v := range requiredArgs { - if *v == "" { - errs = packersdk.MultiErrorAppend( - errs, fmt.Errorf("%s must be set", k)) - } - } - - if len(errs.Errors) > 0 { - return errs - } - - packersdk.LogSecretFilter.Set(p.config.APIKey, p.config.APISecret) - - return nil -} - -func (p *PostProcessor) PostProcess(ctx context.Context, ui packersdk.Ui, a packersdk.Artifact) (packersdk.Artifact, bool, bool, error) { - switch a.BuilderId() { - case qemu.BuilderId, file.BuilderId, artifice.BuilderId: - break - - default: - err := fmt.Errorf( - "Unknown artifact type: %s\nCan only import from QEMU/file builders and Artifice post-processor artifacts.", - a.BuilderId()) - return nil, false, false, err - } - - ui.Message("Uploading template image") - url, md5sum, err := p.uploadImage(ctx, ui, a) - if err != nil { - return nil, false, false, fmt.Errorf("unable to upload image: %s", err) - } - - ui.Message("Registering template") - id, err := p.registerTemplate(ctx, ui, url, md5sum) - if err != nil { - return nil, false, false, fmt.Errorf("unable to register template: %s", err) - } - - if !p.config.SkipClean { - ui.Message("Deleting uploaded template image") - if err = p.deleteImage(ctx, ui, a); err != nil { - return nil, false, false, fmt.Errorf("unable to delete uploaded template image: %s", err) - } - } - - return &Artifact{id}, false, false, nil -} - -func (p *PostProcessor) uploadImage(ctx context.Context, ui packersdk.Ui, a packersdk.Artifact) (string, string, error) { - var ( - imageFile = a.Files()[0] - bucketFile = filepath.Base(imageFile) - ) - - f, err := os.Open(imageFile) - if err != nil { - return "", "", err - } - defer f.Close() - - fileInfo, err := f.Stat() - if err != nil { - return "", "", err - } - - // For tracking image file upload progress - pf := ui.TrackProgress(imageFile, 0, fileInfo.Size(), f) - defer pf.Close() - - hash := md5.New() - if _, err := io.Copy(hash, f); err != nil { - return "", "", fmt.Errorf("image checksumming failed: %s", err) - } - if _, err := f.Seek(0, 0); err != nil { - return "", "", err - } - - sess := session.Must(session.NewSessionWithOptions(session.Options{Config: aws.Config{ - Region: aws.String(p.config.TemplateZone), - Endpoint: aws.String(p.config.SOSEndpoint), - Credentials: credentials.NewStaticCredentials(p.config.APIKey, p.config.APISecret, "")}})) - - uploader := s3manager.NewUploader(sess) - output, err := uploader.UploadWithContext(ctx, &s3manager.UploadInput{ - Body: pf, - Bucket: aws.String(p.config.ImageBucket), - Key: aws.String(bucketFile), - ContentMD5: aws.String(base64.StdEncoding.EncodeToString(hash.Sum(nil))), - ACL: aws.String("public-read"), - }) - if err != nil { - return "", "", err - } - - return output.Location, fmt.Sprintf("%x", hash.Sum(nil)), nil -} - -func (p *PostProcessor) deleteImage(ctx context.Context, ui packersdk.Ui, a packersdk.Artifact) error { - var ( - imageFile = a.Files()[0] - bucketFile = filepath.Base(imageFile) - ) - - sess := session.Must(session.NewSessionWithOptions(session.Options{Config: aws.Config{ - Region: aws.String(p.config.TemplateZone), - Endpoint: aws.String(p.config.SOSEndpoint), - Credentials: credentials.NewStaticCredentials(p.config.APIKey, p.config.APISecret, "")}})) - - svc := s3.New(sess) - if _, err := svc.DeleteObject(&s3.DeleteObjectInput{ - Bucket: aws.String(p.config.ImageBucket), - Key: aws.String(bucketFile), - }); err != nil { - return err - } - - return nil -} - -func (p *PostProcessor) registerTemplate(ctx context.Context, ui packersdk.Ui, url, md5sum string) (string, error) { - var ( - passwordEnabled = !p.config.TemplateDisablePassword - sshkeyEnabled = !p.config.TemplateDisableSSHKey - regErr error - ) - - exo := egoscale.NewClient(p.config.APIEndpoint, p.config.APIKey, p.config.APISecret) - exo.RetryStrategy = egoscale.FibonacciRetryStrategy - - zone := egoscale.Zone{Name: p.config.TemplateZone} - if resp, err := exo.GetWithContext(ctx, &zone); err != nil { - return "", fmt.Errorf("template zone lookup failed: %s", err) - } else { - zone.ID = resp.(*egoscale.Zone).ID - } - - req := egoscale.RegisterCustomTemplate{ - URL: url, - ZoneID: zone.ID, - Name: p.config.TemplateName, - Displaytext: p.config.TemplateDescription, - PasswordEnabled: &passwordEnabled, - SSHKeyEnabled: &sshkeyEnabled, - Details: map[string]string{"username": p.config.TemplateUsername}, - Checksum: md5sum, - } - - res := make([]egoscale.Template, 0) - - exo.AsyncRequestWithContext(ctx, req, func(jobRes *egoscale.AsyncJobResult, err error) bool { - if err != nil { - regErr = fmt.Errorf("request failed: %s", err) - return false - } else if jobRes.JobStatus == egoscale.Pending { - // Job is not completed yet - ui.Message("template registration in progress") - return true - } - - if err := jobRes.Result(&res); err != nil { - regErr = err - return false - } - - if len(res) != 1 { - regErr = fmt.Errorf("unexpected response from API (expected 1 item, got %d)", len(res)) - return false - } - - return false - }) - if regErr != nil { - return "", regErr - } - - return res[0].ID.String(), nil -} diff --git a/post-processor/exoscale-import/post-processor.hcl2spec.go b/post-processor/exoscale-import/post-processor.hcl2spec.go deleted file mode 100644 index e10719ab4..000000000 --- a/post-processor/exoscale-import/post-processor.hcl2spec.go +++ /dev/null @@ -1,69 +0,0 @@ -// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT. - -package exoscaleimport - -import ( - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/zclconf/go-cty/cty" -) - -// FlatConfig is an auto-generated flat version of Config. -// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. -type FlatConfig struct { - PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"` - PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"` - PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"` - PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"` - PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"` - PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` - PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` - PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` - SkipClean *bool `mapstructure:"skip_clean" cty:"skip_clean" hcl:"skip_clean"` - SOSEndpoint *string `mapstructure:"sos_endpoint" cty:"sos_endpoint" hcl:"sos_endpoint"` - APIEndpoint *string `mapstructure:"api_endpoint" cty:"api_endpoint" hcl:"api_endpoint"` - APIKey *string `mapstructure:"api_key" cty:"api_key" hcl:"api_key"` - APISecret *string `mapstructure:"api_secret" cty:"api_secret" hcl:"api_secret"` - ImageBucket *string `mapstructure:"image_bucket" cty:"image_bucket" hcl:"image_bucket"` - TemplateZone *string `mapstructure:"template_zone" cty:"template_zone" hcl:"template_zone"` - TemplateName *string `mapstructure:"template_name" cty:"template_name" hcl:"template_name"` - TemplateDescription *string `mapstructure:"template_description" cty:"template_description" hcl:"template_description"` - TemplateUsername *string `mapstructure:"template_username" cty:"template_username" hcl:"template_username"` - TemplateDisablePassword *bool `mapstructure:"template_disable_password" cty:"template_disable_password" hcl:"template_disable_password"` - TemplateDisableSSHKey *bool `mapstructure:"template_disable_sshkey" cty:"template_disable_sshkey" hcl:"template_disable_sshkey"` -} - -// FlatMapstructure returns a new FlatConfig. -// FlatConfig is an auto-generated flat version of Config. -// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { - return new(FlatConfig) -} - -// HCL2Spec returns the hcl spec of a Config. -// This spec is used by HCL to read the fields of Config. -// The decoded values from this spec will then be applied to a FlatConfig. -func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { - s := map[string]hcldec.Spec{ - "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, - "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, - "packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false}, - "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, - "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, - "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, - "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, - "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, - "skip_clean": &hcldec.AttrSpec{Name: "skip_clean", Type: cty.Bool, Required: false}, - "sos_endpoint": &hcldec.AttrSpec{Name: "sos_endpoint", Type: cty.String, Required: false}, - "api_endpoint": &hcldec.AttrSpec{Name: "api_endpoint", Type: cty.String, Required: false}, - "api_key": &hcldec.AttrSpec{Name: "api_key", Type: cty.String, Required: false}, - "api_secret": &hcldec.AttrSpec{Name: "api_secret", Type: cty.String, Required: false}, - "image_bucket": &hcldec.AttrSpec{Name: "image_bucket", Type: cty.String, Required: false}, - "template_zone": &hcldec.AttrSpec{Name: "template_zone", Type: cty.String, Required: false}, - "template_name": &hcldec.AttrSpec{Name: "template_name", Type: cty.String, Required: false}, - "template_description": &hcldec.AttrSpec{Name: "template_description", Type: cty.String, Required: false}, - "template_username": &hcldec.AttrSpec{Name: "template_username", Type: cty.String, Required: false}, - "template_disable_password": &hcldec.AttrSpec{Name: "template_disable_password", Type: cty.Bool, Required: false}, - "template_disable_sshkey": &hcldec.AttrSpec{Name: "template_disable_sshkey", Type: cty.Bool, Required: false}, - } - return s -} diff --git a/post-processor/exoscale-import/version/version.go b/post-processor/exoscale-import/version/version.go deleted file mode 100644 index 0da76884d..000000000 --- a/post-processor/exoscale-import/version/version.go +++ /dev/null @@ -1,13 +0,0 @@ -package version - -import ( - "github.com/hashicorp/packer-plugin-sdk/version" - packerVersion "github.com/hashicorp/packer/version" -) - -var ExoscaleImportPluginVersion *version.PluginVersion - -func init() { - ExoscaleImportPluginVersion = version.InitializePluginVersion( - packerVersion.Version, packerVersion.VersionPrerelease) -} diff --git a/website/data/docs-navigation.js b/website/data/docs-navigation.js new file mode 100644 index 000000000..a65fd5c5a --- /dev/null +++ b/website/data/docs-navigation.js @@ -0,0 +1,335 @@ +// The root folder for this documentation category is `pages/docs` +// +// - A string refers to the name of a file +// - A "category" value refers to the name of a directory +// - All directories must have an "index.mdx" file to serve as +// the landing page for the category + +export default [ + '--------', + 'terminology', + { + category: 'commands', + content: ['init', 'build', 'console', 'fix', 'fmt', 'inspect', 'validate', 'hcl2_upgrade'], + }, + { + category: 'templates', + content: [ + { + category: 'hcl_templates', + content: [ + { + category: 'blocks', + content: [ + { + category: 'build', + content: [ + 'source', + 'provisioner', + 'post-processor', + 'post-processors', + ], + }, + 'locals', + 'source', + 'variable', + 'packer', + 'data' + ], + }, + { + category: 'functions', + content: [ + { + category: 'contextual', + content: [ + 'aws_secretsmanager', + 'consul', + 'env', + 'vault', + ], + }, + { + category: 'numeric', + content: [ + 'abs', + 'ceil', + 'floor', + 'log', + 'max', + 'min', + 'parseint', + 'pow', + 'signum', + ], + }, + { + category: 'string', + content: [ + 'chomp', + 'format', + 'formatlist', + 'indent', + 'join', + 'lower', + 'replace', + 'regex_replace', + 'regex', + 'regexall', + 'split', + 'strrev', + 'substr', + 'title', + 'trim', + 'trimprefix', + 'trimsuffix', + 'trimspace', + 'upper', + ], + }, + { + category: 'collection', + content: [ + 'chunklist', + 'coalesce', + 'coalescelist', + 'compact', + 'concat', + 'contains', + 'distinct', + 'element', + 'flatten', + 'keys', + 'length', + 'lookup', + 'merge', + 'range', + 'reverse', + 'setintersection', + 'setproduct', + 'setunion', + 'slice', + 'sort', + 'values', + 'zipmap', + ], + }, + { + category: 'encoding', + content: [ + 'base64decode', + 'base64encode', + 'csvdecode', + 'jsondecode', + 'jsonencode', + 'urlencode', + 'yamldecode', + 'yamlencode', + ], + }, + { + category: 'file', + content: [ + 'abspath', + 'basename', + 'dirname', + 'file', + 'fileexists', + 'fileset', + 'pathexpand', + ], + }, + { + category: 'datetime', + content: ['formatdate', 'timeadd', 'timestamp'], + }, + { + category: 'crypto', + content: [ + 'bcrypt', + 'md5', + 'rsadecrypt', + 'sha1', + 'sha256', + 'sha512', + ], + }, + { + category: 'uuid', + content: ['uuidv4', 'uuidv5'], + }, + { + category: 'ipnet', + content: ['cidrhost', 'cidrnetmask', 'cidrsubnet'], + }, + { + category: 'conversion', + content: ['can', 'convert', 'try'], + }, + ], + }, + 'variables', + 'locals', + 'contextual-variables', + 'datasources', + 'path-variables', + 'syntax', + 'onlyexcept', + 'expressions', + 'syntax-json', + ], + }, + { + category: "legacy_json_templates", + content: [ + 'builders', + 'communicator', + 'engine', + 'post-processors', + 'provisioners', + 'user-variables', + ] + }, + ], + }, + '----------', + {category: 'communicators', content: ['ssh', 'winrm']}, + { + category: 'builders', + content: [ + 'alicloud-ecs', + { + category: 'amazon', + content: ['chroot', 'ebs', 'ebssurrogate', 'ebsvolume', 'instance'], + }, + { + category: 'azure', + content: ['arm', 'chroot'], + }, + 'cloudstack', + 'digitalocean', + 'docker', + 'file', + 'googlecompute', + 'hetzner-cloud', + 'hyperone', + {category: 'hyperv', content: ['iso', 'vmcx']}, + 'linode', + 'lxc', + 'lxd', + 'ncloud', + 'null', + 'oneandone', + 'openstack', + {category: 'oracle', content: ['classic', 'oci']}, + { + category: 'outscale', + content: ['chroot', 'bsu', 'bsusurrogate', 'bsuvolume'], + }, + {category: 'parallels', content: ['iso', 'pvm']}, + 'profitbricks', + {category: 'proxmox', content: ['iso', 'clone']}, + 'qemu', + 'scaleway', + 'tencentcloud-cvm', + 'jdcloud', + 'triton', + 'ucloud-uhost', + 'vagrant', + { + category: 'virtualbox', + content: ['iso', 'ovf', 'vm'], + }, + { + category: 'vmware', + content: ['iso', 'vmx', 'vsphere-iso', 'vsphere-clone'], + }, + 'yandex', + 'custom', + 'community-supported', + ], + }, + { + category: 'datasources', + content: [ + { + category: 'amazon', + content: [ + 'ami', + 'secretsmanager' + ], + }, + ] + }, + { + category: 'provisioners', + content: [ + 'ansible-local', + 'ansible', + 'breakpoint', + 'chef-client', + 'chef-solo', + 'converge', + 'file', + 'inspec', + 'powershell', + 'puppet-masterless', + 'puppet-server', + 'salt-masterless', + 'shell', + 'shell-local', + 'windows-shell', + 'windows-restart', + 'custom', + 'community-supported', + ], + }, + { + category: 'post-processors', + content: [ + 'alicloud-import', + 'amazon-import', + 'artifice', + 'compress', + 'checksum', + 'digitalocean-import', + 'docker-import', + 'docker-push', + 'docker-save', + 'docker-tag', + 'googlecompute-export', + 'googlecompute-import', + 'manifest', + 'shell-local', + 'ucloud-import', + 'vagrant', + 'vagrant-cloud', + 'vsphere', + 'vsphere-template', + 'yandex-export', + 'yandex-import', + 'community-supported', + ], + }, + '----------', + 'install', + 'configure', + '----------', + { + category: 'plugins', + content: [ + { + category: 'creation', + content: [ + 'custom-builders', + 'custom-post-processors', + 'custom-provisioners', + 'custom-datasources', + ], + }, + ], + }, + + '---------', + 'debugging', +]