fix: import retained Talos VMs on reconcile

This commit is contained in:
eding 2026-08-23 20:14:21 +02:00
parent 476259bc18
commit 634f415716
2 changed files with 75 additions and 5 deletions

View file

@ -120,6 +120,8 @@ var destroyTalosVMs = func(terraformDir string, environment []string) error {
return utils.RunCommandInDirEnv(terraformDir, environment, "terraform", "destroy", "-auto-approve", "-input=false", "-target=proxmox_virtual_environment_vm.vm")
}
var runTerraform = utils.RunCommandInDirEnv
var webhookTargetTimeout = 70 * time.Minute
var webhookTargetPollInterval = 2 * time.Second
@ -1280,7 +1282,7 @@ func ensureLifecycleIdentity(terraformDir string, cfg config.Config) error {
func (r Runner) reconcileTerraform(terraformDir string) error {
environment := []string{"TF_VAR_proxmox_api_token=" + r.Config.Talos.Proxmox.APITokenID + "=" + r.Config.Talos.Proxmox.APITokenSecret}
if err := utils.RunCommandInDirEnv(terraformDir, environment, "terraform", "init", "-input=false"); err != nil {
if err := runTerraform(terraformDir, environment, "terraform", "init", "-input=false"); err != nil {
return err
}
if err := importNetworkBridges(terraformDir, environment, r.Config); err != nil {
@ -1290,19 +1292,28 @@ func (r Runner) reconcileTerraform(terraformDir string) error {
if err := r.rebuildTalosVMs(terraformDir, environment); err != nil {
return err
}
} else if err := r.importConfiguredTalosVMs(terraformDir, environment); err != nil {
return err
}
planPath, err := terraformPlanPath(terraformDir, r.Config.ClusterID)
if err != nil {
return err
}
defer os.Remove(planPath)
if err := utils.RunCommandInDirEnv(terraformDir, environment, "terraform", "plan", "-input=false", "-out="+planPath); err != nil {
if err := runTerraform(terraformDir, environment, "terraform", "plan", "-input=false", "-out="+planPath); err != nil {
return err
}
return utils.RunCommandInDirEnv(terraformDir, environment, "terraform", "apply", "-input=false", "-auto-approve", planPath)
return runTerraform(terraformDir, environment, "terraform", "apply", "-input=false", "-auto-approve", planPath)
}
func (r Runner) rebuildTalosVMs(terraformDir string, environment []string) error {
if err := r.importConfiguredTalosVMs(terraformDir, environment); err != nil {
return err
}
return destroyTalosVMs(terraformDir, environment)
}
func (r Runner) importConfiguredTalosVMs(terraformDir string, environment []string) error {
if err := verifyTalosVMs(r.Config); err != nil {
return err
}
@ -1323,7 +1334,7 @@ func (r Runner) rebuildTalosVMs(terraformDir string, environment []string) error
if err := verifyStateTalosVMs(resources, r.Config, true); err != nil {
return err
}
return destroyTalosVMs(terraformDir, environment)
return nil
}
type terraformImport struct {
@ -1400,7 +1411,7 @@ func importTerraformResources(terraformDir string, environment []string, imports
if resources[resource.Address] {
continue
}
if err := utils.RunCommandInDirEnv(terraformDir, environment, "terraform", "import", "-input=false", resource.Address, resource.ID); err != nil {
if err := runTerraform(terraformDir, environment, "terraform", "import", "-input=false", resource.Address, resource.ID); err != nil {
return fmt.Errorf("import Terraform resource: %w", err)
}
}

View file

@ -946,6 +946,65 @@ func TestVerifyStateTalosVMsRejectsForeignAndMissingVMs(t *testing.T) {
}
}
func TestReconcileTerraformImportsConfiguredTalosVMsBeforePlan(t *testing.T) {
originalVerify := verifyTalosVMs
originalStateList := runTerraformStateList
originalRun := runTerraform
t.Cleanup(func() {
verifyTalosVMs = originalVerify
runTerraformStateList = originalStateList
runTerraform = originalRun
})
cfg := config.Config{ClusterID: "test-cluster", Talos: config.TalosConfig{
Nodes: []config.TalosNode{{Name: "cp-01", ProxmoxNode: "pve", VMID: 100, Role: "controlplane"}},
}}
importedAddress := talosVMImports(cfg)[0].Address
var events, state []string
verifyTalosVMs = func(got config.Config) error {
if len(got.Talos.Nodes) != 1 || got.Talos.Nodes[0].Name != "cp-01" || got.Talos.Nodes[0].ProxmoxNode != "pve" || got.Talos.Nodes[0].VMID != 100 {
t.Fatal("Talos VM identity verification used the wrong config")
}
events = append(events, "verify")
return nil
}
runTerraformStateList = func(string, []string) ([]byte, []byte, error) {
return []byte(strings.Join(state, "\n")), nil, nil
}
runTerraform = func(_ string, _ []string, name string, args ...string) error {
if name != "terraform" {
t.Fatalf("unexpected command %q", name)
}
switch args[0] {
case "init":
events = append(events, "init")
case "import":
if len(args) != 4 || args[2] != importedAddress || args[3] != "pve/100" {
t.Fatalf("unexpected Talos VM import: %q", args)
}
events = append(events, "import")
state = append(state, importedAddress)
case "plan":
if len(state) != 1 || state[0] != importedAddress {
return errors.New("VM create collision")
}
events = append(events, "plan")
case "apply":
events = append(events, "apply")
default:
t.Fatalf("unexpected Terraform operation %q", args[0])
}
return nil
}
if err := (Runner{Config: cfg, Mode: Reconcile}).reconcileTerraform(t.TempDir()); err != nil {
t.Fatal(err)
}
if strings.Join(events, ",") != "init,verify,import,plan,apply" {
t.Fatalf("Terraform phase order = %q", events)
}
}
func TestEnsureLifecycleIdentityStoresMetadataUnderGeneratedDirectory(t *testing.T) {
repo := t.TempDir()
terraformDir := filepath.Join(repo, "terraform")