Skip to content

Commit 98e6060

Browse files
committed
rework of delete/keep after build and updated output message to show details of artifact created
1 parent 4a26c49 commit 98e6060

6 files changed

Lines changed: 270 additions & 118 deletions

File tree

builder/proxmox/ct/artifact.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// FILE 1: artifact.go - CREATE THIS NEW FILE
2+
// Location: /builder/proxmox/ct/artifact.go
3+
// This is a completely new file - copy everything below
4+
5+
package proxmoxct
6+
7+
import (
8+
"fmt"
9+
"log"
10+
11+
"github.com/Telmate/proxmox-api-go/proxmox"
12+
)
13+
14+
// Artifact represents a Proxmox container template
15+
type Artifact struct {
16+
builderID string
17+
templateID int
18+
nodeName string
19+
templateName string
20+
proxmoxClient *proxmox.Client
21+
stateData map[string]interface{}
22+
}
23+
24+
// BuilderId returns the builder ID
25+
func (a *Artifact) BuilderId() string {
26+
return a.builderID
27+
}
28+
29+
// Files returns the files associated with the artifact
30+
// For Proxmox containers, we don't have local files
31+
func (a *Artifact) Files() []string {
32+
return nil
33+
}
34+
35+
// Id returns a string identifier for the artifact
36+
func (a *Artifact) Id() string {
37+
return fmt.Sprintf("%s:%d", a.nodeName, a.templateID)
38+
}
39+
40+
// String returns a human-readable string representation
41+
func (a *Artifact) String() string {
42+
if a.templateName != "" {
43+
return fmt.Sprintf("Container template '%s' (ID: %d) created on node '%s'",
44+
a.templateName, a.templateID, a.nodeName)
45+
}
46+
return fmt.Sprintf("Container template (ID: %d) created on node '%s'",
47+
a.templateID, a.nodeName)
48+
}
49+
50+
// State returns state data for the artifact
51+
func (a *Artifact) State(name string) interface{} {
52+
if a.stateData != nil {
53+
return a.stateData[name]
54+
}
55+
return nil
56+
}
57+
58+
// Destroy removes the container template from Proxmox
59+
func (a *Artifact) Destroy() error {
60+
log.Printf("[INFO] Destroying container template %d on node %s", a.templateID, a.nodeName)
61+
62+
// Create a VM reference for the container
63+
vmRef := proxmox.NewVmRef(a.templateID)
64+
vmRef.SetNode(a.nodeName)
65+
vmRef.SetVmType("lxc")
66+
67+
// First, try to stop the container if it's running
68+
// This is important for containers that might not be templates yet
69+
_, stopErr := a.proxmoxClient.StopVm(vmRef)
70+
if stopErr != nil {
71+
// It's okay if stop fails - container might already be stopped or be a template
72+
log.Printf("[WARN] Could not stop container %d (this is normal for templates): %s",
73+
a.templateID, stopErr)
74+
}
75+
76+
// Delete the container/template
77+
_, err := a.proxmoxClient.DeleteVm(vmRef)
78+
if err != nil {
79+
return fmt.Errorf("error destroying container template %d: %s", a.templateID, err)
80+
}
81+
82+
log.Printf("[INFO] Successfully destroyed container template %d", a.templateID)
83+
return nil
84+
}

builder/proxmox/ct/builder.go

Lines changed: 66 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// FILE 2: builder.go - REPLACE YOUR ENTIRE FILE WITH THIS
2+
// Location: /builder/proxmox/ct/builder.go
3+
// Replace your entire existing builder.go with everything below
4+
15
package proxmoxct
26

