-
Notifications
You must be signed in to change notification settings - Fork 74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Issue 71] Adds convert_to_template option to proxmox builders #72
Changes from 4 commits
cdd14b5
2dfa394
fc68e7d
fc876b4
3ca1ef1
fcb4b79
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,29 +47,33 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook, | |
|
||
comm := &b.config.Comm | ||
|
||
// Build the steps | ||
coreSteps := []multistep.Step{ | ||
&stepStartVM{ | ||
vmCreator: b.vmCreator, | ||
}, | ||
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig), | ||
&stepTypeBootCommand{ | ||
BootConfig: b.config.BootConfig, | ||
Ctx: b.config.Ctx, | ||
}, | ||
&communicator.StepConnect{ | ||
Config: comm, | ||
Host: commHost((*comm).Host()), | ||
SSHConfig: (*comm).SSHConfigFunc(), | ||
}, | ||
&commonsteps.StepProvision{}, | ||
&commonsteps.StepCleanupTempKeys{ | ||
Comm: &b.config.Comm, | ||
}, | ||
&stepConvertToTemplate{}, | ||
&stepFinalizeTemplateConfig{}, | ||
&stepSuccess{}, | ||
// Build the steps (Order matters) | ||
coreSteps := []multistep.Step{} | ||
|
||
coreSteps = append(coreSteps, &stepStartVM{vmCreator: b.vmCreator}) | ||
coreSteps = append(coreSteps, commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig)) | ||
coreSteps = append(coreSteps, &stepTypeBootCommand{ | ||
BootConfig: b.config.BootConfig, | ||
Ctx: b.config.Ctx, | ||
}) | ||
coreSteps = append(coreSteps, &communicator.StepConnect{ | ||
Config: comm, | ||
Host: commHost((*comm).Host()), | ||
SSHConfig: (*comm).SSHConfigFunc(), | ||
}) | ||
coreSteps = append(coreSteps, &commonsteps.StepProvision{}) | ||
coreSteps = append(coreSteps, &commonsteps.StepCleanupTempKeys{ | ||
Comm: &b.config.Comm, | ||
}) | ||
coreSteps = append(coreSteps, &stepStopVM{}) | ||
|
||
if b.config.ConvertToTemplate { | ||
coreSteps = append(coreSteps, &stepConvertToTemplate{}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like better when we pass the configuration to the step and within the step is determined if it should be skipped or not based on the configuration. It makes it easier to log "Skipping ..." and keeps this step slice clean. |
||
} | ||
|
||
coreSteps = append(coreSteps, &stepFinalizeVMConfig{}) | ||
coreSteps = append(coreSteps, &stepSuccess{}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason for this change? This might be me only but I don't find this more readable than the original way of defining the steps, and it complexifies the diff so I would consider leaving this change out of the current patchset. |
||
|
||
preSteps := b.preSteps | ||
for idx := range b.config.AdditionalISOFiles { | ||
preSteps = append(preSteps, &commonsteps.StepDownload{ | ||
|
@@ -97,15 +101,16 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook, | |
return nil, errors.New("build was cancelled") | ||
} | ||
|
||
// Verify that the template_id was set properly, otherwise we didn't progress through the last step | ||
tplID, ok := state.Get("template_id").(int) | ||
// Get vmRef for vmid in artifact storage | ||
vmRef, ok := state.Get("vmRef").(*proxmox.VmRef) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To be sure, if the artifact is a template, do we still get the appropriate ID from the vmRef in state? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: yes we do, the current way this is implemented is by writing |
||
if !ok { | ||
return nil, fmt.Errorf("template ID could not be determined") | ||
return nil, fmt.Errorf("vmRef could not be determined") | ||
} | ||
|
||
artifact := &Artifact{ | ||
builderID: b.id, | ||
templateID: tplID, | ||
vmID: vmRef.VmId(), | ||
isTemplate: b.config.ConvertToTemplate, | ||
proxmoxClient: b.proxmoxClient, | ||
StateData: map[string]interface{}{"generated_data": state.Get("generated_data")}, | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -64,6 +64,7 @@ type Config struct { | |
|
||
TemplateName string `mapstructure:"template_name"` | ||
TemplateDescription string `mapstructure:"template_description"` | ||
ConvertToTemplate bool `mapstructure:"convert_to_template"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The default of this configuration will always be false which will indeed change the default behavior of the builder. Also, in the If the intention is to not change the default behavior, the default should be
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @sylviamoss Thanks for the feedback, this should be resolved now. |
||
|
||
CloudInit bool `mapstructure:"cloud_init"` | ||
CloudInitStoragePool string `mapstructure:"cloud_init_storage_pool"` | ||
|
@@ -109,6 +110,9 @@ type vgaConfig struct { | |
func (c *Config) Prepare(upper interface{}, raws ...interface{}) ([]string, []string, error) { | ||
// Do not add a cloud-init cdrom by default | ||
c.CloudInit = false | ||
// Do not covert vm to a template by default | ||
c.ConvertToTemplate = false | ||
|
||
var md mapstructure.Metadata | ||
err := config.Decode(upper, &config.DecodeOpts{ | ||
Metadata: &md, | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,10 +10,10 @@ import ( | |
packersdk "github.com/hashicorp/packer-plugin-sdk/packer" | ||
) | ||
|
||
// stepFinalizeTemplateConfig does any required modifications to the configuration _after_ | ||
// stepFinalizeVMConfig does any required modifications to the configuration _after_ | ||
// the VM has been converted into a template, such as updating name and description, or | ||
// unmounting the installation ISO. | ||
type stepFinalizeTemplateConfig struct{} | ||
type stepFinalizeVMConfig struct{} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we can either produce a template or a VM, might I suggest renaming this step as |
||
|
||
type templateFinalizer interface { | ||
GetVmConfig(*proxmox.VmRef) (map[string]interface{}, error) | ||
|
@@ -22,7 +22,7 @@ type templateFinalizer interface { | |
|
||
var _ templateFinalizer = &proxmox.Client{} | ||
|
||
func (s *stepFinalizeTemplateConfig) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { | ||
func (s *stepFinalizeVMConfig) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { | ||
ui := state.Get("ui").(packersdk.Ui) | ||
client := state.Get("proxmoxClient").(templateFinalizer) | ||
c := state.Get("config").(*Config) | ||
|
@@ -118,4 +118,4 @@ func (s *stepFinalizeTemplateConfig) Run(ctx context.Context, state multistep.St | |
return multistep.ActionContinue | ||
} | ||
|
||
func (s *stepFinalizeTemplateConfig) Cleanup(state multistep.StateBag) {} | ||
func (s *stepFinalizeVMConfig) Cleanup(state multistep.StateBag) {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package proxmox | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/Telmate/proxmox-api-go/proxmox" | ||
"github.com/hashicorp/packer-plugin-sdk/multistep" | ||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer" | ||
) | ||
|
||
// stepStopVM takes the running VM configured in earlier steps and stops it. | ||
|
||
type stepStopVM struct{} | ||
|
||
type vmTerminator interface { | ||
ShutdownVm(*proxmox.VmRef) (string, error) | ||
} | ||
|
||
var _ templateConverter = &proxmox.Client{} | ||
|
||
func (s *stepStopVM) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder why this becomes a step on its own here, it seems that the |
||
ui := state.Get("ui").(packersdk.Ui) | ||
client := state.Get("proxmoxClient").(vmTerminator) | ||
vmRef := state.Get("vmRef").(*proxmox.VmRef) | ||
|
||
ui.Say("Stopping VM") | ||
_, err := client.ShutdownVm(vmRef) | ||
if err != nil { | ||
err := fmt.Errorf("Error converting VM to template, could not stop: %s", err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we keep this step, the error message should be reworked to specify that this is not converting the VM to a template that failed, but the "stop VM" step. |
||
state.Put("error", err) | ||
ui.Error(err.Error()) | ||
return multistep.ActionHalt | ||
} | ||
|
||
return multistep.ActionContinue | ||
} | ||
|
||
func (s *stepStopVM) Cleanup(state multistep.StateBag) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I don't think there's other possible types than VM and template, I would maybe suggest changing this to a string, and have an enumeration to the side.
Something like
Doing it this way also makes it possible to inject this in the
String()
andDestroy()
methods without needing conditionals.