From 4e3919e2f1002944baa053f3c8d92664dae0305c Mon Sep 17 00:00:00 2001 From: Ryan Johnson Date: Fri, 26 Apr 2024 23:21:16 -0400 Subject: [PATCH] feat: option to remove network adapters Adds the option to remove all network adapters at the end of the image build. Ref: #6 Signed-off-by: Ryan Johnson --- .../builder/vsphere-clone/README.md | 7 ++ .../components/builder/vsphere-iso/README.md | 7 ++ builder/vsphere/clone/builder.go | 3 + builder/vsphere/clone/config.go | 31 +++---- builder/vsphere/clone/config.hcl2spec.go | 2 + .../common/step_remove_network_adapter.go | 45 ++++++++++ .../step_remove_network_adapter.hcl2spec.go | 31 +++++++ .../step_remove_network_adapter_test.go | 83 +++++++++++++++++++ builder/vsphere/driver/vm.go | 23 +++++ builder/vsphere/driver/vm_mock.go | 10 +++ builder/vsphere/iso/builder.go | 3 + builder/vsphere/iso/config.go | 31 +++---- builder/vsphere/iso/config.hcl2spec.go | 2 + ...emoveNetworkAdapterConfig-not-required.mdx | 5 ++ .../RemoveNetworkConfig-not-required.mdx | 5 ++ docs/builders/vsphere-clone.mdx | 2 + docs/builders/vsphere-iso.mdx | 2 + 17 files changed, 262 insertions(+), 30 deletions(-) create mode 100644 builder/vsphere/common/step_remove_network_adapter.go create mode 100644 builder/vsphere/common/step_remove_network_adapter.hcl2spec.go create mode 100644 builder/vsphere/common/step_remove_network_adapter_test.go create mode 100644 docs-partials/builder/vsphere/common/RemoveNetworkAdapterConfig-not-required.mdx create mode 100644 docs-partials/builder/vsphere/common/RemoveNetworkConfig-not-required.mdx diff --git a/.web-docs/components/builder/vsphere-clone/README.md b/.web-docs/components/builder/vsphere-clone/README.md index d9228ad2..85f63345 100644 --- a/.web-docs/components/builder/vsphere-clone/README.md +++ b/.web-docs/components/builder/vsphere-clone/README.md @@ -344,6 +344,13 @@ The settings for guest customization include: + + +- `remove_network_adapter` (bool) - Remove all network adapters from template. Defaults to `false`. + + + + #### Global Routing Settings diff --git a/.web-docs/components/builder/vsphere-iso/README.md b/.web-docs/components/builder/vsphere-iso/README.md index 98def1df..abbf052a 100644 --- a/.web-docs/components/builder/vsphere-iso/README.md +++ b/.web-docs/components/builder/vsphere-iso/README.md @@ -914,6 +914,13 @@ In HCL2: + + +- `remove_network_adapter` (bool) - Remove all network adapters from template. Defaults to `false`. + + + + ## Optional diff --git a/builder/vsphere/clone/builder.go b/builder/vsphere/clone/builder.go index 94207482..e6399afa 100644 --- a/builder/vsphere/clone/builder.go +++ b/builder/vsphere/clone/builder.go @@ -143,6 +143,9 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) CreateSnapshot: b.config.CreateSnapshot, SnapshotName: b.config.SnapshotName, }, + &common.StepRemoveNetworkAdapter{ + Config: &b.config.RemoveNetworkAdapterConfig, + }, &common.StepConvertToTemplate{ ConvertToTemplate: b.config.ConvertToTemplate, }, diff --git a/builder/vsphere/clone/config.go b/builder/vsphere/clone/config.go index b66a0571..ff14c096 100644 --- a/builder/vsphere/clone/config.go +++ b/builder/vsphere/clone/config.go @@ -21,21 +21,22 @@ type Config struct { commonsteps.HTTPConfig `mapstructure:",squash"` commonsteps.CDConfig `mapstructure:",squash"` - common.ConnectConfig `mapstructure:",squash"` - CloneConfig `mapstructure:",squash"` - common.LocationConfig `mapstructure:",squash"` - common.HardwareConfig `mapstructure:",squash"` - common.ConfigParamsConfig `mapstructure:",squash"` - common.FlagConfig `mapstructure:",squash"` - common.CDRomConfig `mapstructure:",squash"` - common.RemoveCDRomConfig `mapstructure:",squash"` - common.ReattachCDRomConfig `mapstructure:",squash"` - common.FloppyConfig `mapstructure:",squash"` - common.RunConfig `mapstructure:",squash"` - common.BootConfig `mapstructure:",squash"` - common.WaitIpConfig `mapstructure:",squash"` - Comm communicator.Config `mapstructure:",squash"` - common.ShutdownConfig `mapstructure:",squash"` + common.ConnectConfig `mapstructure:",squash"` + CloneConfig `mapstructure:",squash"` + common.LocationConfig `mapstructure:",squash"` + common.HardwareConfig `mapstructure:",squash"` + common.ConfigParamsConfig `mapstructure:",squash"` + common.FlagConfig `mapstructure:",squash"` + common.CDRomConfig `mapstructure:",squash"` + common.RemoveCDRomConfig `mapstructure:",squash"` + common.ReattachCDRomConfig `mapstructure:",squash"` + common.RemoveNetworkAdapterConfig `mapstructure:",squash"` + common.FloppyConfig `mapstructure:",squash"` + common.RunConfig `mapstructure:",squash"` + common.BootConfig `mapstructure:",squash"` + common.WaitIpConfig `mapstructure:",squash"` + Comm communicator.Config `mapstructure:",squash"` + common.ShutdownConfig `mapstructure:",squash"` // Specifies to create a snapshot of the virtual machine to use as a base for linked clones. // Defaults to `false`. diff --git a/builder/vsphere/clone/config.hcl2spec.go b/builder/vsphere/clone/config.hcl2spec.go index 41916013..b8900355 100644 --- a/builder/vsphere/clone/config.hcl2spec.go +++ b/builder/vsphere/clone/config.hcl2spec.go @@ -76,6 +76,7 @@ type FlatConfig struct { ISOPaths []string `mapstructure:"iso_paths" cty:"iso_paths" hcl:"iso_paths"` RemoveCdrom *bool `mapstructure:"remove_cdrom" cty:"remove_cdrom" hcl:"remove_cdrom"` ReattachCDRom *int `mapstructure:"reattach_cdroms" cty:"reattach_cdroms" hcl:"reattach_cdroms"` + RemoveNetworkAdapter *bool `mapstructure:"remove_network_adapter" cty:"remove_network_adapter" hcl:"remove_network_adapter"` FloppyIMGPath *string `mapstructure:"floppy_img_path" cty:"floppy_img_path" hcl:"floppy_img_path"` FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files" hcl:"floppy_files"` FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs" hcl:"floppy_dirs"` @@ -226,6 +227,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "iso_paths": &hcldec.AttrSpec{Name: "iso_paths", Type: cty.List(cty.String), Required: false}, "remove_cdrom": &hcldec.AttrSpec{Name: "remove_cdrom", Type: cty.Bool, Required: false}, "reattach_cdroms": &hcldec.AttrSpec{Name: "reattach_cdroms", Type: cty.Number, Required: false}, + "remove_network_adapter": &hcldec.AttrSpec{Name: "remove_network_adapter", Type: cty.Bool, Required: false}, "floppy_img_path": &hcldec.AttrSpec{Name: "floppy_img_path", Type: cty.String, Required: false}, "floppy_files": &hcldec.AttrSpec{Name: "floppy_files", Type: cty.List(cty.String), Required: false}, "floppy_dirs": &hcldec.AttrSpec{Name: "floppy_dirs", Type: cty.List(cty.String), Required: false}, diff --git a/builder/vsphere/common/step_remove_network_adapter.go b/builder/vsphere/common/step_remove_network_adapter.go new file mode 100644 index 00000000..0bbbba8d --- /dev/null +++ b/builder/vsphere/common/step_remove_network_adapter.go @@ -0,0 +1,45 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +//go:generate packer-sdc struct-markdown +//go:generate packer-sdc mapstructure-to-hcl2 -type RemoveNetworkAdapterConfig + +package common + +import ( + "context" + "fmt" + + "github.com/hashicorp/packer-plugin-sdk/multistep" + packersdk "github.com/hashicorp/packer-plugin-sdk/packer" + "github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver" +) + +type RemoveNetworkAdapterConfig struct { + // Remove all network adapters from template. Defaults to `false`. + RemoveNetworkAdapter bool `mapstructure:"remove_network_adapter"` +} + +type StepRemoveNetworkAdapter struct { + Config *RemoveNetworkAdapterConfig +} + +func (s *StepRemoveNetworkAdapter) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { + ui := state.Get("ui").(packersdk.Ui) + vm := state.Get("vm").(driver.VirtualMachine) + + if s.Config.RemoveNetworkAdapter == true { + ui.Say("Removing network adapters...") + err := vm.RemoveNetworkAdapters() + if err != nil { + state.Put("error", fmt.Errorf("error removing network: %v", err)) + return multistep.ActionHalt + } + } + + return multistep.ActionContinue +} + +func (s *StepRemoveNetworkAdapter) Cleanup(state multistep.StateBag) { + // no cleanup +} diff --git a/builder/vsphere/common/step_remove_network_adapter.hcl2spec.go b/builder/vsphere/common/step_remove_network_adapter.hcl2spec.go new file mode 100644 index 00000000..bf102d4a --- /dev/null +++ b/builder/vsphere/common/step_remove_network_adapter.hcl2spec.go @@ -0,0 +1,31 @@ +// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT. + +package common + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" +) + +// FlatRemoveNetworkAdapterConfig is an auto-generated flat version of RemoveNetworkAdapterConfig. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatRemoveNetworkAdapterConfig struct { + RemoveNetworkAdapter *bool `mapstructure:"remove_network_adapter" cty:"remove_network_adapter" hcl:"remove_network_adapter"` +} + +// FlatMapstructure returns a new FlatRemoveNetworkAdapterConfig. +// FlatRemoveNetworkAdapterConfig is an auto-generated flat version of RemoveNetworkAdapterConfig. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*RemoveNetworkAdapterConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatRemoveNetworkAdapterConfig) +} + +// HCL2Spec returns the hcl spec of a RemoveNetworkAdapterConfig. +// This spec is used by HCL to read the fields of RemoveNetworkAdapterConfig. +// The decoded values from this spec will then be applied to a FlatRemoveNetworkAdapterConfig. +func (*FlatRemoveNetworkAdapterConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "remove_network_adapter": &hcldec.AttrSpec{Name: "remove_network_adapter", Type: cty.Bool, Required: false}, + } + return s +} diff --git a/builder/vsphere/common/step_remove_network_adapter_test.go b/builder/vsphere/common/step_remove_network_adapter_test.go new file mode 100644 index 00000000..3eaa8c81 --- /dev/null +++ b/builder/vsphere/common/step_remove_network_adapter_test.go @@ -0,0 +1,83 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package common + +import ( + "context" + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/packer-plugin-sdk/multistep" + "github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver" +) + +func TestStepRemoveNetworkAdapter_Run(t *testing.T) { + tc := []struct { + name string + step *StepRemoveNetworkAdapter + expectedAction multistep.StepAction + vmMock *driver.VirtualMachineMock + expectedVmMock *driver.VirtualMachineMock + errMessage string + }{ + { + name: "Successfully remove network adapter", + step: &StepRemoveNetworkAdapter{ + Config: &RemoveNetworkAdapterConfig{ + RemoveNetworkAdapter: true, + }, + }, + expectedAction: multistep.ActionContinue, + vmMock: &driver.VirtualMachineMock{ + RemoveNetworkAdaptersCalled: true, + }, + expectedVmMock: &driver.VirtualMachineMock{ + RemoveNetworkAdaptersCalled: true, + }, + }, + { + name: "Fail to remove network adapter", + step: &StepRemoveNetworkAdapter{ + Config: &RemoveNetworkAdapterConfig{ + RemoveNetworkAdapter: true, + }, + }, + expectedAction: multistep.ActionHalt, + vmMock: &driver.VirtualMachineMock{ + RemoveNetworkAdaptersCalled: true, + RemoveNetworkAdaptersErr: fmt.Errorf("failed to remove network adapter"), + }, + expectedVmMock: &driver.VirtualMachineMock{ + RemoveNetworkAdaptersCalled: true, + }, + errMessage: "error removing network: failed to remove network adapter", + }, + } + + for _, c := range tc { + t.Run(c.name, func(t *testing.T) { + state := basicStateBag(nil) + state.Put("vm", c.vmMock) + + if action := c.step.Run(context.TODO(), state); action != c.expectedAction { + t.Fatalf("unexpected action %v", action) + } + err, ok := state.Get("error").(error) + if ok { + if err.Error() != c.errMessage { + t.Fatalf("unexpected error %s", err.Error()) + } + } else if c.errMessage != "" { + t.Fatalf("expected to fail with %s but it didn't", c.errMessage) + } + + if diff := cmp.Diff(c.vmMock, c.expectedVmMock, + cmpopts.IgnoreInterfaces(struct{ error }{})); diff != "" { + t.Fatalf("unexpected VirtualMachine calls: %s", diff) + } + }) + } +} diff --git a/builder/vsphere/driver/vm.go b/builder/vsphere/driver/vm.go index 99aeec1c..427169e7 100644 --- a/builder/vsphere/driver/vm.go +++ b/builder/vsphere/driver/vm.go @@ -70,6 +70,8 @@ type VirtualMachine interface { EjectCdroms() error AddSATAController() error FindSATAController() (*types.VirtualAHCIController, error) + + RemoveNetworkAdapters() error } type VirtualMachineDriver struct { @@ -1336,3 +1338,24 @@ func findNetworkAdapter(l object.VirtualDeviceList) (types.BaseVirtualEthernetCa return c[0].(types.BaseVirtualEthernetCard), nil } + +func (vm *VirtualMachineDriver) RemoveNetworkAdapters() error { + devices, err := vm.Devices() + if err != nil { + return fmt.Errorf("error retrieving devices: %s", err) + } + + networkAdapters := devices.SelectByType((*types.VirtualEthernetCard)(nil)) + if len(networkAdapters) == 0 { + return nil + } + + for _, adapter := range networkAdapters { + err = vm.RemoveDevice(false, adapter) + if err != nil { + return fmt.Errorf("error removing network adapter: %s", err) + } + } + + return nil +} diff --git a/builder/vsphere/driver/vm_mock.go b/builder/vsphere/driver/vm_mock.go index 468af60d..71c51fdd 100644 --- a/builder/vsphere/driver/vm_mock.go +++ b/builder/vsphere/driver/vm_mock.go @@ -74,6 +74,10 @@ type VirtualMachineMock struct { ReattachCDRomsCalled bool ReattachCDRomsErr error + RemoveNetworkAdaptersCalled bool + NetworkAdaptersList object.VirtualDeviceList + RemoveNetworkAdaptersErr error + CloneCalled bool CloneConfig *CloneConfig CloneError error @@ -299,6 +303,12 @@ func (vm *VirtualMachineMock) EjectCdroms() error { return vm.EjectCdromsErr } +func (vm *VirtualMachineMock) RemoveNetworkAdapters() error { + vm.RemoveNetworkAdaptersCalled = true + vm.NetworkAdaptersList = nil + return vm.RemoveNetworkAdaptersErr +} + func (vm *VirtualMachineMock) Datacenter() *object.Datacenter { return nil } diff --git a/builder/vsphere/iso/builder.go b/builder/vsphere/iso/builder.go index 01925b1a..348e095a 100644 --- a/builder/vsphere/iso/builder.go +++ b/builder/vsphere/iso/builder.go @@ -149,6 +149,9 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) Config: &b.config.ReattachCDRomConfig, CDRomConfig: &b.config.CDRomConfig, }, + &common.StepRemoveNetworkAdapter{ + Config: &b.config.RemoveNetworkAdapterConfig, + }, &common.StepCreateSnapshot{ CreateSnapshot: b.config.CreateSnapshot, SnapshotName: b.config.SnapshotName, diff --git a/builder/vsphere/iso/config.go b/builder/vsphere/iso/config.go index 47fda9f5..b04df1d4 100644 --- a/builder/vsphere/iso/config.go +++ b/builder/vsphere/iso/config.go @@ -21,21 +21,22 @@ type Config struct { commonsteps.HTTPConfig `mapstructure:",squash"` commonsteps.CDConfig `mapstructure:",squash"` - common.ConnectConfig `mapstructure:",squash"` - CreateConfig `mapstructure:",squash"` - common.LocationConfig `mapstructure:",squash"` - common.HardwareConfig `mapstructure:",squash"` - common.ConfigParamsConfig `mapstructure:",squash"` - common.FlagConfig `mapstructure:",squash"` - commonsteps.ISOConfig `mapstructure:",squash"` - common.CDRomConfig `mapstructure:",squash"` - common.RemoveCDRomConfig `mapstructure:",squash"` - common.ReattachCDRomConfig `mapstructure:",squash"` - common.FloppyConfig `mapstructure:",squash"` - common.RunConfig `mapstructure:",squash"` - common.BootConfig `mapstructure:",squash"` - common.WaitIpConfig `mapstructure:",squash"` - Comm communicator.Config `mapstructure:",squash"` + common.ConnectConfig `mapstructure:",squash"` + CreateConfig `mapstructure:",squash"` + common.LocationConfig `mapstructure:",squash"` + common.HardwareConfig `mapstructure:",squash"` + common.ConfigParamsConfig `mapstructure:",squash"` + common.FlagConfig `mapstructure:",squash"` + commonsteps.ISOConfig `mapstructure:",squash"` + common.CDRomConfig `mapstructure:",squash"` + common.RemoveCDRomConfig `mapstructure:",squash"` + common.ReattachCDRomConfig `mapstructure:",squash"` + common.RemoveNetworkAdapterConfig `mapstructure:",squash"` + common.FloppyConfig `mapstructure:",squash"` + common.RunConfig `mapstructure:",squash"` + common.BootConfig `mapstructure:",squash"` + common.WaitIpConfig `mapstructure:",squash"` + Comm communicator.Config `mapstructure:",squash"` common.ShutdownConfig `mapstructure:",squash"` diff --git a/builder/vsphere/iso/config.hcl2spec.go b/builder/vsphere/iso/config.hcl2spec.go index c75ca9dd..4553402b 100644 --- a/builder/vsphere/iso/config.hcl2spec.go +++ b/builder/vsphere/iso/config.hcl2spec.go @@ -79,6 +79,7 @@ type FlatConfig struct { ISOPaths []string `mapstructure:"iso_paths" cty:"iso_paths" hcl:"iso_paths"` RemoveCdrom *bool `mapstructure:"remove_cdrom" cty:"remove_cdrom" hcl:"remove_cdrom"` ReattachCDRom *int `mapstructure:"reattach_cdroms" cty:"reattach_cdroms" hcl:"reattach_cdroms"` + RemoveNetworkAdapter *bool `mapstructure:"remove_network_adapter" cty:"remove_network_adapter" hcl:"remove_network_adapter"` FloppyIMGPath *string `mapstructure:"floppy_img_path" cty:"floppy_img_path" hcl:"floppy_img_path"` FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files" hcl:"floppy_files"` FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs" hcl:"floppy_dirs"` @@ -236,6 +237,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "iso_paths": &hcldec.AttrSpec{Name: "iso_paths", Type: cty.List(cty.String), Required: false}, "remove_cdrom": &hcldec.AttrSpec{Name: "remove_cdrom", Type: cty.Bool, Required: false}, "reattach_cdroms": &hcldec.AttrSpec{Name: "reattach_cdroms", Type: cty.Number, Required: false}, + "remove_network_adapter": &hcldec.AttrSpec{Name: "remove_network_adapter", Type: cty.Bool, Required: false}, "floppy_img_path": &hcldec.AttrSpec{Name: "floppy_img_path", Type: cty.String, Required: false}, "floppy_files": &hcldec.AttrSpec{Name: "floppy_files", Type: cty.List(cty.String), Required: false}, "floppy_dirs": &hcldec.AttrSpec{Name: "floppy_dirs", Type: cty.List(cty.String), Required: false}, diff --git a/docs-partials/builder/vsphere/common/RemoveNetworkAdapterConfig-not-required.mdx b/docs-partials/builder/vsphere/common/RemoveNetworkAdapterConfig-not-required.mdx new file mode 100644 index 00000000..14f35d28 --- /dev/null +++ b/docs-partials/builder/vsphere/common/RemoveNetworkAdapterConfig-not-required.mdx @@ -0,0 +1,5 @@ + + +- `remove_network_adapter` (bool) - Remove all network adapters from template. Defaults to `false`. + + diff --git a/docs-partials/builder/vsphere/common/RemoveNetworkConfig-not-required.mdx b/docs-partials/builder/vsphere/common/RemoveNetworkConfig-not-required.mdx new file mode 100644 index 00000000..24c6cef4 --- /dev/null +++ b/docs-partials/builder/vsphere/common/RemoveNetworkConfig-not-required.mdx @@ -0,0 +1,5 @@ + + +- `remove_network_adapter` (bool) - Remove all network adapters from template. Defaults to `false`. + + diff --git a/docs/builders/vsphere-clone.mdx b/docs/builders/vsphere-clone.mdx index c807dca3..700e773b 100644 --- a/docs/builders/vsphere-clone.mdx +++ b/docs/builders/vsphere-clone.mdx @@ -108,6 +108,8 @@ can be done via environment variable: @include 'builder/vsphere/clone/NetworkInterface-not-required.mdx' +@include 'builder/vsphere/common/RemoveNetworkConfig-not-required.mdx' + #### Global Routing Settings @include 'builder/vsphere/clone/GlobalRoutingSettings.mdx' diff --git a/docs/builders/vsphere-iso.mdx b/docs/builders/vsphere-iso.mdx index c3029125..3f1ff2b2 100644 --- a/docs/builders/vsphere-iso.mdx +++ b/docs/builders/vsphere-iso.mdx @@ -188,6 +188,8 @@ iso_paths = [ @include 'builder/vsphere/iso/NIC-required.mdx' +@include 'builder/vsphere/common/RemoveNetworkConfig-not-required.mdx' + ## Optional @include 'builder/vsphere/iso/NIC-not-required.mdx'