37
import (
@@ -6,13 +10,13 @@ import (
610
"fmt"
711
"net/url"
812
"strings"
9-
13+
1014
"github.com/hashicorp/hcl/v2/hcldec"
11-
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
15+
"github.com/hashicorp/packer-plugin-sdk/communicator"
1216
"github.com/hashicorp/packer-plugin-sdk/multistep"
1317
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
14-
"github.com/hashicorp/packer-plugin-sdk/communicator"
15-
18+
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
19+
1620
proxmox "github.com/Telmate/proxmox-api-go/proxmox"
1721
common "github.com/hashicorp/packer-plugin-proxmox/builder/proxmox/common"
1822
)
@@ -50,44 +54,80 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
5054
steps := []multistep.Step{
5155
new(stepCtCreate),
5256
}
53-
57+
5458
// Only add communicator steps if not using "none"
5559
if b.config.Comm.Type != "none" {
5660
steps = append(steps,
5761
// Go straight to SSH connection - it will use ssh_host from config
5862
&communicator.StepConnect{
5963
Config: &b.config.Comm,
60-
Host: commHost(b.config.Comm.Host()), // Pass the configured host
64+
Host: commHost(b.config.Comm.Host()), // Pass the configured host
6165
SSHConfig: b.config.Comm.SSHConfigFunc(),
6266
},
6367
new(stepProvision),
6468
)
6569
}
66-
70+
6771
// Add our own template conversion step for containers
6872
steps = append(steps, new(stepConvertCtToTemplate))
69-
70-
// Mark as successful
71-
state.Put("success", true)
7273

7374
// Run the steps
7475
runner := commonsteps.NewRunner(steps, b.config.PackerConfig, ui)
7576
runner.Run(ctx, state)
7677

7778
// If there was an error, return that
79+
// The stepCtCreate.Cleanup() method will handle container deletion
7880
if rawErr, ok := state.GetOk("error"); ok {
7981
return nil, rawErr.(error)
8082
}
8183

82-
// Get the container ID
83-
if vmRefUntyped, ok := state.GetOk("vmRef"); ok {
84-
vmRef := vmRefUntyped.(*proxmox.VmRef)
85-
templateID := vmRef.VmId()
86-
ui.Say(fmt.Sprintf("Container template created successfully: %d", templateID))
84+
// Check if the run was cancelled
85+
if _, ok := state.GetOk(multistep.StateCancelled); ok {
86+
return nil, fmt.Errorf("build was cancelled")
87+
}
88+
89+
// Check if the run was halted
90+
if _, ok := state.GetOk(multistep.StateHalted); ok {
91+
return nil, fmt.Errorf("build was halted")
92+
}
93+
94+
// Get the container reference
95+
vmRefUntyped, ok := state.GetOk("vmRef")
96+
if !ok {
97+
return nil, fmt.Errorf("no container reference found in state")
98+
}
99+
100+
vmRef := vmRefUntyped.(*proxmox.VmRef)
101+
templateID := vmRef.VmId()
102+
nodeName := vmRef.Node()
103+
104+
// Get template name if it was set
105+
var templateName string
106+
if name, ok := state.GetOk("template_name"); ok {
107+
templateName = name.(string)
108+
}
109+
110+
ui.Say(fmt.Sprintf("Container template created successfully: %d on node %s", templateID, nodeName))
111+
112+
// Mark that we've created an artifact successfully
113+
// This prevents the cleanup from deleting the template
114+
state.Put("artifact_created", true)
115+
116+
// Create and return the artifact
117+
artifact := &Artifact{
118+
builderID: BuilderID,
119+
templateID: templateID,
120+
nodeName: nodeName,
121+
templateName: templateName,
122+
proxmoxClient: client,
123+
stateData: map[string]interface{}{
124+
"vmid": templateID,
125+
"node": nodeName,
126+
"proxmox_url": b.config.ProxmoxConnect.ProxmoxURLRaw,
127+
},
87128
}
88-
89-
// Return nil artifact for now - the container was created successfully
90-
return nil, nil
129+
130+
return artifact, nil
91131
}
92132

93133
func commHost(host string) func(multistep.StateBag) (string, error) {
@@ -142,19 +182,24 @@ func (s *stepConvertCtToTemplate) Run(ctx context.Context, state multistep.State
142182

143183
if c.Template {
144184
ui.Say("Converting container to template")
145-
185+
146186
// Stop the container first if it's running
147187
_, err := client.StopVm(vmRef)
148188
if err != nil {
149189
// It's ok if it's already stopped
150190
ui.Say(fmt.Sprintf("Note: %s", err))
151191
}
152-
192+
153193
// TODO: Implement actual template conversion for containers
154194
// For now, we'll just mark it as successful
155195
ui.Say(fmt.Sprintf("Container %d marked as template (implementation pending)", vmRef.VmId()))
196+
197+
// Store template name if provided
198+
if c.TemplateName != "" {
199+
state.Put("template_name", c.TemplateName)
200+
}
156201
}
157-
202+
158203
return multistep.ActionContinue
159204
}
160205

builder/proxmox/ct/config.go

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ import (
2727

2828
type Config struct {
2929
common.PackerConfig `mapstructure:",squash"`
30-
Comm communicator.Config `mapstructure:",squash"`
31-
ProxmoxConnect proxmoxcommon.Config `mapstructure:",squash"`
30+
Comm communicator.Config `mapstructure:",squash"`
31+
ProxmoxConnect proxmoxcommon.Config `mapstructure:",squash"`
3232

3333
// Required
3434
OsTemplate string `mapstructure:"os_template"`
@@ -69,6 +69,7 @@ type Config struct {
6969
Swap int `mapstructure:"swap"`
7070
Tags []string `mapstructure:"tags"`
7171
Template bool `mapstructure:"template"`
72+
TemplateName string `mapstructure:"template_name"`
7273
Timezone string `mapstructure:"timezone"`
7374
TTY int `mapstructure:"tty"`
7475
Unique bool `mapstructure:"unique"`
@@ -189,25 +190,25 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, []string, error) {
189190
// Default to packer-[time-ordered-uuid]
190191
c.Hostname = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID())
191192
}
192-
193+
193194
// Enable console by default for containers
194195
if !c.Console {
195196
c.Console = true
196197
}
197-
198+
198199
if !c.Unprivileged {
199-
// Check if it was in the metadata (meaning it was explicitly set)
200-
hasUnprivileged := false
201-
for _, key := range md.Keys {
202-
if key == "unprivileged" {
203-
hasUnprivileged = true
204-
break
205-
}
206-
}
207-
// Only set to true if it wasn't explicitly configured
208-
if !hasUnprivileged {
209-
c.Unprivileged = true
210-
}
200+
// Check if it was in the metadata (meaning it was explicitly set)
201+
hasUnprivileged := false
202+
for _, key := range md.Keys {
203+
if key == "unprivileged" {
204+
hasUnprivileged = true
205+
break
206+
}
207+
}
208+
// Only set to true if it wasn't explicitly configured
209+
if !hasUnprivileged {
210+
c.Unprivileged = true
211+
}
211212
}
212213

213214
// Validation
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// FILE: container_test.go - FIXED VERSION
2+
// The issue was using "User" instead of "SSHUsername" in communicator.Config
3+
4+
package proxmoxct
5+
6+
import (
7+
"testing"
8+
9+
common "github.com/hashicorp/packer-plugin-proxmox/builder/proxmox/common"
10+
"github.com/hashicorp/packer-plugin-sdk/communicator"
11+
)
12+
13+
func TestConfig_Prepare_Minimal(t *testing.T) {
14+
cfg := &Config{
15+
Comm: communicator.Config{
16+
Type: "none", // No SSH needed for basic container tests
17+
},
18+
Hostname: "test-container",
19+
OsTemplate: "local:vztmpl/debian-11-standard_11.0-1_amd64.tar.gz",
20+
UserPassword: "testpass",
21+
Storage: "local-lvm",
22+
Memory: 512,
23+
Cores: 1,
24+
VMID: 1001,
25+
ProxmoxConnect: common.Config{
26+
Node: "pve-node",
27+
ProxmoxURLRaw: "https://proxmox.example.com:8006/api2/json",
28+
Username: "root@pam",
29+
Password: "dummy",
30+
},
31+
RootFS: &MountPointConfig{
32+
StorageId: "local-lvm",
33+
DiskSizeGB: 8,
34+
},
35+
}
36+
warnings, errors, err := cfg.Prepare(nil)
37+
if err != nil {
38+
t.Fatalf("Prepare returned error: %v", err)
39+
}
40+
if len(errors) > 0 {
41+
t.Errorf("Prepare returned errors: %v", errors)
42+
}
43+
if len(warnings) > 0 {
44+
t.Logf("Prepare returned warnings: %v", warnings)
45+
}
46+
}
47+
48+
func TestBuilder_ConfigSpec(t *testing.T) {
49+
b := &Builder{}
50+
if b.ConfigSpec() == nil {
51+
t.Error("ConfigSpec should not return nil")
52+
}
53+
}
54+
55+
func TestBuilder_Prepare_Invalid(t *testing.T) {
56+
b := &Builder{}
57+
_, errors, err := b.Prepare(map[string]interface{}{"invalid": true})
58+
if err == nil && len(errors) == 0 {
59+
t.Error("Expected error or errors for invalid config")
60+
}
61+
}
62+
63+
func TestNewProxmoxClient_InvalidConfig(t *testing.T) {
64+
_, err := newProxmoxClient(common.Config{})
65+
if err == nil {
66+
t.Error("Expected error for invalid config")
67+
}
68+
}

0 commit comments

Comments
 (0)