From 874badce7708bdd1c65c1a71d390a275ae60063d Mon Sep 17 00:00:00 2001 From: Simon Richardson Date: Thu, 29 Feb 2024 17:34:01 +0000 Subject: [PATCH] Remove KVM brroker This removes the KVM broker in favour of the LXD virtual-machine support. This is a rough first pass whilst I work out what tests need to be refactored. --- acceptancetests/assess | 5 +- agent/agentbootstrap/bootstrap_test.go | 3 - api/agent/provisioner/provisioner_test.go | 8 +- api/agent/upgradesteps/upgradesteps_test.go | 41 -- api/facadeversions.go | 2 +- apiserver/admin_test.go | 2 +- .../agent/provisioner/provisioner_test.go | 11 +- apiserver/facades/agent/reboot/reboot_test.go | 2 +- .../facades/agent/uniter/newlxdprofile.go | 3 - .../agent/uniter/newlxdprofile_test.go | 16 - .../facades/agent/upgradesteps/register.go | 8 +- .../agent/upgradesteps/upgradesteps.go | 71 +-- .../agent/upgradesteps/upgradesteps_test.go | 122 ----- apiserver/facades/schema.json | 26 +- caas/kubernetes/provider/constraints_test.go | 4 +- .../deployer/bundlehandler_test.go | 20 +- cmd/jujud-controller/agent/machine.go | 8 - .../agent/machine/manifolds.go | 9 - .../agent/machine/manifolds_test.go | 13 - cmd/jujud-controller/agent/machine_test.go | 5 - cmd/jujud/agent/machine.go | 8 - cmd/jujud/agent/machine/manifolds.go | 9 - cmd/jujud/agent/machine/manifolds_test.go | 13 - cmd/jujud/agent/machine_test.go | 5 - cmd/jujud/reboot/mocks/clock_mock.go | 97 ++++ cmd/jujud/reboot/package_test.go | 12 +- cmd/jujud/reboot/reboot.go | 14 +- cmd/jujud/reboot/reboot_test.go | 29 +- core/container/container_test.go | 1 - core/instance/container.go | 2 - core/instance/container_test.go | 8 - core/instance/placement_test.go | 7 - internal/bundle/changes/changes.go | 4 +- internal/bundle/changes/handlers.go | 2 +- internal/bundle/changes/model_test.go | 14 +- internal/container/broker/broker_test.go | 15 - internal/container/broker/instance_broker.go | 2 - internal/container/broker/kvm-broker.go | 159 ------ internal/container/broker/kvm-broker_test.go | 279 ---------- internal/container/broker/lxd-broker_test.go | 15 + internal/container/factory/factory.go | 3 - internal/container/factory/factory_test.go | 3 - internal/container/kvm/container.go | 175 ------ .../container/kvm/container_internal_test.go | 25 - internal/container/kvm/containerfactory.go | 41 -- .../container/kvm/containerfactory_test.go | 27 - internal/container/kvm/doc.go | 67 --- internal/container/kvm/export_test.go | 69 --- internal/container/kvm/initialisation.go | 195 ------- .../kvm/initialisation_internal_test.go | 112 ---- internal/container/kvm/instance.go | 70 --- internal/container/kvm/interface.go | 60 --- internal/container/kvm/kvm.go | 353 ------------ internal/container/kvm/kvm_test.go | 399 -------------- internal/container/kvm/libvirt/doc.go | 7 - internal/container/kvm/libvirt/domainxml.go | 427 --------------- .../kvm/libvirt/domainxml_internal_test.go | 44 -- .../container/kvm/libvirt/domainxml_test.go | 247 --------- .../container/kvm/libvirt/package_test.go | 27 - internal/container/kvm/libvirt/ssh_test.go | 19 - internal/container/kvm/live_test.go | 165 ------ internal/container/kvm/mock/mock-kvm.go | 171 ------ internal/container/kvm/mock/mock-kvm_test.go | 161 ------ internal/container/kvm/mock/package_test.go | 14 - internal/container/kvm/package_test.go | 29 - internal/container/kvm/run.go | 38 -- internal/container/kvm/run_linux.go | 68 --- internal/container/kvm/run_other.go | 16 - internal/container/kvm/sync.go | 306 ----------- internal/container/kvm/sync_internal_test.go | 312 ----------- internal/container/kvm/sync_test.go | 80 --- internal/container/kvm/testing/test.go | 36 -- internal/container/kvm/wrappedcmds.go | 505 ------------------ .../kvm/wrappedcmds_internal_test.go | 161 ------ internal/container/kvm/wrappedcmds_test.go | 251 --------- internal/database/node.go | 1 - .../network/containerizer/bridgepolicy.go | 5 - .../containerizer/bridgepolicy_test.go | 2 - internal/network/network.go | 5 - internal/network/network_test.go | 5 +- internal/packaging/dependency/kvm.go | 55 -- internal/provider/azure/environ_test.go | 2 +- internal/provider/ec2/local_test.go | 2 +- .../apiaddressupdater_test.go | 9 +- .../worker/containerbroker/broker_test.go | 18 - internal/worker/instancemutater/mutater.go | 6 +- .../worker/instancemutater/worker_test.go | 34 +- internal/worker/machiner/machiner_test.go | 4 - .../provisioner/container_initialisation.go | 15 +- .../container_initialisation_test.go | 12 +- .../provisioner/containerprovisioner_test.go | 34 +- .../provisioner/containerworker_test.go | 4 +- state/constraintsvalidation_test.go | 28 +- state/linklayerdevices_test.go | 1 - state/machine_test.go | 86 --- state/migration_export_test.go | 7 +- state/migration_import_test.go | 2 +- state/state_test.go | 46 +- 98 files changed, 288 insertions(+), 5852 deletions(-) create mode 100644 cmd/jujud/reboot/mocks/clock_mock.go delete mode 100644 internal/container/broker/kvm-broker.go delete mode 100644 internal/container/broker/kvm-broker_test.go delete mode 100644 internal/container/kvm/container.go delete mode 100644 internal/container/kvm/container_internal_test.go delete mode 100644 internal/container/kvm/containerfactory.go delete mode 100644 internal/container/kvm/containerfactory_test.go delete mode 100644 internal/container/kvm/doc.go delete mode 100644 internal/container/kvm/export_test.go delete mode 100644 internal/container/kvm/initialisation.go delete mode 100644 internal/container/kvm/initialisation_internal_test.go delete mode 100644 internal/container/kvm/instance.go delete mode 100644 internal/container/kvm/interface.go delete mode 100644 internal/container/kvm/kvm.go delete mode 100644 internal/container/kvm/kvm_test.go delete mode 100644 internal/container/kvm/libvirt/doc.go delete mode 100644 internal/container/kvm/libvirt/domainxml.go delete mode 100644 internal/container/kvm/libvirt/domainxml_internal_test.go delete mode 100644 internal/container/kvm/libvirt/domainxml_test.go delete mode 100644 internal/container/kvm/libvirt/package_test.go delete mode 100644 internal/container/kvm/libvirt/ssh_test.go delete mode 100644 internal/container/kvm/live_test.go delete mode 100644 internal/container/kvm/mock/mock-kvm.go delete mode 100644 internal/container/kvm/mock/mock-kvm_test.go delete mode 100644 internal/container/kvm/mock/package_test.go delete mode 100644 internal/container/kvm/package_test.go delete mode 100644 internal/container/kvm/run.go delete mode 100644 internal/container/kvm/run_linux.go delete mode 100644 internal/container/kvm/run_other.go delete mode 100644 internal/container/kvm/sync.go delete mode 100644 internal/container/kvm/sync_internal_test.go delete mode 100644 internal/container/kvm/sync_test.go delete mode 100644 internal/container/kvm/testing/test.go delete mode 100644 internal/container/kvm/wrappedcmds.go delete mode 100644 internal/container/kvm/wrappedcmds_internal_test.go delete mode 100644 internal/container/kvm/wrappedcmds_test.go delete mode 100644 internal/packaging/dependency/kvm.go diff --git a/acceptancetests/assess b/acceptancetests/assess index 389536e3db1..154af6fd63f 100755 --- a/acceptancetests/assess +++ b/acceptancetests/assess @@ -199,7 +199,7 @@ def parse_args(): default="jammy") pass_through.add_argument("--machine-type", "--virt", dest="machine_type", help="Virtualisation technology for machine (only relevant for container_networking, constraints tests) [default: lxd]", - choices=["lxc", "lxd", "kvm"], default="lxd") + choices=["lxc", "lxd"], default="lxd") pass_through.add_argument("--space-constraint", metavar="C", help="Adds --constraints spaces=C to the underlying hypervisor (only relevant for container_networking test). ") pass_through.add_argument("--secondary-env", metavar="ENV", @@ -454,9 +454,6 @@ def main(): testrun_argv.extend(["--machine-type", args.machine_type]) if args.space_constraint: testrun_argv.extend(["--space-constraint", args.space_constraint]) - elif assess == "constraints": - if args.machine_type == "kvm": - testrun_argv.append("--with-virttype-kvm") elif assess == "cross_model_relations": if args.secondary_env: testrun_argv.extend(["--secondary-env", args.secondary_env]) diff --git a/agent/agentbootstrap/bootstrap_test.go b/agent/agentbootstrap/bootstrap_test.go index 8442e9bdb2a..c1aaa5b8b3f 100644 --- a/agent/agentbootstrap/bootstrap_test.go +++ b/agent/agentbootstrap/bootstrap_test.go @@ -73,9 +73,6 @@ func (s *bootstrapSuite) TestInitializeState(c *gc.C) { "10.0.4.1", "10.0.4.4", }, nil - } else if name == network.DefaultKVMBridge { - // claim we don't have a virbr0 bridge - return nil, nil } c.Fatalf("unknown bridge in testing: %v", name) return nil, nil diff --git a/api/agent/provisioner/provisioner_test.go b/api/agent/provisioner/provisioner_test.go index 01372960fe8..082942deb4d 100644 --- a/api/agent/provisioner/provisioner_test.go +++ b/api/agent/provisioner/provisioner_test.go @@ -665,7 +665,7 @@ func (s *provisionerSuite) TestSetSupportedContainers(c *gc.C) { args := params.MachineContainersParams{ Params: []params.MachineContainers{{ MachineTag: "machine-666", - ContainerTypes: []instance.ContainerType{"lxd", "kvm"}, + ContainerTypes: []instance.ContainerType{"lxd"}, }}, } results := params.ErrorResults{ @@ -673,7 +673,7 @@ func (s *provisionerSuite) TestSetSupportedContainers(c *gc.C) { } s.expectCall(caller, "SetSupportedContainers", args, results) - err := machine.SetSupportedContainers([]instance.ContainerType{"lxd", "kvm"}...) + err := machine.SetSupportedContainers([]instance.ContainerType{"lxd"}...) c.Assert(err, jc.ErrorIsNil) } @@ -688,14 +688,14 @@ func (s *provisionerSuite) TestSupportedContainers(c *gc.C) { Entities: []params.Entity{{Tag: "machine-666"}}, } results := params.MachineContainerResults{ - Results: []params.MachineContainerResult{{ContainerTypes: []instance.ContainerType{"lxd", "kvm"}, Determined: true}}, + Results: []params.MachineContainerResult{{ContainerTypes: []instance.ContainerType{"lxd"}, Determined: true}}, } s.expectCall(caller, "SupportedContainers", args, results) result, determined, err := machine.SupportedContainers() c.Assert(err, jc.ErrorIsNil) - c.Assert(result, jc.SameContents, []instance.ContainerType{"lxd", "kvm"}) + c.Assert(result, jc.SameContents, []instance.ContainerType{"lxd"}) c.Assert(determined, jc.IsTrue) } diff --git a/api/agent/upgradesteps/upgradesteps_test.go b/api/agent/upgradesteps/upgradesteps_test.go index e5799c58de5..451c58449a7 100644 --- a/api/agent/upgradesteps/upgradesteps_test.go +++ b/api/agent/upgradesteps/upgradesteps_test.go @@ -27,30 +27,6 @@ func (s *upgradeStepsSuite) SetUpTest(c *gc.C) { s.BaseSuite.SetUpTest(c) } -func (s *upgradeStepsSuite) TestResetKVMMachineModificationStatusIdle(c *gc.C) { - defer s.setupMocks(c).Finish() - mTag := names.NewMachineTag("0/kvm/0") - resetArg := params.Entity{Tag: mTag.String()} - - s.expectResetKVMMachineModificationStatusIdleSuccess(resetArg) - - client := upgradesteps.NewClientFromFacade(s.fCaller) - err := client.ResetKVMMachineModificationStatusIdle(mTag) - c.Assert(err, jc.ErrorIsNil) -} - -func (s *upgradeStepsSuite) TestResetKVMMachineModificationStatusIdleError(c *gc.C) { - defer s.setupMocks(c).Finish() - mTag := names.NewMachineTag("0/kvm/0") - resetArg := params.Entity{Tag: mTag.String()} - - s.expectResetKVMMachineModificationStatusIdleError(resetArg) - - client := upgradesteps.NewClientFromFacade(s.fCaller) - err := client.ResetKVMMachineModificationStatusIdle(mTag) - c.Assert(err, gc.ErrorMatches, "did not find") -} - func (s *upgradeStepsSuite) TestWriteAgentState(c *gc.C) { defer s.setupMocks(c).Finish() @@ -95,23 +71,6 @@ func (s *upgradeStepsSuite) setupMocks(c *gc.C) *gomock.Controller { return ctrl } -func (s *upgradeStepsSuite) expectResetKVMMachineModificationStatusIdleSuccess(resetArg params.Entity) { - fExp := s.fCaller.EXPECT() - resultSource := params.ErrorResult{} - fExp.FacadeCall(gomock.Any(), "ResetKVMMachineModificationStatusIdle", resetArg, gomock.Any()).SetArg(3, resultSource) -} - -func (s *upgradeStepsSuite) expectResetKVMMachineModificationStatusIdleError(resetArg params.Entity) { - fExp := s.fCaller.EXPECT() - resultSource := params.ErrorResult{ - Error: ¶ms.Error{ - Code: params.CodeNotFound, - Message: "did not find", - }, - } - fExp.FacadeCall(gomock.Any(), "ResetKVMMachineModificationStatusIdle", resetArg, gomock.Any()).SetArg(3, resultSource) -} - func (s *upgradeStepsSuite) expectWriteAgentStateSuccess(c *gc.C, args params.SetUnitStateArgs) { fExp := s.fCaller.EXPECT() resultSource := params.ErrorResults{} diff --git a/api/facadeversions.go b/api/facadeversions.go index 31794ce2e8d..e35cfff0f91 100644 --- a/api/facadeversions.go +++ b/api/facadeversions.go @@ -127,7 +127,7 @@ var facadeVersions = facades.FacadeVersions{ "Uniter": {19}, "Upgrader": {1}, "UpgradeSeries": {3}, - "UpgradeSteps": {2}, + "UpgradeSteps": {3}, "UserManager": {3}, "VolumeAttachmentsWatcher": {2}, "VolumeAttachmentPlansWatcher": {1}, diff --git a/apiserver/admin_test.go b/apiserver/admin_test.go index 371f231d3bb..f252a3371e6 100644 --- a/apiserver/admin_test.go +++ b/apiserver/admin_test.go @@ -1048,7 +1048,7 @@ func (s *loginV3Suite) TestClientLoginToControllerNoAccessToControllerModel(c *g Name: "bobbrown", }) - now := s.Clock.Now().UTC() + now := s.Clock.Now().UTC().Truncate(time.Second) s.OpenControllerAPIAs(c, names.NewUserTag("bobbrown"), "password") diff --git a/apiserver/facades/agent/provisioner/provisioner_test.go b/apiserver/facades/agent/provisioner/provisioner_test.go index 11f0ca7e47b..551c0c87199 100644 --- a/apiserver/facades/agent/provisioner/provisioner_test.go +++ b/apiserver/facades/agent/provisioner/provisioner_test.go @@ -709,7 +709,7 @@ func (s *withoutControllerSuite) TestWatchContainers(c *gc.C) { args := params.WatchContainers{Params: []params.WatchContainer{ {MachineTag: s.machines[0].Tag().String(), ContainerType: string(instance.LXD)}, - {MachineTag: s.machines[1].Tag().String(), ContainerType: string(instance.KVM)}, + {MachineTag: s.machines[1].Tag().String(), ContainerType: string(instance.LXD)}, {MachineTag: "machine-42", ContainerType: ""}, {MachineTag: "unit-foo-0", ContainerType: ""}, {MachineTag: "application-bar", ContainerType: ""}, @@ -1473,11 +1473,12 @@ func (s *provisionerSuite) getManagerConfig(c *gc.C, typ instance.ContainerType) } func (s *withoutControllerSuite) TestContainerManagerConfigDefaults(c *gc.C) { - cfg := s.getManagerConfig(c, instance.KVM) + cfg := s.getManagerConfig(c, instance.LXD) c.Assert(cfg, jc.DeepEquals, map[string]string{ container.ConfigModelUUID: coretesting.ModelTag.Id(), config.ContainerImageStreamKey: "released", config.ContainerNetworkingMethod: config.ConfigDefaults()[config.ContainerNetworkingMethod].(string), + config.LXDSnapChannel: "5.0/stable", }) } @@ -1662,7 +1663,7 @@ func (s *withoutControllerSuite) TestSetSupportedContainers(c *gc.C) { ContainerTypes: []instance.ContainerType{instance.LXD}, }, { MachineTag: "machine-1", - ContainerTypes: []instance.ContainerType{instance.LXD, instance.KVM}, + ContainerTypes: []instance.ContainerType{instance.LXD}, }}} results, err := s.provisioner.SetSupportedContainers(context.Background(), args) c.Assert(err, jc.ErrorIsNil) @@ -1680,7 +1681,7 @@ func (s *withoutControllerSuite) TestSetSupportedContainers(c *gc.C) { c.Assert(err, jc.ErrorIsNil) containers, ok = m1.SupportedContainers() c.Assert(ok, jc.IsTrue) - c.Assert(containers, gc.DeepEquals, []instance.ContainerType{instance.LXD, instance.KVM}) + c.Assert(containers, gc.DeepEquals, []instance.ContainerType{instance.LXD}) } func (s *withoutControllerSuite) TestSetSupportedContainersPermissions(c *gc.C) { @@ -1729,7 +1730,7 @@ func (s *withoutControllerSuite) TestSupportedContainers(c *gc.C) { ContainerTypes: []instance.ContainerType{instance.LXD}, }, { MachineTag: "machine-1", - ContainerTypes: []instance.ContainerType{instance.LXD, instance.KVM}, + ContainerTypes: []instance.ContainerType{instance.LXD}, }}} _, err := s.provisioner.SetSupportedContainers(context.Background(), setArgs) c.Assert(err, jc.ErrorIsNil) diff --git a/apiserver/facades/agent/reboot/reboot_test.go b/apiserver/facades/agent/reboot/reboot_test.go index 1eca1aa7aa1..805ff6094e9 100644 --- a/apiserver/facades/agent/reboot/reboot_test.go +++ b/apiserver/facades/agent/reboot/reboot_test.go @@ -102,7 +102,7 @@ func (s *rebootSuite) SetUpTest(c *gc.C) { container, err := st.AddMachineInsideMachine(template, machine.Id(), instance.LXD) c.Assert(err, jc.ErrorIsNil) - nestedContainer, err := st.AddMachineInsideMachine(template, container.Id(), instance.KVM) + nestedContainer, err := st.AddMachineInsideMachine(template, container.Id(), instance.LXD) c.Assert(err, jc.ErrorIsNil) s.machine = s.setUpMachine(c, machine) diff --git a/apiserver/facades/agent/uniter/newlxdprofile.go b/apiserver/facades/agent/uniter/newlxdprofile.go index ceb56dfef69..be2b5194c5d 100644 --- a/apiserver/facades/agent/uniter/newlxdprofile.go +++ b/apiserver/facades/agent/uniter/newlxdprofile.go @@ -301,9 +301,6 @@ func (u *LXDProfileAPIv2) getOneCanApplyLXDProfile(machine LXDProfileMachineV2, return true, nil } switch machine.ContainerType() { - case instance.KVM: - // charm profiles cannot be applied to KVM containers. - return false, nil case instance.LXD: return true, nil } diff --git a/apiserver/facades/agent/uniter/newlxdprofile_test.go b/apiserver/facades/agent/uniter/newlxdprofile_test.go index 6bc8d2e6bec..a396c93f9d7 100644 --- a/apiserver/facades/agent/uniter/newlxdprofile_test.go +++ b/apiserver/facades/agent/uniter/newlxdprofile_test.go @@ -179,22 +179,6 @@ func (s *newLxdProfileSuite) TestCanApplyLXDProfileCAAS(c *gc.C) { s.testCanApplyLXDProfile(c, false) } -func (s *newLxdProfileSuite) TestCanApplyLXDProfileIAASMAASNotManualKVM(c *gc.C) { - // model type: IAAS - // provider type: maas - // manual: false - // container: KVM - defer s.setupMocks(c).Finish() - s.expectUnitAndMachine() - s.expectModel() - s.expectModelTypeIAAS() - s.expectProviderType(c, "maas") - s.expectManual(false) - s.expectContainerType(instance.KVM) - - s.testCanApplyLXDProfile(c, false) -} - func (s *newLxdProfileSuite) TestCanApplyLXDProfileIAASMAASNotManualLXD(c *gc.C) { // model type: IAAS // provider type: maas diff --git a/apiserver/facades/agent/upgradesteps/register.go b/apiserver/facades/agent/upgradesteps/register.go index 0e09c13f17a..a9e13b78c02 100644 --- a/apiserver/facades/agent/upgradesteps/register.go +++ b/apiserver/facades/agent/upgradesteps/register.go @@ -12,13 +12,13 @@ import ( // Register is called to expose a package of facades onto a given registry. func Register(registry facade.FacadeRegistry) { - registry.MustRegister("UpgradeSteps", 2, func(stdCtx context.Context, ctx facade.ModelContext) (facade.Facade, error) { - return newFacadeV2(ctx) + registry.MustRegister("UpgradeSteps", 3, func(stdCtx context.Context, ctx facade.ModelContext) (facade.Facade, error) { + return newFacadeV3(ctx) }, reflect.TypeOf((*UpgradeStepsAPI)(nil))) } -// newFacadeV2 is used for API registration. -func newFacadeV2(ctx facade.ModelContext) (*UpgradeStepsAPI, error) { +// newFacadeV3 is used for API registration. +func newFacadeV3(ctx facade.ModelContext) (*UpgradeStepsAPI, error) { return NewUpgradeStepsAPI( ctx.State(), ctx.ServiceFactory().ControllerConfig(), diff --git a/apiserver/facades/agent/upgradesteps/upgradesteps.go b/apiserver/facades/agent/upgradesteps/upgradesteps.go index c91080c9dbb..49fd76658ec 100644 --- a/apiserver/facades/agent/upgradesteps/upgradesteps.go +++ b/apiserver/facades/agent/upgradesteps/upgradesteps.go @@ -12,25 +12,16 @@ import ( "github.com/juju/juju/apiserver/common" apiservererrors "github.com/juju/juju/apiserver/errors" "github.com/juju/juju/apiserver/facade" - "github.com/juju/juju/core/instance" - "github.com/juju/juju/core/status" "github.com/juju/juju/rpc/params" "github.com/juju/juju/state" ) -// UpgradeStepsV2 defines the methods on the version 2 facade for the +// UpgradeStepsV3 defines the methods on the version 2 facade for the // upgrade steps API endpoint. -type UpgradeStepsV2 interface { - UpgradeStepsV1 +type UpgradeSteps interface { WriteAgentState(context.Context, params.SetUnitStateArgs) (params.ErrorResults, error) } -// UpgradeStepsV1 defines the methods on the version 2 facade for the -// upgrade steps API endpoint. -type UpgradeStepsV1 interface { - ResetKVMMachineModificationStatusIdle(context.Context, params.Entity) (params.ErrorResult, error) -} - // Logger represents the logging methods used by the upgrade steps. type Logger interface { Criticalf(string, ...any) @@ -54,8 +45,6 @@ type UpgradeStepsAPIV1 struct { *UpgradeStepsAPI } -var _ UpgradeStepsV2 = (*UpgradeStepsAPI)(nil) - func NewUpgradeStepsAPI( st UpgradeStepsState, ctrlConfigGetter ControllerConfigGetter, @@ -80,44 +69,6 @@ func NewUpgradeStepsAPI( }, nil } -// ResetKVMMachineModificationStatusIdle sets the modification status -// of a kvm machine to idle if it is in an error state before upgrade. -// Related to lp:1829393. -func (api *UpgradeStepsAPI) ResetKVMMachineModificationStatusIdle(ctx context.Context, arg params.Entity) (params.ErrorResult, error) { - var result params.ErrorResult - canAccess, err := api.getMachineAuthFunc() - if err != nil { - return result, errors.Trace(err) - } - - mTag, err := names.ParseMachineTag(arg.Tag) - if err != nil { - return result, errors.Trace(err) - } - m, err := api.getMachine(canAccess, mTag) - if err != nil { - return result, errors.Trace(err) - } - - if m.ContainerType() != instance.KVM { - // noop - return result, nil - } - - modStatus, err := m.ModificationStatus() - if err != nil { - result.Error = apiservererrors.ServerError(err) - return result, nil - } - - if modStatus.Status == status.Error { - err = m.SetModificationStatus(status.StatusInfo{Status: status.Idle}) - result.Error = apiservererrors.ServerError(err) - } - - return result, nil -} - // WriteAgentState writes the agent state for the set of units provided. This // call presently deals with the state for the unit agent. func (api *UpgradeStepsAPI) WriteAgentState(ctx context.Context, args params.SetUnitStateArgs) (params.ErrorResults, error) { @@ -170,24 +121,6 @@ func (api *UpgradeStepsAPI) WriteAgentState(ctx context.Context, args params.Set return results, nil } -func (api *UpgradeStepsAPI) getMachine(canAccess common.AuthFunc, tag names.MachineTag) (Machine, error) { - if !canAccess(tag) { - return nil, apiservererrors.ErrPerm - } - entity, err := api.st.FindEntity(tag) - if err != nil { - return nil, err - } - // The authorization function guarantees that the tag represents a - // machine. - var machine Machine - var ok bool - if machine, ok = entity.(Machine); !ok { - return nil, errors.NotValidf("machine entity") - } - return machine, nil -} - func (api *UpgradeStepsAPI) getUnit(canAccess common.AuthFunc, tag names.UnitTag) (Unit, error) { if !canAccess(tag) { api.logger.Criticalf("getUnit kind=%q, name=%q", tag.Kind(), tag.Id()) diff --git a/apiserver/facades/agent/upgradesteps/upgradesteps_test.go b/apiserver/facades/agent/upgradesteps/upgradesteps_test.go index 543bfd9d074..649491db3af 100644 --- a/apiserver/facades/agent/upgradesteps/upgradesteps_test.go +++ b/apiserver/facades/agent/upgradesteps/upgradesteps_test.go @@ -15,8 +15,6 @@ import ( "github.com/juju/juju/apiserver/facades/agent/upgradesteps" "github.com/juju/juju/controller" - "github.com/juju/juju/core/instance" - "github.com/juju/juju/core/status" "github.com/juju/juju/rpc/params" "github.com/juju/juju/state" jujutesting "github.com/juju/juju/testing" @@ -33,78 +31,6 @@ type upgradeStepsSuite struct { ctrlConfigGetter *MockControllerConfigGetter } -type machineUpgradeStepsSuite struct { - upgradeStepsSuite - - tag names.Tag - arg params.Entity - machine *MockMachine -} - -var _ = gc.Suite(&machineUpgradeStepsSuite{}) - -func (s *machineUpgradeStepsSuite) SetUpTest(c *gc.C) { - s.tag = names.NewMachineTag("0/kvm/0") - s.arg = params.Entity{Tag: s.tag.String()} - s.BaseSuite.SetUpTest(c) -} - -func (s *machineUpgradeStepsSuite) TestResetKVMMachineModificationStatusIdle(c *gc.C) { - defer s.setup(c).Finish() - - s.expectContainerType(instance.KVM) - s.expectModificationStatus(status.Error) - s.expectSetModificationStatus(nil) - - s.setupFacadeAPI(c) - - result, err := s.api.ResetKVMMachineModificationStatusIdle(context.Background(), s.arg) - c.Assert(err, jc.ErrorIsNil) - c.Assert(result, gc.DeepEquals, params.ErrorResult{}) -} - -func (s *machineUpgradeStepsSuite) TestResetKVMMachineModificationStatusIdleSetError(c *gc.C) { - defer s.setup(c).Finish() - - s.expectContainerType(instance.KVM) - s.expectModificationStatus(status.Error) - s.expectSetModificationStatus(errors.NotFoundf("testing")) - - s.setupFacadeAPI(c) - - result, err := s.api.ResetKVMMachineModificationStatusIdle(context.Background(), s.arg) - c.Assert(err, jc.ErrorIsNil) - c.Assert(result, gc.DeepEquals, params.ErrorResult{ - Error: ¶ms.Error{ - Message: "testing not found", - Code: "not found", - }, - }) -} - -func (s *machineUpgradeStepsSuite) TestResetKVMMachineModificationStatusIdleKVMIdle(c *gc.C) { - defer s.setup(c).Finish() - - s.expectContainerType(instance.KVM) - s.expectModificationStatus(status.Idle) - - s.setupFacadeAPI(c) - - _, err := s.api.ResetKVMMachineModificationStatusIdle(context.Background(), s.arg) - c.Assert(err, jc.ErrorIsNil) -} - -func (s *machineUpgradeStepsSuite) TestResetKVMMachineModificationStatusIdleLXD(c *gc.C) { - defer s.setup(c).Finish() - - s.expectContainerType(instance.LXD) - - s.setupFacadeAPI(c) - - _, err := s.api.ResetKVMMachineModificationStatusIdle(context.Background(), s.arg) - c.Assert(err, jc.ErrorIsNil) -} - type unitUpgradeStepsSuite struct { upgradeStepsSuite tag1 names.Tag @@ -198,49 +124,6 @@ func (s *upgradeStepsSuite) setupFacadeAPI(c *gc.C) { s.api = api } -func (s *machineUpgradeStepsSuite) setup(c *gc.C) *gomock.Controller { - ctlr := s.upgradeStepsSuite.setup(c) - s.machine = NewMockMachine(ctlr) - - s.expectAuthCalls() - s.expectFindEntityMachine() - - return ctlr -} - -func (s *machineUpgradeStepsSuite) expectAuthCalls() { - s.upgradeStepsSuite.expectAuthCalls() - aExp := s.authorizer.EXPECT() - aExp.GetAuthTag().Return(s.tag).AnyTimes() -} - -func (s *machineUpgradeStepsSuite) expectFindEntityMachine() { - mEntity := machineEntityShim{ - Machine: s.machine, - Entity: s.entity, - } - s.state.EXPECT().FindEntity(s.tag.(names.MachineTag)).Return(mEntity, nil) -} - -func (s *machineUpgradeStepsSuite) expectContainerType(cType instance.ContainerType) { - mExp := s.machine.EXPECT() - mExp.ContainerType().Return(cType) -} - -func (s *machineUpgradeStepsSuite) expectModificationStatus(sValue status.Status) { - mExp := s.machine.EXPECT() - mExp.ModificationStatus().Return(status.StatusInfo{Status: sValue}, nil) -} - -func (s *machineUpgradeStepsSuite) expectSetModificationStatus(err error) { - mExp := s.machine.EXPECT() - mExp.SetModificationStatus(status.StatusInfo{ - Status: status.Idle, - Message: "", - Data: nil, - }).Return(err) -} - func (s *unitUpgradeStepsSuite) setup(c *gc.C) *gomock.Controller { ctlr := s.upgradeStepsSuite.setup(c) s.unit1 = NewMockUnit(ctlr) @@ -301,11 +184,6 @@ func (s *unitUpgradeStepsSuite) expectSetAndApplyStateOperation(err1, err2 error s.state.EXPECT().ApplyOperation(op2).Return(err2) } -type machineEntityShim struct { - upgradesteps.Machine - state.Entity -} - type unitEntityShim struct { upgradesteps.Unit state.Entity diff --git a/apiserver/facades/schema.json b/apiserver/facades/schema.json index 9e56be9a9dd..61366677ad6 100644 --- a/apiserver/facades/schema.json +++ b/apiserver/facades/schema.json @@ -45811,7 +45811,7 @@ { "Name": "UpgradeSteps", "Description": "UpgradeStepsAPI implements version 2 of the Upgrade Steps API,\nwhich adds WriteUniterState.", - "Version": 2, + "Version": 3, "AvailableTo": [ "controller-machine-agent", "machine-agent", @@ -45821,18 +45821,6 @@ "Schema": { "type": "object", "properties": { - "ResetKVMMachineModificationStatusIdle": { - "type": "object", - "properties": { - "Params": { - "$ref": "#/definitions/Entity" - }, - "Result": { - "$ref": "#/definitions/ErrorResult" - } - }, - "description": "ResetKVMMachineModificationStatusIdle sets the modification status\nof a kvm machine to idle if it is in an error state before upgrade.\nRelated to lp:1829393." - }, "WriteAgentState": { "type": "object", "properties": { @@ -45847,18 +45835,6 @@ } }, "definitions": { - "Entity": { - "type": "object", - "properties": { - "tag": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "tag" - ] - }, "Error": { "type": "object", "properties": { diff --git a/caas/kubernetes/provider/constraints_test.go b/caas/kubernetes/provider/constraints_test.go index 589b5712072..6c16746fddc 100644 --- a/caas/kubernetes/provider/constraints_test.go +++ b/caas/kubernetes/provider/constraints_test.go @@ -61,10 +61,10 @@ func (s *ConstraintsSuite) TestConstraintsValidatorUnsupported(c *gc.C) { "instance-type=some-type", "cores=2", "cpu-power=250", - "virt-type=kvm", + "virt-type=lxd", "root-disk=10M", "spaces=foo", - "container=kvm", + "container=lxd", }, " ")) unsupported, err := validator.Validate(cons) c.Assert(err, jc.ErrorIsNil) diff --git a/cmd/juju/application/deployer/bundlehandler_test.go b/cmd/juju/application/deployer/bundlehandler_test.go index bbe9e0d3ddd..99fb6571590 100644 --- a/cmd/juju/application/deployer/bundlehandler_test.go +++ b/cmd/juju/application/deployer/bundlehandler_test.go @@ -1372,7 +1372,7 @@ const unitColocationWithUnitBundle = ` - mem/0 - lxd:mem/1 - lxd:mem/2 - - kvm:ror + - lxd:ror ror: charm: ch:rails series: focal @@ -1397,8 +1397,8 @@ func (s *BundleDeployRepositorySuite) TestDeployBundleUnitColocationWithUnit(c * s.expectAddMachine("3", "20.04") s.expectAddContainer("1", "1/lxd/0", "20.04", "lxd") s.expectAddContainer("2", "2/lxd/0", "20.04", "lxd") - s.expectAddContainer("3", "3/kvm/0", "20.04", "kvm") - s.expectAddContainer("0", "0/kvm/0", "20.04", "kvm") + s.expectAddContainer("3", "3/lxd/0", "20.04", "lxd") + s.expectAddContainer("0", "0/lxd/0", "20.04", "lxd") // Setup for mem charm memCurl := charm.MustParseURL("ch:mem") @@ -1433,8 +1433,8 @@ func (s *BundleDeployRepositorySuite) TestDeployBundleUnitColocationWithUnit(c * s.expectAddOneUnit("django", "0", "0") s.expectAddOneUnit("django", "1/lxd/0", "1") s.expectAddOneUnit("django", "2/lxd/0", "2") - s.expectAddOneUnit("django", "3/kvm/0", "3") - s.expectAddOneUnit("django", "0/kvm/0", "4") + s.expectAddOneUnit("django", "3/lxd/0", "3") + s.expectAddOneUnit("django", "0/lxd/0", "4") // Setup for rails charm railsCurl := charm.MustParseURL("ch:rails") @@ -1691,14 +1691,14 @@ func (s *BundleDeployRepositorySuite) testDeployBundleUnitPlacedToMachines(c *gc s.expectAddMachine("0", "22.04") s.expectAddContainer("0", "0/lxd/0", "22.04", "lxd") s.expectAddMachine("1", "22.04") - s.expectAddContainer("1", "1/kvm/0", "22.04", "kvm") + s.expectAddContainer("1", "1/lxd/0", "22.04", "lxd") s.expectAddMachine("2", "22.04") s.expectAddContainer("", "3/lxd/0", "22.04", "lxd") s.expectAddContainer("", "4/lxd/0", "22.04", "lxd") s.expectAddContainer("", "5/lxd/0", "22.04", "lxd") s.expectAddOneUnit("wp", "2", "0") s.expectAddOneUnit("wp", "0", "1") - s.expectAddOneUnit("wp", "1/kvm/0", "2") + s.expectAddOneUnit("wp", "1/lxd/0", "2") s.expectAddOneUnit("wp", "0/lxd/0", "3") s.expectAddOneUnit("wp", "3/lxd/0", "4") s.expectAddOneUnit("wp", "4/lxd/0", "5") @@ -1713,7 +1713,7 @@ applications: to: - new - 4 - - kvm:8 + - lxd:8 - lxd:4 - lxd:new machines: @@ -1731,14 +1731,14 @@ machines: "- add new machine 0 (bundle machine 4)\n"+ "- add new machine 1 (bundle machine 8)\n"+ "- add new machine 2\n"+ - "- add kvm container 1/kvm/0 on new machine 1\n"+ + "- add lxd container 1/lxd/0 on new machine 1\n"+ "- add lxd container 0/lxd/0 on new machine 0\n"+ "- add lxd container 3/lxd/0 on new machine 3\n"+ "- add lxd container 4/lxd/0 on new machine 4\n"+ "- add lxd container 5/lxd/0 on new machine 5\n"+ "- add unit wp/0 to new machine 2\n"+ "- add unit wp/1 to new machine 0\n"+ - "- add unit wp/2 to 1/kvm/0\n"+ + "- add unit wp/2 to 1/lxd/0\n"+ "- add unit wp/3 to 0/lxd/0\n"+ "- add unit wp/4 to 3/lxd/0\n"+ "- add unit wp/5 to 4/lxd/0\n"+ diff --git a/cmd/jujud-controller/agent/machine.go b/cmd/jujud-controller/agent/machine.go index 8888861111d..ad90e7dcea6 100644 --- a/cmd/jujud-controller/agent/machine.go +++ b/cmd/jujud-controller/agent/machine.go @@ -65,7 +65,6 @@ import ( "github.com/juju/juju/internal/charmhub" "github.com/juju/juju/internal/container" "github.com/juju/juju/internal/container/broker" - "github.com/juju/juju/internal/container/kvm" "github.com/juju/juju/internal/mongo" "github.com/juju/juju/internal/mongo/mongometrics" "github.com/juju/juju/internal/pki" @@ -886,13 +885,6 @@ func (a *MachineAgent) setupContainerSupport(ctx stdcontext.Context, st api.Conn supportedContainers = append(supportedContainers, instance.LXD) } - supportsKvm, err := kvm.IsKVMSupported() - if err != nil { - logger.Warningf("determining kvm support: %v\nno kvm containers possible", err) - } - if err == nil && supportsKvm { - supportedContainers = append(supportedContainers, instance.KVM) - } logger.Debugf("Supported container types %q", supportedContainers) if len(supportedContainers) == 0 { diff --git a/cmd/jujud-controller/agent/machine/manifolds.go b/cmd/jujud-controller/agent/machine/manifolds.go index 785b37737b6..24bdab353f8 100644 --- a/cmd/jujud-controller/agent/machine/manifolds.go +++ b/cmd/jujud-controller/agent/machine/manifolds.go @@ -1069,14 +1069,6 @@ func IAASManifolds(config ManifoldsConfig) dependency.Manifolds { MachineStartup: config.MachineStartup, Logger: loggo.GetLogger("juju.worker.machinesetup"), })), - kvmContainerProvisioner: ifNotMigrating(provisioner.ContainerProvisioningManifold(provisioner.ContainerManifoldConfig{ - AgentName: agentName, - APICallerName: apiCallerName, - Logger: loggo.GetLogger("juju.worker.kvmprovisioner"), - MachineLock: config.MachineLock, - NewCredentialValidatorFacade: common.NewCredentialInvalidatorFacade, - ContainerType: instance.KVM, - })), lxdContainerProvisioner: ifNotMigrating(provisioner.ContainerProvisioningManifold(provisioner.ContainerManifoldConfig{ AgentName: agentName, APICallerName: apiCallerName, @@ -1323,7 +1315,6 @@ const ( stateConverterName = "state-converter" serviceFactoryName = "service-factory" lxdContainerProvisioner = "lxd-container-provisioner" - kvmContainerProvisioner = "kvm-container-provisioner" controllerAgentConfigName = "controller-agent-config" objectStoreName = "object-store" objectStoreS3CallerName = "object-store-s3-caller" diff --git a/cmd/jujud-controller/agent/machine/manifolds_test.go b/cmd/jujud-controller/agent/machine/manifolds_test.go index eea47842be3..d3a09e2484c 100644 --- a/cmd/jujud-controller/agent/machine/manifolds_test.go +++ b/cmd/jujud-controller/agent/machine/manifolds_test.go @@ -97,7 +97,6 @@ func (s *ManifoldsSuite) TestManifoldNamesIAAS(c *gc.C) { "is-controller-flag", "is-not-controller-flag", "is-primary-controller-flag", - "kvm-container-provisioner", "lease-expiry", "lease-manager", "log-sender", @@ -877,18 +876,6 @@ var expectedMachineManifoldsWithDependenciesIAAS = map[string][]string{ "state-config-watcher", }, - "kvm-container-provisioner": { - "agent", - "api-caller", - "api-config-watcher", - "migration-fortress", - "migration-inactive-flag", - "upgrade-check-flag", - "upgrade-check-gate", - "upgrade-steps-flag", - "upgrade-steps-gate", - }, - "machine-action-runner": { "agent", "api-caller", diff --git a/cmd/jujud-controller/agent/machine_test.go b/cmd/jujud-controller/agent/machine_test.go index ce67fb19abd..3fe18681edf 100644 --- a/cmd/jujud-controller/agent/machine_test.go +++ b/cmd/jujud-controller/agent/machine_test.go @@ -43,7 +43,6 @@ import ( envstorage "github.com/juju/juju/environs/storage" envtesting "github.com/juju/juju/environs/testing" envtools "github.com/juju/juju/environs/tools" - "github.com/juju/juju/internal/container/kvm" "github.com/juju/juju/internal/mongo" "github.com/juju/juju/internal/tools" jworker "github.com/juju/juju/internal/worker" @@ -590,10 +589,6 @@ func (s *MachineSuite) TestMachineWorkers(c *gc.C) { matcher := agenttest.NewWorkerMatcher(c, tracker, a.Tag().String(), append(alwaysMachineWorkers, notMigratingMachineWorkers...)) - // Indicate that this machine supports KVM containers rather than doing - // detection that may return true/false based on the machine running tests. - s.PatchValue(&kvm.IsKVMSupported, func() (bool, error) { return true, nil }) - agenttest.WaitMatch(c, matcher.Check, coretesting.LongWait) } diff --git a/cmd/jujud/agent/machine.go b/cmd/jujud/agent/machine.go index 8c9d8154895..7726ce2c519 100644 --- a/cmd/jujud/agent/machine.go +++ b/cmd/jujud/agent/machine.go @@ -57,7 +57,6 @@ import ( "github.com/juju/juju/internal/charmhub" "github.com/juju/juju/internal/container" "github.com/juju/juju/internal/container/broker" - "github.com/juju/juju/internal/container/kvm" "github.com/juju/juju/internal/mongo" "github.com/juju/juju/internal/mongo/mongometrics" "github.com/juju/juju/internal/pki" @@ -763,13 +762,6 @@ func (a *MachineAgent) setupContainerSupport(ctx stdcontext.Context, st api.Conn supportedContainers = append(supportedContainers, instance.LXD) } - supportsKvm, err := kvm.IsKVMSupported() - if err != nil { - logger.Warningf("determining kvm support: %v\nno kvm containers possible", err) - } - if err == nil && supportsKvm { - supportedContainers = append(supportedContainers, instance.KVM) - } logger.Debugf("Supported container types %q", supportedContainers) if len(supportedContainers) == 0 { diff --git a/cmd/jujud/agent/machine/manifolds.go b/cmd/jujud/agent/machine/manifolds.go index b0c5f25c464..0e6b6721a51 100644 --- a/cmd/jujud/agent/machine/manifolds.go +++ b/cmd/jujud/agent/machine/manifolds.go @@ -552,14 +552,6 @@ func IAASManifolds(config ManifoldsConfig) dependency.Manifolds { MachineStartup: config.MachineStartup, Logger: loggo.GetLogger("juju.worker.machinesetup"), })), - kvmContainerProvisioner: ifNotMigrating(provisioner.ContainerProvisioningManifold(provisioner.ContainerManifoldConfig{ - AgentName: agentName, - APICallerName: apiCallerName, - Logger: loggo.GetLogger("juju.worker.kvmprovisioner"), - MachineLock: config.MachineLock, - NewCredentialValidatorFacade: common.NewCredentialInvalidatorFacade, - ContainerType: instance.KVM, - })), lxdContainerProvisioner: ifNotMigrating(provisioner.ContainerProvisioningManifold(provisioner.ContainerManifoldConfig{ AgentName: agentName, APICallerName: apiCallerName, @@ -692,7 +684,6 @@ const ( auditConfigUpdaterName = "audit-config-updater" stateConverterName = "state-converter" lxdContainerProvisioner = "lxd-container-provisioner" - kvmContainerProvisioner = "kvm-container-provisioner" upgradeSeriesWorkerName = "upgrade-series" diff --git a/cmd/jujud/agent/machine/manifolds_test.go b/cmd/jujud/agent/machine/manifolds_test.go index 6559d78f65d..ecee7b47534 100644 --- a/cmd/jujud/agent/machine/manifolds_test.go +++ b/cmd/jujud/agent/machine/manifolds_test.go @@ -76,7 +76,6 @@ func (s *ManifoldsSuite) TestManifoldNamesIAAS(c *gc.C) { "fan-configurer", "host-key-reporter", "instance-mutater", - "kvm-container-provisioner", "log-sender", "logging-config-updater", "lxd-container-provisioner", @@ -538,18 +537,6 @@ var expectedMachineManifoldsWithDependenciesIAAS = map[string][]string{ "upgrade-steps-gate", }, - "kvm-container-provisioner": { - "agent", - "api-caller", - "api-config-watcher", - "migration-fortress", - "migration-inactive-flag", - "upgrade-check-flag", - "upgrade-check-gate", - "upgrade-steps-flag", - "upgrade-steps-gate", - }, - "machine-action-runner": { "agent", "api-caller", diff --git a/cmd/jujud/agent/machine_test.go b/cmd/jujud/agent/machine_test.go index 45a0e1e723b..9c5194823ec 100644 --- a/cmd/jujud/agent/machine_test.go +++ b/cmd/jujud/agent/machine_test.go @@ -41,7 +41,6 @@ import ( envstorage "github.com/juju/juju/environs/storage" envtesting "github.com/juju/juju/environs/testing" envtools "github.com/juju/juju/environs/tools" - "github.com/juju/juju/internal/container/kvm" "github.com/juju/juju/internal/mongo" "github.com/juju/juju/internal/tools" jworker "github.com/juju/juju/internal/worker" @@ -518,10 +517,6 @@ func (s *MachineSuite) TestMachineWorkers(c *gc.C) { matcher := agenttest.NewWorkerMatcher(c, tracker, a.Tag().String(), append(alwaysMachineWorkers, notMigratingMachineWorkers...)) - // Indicate that this machine supports KVM containers rather than doing - // detection that may return true/false based on the machine running tests. - s.PatchValue(&kvm.IsKVMSupported, func() (bool, error) { return true, nil }) - agenttest.WaitMatch(c, matcher.Check, coretesting.LongWait) } diff --git a/cmd/jujud/reboot/mocks/clock_mock.go b/cmd/jujud/reboot/mocks/clock_mock.go new file mode 100644 index 00000000000..d7c49f0a5e2 --- /dev/null +++ b/cmd/jujud/reboot/mocks/clock_mock.go @@ -0,0 +1,97 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/juju/clock (interfaces: Clock) +// +// Generated by this command: +// +// mockgen -package mocks -destination mocks/clock_mock.go github.com/juju/clock Clock +// + +// Package mocks is a generated GoMock package. +package mocks + +import ( + reflect "reflect" + time "time" + + clock "github.com/juju/clock" + gomock "go.uber.org/mock/gomock" +) + +// MockClock is a mock of Clock interface. +type MockClock struct { + ctrl *gomock.Controller + recorder *MockClockMockRecorder +} + +// MockClockMockRecorder is the mock recorder for MockClock. +type MockClockMockRecorder struct { + mock *MockClock +} + +// NewMockClock creates a new mock instance. +func NewMockClock(ctrl *gomock.Controller) *MockClock { + mock := &MockClock{ctrl: ctrl} + mock.recorder = &MockClockMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClock) EXPECT() *MockClockMockRecorder { + return m.recorder +} + +// After mocks base method. +func (m *MockClock) After(arg0 time.Duration) <-chan time.Time { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "After", arg0) + ret0, _ := ret[0].(<-chan time.Time) + return ret0 +} + +// After indicates an expected call of After. +func (mr *MockClockMockRecorder) After(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "After", reflect.TypeOf((*MockClock)(nil).After), arg0) +} + +// AfterFunc mocks base method. +func (m *MockClock) AfterFunc(arg0 time.Duration, arg1 func()) clock.Timer { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AfterFunc", arg0, arg1) + ret0, _ := ret[0].(clock.Timer) + return ret0 +} + +// AfterFunc indicates an expected call of AfterFunc. +func (mr *MockClockMockRecorder) AfterFunc(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AfterFunc", reflect.TypeOf((*MockClock)(nil).AfterFunc), arg0, arg1) +} + +// NewTimer mocks base method. +func (m *MockClock) NewTimer(arg0 time.Duration) clock.Timer { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewTimer", arg0) + ret0, _ := ret[0].(clock.Timer) + return ret0 +} + +// NewTimer indicates an expected call of NewTimer. +func (mr *MockClockMockRecorder) NewTimer(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewTimer", reflect.TypeOf((*MockClock)(nil).NewTimer), arg0) +} + +// Now mocks base method. +func (m *MockClock) Now() time.Time { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Now") + ret0, _ := ret[0].(time.Time) + return ret0 +} + +// Now indicates an expected call of Now. +func (mr *MockClockMockRecorder) Now() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Now", reflect.TypeOf((*MockClock)(nil).Now)) +} diff --git a/cmd/jujud/reboot/package_test.go b/cmd/jujud/reboot/package_test.go index 8d8f3f81b3e..bae51315a20 100644 --- a/cmd/jujud/reboot/package_test.go +++ b/cmd/jujud/reboot/package_test.go @@ -6,17 +6,19 @@ package reboot import ( "testing" + "github.com/juju/clock" gc "gopkg.in/check.v1" ) +//go:generate go run go.uber.org/mock/mockgen -package mocks -destination mocks/service_mock.go github.com/juju/juju/cmd/jujud/reboot AgentConfig,Manager,Model,RebootWaiter,Service +//go:generate go run go.uber.org/mock/mockgen -package mocks -destination mocks/instance_mock.go github.com/juju/juju/environs/instances Instance +//go:generate go run go.uber.org/mock/mockgen -package mocks -destination mocks/clock_mock.go github.com/juju/clock Clock + func TestPackage(t *testing.T) { gc.TestingT(t) } // NewRebootForTest returns a Reboot object to be used for testing. -func NewRebootForTest(acfg AgentConfig, reboot RebootWaiter) *Reboot { - return &Reboot{acfg: acfg, reboot: reboot} +func NewRebootForTest(acfg AgentConfig, reboot RebootWaiter, clock clock.Clock) *Reboot { + return &Reboot{acfg: acfg, reboot: reboot, clock: clock} } - -//go:generate go run go.uber.org/mock/mockgen -package mocks -destination mocks/service_mock.go github.com/juju/juju/cmd/jujud/reboot AgentConfig,Manager,Model,RebootWaiter,Service -//go:generate go run go.uber.org/mock/mockgen -package mocks -destination mocks/instance_mock.go github.com/juju/juju/environs/instances Instance diff --git a/cmd/jujud/reboot/reboot.go b/cmd/jujud/reboot/reboot.go index cc11012bc5b..d1e771c375d 100644 --- a/cmd/jujud/reboot/reboot.go +++ b/cmd/jujud/reboot/reboot.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/juju/clock" "github.com/juju/errors" "github.com/juju/loggo/v2" "github.com/juju/names/v5" @@ -40,8 +41,11 @@ var tmpFile = func() (*os.File, error) { type Reboot struct { acfg AgentConfig reboot RebootWaiter + clock clock.Clock } +// NewRebootWaiter creates a new Reboot command that waits for all containers +// to shut down before executing a reboot. func NewRebootWaiter(acfg agent.Config) (*Reboot, error) { // ensure we're only running on a machine agent. if _, ok := acfg.Tag().(names.MachineTag); !ok { @@ -50,6 +54,7 @@ func NewRebootWaiter(acfg agent.Config) (*Reboot, error) { return &Reboot{ acfg: &agentConfigShim{aCfg: acfg}, reboot: rebootWaiterShim{}, + clock: clock.WallClock, }, nil } @@ -143,13 +148,18 @@ func (r *Reboot) waitForContainersOrTimeout() error { return } logger.Warningf("Waiting for containers to shutdown: %v", containers) - time.Sleep(1 * time.Second) + select { + case <-quit: + c <- nil + return + case <-r.clock.After(time.Second): + } } } }() select { - case <-time.After(timeout): + case <-r.clock.After(timeout): // TODO(fwereade): 2016-03-17 lp:1558657 // Containers are still up after timeout. C'est la vie quit <- true diff --git a/cmd/jujud/reboot/reboot_test.go b/cmd/jujud/reboot/reboot_test.go index 64f1c6c914c..19d5957e65f 100644 --- a/cmd/jujud/reboot/reboot_test.go +++ b/cmd/jujud/reboot/reboot_test.go @@ -6,6 +6,7 @@ package reboot_test import ( "os" "path/filepath" + "time" "github.com/juju/testing" jc "github.com/juju/testing/checkers" @@ -27,13 +28,14 @@ type NewRebootSuite struct { model *mocks.MockModel rebootWaiter *mocks.MockRebootWaiter service *mocks.MockService + clock *mocks.MockClock } var _ = gc.Suite(&NewRebootSuite{}) func (s *NewRebootSuite) TestExecuteReboot(c *gc.C) { defer s.setupMocks(c).Finish() - s.expectManagerIsInitialized(false, false) + s.expectManagerIsInitialized(false, 1) s.expectListServices() s.expectStopDeployedUnits() s.expectScheduleAction() @@ -44,8 +46,7 @@ func (s *NewRebootSuite) TestExecuteReboot(c *gc.C) { func (s *NewRebootSuite) TestExecuteRebootWaitForContainers(c *gc.C) { defer s.setupMocks(c).Finish() - s.expectManagerIsInitialized(true, false) - s.expectManagerIsInitialized(true, false) + s.expectManagerIsInitialized(true, 2) s.expectListContainers() s.expectListServices() s.expectStopDeployedUnits() @@ -56,7 +57,7 @@ func (s *NewRebootSuite) TestExecuteRebootWaitForContainers(c *gc.C) { } func (s *NewRebootSuite) newRebootWaiter() *reboot.Reboot { - return reboot.NewRebootForTest(s.agentConfig, s.rebootWaiter) + return reboot.NewRebootForTest(s.agentConfig, s.rebootWaiter, s.clock) } func (s *NewRebootSuite) setupMocks(c *gc.C) *gomock.Controller { @@ -72,12 +73,22 @@ func (s *NewRebootSuite) setupMocks(c *gc.C) *gomock.Controller { s.model.EXPECT().Id().Return("model-uuid").AnyTimes() s.rebootWaiter.EXPECT().NewContainerManager(gomock.Any(), gomock.Any()).Return(s.containerManager, nil).AnyTimes() + + s.clock = mocks.NewMockClock(ctrl) + s.clock.EXPECT().After(time.Minute * 10).DoAndReturn(func(time.Duration) <-chan time.Time { + ch := make(chan time.Time) + return ch + }) + s.clock.EXPECT().After(time.Second).DoAndReturn(func(time.Duration) <-chan time.Time { + ch := make(chan time.Time) + close(ch) + return ch + }).AnyTimes() return ctrl } -func (s *NewRebootSuite) expectManagerIsInitialized(lxd, kvm bool) { - s.containerManager.EXPECT().IsInitialized().Return(lxd) - s.containerManager.EXPECT().IsInitialized().Return(kvm) +func (s *NewRebootSuite) expectManagerIsInitialized(lxd bool, times int) { + s.containerManager.EXPECT().IsInitialized().Return(lxd).Times(times) } func (s *NewRebootSuite) expectListServices() { @@ -141,7 +152,7 @@ func (s *NixRebootSuite) TestReboot(c *gc.C) { err := reboot.ScheduleAction(params.ShouldReboot, 15) c.Assert(err, jc.ErrorIsNil) testing.AssertEchoArgs(c, rebootBin, expectedParams...) - ft.File{s.rebootScriptName, expectedRebootScript, 0755}.Check(c, s.tmpDir) + ft.File{Path: s.rebootScriptName, Data: expectedRebootScript, Perm: 0755}.Check(c, s.tmpDir) } func (s *NixRebootSuite) TestShutdownNoContainers(c *gc.C) { @@ -150,7 +161,7 @@ func (s *NixRebootSuite) TestShutdownNoContainers(c *gc.C) { err := reboot.ScheduleAction(params.ShouldShutdown, 15) c.Assert(err, jc.ErrorIsNil) testing.AssertEchoArgs(c, rebootBin, expectedParams...) - ft.File{s.rebootScriptName, expectedShutdownScript, 0755}.Check(c, s.tmpDir) + ft.File{Path: s.rebootScriptName, Data: expectedShutdownScript, Perm: 0755}.Check(c, s.tmpDir) } func (s *NixRebootSuite) rebootScript() string { diff --git a/core/container/container_test.go b/core/container/container_test.go index 46d91f20bcf..732f715f63c 100644 --- a/core/container/container_test.go +++ b/core/container/container_test.go @@ -35,5 +35,4 @@ func (s *ContainerSuite) TestParentId(c *gc.C) { func (s *ContainerSuite) TestContainerTypeFromId(c *gc.C) { c.Assert(container.ContainerTypeFromId("0"), gc.Equals, instance.ContainerType("")) c.Assert(container.ContainerTypeFromId("0/lxd/1"), gc.Equals, instance.LXD) - c.Assert(container.ContainerTypeFromId("0/lxd/1/kvm/0"), gc.Equals, instance.KVM) } diff --git a/core/instance/container.go b/core/instance/container.go index 34ab8793fef..c2d5209a190 100644 --- a/core/instance/container.go +++ b/core/instance/container.go @@ -14,13 +14,11 @@ type ContainerType string const ( NONE ContainerType = "none" LXD ContainerType = "lxd" - KVM ContainerType = "kvm" ) // ContainerTypes is used to validate add-machine arguments. var ContainerTypes = []ContainerType{ LXD, - KVM, } // ParseContainerTypeOrNone converts the specified string into a supported diff --git a/core/instance/container_test.go b/core/instance/container_test.go index e118ee9289c..d714614d6ee 100644 --- a/core/instance/container_test.go +++ b/core/instance/container_test.go @@ -29,10 +29,6 @@ func (s *InstanceSuite) TestParseContainerType(c *gc.C) { c.Assert(err, jc.ErrorIsNil) c.Assert(ctype, gc.Equals, instance.LXD) - ctype, err = instance.ParseContainerType("kvm") - c.Assert(err, jc.ErrorIsNil) - c.Assert(ctype, gc.Equals, instance.KVM) - _, err = instance.ParseContainerType("none") c.Assert(err, gc.ErrorMatches, `invalid container type "none"`) @@ -49,10 +45,6 @@ func (s *InstanceSuite) TestParseContainerTypeOrNone(c *gc.C) { c.Assert(err, jc.ErrorIsNil) c.Assert(ctype, gc.Equals, instance.LXD) - ctype, err = instance.ParseContainerTypeOrNone("kvm") - c.Assert(err, jc.ErrorIsNil) - c.Assert(ctype, gc.Equals, instance.KVM) - ctype, err = instance.ParseContainerTypeOrNone("none") c.Assert(err, jc.ErrorIsNil) c.Assert(ctype, gc.Equals, instance.NONE) diff --git a/core/instance/placement_test.go b/core/instance/placement_test.go index d2d198a02c8..f0686449def 100644 --- a/core/instance/placement_test.go +++ b/core/instance/placement_test.go @@ -35,13 +35,6 @@ func (s *PlacementSuite) TestParsePlacement(c *gc.C) { }, { arg: "lxd:x", err: `invalid value "x" for "lxd" scope: expected machine-id`, - }, { - arg: "kvm:x", - err: `invalid value "x" for "kvm" scope: expected machine-id`, - }, { - arg: "kvm:123", - expectScope: string(instance.KVM), - expectDirective: "123", }, { arg: "lxd", expectScope: string(instance.LXD), diff --git a/internal/bundle/changes/changes.go b/internal/bundle/changes/changes.go index e6504db8da1..76bb36f297b 100644 --- a/internal/bundle/changes/changes.go +++ b/internal/bundle/changes/changes.go @@ -390,7 +390,7 @@ type AddMachineOptions struct { Base string `json:"base,omitempty"` // Constraints holds the machine constraints. Constraints string `json:"constraints,omitempty"` - // ContainerType holds the machine container type (like "lxc" or "kvm"). + // ContainerType holds the machine container type (like "lxc"). ContainerType string `json:"containerType,omitempty"` // ParentId holds the id of the parent machine. ParentId string `json:"parentId,omitempty"` @@ -403,7 +403,7 @@ type AddMachineParams struct { // Constraints holds the optional machine constraints. Constraints string `json:"constraints,omitempty"` // ContainerType optionally holds the type of the container (for instance - // ""lxc" or kvm"). It is not specified for top level machines. + // "lxc"). It is not specified for top level machines. ContainerType string `json:"container-type,omitempty"` // ParentId optionally holds a placeholder pointing to another machine // change or to a unit change. This value is only specified in the case diff --git a/internal/bundle/changes/handlers.go b/internal/bundle/changes/handlers.go index 303d7c0351d..ebe004a446a 100644 --- a/internal/bundle/changes/handlers.go +++ b/internal/bundle/changes/handlers.go @@ -794,7 +794,7 @@ func (p *unitProcessor) existingMachinePlacement(machineID, container string) un // newMachineForUnit handles the placement directives "new" and // "container:new", where container is a supported container type. Most often -// "lxd" or "kvm". +// "lxd". func (p *unitProcessor) newMachineForUnit(application *charm.ApplicationSpec, placement *charm.UnitPlacement) (unitPlacement, error) { return p.addNewMachine(application, placement.ContainerType) } diff --git a/internal/bundle/changes/model_test.go b/internal/bundle/changes/model_test.go index aa28c95368e..bce6dbc1710 100644 --- a/internal/bundle/changes/model_test.go +++ b/internal/bundle/changes/model_test.go @@ -122,7 +122,7 @@ func (*modelSuite) TestMachineHasApp(c *gc.C) { "nginx": { Units: []Unit{ {"nginx/0", "0/lxd/3"}, - {"nginx/2", "2/kvm/2"}, + {"nginx/2", "2/lxd/2"}, }, }, }, @@ -133,11 +133,9 @@ func (*modelSuite) TestMachineHasApp(c *gc.C) { c.Check(model.machineHasApp("0", "nginx", ""), jc.IsFalse) c.Check(model.machineHasApp("0", "nginx", "lxd"), jc.IsTrue) - c.Check(model.machineHasApp("0", "nginx", "kvm"), jc.IsFalse) c.Check(model.machineHasApp("2", "nginx", ""), jc.IsFalse) - c.Check(model.machineHasApp("2", "nginx", "lxd"), jc.IsFalse) - c.Check(model.machineHasApp("2", "nginx", "kvm"), jc.IsTrue) + c.Check(model.machineHasApp("2", "nginx", "lxd"), jc.IsTrue) } func (*modelSuite) TestUnsatisfiedMachineAndUnitPlacement(c *gc.C) { @@ -228,7 +226,7 @@ func (*modelSuite) TestUnitMachinesWithoutAppSourceSomeTargetContainer(c *gc.C) {"nginx/1", "1/lxd/3"}, {"nginx/2", "2/lxd/0"}, {"nginx/3", "1/lxd/2"}, - {"nginx/4", "3/kvm/2"}, + {"nginx/4", "3/lxd/2"}, }, }, }, @@ -236,7 +234,7 @@ func (*modelSuite) TestUnitMachinesWithoutAppSourceSomeTargetContainer(c *gc.C) machines := model.unitMachinesWithoutApp("django", "nginx", "lxd") // Machine 2 is shown because the nginx isn't next to the django unit, but // instead in a container. - c.Check(machines, jc.DeepEquals, []string{"0", "3", "4"}) + c.Check(machines, jc.DeepEquals, []string{"0", "4"}) } func (*modelSuite) TestBundleMachineMapped(c *gc.C) { @@ -306,7 +304,7 @@ func (s *inferMachineMapSuite) SetUpTest(c *gc.C) { to: - new - 4 - - kvm:8 + - lxc:8 - lxc:new machines: 4: @@ -378,7 +376,7 @@ func (s *inferMachineMapSuite) TestInferMachineMapDeployedUnits(c *gc.C) { Units: []Unit{ {"django/0", "0"}, {"django/1", "1"}, - {"django/2", "2/kvm/0"}, + {"django/2", "2/lxc/0"}, {"django/3", "3/lxc/0"}, {"django/4", "4/lxc/0"}, }, diff --git a/internal/container/broker/broker_test.go b/internal/container/broker/broker_test.go index 39f93e88b1a..7739c99ee0e 100644 --- a/internal/container/broker/broker_test.go +++ b/internal/container/broker/broker_test.go @@ -27,7 +27,6 @@ import ( "github.com/juju/juju/environs" "github.com/juju/juju/environs/envcontext" "github.com/juju/juju/environs/instances" - "github.com/juju/juju/environs/instances/instancetest" "github.com/juju/juju/internal/cloudconfig" "github.com/juju/juju/internal/cloudconfig/instancecfg" "github.com/juju/juju/internal/container" @@ -293,20 +292,6 @@ nameserver ns2.dummy s.PatchValue(broker.ResolvConfFiles, []string{fakeResolvConf}) } -func instancesFromResults(results ...*environs.StartInstanceResult) []instances.Instance { - instances := make([]instances.Instance, len(results)) - for i := range results { - instances[i] = results[i].Instance - } - return instances -} - -func assertInstancesStarted(c *gc.C, broker environs.InstanceBroker, results ...*environs.StartInstanceResult) { - allInstances, err := broker.AllRunningInstances(envcontext.WithoutCredentialInvalidator(context.Background())) - c.Assert(err, jc.ErrorIsNil) - instancetest.MatchInstances(c, allInstances, instancesFromResults(results...)...) -} - func makeInstanceConfig(c *gc.C, s patcher, machineId string) *instancecfg.InstanceConfig { machineNonce := "fake-nonce" // To isolate the tests from the host's architecture, we override it here. diff --git a/internal/container/broker/instance_broker.go b/internal/container/broker/instance_broker.go index 9bba2cc1c42..7306375b42c 100644 --- a/internal/container/broker/instance_broker.go +++ b/internal/container/broker/instance_broker.go @@ -90,8 +90,6 @@ func New(config Config) (environs.InstanceBroker, error) { var newBroker ContainerBrokerFunc switch config.ContainerType { - case instance.KVM: - newBroker = NewKVMBroker case instance.LXD: newBroker = NewLXDBroker default: diff --git a/internal/container/broker/kvm-broker.go b/internal/container/broker/kvm-broker.go deleted file mode 100644 index 71095e0c6e1..00000000000 --- a/internal/container/broker/kvm-broker.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2013 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package broker - -import ( - "github.com/juju/errors" - "github.com/juju/loggo/v2" - "github.com/juju/names/v5" - - "github.com/juju/juju/agent" - "github.com/juju/juju/core/instance" - "github.com/juju/juju/environs" - "github.com/juju/juju/environs/envcontext" - "github.com/juju/juju/environs/instances" - "github.com/juju/juju/internal/cloudconfig/instancecfg" - "github.com/juju/juju/internal/container" -) - -var kvmLogger = loggo.GetLogger("juju.container.broker.kvm") - -// NewKVMBroker creates a Broker that can be used to start KVM guests in a -// similar fashion to normal StartInstance requests. -// prepareHost is a callback that will be called when a new container is about -// to be started. It provides the intersection point where the host can update -// itself to be ready for whatever changes are necessary to have a functioning -// container. (such as bridging host devices.) -// manager is the infrastructure to actually launch the container. -// agentConfig is currently only used to find out the 'default' bridge to use -// when a specific network device is not specified in StartInstanceParams. This -// should be deprecated. And hopefully removed in the future. -func NewKVMBroker( - prepareHost PrepareHostFunc, - api APICalls, - manager container.Manager, - agentConfig agent.Config, -) (environs.InstanceBroker, error) { - return &kvmBroker{ - prepareHost: prepareHost, - manager: manager, - api: api, - agentConfig: agentConfig, - }, nil -} - -type kvmBroker struct { - prepareHost PrepareHostFunc - manager container.Manager - api APICalls - agentConfig agent.Config -} - -// StartInstance is specified in the Broker interface. -func (broker *kvmBroker) StartInstance(ctx envcontext.ProviderCallContext, args environs.StartInstanceParams) (*environs.StartInstanceResult, error) { - // TODO: refactor common code out of the container brokers. - containerMachineID := args.InstanceConfig.MachineId - kvmLogger.Infof("starting kvm container for containerMachineID: %s", containerMachineID) - - config, err := broker.api.ContainerConfig() - if err != nil { - kvmLogger.Errorf("failed to get container config: %v", err) - return nil, err - } - - err = broker.prepareHost(names.NewMachineTag(containerMachineID), kvmLogger, args.Abort) - if err != nil { - return nil, errors.Trace(err) - } - - preparedInfo, err := prepareContainerInterfaceInfo(broker.api, containerMachineID, kvmLogger) - if err != nil { - return nil, errors.Trace(err) - } - - interfaces, err := finishNetworkConfig(preparedInfo) - if err != nil { - return nil, errors.Trace(err) - } - net := container.BridgeNetworkConfig(0, interfaces) - - // The provisioner worker will provide all tools it knows about - // (after applying explicitly specified constraints), which may - // include tools for architectures other than the host's. - // - // container/kvm only allows running container==host arch, so - // we constrain the tools to host arch here regardless of the - // constraints specified. - archTools, err := matchHostArchTools(args.Tools) - if err != nil { - return nil, errors.Trace(err) - } - - args.InstanceConfig.MachineContainerType = instance.KVM - if err := args.InstanceConfig.SetTools(archTools); err != nil { - return nil, errors.Trace(err) - } - - cloudInitUserData, err := combinedCloudInitData( - config.CloudInitUserData, - config.ContainerInheritProperties, - args.InstanceConfig.Base, kvmLogger) - if err != nil { - return nil, errors.Trace(err) - } - - if err := instancecfg.PopulateInstanceConfig( - args.InstanceConfig, - config.ProviderType, - config.AuthorizedKeys, - config.SSLHostnameVerification, - proxyConfigurationFromContainerCfg(config), - config.EnableOSRefreshUpdate, - config.EnableOSUpgrade, - cloudInitUserData, - nil, - ); err != nil { - kvmLogger.Errorf("failed to populate machine config: %v", err) - return nil, err - } - - storageConfig := &container.StorageConfig{ - AllowMount: true, - } - inst, hardware, err := broker.manager.CreateContainer( - ctx, args.InstanceConfig, args.Constraints, args.InstanceConfig.Base, net, storageConfig, args.StatusCallback, - ) - if err != nil { - kvmLogger.Errorf("failed to start container: %v", err) - return nil, err - } - kvmLogger.Infof("started kvm container for containerMachineID: %s, %s, %s", containerMachineID, inst.Id(), hardware.String()) - return &environs.StartInstanceResult{ - Instance: inst, - Hardware: hardware, - }, nil -} - -// StopInstances shuts down the given instances. -func (broker *kvmBroker) StopInstances(ctx envcontext.ProviderCallContext, ids ...instance.Id) error { - for _, id := range ids { - kvmLogger.Infof("stopping kvm container for instance: %s", id) - if err := broker.manager.DestroyContainer(id); err != nil { - kvmLogger.Errorf("container did not stop: %v", err) - return err - } - releaseContainerAddresses(broker.api, id, broker.manager.Namespace(), kvmLogger) - } - return nil -} - -// AllInstances returns all containers. -func (broker *kvmBroker) AllInstances(ctx envcontext.ProviderCallContext) (result []instances.Instance, err error) { - return broker.manager.ListContainers() -} - -// AllRunningInstances only returns running containers. -func (broker *kvmBroker) AllRunningInstances(ctx envcontext.ProviderCallContext) (result []instances.Instance, err error) { - return broker.manager.ListContainers() -} diff --git a/internal/container/broker/kvm-broker_test.go b/internal/container/broker/kvm-broker_test.go deleted file mode 100644 index 8e81f9c20db..00000000000 --- a/internal/container/broker/kvm-broker_test.go +++ /dev/null @@ -1,279 +0,0 @@ -// Copyright 2013 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package broker_test - -import ( - "context" - "fmt" - "path/filepath" - - "github.com/juju/errors" - "github.com/juju/names/v5" - jujutesting "github.com/juju/testing" - jc "github.com/juju/testing/checkers" - gc "gopkg.in/check.v1" - - "github.com/juju/juju/agent" - corebase "github.com/juju/juju/core/base" - "github.com/juju/juju/core/instance" - "github.com/juju/juju/environs" - "github.com/juju/juju/environs/envcontext" - "github.com/juju/juju/internal/cloudconfig" - "github.com/juju/juju/internal/cloudconfig/instancecfg" - "github.com/juju/juju/internal/container" - "github.com/juju/juju/internal/container/broker" - "github.com/juju/juju/internal/container/kvm" - "github.com/juju/juju/internal/container/kvm/mock" - kvmtesting "github.com/juju/juju/internal/container/kvm/testing" - coretesting "github.com/juju/juju/testing" - jujuversion "github.com/juju/juju/version" -) - -type kvmSuite struct { - kvmtesting.TestSuite - events chan mock.Event - eventsDone chan struct{} -} - -type kvmBrokerSuite struct { - kvmSuite - agentConfig agent.Config - api *fakeAPI - manager *fakeContainerManager -} - -var _ = gc.Suite(&kvmBrokerSuite{}) - -func (s *kvmSuite) SetUpTest(c *gc.C) { - s.TestSuite.SetUpTest(c) - s.events = make(chan mock.Event) - s.eventsDone = make(chan struct{}) - go func() { - defer close(s.eventsDone) - for event := range s.events { - c.Output(3, fmt.Sprintf("kvm event: <%s, %s>", event.Action, event.InstanceId)) - } - }() - s.TestSuite.ContainerFactory.AddListener(s.events) -} - -func (s *kvmSuite) TearDownTest(c *gc.C) { - close(s.events) - <-s.eventsDone - s.TestSuite.TearDownTest(c) -} - -func (s *kvmBrokerSuite) SetUpTest(c *gc.C) { - s.kvmSuite.SetUpTest(c) - broker.PatchNewMachineInitReader(s, newBlankMachineInitReader) - - var err error - s.agentConfig, err = agent.NewAgentConfig( - agent.AgentConfigParams{ - Paths: agent.NewPathsWithDefaults(agent.Paths{DataDir: "/not/used/here"}), - Tag: names.NewUnitTag("ubuntu/1"), - UpgradedToVersion: jujuversion.Current, - Password: "dummy-secret", - Nonce: "nonce", - APIAddresses: []string{"10.0.0.1:1234"}, - CACert: coretesting.CACert, - Controller: coretesting.ControllerTag, - Model: coretesting.ModelTag, - }) - c.Assert(err, jc.ErrorIsNil) - s.api = NewFakeAPI() - s.manager = &fakeContainerManager{} -} - -func (s *kvmBrokerSuite) startInstance(c *gc.C, broker environs.InstanceBroker, machineId string) (*environs.StartInstanceResult, error) { - return callStartInstance(c, s, broker, machineId) -} - -func (s *kvmBrokerSuite) newKVMBroker(c *gc.C) (environs.InstanceBroker, error) { - managerConfig := container.ManagerConfig{container.ConfigModelUUID: coretesting.ModelTag.Id()} - manager, err := kvm.NewContainerManager(managerConfig) - c.Assert(err, jc.ErrorIsNil) - return broker.NewKVMBroker(s.api.PrepareHost, s.api, manager, s.agentConfig) -} - -func (s *kvmBrokerSuite) newKVMBrokerFakeManager(c *gc.C) (environs.InstanceBroker, error) { - return broker.NewKVMBroker(s.api.PrepareHost, s.api, s.manager, s.agentConfig) -} - -func (s *kvmBrokerSuite) TestStartInstanceWithoutNetworkChanges(c *gc.C) { - broker, brokerErr := s.newKVMBroker(c) - c.Assert(brokerErr, jc.ErrorIsNil) - - machineId := "1/kvm/0" - result, err := s.startInstance(c, broker, machineId) - c.Assert(err, jc.ErrorIsNil) - s.api.CheckCalls(c, []jujutesting.StubCall{{ - FuncName: "ContainerConfig", - }, { - FuncName: "PrepareHost", - Args: []interface{}{names.NewMachineTag("1-kvm-0")}, - }, { - FuncName: "PrepareContainerInterfaceInfo", - Args: []interface{}{names.NewMachineTag("1-kvm-0")}, - }}) - c.Assert(result.Instance.Id(), gc.Equals, instance.Id("juju-06f00d-1-kvm-0")) - s.assertResults(c, broker, result) -} - -func (s *kvmBrokerSuite) TestMaintainInstanceAddress(c *gc.C) { - broker, brokerErr := s.newKVMBroker(c) - c.Assert(brokerErr, jc.ErrorIsNil) - - machineId := "1/kvm/0" - result, err := s.startInstance(c, broker, machineId) - c.Assert(err, jc.ErrorIsNil) - - s.api.ResetCalls() - - s.api.CheckCalls(c, []jujutesting.StubCall{}) - c.Assert(result.Instance.Id(), gc.Equals, instance.Id("juju-06f00d-1-kvm-0")) - s.assertResults(c, broker, result) -} - -func (s *kvmBrokerSuite) TestStopInstance(c *gc.C) { - broker, brokerErr := s.newKVMBroker(c) - c.Assert(brokerErr, jc.ErrorIsNil) - - result0, err0 := s.startInstance(c, broker, "1/kvm/0") - c.Assert(err0, jc.ErrorIsNil) - - result1, err1 := s.startInstance(c, broker, "1/kvm/1") - c.Assert(err1, jc.ErrorIsNil) - - result2, err2 := s.startInstance(c, broker, "1/kvm/2") - c.Assert(err2, jc.ErrorIsNil) - - callCtx := envcontext.WithoutCredentialInvalidator(context.Background()) - err := broker.StopInstances(callCtx, result0.Instance.Id()) - c.Assert(err, jc.ErrorIsNil) - s.assertResults(c, broker, result1, result2) - c.Assert(s.kvmContainerDir(result0), jc.DoesNotExist) - c.Assert(s.kvmRemovedContainerDir(result0), jc.IsDirectory) - - err = broker.StopInstances(callCtx, result1.Instance.Id(), result2.Instance.Id()) - c.Assert(err, jc.ErrorIsNil) - s.assertNoResults(c, broker) -} - -func (s *kvmBrokerSuite) TestAllRunningInstances(c *gc.C) { - broker, brokerErr := s.newKVMBroker(c) - c.Assert(brokerErr, jc.ErrorIsNil) - - result0, err0 := s.startInstance(c, broker, "1/kvm/0") - c.Assert(err0, jc.ErrorIsNil) - - result1, err1 := s.startInstance(c, broker, "1/kvm/1") - c.Assert(err1, jc.ErrorIsNil) - s.assertResults(c, broker, result0, result1) - - err := broker.StopInstances(envcontext.WithoutCredentialInvalidator(context.Background()), result1.Instance.Id()) - c.Assert(err, jc.ErrorIsNil) - result2, err2 := s.startInstance(c, broker, "1/kvm/2") - c.Assert(err2, jc.ErrorIsNil) - s.assertResults(c, broker, result0, result2) -} - -func (s *kvmBrokerSuite) assertResults(c *gc.C, broker environs.InstanceBroker, results ...*environs.StartInstanceResult) { - assertInstancesStarted(c, broker, results...) -} - -func (s *kvmBrokerSuite) assertNoResults(c *gc.C, broker environs.InstanceBroker) { - s.assertResults(c, broker) -} - -func (s *kvmBrokerSuite) kvmContainerDir(result *environs.StartInstanceResult) string { - inst := result.Instance - return filepath.Join(s.ContainerDir, string(inst.Id())) -} - -func (s *kvmBrokerSuite) kvmRemovedContainerDir(result *environs.StartInstanceResult) string { - inst := result.Instance - return filepath.Join(s.RemovedDir, string(inst.Id())) -} - -func (s *kvmBrokerSuite) TestStartInstancePopulatesFallbackNetworkInfo(c *gc.C) { - broker, brokerErr := s.newKVMBroker(c) - c.Assert(brokerErr, jc.ErrorIsNil) - - patchResolvConf(s, c) - - s.api.SetErrors( - nil, // ContainerConfig succeeds - nil, // HostChangesForContainer succeeds - errors.NotSupportedf("container address allocation"), - ) - _, err := s.startInstance(c, broker, "1/kvm/2") - c.Assert(err, gc.ErrorMatches, "container address allocation not supported") -} - -func (s *kvmBrokerSuite) TestStartInstanceWithCloudInitUserData(c *gc.C) { - broker, brokerErr := s.newKVMBrokerFakeManager(c) - c.Assert(brokerErr, jc.ErrorIsNil) - - _, err := s.startInstance(c, broker, "1/kvm/0") - c.Assert(err, jc.ErrorIsNil) - - s.manager.CheckCallNames(c, "CreateContainer") - call := s.manager.Calls()[0] - c.Assert(call.Args[0], gc.FitsTypeOf, &instancecfg.InstanceConfig{}) - instanceConfig := call.Args[0].(*instancecfg.InstanceConfig) - assertCloudInitUserData(instanceConfig.CloudInitUserData, map[string]interface{}{ - "packages": []interface{}{"python-keystoneclient", "python-glanceclient"}, - "preruncmd": []interface{}{"mkdir /tmp/preruncmd", "mkdir /tmp/preruncmd2"}, - "postruncmd": []interface{}{"mkdir /tmp/postruncmd", "mkdir /tmp/postruncmd2"}, - "package_upgrade": false, - }, c) -} - -func (s *kvmBrokerSuite) TestStartInstanceWithContainerInheritProperties(c *gc.C) { - broker.PatchNewMachineInitReader(s, newFakeMachineInitReader) - s.api.fakeContainerConfig.ContainerInheritProperties = "ca-certs,apt-security" - - broker, brokerErr := s.newKVMBrokerFakeManager(c) - c.Assert(brokerErr, jc.ErrorIsNil) - - _, err := s.startInstance(c, broker, "1/kvm/0") - c.Assert(err, jc.ErrorIsNil) - - s.manager.CheckCallNames(c, "CreateContainer") - call := s.manager.Calls()[0] - c.Assert(call.Args[0], gc.FitsTypeOf, &instancecfg.InstanceConfig{}) - instanceConfig := call.Args[0].(*instancecfg.InstanceConfig) - assertCloudInitUserData(instanceConfig.CloudInitUserData, map[string]interface{}{ - "packages": []interface{}{"python-keystoneclient", "python-glanceclient"}, - "preruncmd": []interface{}{"mkdir /tmp/preruncmd", "mkdir /tmp/preruncmd2"}, - "postruncmd": []interface{}{"mkdir /tmp/postruncmd", "mkdir /tmp/postruncmd2"}, - "package_upgrade": false, - "apt": map[string]interface{}{ - "security": []interface{}{ - map[interface{}]interface{}{ - "arches": []interface{}{"default"}, - "uri": "http://archive.ubuntu.com/ubuntu", - }, - }, - }, - "ca-certs": map[interface{}]interface{}{ - "remove-defaults": true, - "trusted": []interface{}{"-----BEGIN CERTIFICATE-----\nYOUR-ORGS-TRUSTED-CA-CERT-HERE\n-----END CERTIFICATE-----\n"}, - }, - }, c) -} - -type blankMachineInitReader struct { - cloudconfig.InitReader -} - -func (r *blankMachineInitReader) GetInitConfig() (map[string]interface{}, error) { - return nil, nil -} - -var newBlankMachineInitReader = func(base corebase.Base) (cloudconfig.InitReader, error) { - r, err := cloudconfig.NewMachineInitReader(base) - return &blankMachineInitReader{r}, err -} diff --git a/internal/container/broker/lxd-broker_test.go b/internal/container/broker/lxd-broker_test.go index d9d16b5d468..dfea964795c 100644 --- a/internal/container/broker/lxd-broker_test.go +++ b/internal/container/broker/lxd-broker_test.go @@ -20,11 +20,13 @@ import ( "github.com/juju/juju/agent" apiprovisioner "github.com/juju/juju/api/agent/provisioner" "github.com/juju/juju/core/arch" + corebase "github.com/juju/juju/core/base" "github.com/juju/juju/core/instance" "github.com/juju/juju/core/lxdprofile" corenetwork "github.com/juju/juju/core/network" "github.com/juju/juju/environs" "github.com/juju/juju/environs/envcontext" + "github.com/juju/juju/internal/cloudconfig" "github.com/juju/juju/internal/cloudconfig/instancecfg" "github.com/juju/juju/internal/container" "github.com/juju/juju/internal/container/broker" @@ -35,6 +37,19 @@ import ( jujuversion "github.com/juju/juju/version" ) +type blankMachineInitReader struct { + cloudconfig.InitReader +} + +func (r *blankMachineInitReader) GetInitConfig() (map[string]interface{}, error) { + return nil, nil +} + +var newBlankMachineInitReader = func(base corebase.Base) (cloudconfig.InitReader, error) { + r, err := cloudconfig.NewMachineInitReader(base) + return &blankMachineInitReader{r}, err +} + type lxdBrokerSuite struct { coretesting.BaseSuite agentConfig agent.ConfigSetterWriter diff --git a/internal/container/factory/factory.go b/internal/container/factory/factory.go index 85567012d33..f7f0ccc6eb5 100644 --- a/internal/container/factory/factory.go +++ b/internal/container/factory/factory.go @@ -10,7 +10,6 @@ import ( "github.com/juju/juju/core/instance" "github.com/juju/juju/internal/container" - "github.com/juju/juju/internal/container/kvm" "github.com/juju/juju/internal/container/lxd" ) @@ -20,8 +19,6 @@ var NewContainerManager = func(forType instance.ContainerType, conf container.Ma switch forType { case instance.LXD: return lxd.NewContainerManager(conf, lxd.NewLocalServer) - case instance.KVM: - return kvm.NewContainerManager(conf) } return nil, errors.Errorf("unknown container type: %q", forType) } diff --git a/internal/container/factory/factory_test.go b/internal/container/factory/factory_test.go index c209d9de4fa..8f2e8d379d7 100644 --- a/internal/container/factory/factory_test.go +++ b/internal/container/factory/factory_test.go @@ -26,9 +26,6 @@ func (*factorySuite) TestNewContainerManager(c *gc.C) { }{{ containerType: instance.LXD, valid: true, - }, { - containerType: instance.KVM, - valid: true, }, { containerType: instance.NONE, valid: false, diff --git a/internal/container/kvm/container.go b/internal/container/kvm/container.go deleted file mode 100644 index d15fc25b3c5..00000000000 --- a/internal/container/kvm/container.go +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2013-2106 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package kvm - -import ( - "fmt" - - "github.com/juju/errors" - - "github.com/juju/juju/core/network" - "github.com/juju/juju/core/status" - "github.com/juju/juju/environs/imagedownloads" - "github.com/juju/juju/environs/imagemetadata" - "github.com/juju/juju/environs/simplestreams" - "github.com/juju/juju/internal/container" - "github.com/juju/juju/internal/container/kvm/libvirt" -) - -type kvmContainer struct { - fetcher imagemetadata.SimplestreamsFetcher - factory *containerFactory - name string - // started is a three state boolean, true, false, or unknown - // this allows for checking when we don't know, but using a - // value if we already know it (like in the list situation). - started *bool - - pathfinder pathfinderFunc - runCmd runFunc -} - -var _ Container = (*kvmContainer)(nil) - -func (c *kvmContainer) Name() string { - return c.name -} - -// EnsureCachedImage ensures that a container image suitable for satisfying -// the input start parameters has been cached on disk. -func (c *kvmContainer) EnsureCachedImage(params StartParams) error { - var srcFunc func() simplestreams.DataSource - if params.ImageDownloadURL != "" { - srcFunc = func() simplestreams.DataSource { - return imagedownloads.NewDataSource(c.fetcher, params.ImageDownloadURL) - } - } - - sp := syncParams{ - fetcher: c.fetcher, - arch: params.Arch, - version: params.Version, - stream: params.Stream, - fType: DiskImageType, - srcFunc: srcFunc, - } - logger.Debugf("synchronise images for %s %s %s %s", sp.arch, sp.version, sp.stream, params.ImageDownloadURL) - var callback ProgressCallback - if params.StatusCallback != nil { - callback = func(msg string) { - _ = params.StatusCallback(status.Provisioning, msg, nil) - } - } - if err := Sync(sp, nil, params.ImageDownloadURL, callback); err != nil { - if !errors.Is(err, errors.AlreadyExists) { - return errors.Trace(err) - } - logger.Debugf("image already cached %s", err) - } - return nil -} - -// Start creates and starts a new container. -// It assumes that the backing image is already cached on disk. -func (c *kvmContainer) Start(params StartParams) error { - var interfaces []libvirt.InterfaceInfo - ovsBridgeNames, err := network.OvsManagedBridges() - if err != nil { - return errors.Trace(err) - } - if params.Network != nil { - if params.Network.NetworkType == container.BridgeNetwork { - for _, iface := range params.Network.Interfaces { - parentVirtualPortType := network.NonVirtualPort - if ovsBridgeNames.Contains(iface.ParentInterfaceName) { - parentVirtualPortType = network.OvsPort - } - - interfaces = append(interfaces, interfaceInfo{ - config: iface, - parentVirtualPortType: parentVirtualPortType, - }) - } - } else { - err := errors.New("Non-bridge network devices not yet supported") - logger.Infof(err.Error()) - return err - } - } - logger.Debugf("create the machine %s", c.name) - if params.StatusCallback != nil { - _ = params.StatusCallback(status.Provisioning, "Creating instance", nil) - } - mparams := CreateMachineParams{ - Hostname: c.name, - Version: params.Version, - UserDataFile: params.UserDataFile, - NetworkConfigData: params.NetworkConfigData, - Memory: params.Memory, - CpuCores: params.CpuCores, - RootDisk: params.RootDisk, - Interfaces: interfaces, - } - if err := CreateMachine(mparams); err != nil { - return errors.Trace(err) - } - - logger.Debugf("Set machine %s to autostart", c.name) - if params.StatusCallback != nil { - _ = params.StatusCallback(status.Provisioning, "Starting instance", nil) - } - return AutostartMachine(c) -} - -func (c *kvmContainer) Stop() error { - if !c.IsRunning() { - logger.Debugf("%s is already stopped", c.name) - return nil - } - // Make started state unknown again. - c.started = nil - logger.Debugf("Stop %s", c) - - return DestroyMachine(c) -} - -func (c *kvmContainer) IsRunning() bool { - if c.started != nil { - return *c.started - } - machines, err := ListMachines(run) - if err != nil { - return false - } - c.started = isRunning(machines[c.name]) - return *c.started -} - -func (c *kvmContainer) String() string { - return fmt.Sprintf("", *c) -} - -type interfaceInfo struct { - config network.InterfaceInfo - parentVirtualPortType network.VirtualPortType -} - -// MACAddress returns the embedded MacAddress value. -func (i interfaceInfo) MACAddress() string { - return i.config.MACAddress -} - -// InterfaceName returns the embedded InterfaceName value. -func (i interfaceInfo) InterfaceName() string { - return i.config.InterfaceName -} - -// ParentInterfaceName returns the embedded ParentInterfaceName value. -func (i interfaceInfo) ParentInterfaceName() string { - return i.config.ParentInterfaceName -} - -func (i interfaceInfo) ParentVirtualPortType() string { - return string(i.parentVirtualPortType) -} diff --git a/internal/container/kvm/container_internal_test.go b/internal/container/kvm/container_internal_test.go deleted file mode 100644 index f13267efa19..00000000000 --- a/internal/container/kvm/container_internal_test.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2017 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package kvm - -import ( - "github.com/juju/testing" - gc "gopkg.in/check.v1" - - corenetwork "github.com/juju/juju/core/network" -) - -type containerInternalSuite struct { - testing.IsolationSuite -} - -var _ = gc.Suite(&containerInternalSuite{}) - -func (containerInternalSuite) TestInterfaceInfo(c *gc.C) { - i := interfaceInfo{config: corenetwork.InterfaceInfo{ - MACAddress: "mac", ParentInterfaceName: "piname", InterfaceName: "iname"}} - c.Check(i.InterfaceName(), gc.Equals, "iname") - c.Check(i.ParentInterfaceName(), gc.Equals, "piname") - c.Assert(i.MACAddress(), gc.Equals, "mac") -} diff --git a/internal/container/kvm/containerfactory.go b/internal/container/kvm/containerfactory.go deleted file mode 100644 index abc9d5ef466..00000000000 --- a/internal/container/kvm/containerfactory.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2013 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package kvm - -import "github.com/juju/juju/environs/imagemetadata" - -type containerFactory struct { - fetcher imagemetadata.SimplestreamsFetcher -} - -var _ ContainerFactory = (*containerFactory)(nil) - -func (factory *containerFactory) New(name string) Container { - return factory.new(name, nil) -} - -func (factory *containerFactory) List() (result []Container, err error) { - machines, err := ListMachines(run) - if err != nil { - return nil, err - } - for hostname, status := range machines { - result = append(result, factory.new(hostname, isRunning(status))) - } - return result, nil -} - -func (factory *containerFactory) new(name string, started *bool) *kvmContainer { - return &kvmContainer{ - fetcher: factory.fetcher, - factory: factory, - name: name, - started: started, - } -} - -func isRunning(value string) *bool { - result := value == "running" - return &result -} diff --git a/internal/container/kvm/containerfactory_test.go b/internal/container/kvm/containerfactory_test.go deleted file mode 100644 index 0193ebb34e9..00000000000 --- a/internal/container/kvm/containerfactory_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2022 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package kvm - -import ( - "github.com/juju/testing" - jc "github.com/juju/testing/checkers" - gc "gopkg.in/check.v1" -) - -type containerFactorySuite struct { - testing.IsolationSuite -} - -var _ = gc.Suite(&containerFactorySuite{}) - -func (containerFactorySuite) TestNewContainerStartedIsNil(c *gc.C) { - vm := new(containerFactory).New("some-kvm") - - raw, ok := vm.(*kvmContainer) - c.Assert(ok, jc.IsTrue) - - // A new container instantiated in this way must have an "unknown" - // started state, which will get queried and set at need. - c.Assert(raw.started, gc.IsNil) -} diff --git a/internal/container/kvm/doc.go b/internal/container/kvm/doc.go deleted file mode 100644 index b3e200d5b0b..00000000000 --- a/internal/container/kvm/doc.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2016 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -/* -Package kvm provides the facilities to deploy to kvm instances. - -kvm implements the container interface for worker/provisioner to manage -applications deployed to kvm based 'containers' (see -juju/worker/provisioner/provisioner.go:containerProvisioner and -juju/container/container.go:container.Manager). The worker provisioner -specifics are in juju/worker/provisioner/kvm-broker.go and -juju/worker/container_initilisation.go. - -The provisioner worker manages kvm containers through the interface provided in -this package, see: containerfactory.go, container.go, instance, and -initialization.go. That is to say those files provide the container.Manager -interface while the rest of this package are the implementation of -container.Manager for kvm instances. - -This package originally depended on the ubuntu uvtool apt package. This meant -that kvm would only work on ubuntu on amd64. The goal of removing Juju's -dependency on uvtool is to allow kvm to also work on arm64 and ppc64el. -However, it is still only expected to work on ubuntu. - -When removing uvtool we (redir) performed a survey of the libvirt and qemu go -package landscape. There are a number of cgo based libraries and two possibly -promising ones once they are further developed: -github.com/digitalocean/go-libvirt and github.com/digitalocean/go-qemu. Those -packages are nascent and alpha at the time of this writing. They implement pure -go interfaces to libvirt and qemu by way of libvirt/qemu's custom RPC protocol. -While this would reduce the number of commands that require shelling out, it -wouldn't provide a way to create and manage disk images. So unless someone -implements qemu-utils and genisoimage in go, we'll always need to shell out for -those calls. The wrapped commands exist, shockingly, in wrappedcmds.go with -the exception of libvirt pool initialisation bits which are in -initialization.go. - -After the provisioner initializes the kvm environment, we synchronise (fetch if -we don't have one) an ubuntu qcow image for the appropriate series and -architecture. This happens in sync.go and uses Juju's simplestreams -implementation in juju/environs/simplestreams and juju/environs/imagedownloads. -Once we fetch a compressed ubuntu image we then uncompress and convert it for -use into the libvirt storage pool. The storage pool is named 'juju-pool' and it -is located in $JUJU_DATADIR/kvm/guests, where JUJU_DATADIR is the value -returned by paths.DataDir. This ubuntu image is then used as a backing store -for our kvm instances for given series. - -NB: Sharing a backing store across multiple instances allow us to save -significant disk space, but comes at a price too. The backing store is read -only to the volumes which use it and it cannot be updated. So we cannot easily -update common elements the way that lxd and snappy do with squashfs based -backing stores.This is to the best of my understanding, so corrections or -updates are welcome. - -Once the backing store is ready, we create a system disk and a datasource disk. -The system disk is a sparse file with a maximum file size which uses the -aforementioned backing store as its base image. The data source disk is an iso -image with user-data and meta-data for cloud-init's NoCloud method to configure -our system. The cloud init data is written in kvm.go, via a call to machinery -in juju/cloudconfig/containerinit/container_userdata.go. Destruction of a -container removes the system and data source disk files, but leaves the backing -store alone as it may be in use by other domains. - -TBD: Put together something to send progress through a reader to the callback -function. We need to follow along with the method as implemented by LXD. -*/ -package kvm diff --git a/internal/container/kvm/export_test.go b/internal/container/kvm/export_test.go deleted file mode 100644 index efe57e483ca..00000000000 --- a/internal/container/kvm/export_test.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2014 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package kvm - -import ( - "strings" - - "github.com/juju/juju/environs/instances" -) - -// This file exports internal package implementations so that tests -// can utilize them to mock behavior. - -var KVMPath = &kvmPath - -// MakeCreateMachineParamsTestable adds test values to non exported values on -// CreateMachineParams. -func MakeCreateMachineParamsTestable(params *CreateMachineParams, pathfinder pathfinderFunc, runCmd runFunc, arch string) { - params.findPath = pathfinder - params.runCmd = runCmd - params.runCmdAsRoot = runCmd - params.arch = arch - return -} - -// NewEmptyKvmContainer returns an empty kvmContainer for testing. -func NewEmptyKvmContainer() *kvmContainer { - return &kvmContainer{} -} - -// NewTestContainer returns a new container for testing. -func NewTestContainer(name string, runCmd runFunc, pathfinder pathfinderFunc) *kvmContainer { - return &kvmContainer{name: name, runCmd: runCmd, pathfinder: pathfinder} -} - -// ContainerFromInstance extracts the inner container from input instance, -// so we can access it for test assertions. -func ContainerFromInstance(inst instances.Instance) Container { - kvm := inst.(*kvmInstance) - return kvm.container -} - -// NewRunStub is a stub to fake shelling out to os.Exec or utils.RunCommand. -func NewRunStub(output string, err error) *runStub { - return &runStub{output: output, err: err} -} - -type runStub struct { - output string - err error - calls []string -} - -// Run fakes running commands, instead recording calls made for use in testing. -func (s *runStub) Run(dir, cmd string, args ...string) (string, error) { - call := []string{dir, cmd} - call = append(call, args...) - s.calls = append(s.calls, strings.Join(call, " ")) - if s.err != nil { - return s.err.Error(), s.err - } - return s.output, nil -} - -// Calls returns the calls made on a runStub. -func (s *runStub) Calls() []string { - return s.calls -} diff --git a/internal/container/kvm/initialisation.go b/internal/container/kvm/initialisation.go deleted file mode 100644 index e96c055609f..00000000000 --- a/internal/container/kvm/initialisation.go +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2013 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package kvm - -import ( - "os" - "runtime" - - "github.com/juju/errors" - "github.com/juju/os/v2/series" - - "github.com/juju/juju/core/paths" - "github.com/juju/juju/internal/container" - "github.com/juju/juju/internal/packaging" - "github.com/juju/juju/internal/packaging/dependency" -) - -type containerInitialiser struct{} - -// containerInitialiser implements container.Initialiser. -var _ container.Initialiser = (*containerInitialiser)(nil) - -// NewContainerInitialiser returns an instance used to perform the steps -// required to allow a host machine to run a KVM container. -func NewContainerInitialiser() container.Initialiser { - return &containerInitialiser{} -} - -// Initialise is specified on the container.Initialiser interface. -func (ci *containerInitialiser) Initialise() error { - if err := ensureDependencies(); err != nil { - return errors.Trace(err) - } - - poolInfo, err := poolInfo(run) - if err != nil { - return errors.Trace(err) - } - if err := ensurePool(poolInfo, paths.DataDir, run, chownToLibvirt); err != nil { - return errors.Trace(err) - } - return nil -} - -func ensureDependencies() error { - hostSeries, err := series.HostSeries() - if err != nil { - return errors.Trace(err) - } - - dep := dependency.KVM(runtime.GOARCH) - if err = packaging.InstallDependency(dep, hostSeries); err != nil { - return errors.Trace(err) - } - - return nil -} - -type pathfinderFunc func(paths.OS) string - -// ensurePool creates the libvirt storage pool and ensures its is active. -// runCmd and chownFunc are here for testing. runCmd so we can check the -// right shell out calls are made, and chownFunc because we cannot chown -// unless we are root. -func ensurePool(poolInfo *libvirtPool, pathfinder pathfinderFunc, runCmd runFunc, chownFunc func(string) error) error { - poolDir, err := guestPath(pathfinder) - if err != nil { - return errors.Trace(err) - } - - if poolInfo == nil { - poolInfo = &libvirtPool{} - } - - if poolInfo.Name == "" { - if err = definePool(poolDir, runCmd, chownFunc); err != nil { - return errors.Trace(err) - } - } else { - logger.Debugf(`pool %q already created`, poolInfo.Name) - } - - if poolInfo.State != "running" { - if err = buildPool(runCmd); err != nil { - return errors.Trace(err) - } - - if err = startPool(runCmd); err != nil { - return errors.Trace(err) - } - } else { - logger.Debugf(`pool %q already active`, poolInfo.Name) - } - - if poolInfo.Autostart != "yes" { - if err = autostartPool(runCmd); err != nil { - return errors.Trace(err) - } - } - - // We have to set ownership of the guest pool directory after running virsh - // commands above, because it appears that the libvirt-bin version that - // ships with trusty sets the ownership of the pool directory to the user - // running the commands -- root in our case. Which causes container - // initialization to fail as we couldn't write volumes to the pool. We - // write them as libvirt-qemu:kvm so that libvirt -- which runs as that - // user -- can read them to boot the domains. - if err = chownFunc(poolDir); err != nil { - return errors.Trace(err) - } - - return nil -} - -// definePool creates the required directories and changes ownership of the -// guest directory so that libvirt-qemu can read, write, and execute its -// guest volumes. -func definePool(dir string, runCmd runFunc, _ func(string) error) error { - // Permissions gleaned from https://goo.gl/SZIw14 - // The command itself would change the permissions to match anyhow. - err := os.MkdirAll(dir, 0755) - if err != nil { - return errors.Trace(err) - } - - // The dashes are empty positional args for other types of pool storage: - // e.g. file, lvm, scsi, disk, NFS. Newer versions support using only named - // args (--type, --target) but this is backwards compatible for trusty. - output, err := runCmd( - "", - virsh, - "pool-define-as", - poolName, - "dir", - "-", "-", "-", "-", - dir) - if err != nil { - return errors.Trace(err) - } - logger.Debugf("pool-define-as output %s", output) - - return nil -} - -// chownToLibvirt changes ownership of the provided directory to -// libvirt-qemu:kvm. -func chownToLibvirt(dir string) error { - uid, gid, err := getUserUIDGID(libvirtUser) - if err != nil { - logger.Errorf("failed to get livirt-qemu uid:gid %s", err) - return errors.Trace(err) - } - - err = os.Chown(dir, uid, gid) - if err != nil { - logger.Errorf("failed to change ownership of %q to uid:gid %d:%d %s", dir, uid, gid, err) - return errors.Trace(err) - } - logger.Tracef("%q is now owned by %q %d:%d", dir, libvirtUser, uid, gid) - return nil -} - -// buildPool sets up libvirt internals for the guest pool. -func buildPool(runCmd runFunc) error { - // This can run without error if the pool isn't active. - output, err := runCmd("", virsh, "pool-build", poolName) - if err != nil { - return errors.Trace(err) - } - logger.Debugf("pool-build output %s", output) - return nil -} - -// startPool makes the pool available for use in libvirt. -func startPool(runCmd runFunc) error { - output, err := runCmd("", virsh, "pool-start", poolName) - if err != nil { - return errors.Trace(err) - } - logger.Debugf("pool-start output %s", output) - - return nil -} - -// autostartPool sets up the pool to run automatically when libvirt starts. -func autostartPool(runCmd runFunc) error { - output, err := runCmd("", virsh, "pool-autostart", poolName) - if err != nil { - return errors.Trace(err) - } - logger.Debugf("pool-autostart output %s", output) - - return nil -} diff --git a/internal/container/kvm/initialisation_internal_test.go b/internal/container/kvm/initialisation_internal_test.go deleted file mode 100644 index c7cb1df2f33..00000000000 --- a/internal/container/kvm/initialisation_internal_test.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2016 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package kvm - -import ( - "fmt" - "os" - - "github.com/juju/testing" - jc "github.com/juju/testing/checkers" - gc "gopkg.in/check.v1" - - "github.com/juju/juju/core/paths" - "github.com/juju/juju/internal/packaging" - "github.com/juju/juju/internal/packaging/dependency" -) - -type initialisationInternalSuite struct { - testing.IsolationSuite -} - -var _ = gc.Suite(&initialisationInternalSuite{}) - -func (initialisationInternalSuite) TestCreatePool(c *gc.C) { - tmpDir, err := os.MkdirTemp("", "juju-initialisationInternalSuite") - defer func() { - err = os.RemoveAll(tmpDir) - if err != nil { - c.Errorf("failed to remove tmpDir: %s\n", err) - } - }() - c.Check(err, jc.ErrorIsNil) - pathfinder := func(_ paths.OS) string { - return tmpDir - } - stub := runStub{} - chown := func(string) error { return nil } - err = ensurePool(nil, pathfinder, stub.Run, chown) - c.Check(err, jc.ErrorIsNil) - c.Assert(stub.Calls(), jc.DeepEquals, []string{ - fmt.Sprintf(" virsh pool-define-as juju-pool dir - - - - %s/kvm/guests", tmpDir), - " virsh pool-build juju-pool", - " virsh pool-start juju-pool", - " virsh pool-autostart juju-pool", - }) -} - -func (initialisationInternalSuite) TestStartPool(c *gc.C) { - tmpDir, err := os.MkdirTemp("", "juju-initialisationInternalSuite") - defer func() { - err = os.RemoveAll(tmpDir) - if err != nil { - c.Errorf("failed to remove tmpDir: %s\n", err) - } - }() - c.Check(err, jc.ErrorIsNil) - pathfinder := func(_ paths.OS) string { - return tmpDir - } - poolInfo := &libvirtPool{Name: "juju-pool", Autostart: "no", State: "inactive"} - stub := runStub{} - chown := func(string) error { return nil } - err = ensurePool(poolInfo, pathfinder, stub.Run, chown) - c.Check(err, jc.ErrorIsNil) - c.Assert(stub.Calls(), jc.DeepEquals, []string{ - " virsh pool-build juju-pool", - " virsh pool-start juju-pool", - " virsh pool-autostart juju-pool", - }) -} - -func (initialisationInternalSuite) TestAutoStartPool(c *gc.C) { - tmpDir, err := os.MkdirTemp("", "juju-initialisationInternalSuite") - defer func() { - err = os.RemoveAll(tmpDir) - if err != nil { - c.Errorf("failed to remove tmpDir: %s\n", err) - } - }() - c.Check(err, jc.ErrorIsNil) - pathfinder := func(_ paths.OS) string { - return tmpDir - } - poolInfo := &libvirtPool{Name: "juju-pool", Autostart: "no", State: "running"} - stub := runStub{} - chown := func(string) error { return nil } - err = ensurePool(poolInfo, pathfinder, stub.Run, chown) - c.Check(err, jc.ErrorIsNil) - c.Assert(stub.Calls(), jc.DeepEquals, []string{ - " virsh pool-autostart juju-pool", - }) -} - -func (initialisationInternalSuite) TestRequiredPackagesAMD64(c *gc.C) { - got, err := dependency.KVM("amd64").PackageList("bionic") - c.Assert(err, gc.IsNil) - assertPkgListMatches(c, got, []string{"qemu-kvm", "qemu-utils", "genisoimage", "libvirt-bin"}) -} - -func (initialisationInternalSuite) TestRequiredPackagesARM64(c *gc.C) { - got, err := dependency.KVM("arm64").PackageList("bionic") - c.Assert(err, gc.IsNil) - assertPkgListMatches(c, got, []string{"qemu-efi", "qemu-kvm", "qemu-utils", "genisoimage", "libvirt-bin"}) -} - -func assertPkgListMatches(c *gc.C, got []packaging.Package, exp []string) { - c.Assert(len(got), gc.Equals, len(exp)) - for i := 0; i < len(got); i++ { - c.Assert(got[i].Name, gc.Equals, exp[i]) - } -} diff --git a/internal/container/kvm/instance.go b/internal/container/kvm/instance.go deleted file mode 100644 index acde530306b..00000000000 --- a/internal/container/kvm/instance.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2013 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package kvm - -import ( - "fmt" - - "github.com/juju/juju/core/instance" - corenetwork "github.com/juju/juju/core/network" - "github.com/juju/juju/core/network/firewall" - "github.com/juju/juju/core/status" - "github.com/juju/juju/environs/envcontext" - "github.com/juju/juju/environs/instances" -) - -type kvmInstance struct { - container Container - id string -} - -var _ instances.Instance = (*kvmInstance)(nil) - -// Id implements instances.instance.Id. -func (kvm *kvmInstance) Id() instance.Id { - return instance.Id(kvm.id) -} - -// Status implements instances.Instance.Status. -func (kvm *kvmInstance) Status(ctx envcontext.ProviderCallContext) instance.Status { - if kvm.container.IsRunning() { - return instance.Status{ - Status: status.Running, - Message: "running", - } - } - return instance.Status{ - Status: status.Stopped, - Message: "stopped", - } -} - -func (*kvmInstance) Refresh() error { - return nil -} - -func (kvm *kvmInstance) Addresses(ctx envcontext.ProviderCallContext) (corenetwork.ProviderAddresses, error) { - logger.Errorf("kvmInstance.Addresses not implemented") - return nil, nil -} - -// OpenPorts implements instances.Instance.OpenPorts. -func (kvm *kvmInstance) OpenPorts(ctx envcontext.ProviderCallContext, machineId string, rules firewall.IngressRules) error { - return fmt.Errorf("not implemented") -} - -// ClosePorts implements instances.Instance.ClosePorts. -func (kvm *kvmInstance) ClosePorts(ctx envcontext.ProviderCallContext, machineId string, rules firewall.IngressRules) error { - return fmt.Errorf("not implemented") -} - -// IngressRules implements instances.Instance.IngressRules. -func (kvm *kvmInstance) IngressRules(ctx envcontext.ProviderCallContext, machineId string) (firewall.IngressRules, error) { - return nil, fmt.Errorf("not implemented") -} - -// Add a string representation of the id. -func (kvm *kvmInstance) String() string { - return fmt.Sprintf("kvm:%s", kvm.id) -} diff --git a/internal/container/kvm/interface.go b/internal/container/kvm/interface.go deleted file mode 100644 index 7a5d13153e5..00000000000 --- a/internal/container/kvm/interface.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2013 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package kvm - -import ( - "github.com/juju/juju/core/status" - "github.com/juju/juju/internal/container" -) - -// StartParams is a simple parameter struct for Container.Start. -type StartParams struct { - Version string - Arch string - Stream string - UserDataFile string - NetworkConfigData string - Network *container.NetworkConfig - Memory uint64 // MB - CpuCores uint64 - RootDisk uint64 // GB - ImageDownloadURL string - StatusCallback func(status status.Status, info string, data map[string]interface{}) error -} - -// Container represents a virtualized container instance and provides -// operations to create, maintain and destroy the container. -type Container interface { - - // Name returns the name of the container. - Name() string - - // EnsureCachedImage ensures that a container image suitable for satisfying - // the input start parameters has been cached on disk. - EnsureCachedImage(params StartParams) error - - // Start runs the container as a daemon. - Start(params StartParams) error - - // Stop terminates the running container. - Stop() error - - // IsRunning returns whether or not the container is running and active. - IsRunning() bool - - // String returns information about the container, like the name, state, - // and process id. - String() string -} - -// ContainerFactory represents the methods used to create Containers. This -// wraps the low level OS functions for dealing with the containers. -type ContainerFactory interface { - // New returns a container instance which can then be used for operations - // like Start() and Stop() - New(string) Container - - // List returns all the existing containers on the system. - List() ([]Container, error) -} diff --git a/internal/container/kvm/kvm.go b/internal/container/kvm/kvm.go deleted file mode 100644 index 8a92cff8255..00000000000 --- a/internal/container/kvm/kvm.go +++ /dev/null @@ -1,353 +0,0 @@ -// Copyright 2013 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package kvm - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - "sync" - - "github.com/juju/errors" - "github.com/juju/loggo/v2" - - "github.com/juju/juju/agent" - "github.com/juju/juju/core/arch" - corebase "github.com/juju/juju/core/base" - "github.com/juju/juju/core/constraints" - "github.com/juju/juju/core/instance" - "github.com/juju/juju/core/status" - "github.com/juju/juju/environs" - "github.com/juju/juju/environs/config" - "github.com/juju/juju/environs/imagemetadata" - "github.com/juju/juju/environs/instances" - "github.com/juju/juju/environs/simplestreams" - "github.com/juju/juju/internal/cloudconfig/cloudinit" - "github.com/juju/juju/internal/cloudconfig/containerinit" - "github.com/juju/juju/internal/cloudconfig/instancecfg" - "github.com/juju/juju/internal/container" -) - -var ( - logger = loggo.GetLogger("juju.container.kvm") - - // KVMObjectFactory implements the container factory interface for kvm - // containers. - // TODO (stickupkid): This _only_ exists here because we can patch it in - // tests. This is horrid! - KVMObjectFactory ContainerFactory = &containerFactory{ - fetcher: simplestreams.NewSimpleStreams(simplestreams.DefaultDataSourceFactory()), - } - - // In order for Juju to be able to create the hardware characteristics of - // the kvm machines it creates, we need to be explicit in our definition - // of memory, cores and root-disk. The defaults here have been - // extracted from the uvt-kvm executable. - - // DefaultMemory is the default RAM to use in a container. - DefaultMemory uint64 = 512 // MB - // DefaultCpu is the default number of CPUs to use in a container. - DefaultCpu uint64 = 1 - // DefaultDisk is the default root disk size. - DefaultDisk uint64 = 8 // GB - - // There are some values where it doesn't make sense to go below. - - // MinMemory is the minimum RAM we will launch with. - MinMemory uint64 = 512 // MB - // MinCpu is the minimum number of CPUs to launch with. - MinCpu uint64 = 1 - // MinDisk is the minimum root disk size we will launch with. - MinDisk uint64 = 2 // GB -) - -// Utilized to provide a hard-coded path to kvm-ok -var kvmPath = "/usr/sbin" - -// IsKVMSupported calls into the kvm-ok executable from the cpu-checkers package. -// It is a variable to allow us to override behaviour in the tests. -var IsKVMSupported = func() (bool, error) { - - // Prefer the user's $PATH first, but check /usr/sbin if we can't - // find kvm-ok there - var foundPath string - const binName = "kvm-ok" - if path, err := exec.LookPath(binName); err == nil { - foundPath = path - } else if path, err := exec.LookPath(filepath.Join(kvmPath, binName)); err == nil { - foundPath = path - } else { - return false, errors.NotFoundf("%s executable", binName) - } - - command := exec.Command(foundPath) - output, err := command.CombinedOutput() - - if err != nil { - return false, errors.Annotate(err, string(output)) - } - logger.Debugf("%s output:\n%s", binName, output) - return command.ProcessState.Success(), nil -} - -// NewContainerManager returns a manager object that can start and stop kvm -// containers. -func NewContainerManager(conf container.ManagerConfig) (container.Manager, error) { - modelUUID := conf.PopValue(container.ConfigModelUUID) - if modelUUID == "" { - return nil, errors.Errorf("model UUID is required") - } - namespace, err := instance.NewNamespace(modelUUID) - if err != nil { - return nil, errors.Trace(err) - } - logDir := conf.PopValue(container.ConfigLogDir) - if logDir == "" { - logDir = agent.DefaultPaths.LogDir - } - - availabilityZone := conf.PopValue(container.ConfigAvailabilityZone) - if availabilityZone == "" { - logger.Infof("Availability zone will be empty for this container manager") - } - - imageMetaDataURL := conf.PopValue(config.ContainerImageMetadataURLKey) - imageStream := conf.PopValue(config.ContainerImageStreamKey) - - conf.WarnAboutUnused() - return &containerManager{ - namespace: namespace, - logDir: logDir, - availabilityZone: availabilityZone, - imageMetadataURL: imageMetaDataURL, - imageStream: imageStream, - }, nil -} - -// containerManager handles all of the business logic at the juju specific -// level. It makes sure that the necessary directories are in place, that the -// user-data is written out in the right place, and that OS images are sourced -// from the correct location. -type containerManager struct { - namespace instance.Namespace - logDir string - availabilityZone string - imageMetadataURL string - imageStream string - imageMutex sync.Mutex -} - -var _ container.Manager = (*containerManager)(nil) - -// Namespace implements container.Manager. -func (manager *containerManager) Namespace() instance.Namespace { - return manager.namespace -} - -func (manager *containerManager) CreateContainer( - _ context.Context, - instanceConfig *instancecfg.InstanceConfig, - cons constraints.Value, - base corebase.Base, - networkConfig *container.NetworkConfig, - storageConfig *container.StorageConfig, - callback environs.StatusCallbackFunc, -) (_ instances.Instance, hc *instance.HardwareCharacteristics, err error) { - - name, err := manager.namespace.Hostname(instanceConfig.MachineId) - if err != nil { - return nil, nil, errors.Trace(err) - } - - defer func() { - if err != nil { - _ = callback(status.ProvisioningError, fmt.Sprintf("Creating container: %v", err), nil) - } - }() - - // Set the MachineContainerHostname to match the name returned by virsh list - instanceConfig.MachineContainerHostname = name - - // Note here that the kvmObjectFactory only returns a valid container - // object, and doesn't actually construct the underlying kvm container on - // disk. - kvmContainer := KVMObjectFactory.New(name) - - hc = &instance.HardwareCharacteristics{AvailabilityZone: &manager.availabilityZone} - - // Create the cloud-init. - cloudConfig, err := cloudinit.New(instanceConfig.Base.OS) - if err != nil { - return nil, nil, errors.Trace(err) - } - - logger.Tracef("write cloud-init") - userData, err := containerinit.CloudInitUserData(cloudConfig, instanceConfig, networkConfig) - if err != nil { - logger.Infof("machine config api %#v", *instanceConfig.APIInfo) - err = errors.Annotate(err, "failed to write generate data") - logger.Errorf(err.Error()) - return nil, nil, errors.Trace(err) - } - - directory, err := container.NewDirectory(name) - if err != nil { - return nil, nil, errors.Annotate(err, "failed to create container directory") - } - - userDataFilename := filepath.Join(directory, "cloud-init") - if err := os.WriteFile(userDataFilename, userData, 0644); err != nil { - err = errors.Annotate(err, "failed to write generate data") - logger.Errorf(err.Error()) - return nil, nil, errors.Trace(err) - } - - // Create the container. - startParams := ParseConstraintsToStartParams(cons) - startParams.Arch = arch.HostArch() - startParams.Version = base.Channel.Track - startParams.Network = networkConfig - startParams.UserDataFile = userDataFilename - startParams.NetworkConfigData = cloudinit.CloudInitNetworkConfigDisabled - startParams.StatusCallback = callback - startParams.Stream = manager.imageStream - - // Check whether a container image metadata URL was configured. - // Default to Ubuntu cloud images if configured stream is not "released". - imURL := manager.imageMetadataURL - if manager.imageMetadataURL == "" && manager.imageStream != imagemetadata.ReleasedStream { - imURL = imagemetadata.UbuntuCloudImagesURL - imURL, err = imagemetadata.ImageMetadataURL(imURL, manager.imageStream) - if err != nil { - return nil, nil, errors.Annotate(err, "generating image metadata source") - } - } - startParams.ImageDownloadURL = imURL - - var hardware instance.HardwareCharacteristics - hardware, err = instance.ParseHardware( - fmt.Sprintf("arch=%s mem=%vM root-disk=%vG cores=%v", - startParams.Arch, startParams.Memory, startParams.RootDisk, startParams.CpuCores)) - if err != nil { - return nil, nil, errors.Annotate(err, "failed to parse hardware") - } - - _ = callback(status.Provisioning, "Creating container; it might take some time", nil) - logger.Tracef("create the container, constraints: %v", cons) - - // Lock around finding an image. - // The provisioner works concurrently to create containers. - // If an image needs to be copied from a remote, we don't want many - // goroutines attempting to do it at once. - manager.imageMutex.Lock() - err = kvmContainer.EnsureCachedImage(startParams) - manager.imageMutex.Unlock() - if err != nil { - return nil, nil, errors.Annotate(err, "acquiring container image") - } - - if err := kvmContainer.Start(startParams); err != nil { - return nil, nil, errors.Annotate(err, "kvm container creation failed") - } - logger.Tracef("kvm container created") - _ = callback(status.Running, "Container started", nil) - return &kvmInstance{kvmContainer, name}, &hardware, nil -} - -func (manager *containerManager) IsInitialized() bool { - requiredBinaries := []string{ - "virsh", - "qemu-utils", - } - for _, bin := range requiredBinaries { - if _, err := exec.LookPath(bin); err != nil { - return false - } - } - return true -} - -func (manager *containerManager) DestroyContainer(id instance.Id) error { - name := string(id) - kvmContainer := KVMObjectFactory.New(name) - if err := kvmContainer.Stop(); err != nil { - logger.Errorf("failed to stop kvm container: %v", err) - return err - } - return container.RemoveDirectory(name) -} - -func (manager *containerManager) ListContainers() (result []instances.Instance, err error) { - containers, err := KVMObjectFactory.List() - if err != nil { - logger.Errorf("failed getting all instances: %v", err) - return - } - managerPrefix := manager.namespace.Prefix() - for _, c := range containers { - // Filter out those not starting with our name. - name := c.Name() - if !strings.HasPrefix(name, managerPrefix) { - continue - } - if c.IsRunning() { - result = append(result, &kvmInstance{c, name}) - } - } - return -} - -// ParseConstraintsToStartParams takes a constraints object and returns a bare -// StartParams object that has Memory, Cpu, and Disk populated. If there are -// no defined values in the constraints for those fields, default values are -// used. Other constrains cause a warning to be emitted. -func ParseConstraintsToStartParams(cons constraints.Value) StartParams { - params := StartParams{ - Memory: DefaultMemory, - CpuCores: DefaultCpu, - RootDisk: DefaultDisk, - } - - if cons.Mem != nil { - mem := *cons.Mem - if mem < MinMemory { - params.Memory = MinMemory - } else { - params.Memory = mem - } - } - if cons.CpuCores != nil { - cores := *cons.CpuCores - if cores < MinCpu { - params.CpuCores = MinCpu - } else { - params.CpuCores = cores - } - } - if cons.RootDisk != nil { - size := *cons.RootDisk / 1024 - if size < MinDisk { - params.RootDisk = MinDisk - } else { - params.RootDisk = size - } - } - if cons.Arch != nil { - logger.Infof("arch constraint of %q being ignored as not supported", *cons.Arch) - } - if cons.Container != nil { - logger.Infof("container constraint of %q being ignored as not supported", *cons.Container) - } - if cons.CpuPower != nil { - logger.Infof("cpu-power constraint of %v being ignored as not supported", *cons.CpuPower) - } - if cons.Tags != nil { - logger.Infof("tags constraint of %q being ignored as not supported", strings.Join(*cons.Tags, ",")) - } - - return params -} diff --git a/internal/container/kvm/kvm_test.go b/internal/container/kvm/kvm_test.go deleted file mode 100644 index 9a9cb1610cf..00000000000 --- a/internal/container/kvm/kvm_test.go +++ /dev/null @@ -1,399 +0,0 @@ -// Copyright 2013 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package kvm_test - -import ( - "fmt" - "os" - "path/filepath" - "sync" - - "github.com/juju/loggo/v2" - jc "github.com/juju/testing/checkers" - gc "gopkg.in/check.v1" - - "github.com/juju/juju/core/arch" - "github.com/juju/juju/core/constraints" - "github.com/juju/juju/core/instance" - "github.com/juju/juju/core/network" - "github.com/juju/juju/environs/config" - "github.com/juju/juju/environs/imagemetadata" - "github.com/juju/juju/internal/container" - "github.com/juju/juju/internal/container/kvm" - "github.com/juju/juju/internal/container/kvm/mock" - kvmtesting "github.com/juju/juju/internal/container/kvm/testing" - containertesting "github.com/juju/juju/internal/container/testing" - coretesting "github.com/juju/juju/testing" -) - -type KVMSuite struct { - kvmtesting.TestSuite - manager container.Manager -} - -var _ = gc.Suite(&KVMSuite{}) - -func (s *KVMSuite) SetUpTest(c *gc.C) { - s.TestSuite.SetUpTest(c) - var err error - s.manager, err = kvm.NewContainerManager(container.ManagerConfig{ - container.ConfigModelUUID: coretesting.ModelTag.Id(), - config.ContainerImageStreamKey: imagemetadata.ReleasedStream, - }) - c.Assert(err, jc.ErrorIsNil) -} - -func (*KVMSuite) TestManagerModelUUIDNeeded(c *gc.C) { - manager, err := kvm.NewContainerManager(container.ManagerConfig{container.ConfigModelUUID: ""}) - c.Assert(err, gc.ErrorMatches, "model UUID is required") - c.Assert(manager, gc.IsNil) -} - -func (*KVMSuite) TestManagerWarnsAboutUnknownOption(c *gc.C) { - _, err := kvm.NewContainerManager(container.ManagerConfig{ - container.ConfigModelUUID: coretesting.ModelTag.Id(), - "shazam": "Captain Marvel", - }) - c.Assert(err, jc.ErrorIsNil) - c.Assert(c.GetTestLog(), jc.Contains, `INFO juju.container unused config option: "shazam" -> "Captain Marvel"`) -} - -func (s *KVMSuite) TestListInitiallyEmpty(c *gc.C) { - containers, err := s.manager.ListContainers() - c.Assert(err, jc.ErrorIsNil) - c.Assert(containers, gc.HasLen, 0) -} - -func (s *KVMSuite) createRunningContainer(c *gc.C, name string) kvm.Container { - kvmContainer := s.ContainerFactory.New(name) - - nics := network.InterfaceInfos{{ - InterfaceName: "eth0", - InterfaceType: network.EthernetDevice, - ConfigType: network.ConfigDHCP, - }} - net := container.BridgeNetworkConfig(0, nics) - c.Assert(kvmContainer.Start(kvm.StartParams{ - Version: "12.10", - Arch: arch.HostArch(), - UserDataFile: "userdata.txt", - Network: net}), gc.IsNil) - return kvmContainer -} - -func (s *KVMSuite) TestListMatchesManagerName(c *gc.C) { - s.createRunningContainer(c, "juju-06f00d-match1") - s.createRunningContainer(c, "juju-06f00d-match2") - s.createRunningContainer(c, "testNoMatch") - s.createRunningContainer(c, "other") - containers, err := s.manager.ListContainers() - c.Assert(err, jc.ErrorIsNil) - c.Assert(containers, gc.HasLen, 2) - expectedIds := []instance.Id{"juju-06f00d-match1", "juju-06f00d-match2"} - ids := []instance.Id{containers[0].Id(), containers[1].Id()} - c.Assert(ids, jc.SameContents, expectedIds) -} - -func (s *KVMSuite) TestListMatchesRunningContainers(c *gc.C) { - running := s.createRunningContainer(c, "juju-06f00d-running") - s.ContainerFactory.New("juju-06f00d-stopped") - containers, err := s.manager.ListContainers() - c.Assert(err, jc.ErrorIsNil) - c.Assert(containers, gc.HasLen, 1) - c.Assert(string(containers[0].Id()), gc.Equals, running.Name()) -} - -func (s *KVMSuite) TestCreateContainer(c *gc.C) { - inst := containertesting.CreateContainer(c, s.manager, "1/kvm/0") - name := string(inst.Id()) - cloudInitFilename := filepath.Join(s.ContainerDir, name, "cloud-init") - containertesting.AssertCloudInit(c, cloudInitFilename) -} - -// This test will pass regular unit tests, but is intended for the -// race-checking CI job to assert concurrent creation safety. -func (s *KVMSuite) TestCreateContainerConcurrent(c *gc.C) { - var wg sync.WaitGroup - for i := 0; i < 10; i++ { - wg.Add(1) - go func(idx int) { - _ = containertesting.CreateContainer(c, s.manager, fmt.Sprintf("1/kvm/%d", idx)) - wg.Done() - }(i) - } - wg.Wait() -} - -func (s *KVMSuite) TestDestroyContainer(c *gc.C) { - inst := containertesting.CreateContainer(c, s.manager, "1/kvm/0") - - err := s.manager.DestroyContainer(inst.Id()) - c.Assert(err, jc.ErrorIsNil) - - name := string(inst.Id()) - // Check that the container dir is no longer in the container dir - c.Assert(filepath.Join(s.ContainerDir, name), jc.DoesNotExist) - // but instead, in the removed container dir - c.Assert(filepath.Join(s.RemovedDir, name), jc.IsDirectory) -} - -// Test that CreateContainer creates proper startParams. -func (s *KVMSuite) TestCreateContainerUsesReleaseSimpleStream(c *gc.C) { - - // Mock machineConfig with a mocked simple stream URL. - instanceConfig, err := containertesting.MockMachineConfig("1/kvm/0") - c.Assert(err, jc.ErrorIsNil) - - inst := containertesting.CreateContainerWithMachineConfig(c, s.manager, instanceConfig) - startParams := kvm.ContainerFromInstance(inst).(*mock.MockContainer).StartParams - c.Assert(startParams.ImageDownloadURL, gc.Equals, "") - c.Assert(startParams.Stream, gc.Equals, "released") -} - -// Test that CreateContainer creates proper startParams. -func (s *KVMSuite) TestCreateContainerUsesDailySimpleStream(c *gc.C) { - - // Mock machineConfig with a mocked simple stream URL. - instanceConfig, err := containertesting.MockMachineConfig("1/kvm/0") - c.Assert(err, jc.ErrorIsNil) - - s.manager, err = kvm.NewContainerManager(container.ManagerConfig{ - container.ConfigModelUUID: coretesting.ModelTag.Id(), - config.ContainerImageStreamKey: "daily", - }) - c.Assert(err, jc.ErrorIsNil) - - inst := containertesting.CreateContainerWithMachineConfig(c, s.manager, instanceConfig) - startParams := kvm.ContainerFromInstance(inst).(*mock.MockContainer).StartParams - c.Assert(startParams.ImageDownloadURL, gc.Equals, "http://cloud-images.ubuntu.com/daily") - c.Assert(startParams.Stream, gc.Equals, "daily") -} - -func (s *KVMSuite) TestCreateContainerUsesSetImageMetadataURL(c *gc.C) { - - // Mock machineConfig with a mocked simple stream URL. - instanceConfig, err := containertesting.MockMachineConfig("1/kvm/0") - c.Assert(err, jc.ErrorIsNil) - - s.manager, err = kvm.NewContainerManager(container.ManagerConfig{ - container.ConfigModelUUID: coretesting.ModelTag.Id(), - config.ContainerImageMetadataURLKey: "https://images.linuxcontainers.org", - }) - c.Assert(err, jc.ErrorIsNil) - - inst := containertesting.CreateContainerWithMachineConfig(c, s.manager, instanceConfig) - startParams := kvm.ContainerFromInstance(inst).(*mock.MockContainer).StartParams - c.Assert(startParams.ImageDownloadURL, gc.Equals, "https://images.linuxcontainers.org") -} - -func (s *KVMSuite) TestImageAcquisitionUsesSimpleStream(c *gc.C) { - - startParams := kvm.StartParams{ - Version: "mocked-version", - Arch: "mocked-arch", - Stream: "released", - ImageDownloadURL: "mocked-url", - } - mockedContainer := kvm.NewEmptyKvmContainer() - - // We are testing only the logging side-effect, so the error is ignored. - _ = mockedContainer.EnsureCachedImage(startParams) - - expectedArgs := fmt.Sprintf( - "synchronise images for %s %s %s %s", - startParams.Arch, - startParams.Version, - startParams.Stream, - startParams.ImageDownloadURL, - ) - c.Assert(c.GetTestLog(), jc.Contains, expectedArgs) -} - -type ConstraintsSuite struct { - coretesting.BaseSuite -} - -var _ = gc.Suite(&ConstraintsSuite{}) - -func (s *ConstraintsSuite) TestDefaults(c *gc.C) { - testCases := []struct { - cons string - expected kvm.StartParams - infoLog []string - }{{ - expected: kvm.StartParams{ - Memory: kvm.DefaultMemory, - CpuCores: kvm.DefaultCpu, - RootDisk: kvm.DefaultDisk, - }, - }, { - cons: "mem=256M", - expected: kvm.StartParams{ - Memory: kvm.MinMemory, - CpuCores: kvm.DefaultCpu, - RootDisk: kvm.DefaultDisk, - }, - }, { - cons: "mem=4G", - expected: kvm.StartParams{ - Memory: 4 * 1024, - CpuCores: kvm.DefaultCpu, - RootDisk: kvm.DefaultDisk, - }, - }, { - cons: "cores=4", - expected: kvm.StartParams{ - Memory: kvm.DefaultMemory, - CpuCores: 4, - RootDisk: kvm.DefaultDisk, - }, - }, { - cons: "cores=0", - expected: kvm.StartParams{ - Memory: kvm.DefaultMemory, - CpuCores: kvm.MinCpu, - RootDisk: kvm.DefaultDisk, - }, - }, { - cons: "root-disk=512M", - expected: kvm.StartParams{ - Memory: kvm.DefaultMemory, - CpuCores: kvm.DefaultCpu, - RootDisk: kvm.MinDisk, - }, - }, { - cons: "root-disk=4G", - expected: kvm.StartParams{ - Memory: kvm.DefaultMemory, - CpuCores: kvm.DefaultCpu, - RootDisk: 4, - }, - }, { - cons: "arch=arm64", - expected: kvm.StartParams{ - Memory: kvm.DefaultMemory, - CpuCores: kvm.DefaultCpu, - RootDisk: kvm.DefaultDisk, - }, - infoLog: []string{ - `arch constraint of "arm64" being ignored as not supported`, - }, - }, { - cons: "container=lxd", - expected: kvm.StartParams{ - Memory: kvm.DefaultMemory, - CpuCores: kvm.DefaultCpu, - RootDisk: kvm.DefaultDisk, - }, - infoLog: []string{ - `container constraint of "lxd" being ignored as not supported`, - }, - }, { - cons: "cpu-power=100", - expected: kvm.StartParams{ - Memory: kvm.DefaultMemory, - CpuCores: kvm.DefaultCpu, - RootDisk: kvm.DefaultDisk, - }, - infoLog: []string{ - `cpu-power constraint of 100 being ignored as not supported`, - }, - }, { - cons: "tags=foo,bar", - expected: kvm.StartParams{ - Memory: kvm.DefaultMemory, - CpuCores: kvm.DefaultCpu, - RootDisk: kvm.DefaultDisk, - }, - infoLog: []string{ - `tags constraint of "foo,bar" being ignored as not supported`, - }, - }, { - cons: "mem=4G cores=4 root-disk=20G arch=arm64 cpu-power=100 container=lxd tags=foo,bar", - expected: kvm.StartParams{ - Memory: 4 * 1024, - CpuCores: 4, - RootDisk: 20, - }, - infoLog: []string{ - `arch constraint of "arm64" being ignored as not supported`, - `container constraint of "lxd" being ignored as not supported`, - `cpu-power constraint of 100 being ignored as not supported`, - `tags constraint of "foo,bar" being ignored as not supported`, - }, - }} - - for _, test := range testCases { - c.Logf("testing %q", test.cons) - - var tw loggo.TestWriter - c.Assert(loggo.RegisterWriter("constraint-tester", &tw), gc.IsNil) - cons := constraints.MustParse(test.cons) - params := kvm.ParseConstraintsToStartParams(cons) - c.Check(params, gc.DeepEquals, test.expected) - c.Check(tw.Log(), jc.LogMatches, test.infoLog) - _, _ = loggo.RemoveWriter("constraint-tester") - } -} - -// Test the output when no binary can be found. -func (s *KVMSuite) TestIsKVMSupportedKvmOkNotFound(c *gc.C) { - // With no path, and no backup directory, we should fail. - s.PatchEnvironment("PATH", "") - s.PatchValue(kvm.KVMPath, "") - - supported, err := kvm.IsKVMSupported() - c.Check(supported, jc.IsFalse) - c.Assert(err, gc.ErrorMatches, "kvm-ok executable not found") -} - -// Test the output when the binary is found, but errors out. -func (s *KVMSuite) TestIsKVMSupportedBinaryErrorsOut(c *gc.C) { - // Clear path so real binary is not found. - s.PatchEnvironment("PATH", "") - - // Create mocked binary which returns an error and give the test access. - tmpDir := c.MkDir() - err := os.WriteFile(filepath.Join(tmpDir, "kvm-ok"), []byte("#!/bin/bash\nexit 127"), 0777) - c.Assert(err, jc.ErrorIsNil) - s.PatchValue(kvm.KVMPath, tmpDir) - - supported, err := kvm.IsKVMSupported() - c.Check(supported, jc.IsFalse) - c.Assert(err, gc.ErrorMatches, "exit status 127") -} - -// Test the case where kvm-ok is not in the path, but is in the -// specified directory. -func (s *KVMSuite) TestIsKVMSupportedNoPath(c *gc.C) { - // Create a mocked binary so that this test does not fail for - // developers without kvm-ok. - s.PatchEnvironment("PATH", "") - tmpDir := c.MkDir() - err := os.WriteFile(filepath.Join(tmpDir, "kvm-ok"), []byte("#!/bin/bash"), 0777) - c.Assert(err, jc.ErrorIsNil) - s.PatchValue(kvm.KVMPath, tmpDir) - - supported, err := kvm.IsKVMSupported() - c.Check(supported, jc.IsTrue) - c.Assert(err, jc.ErrorIsNil) -} - -// Test the case that kvm-ok is found in the path. -func (s *KVMSuite) TestIsKVMSupportedOnlyPath(c *gc.C) { - // Create a mocked binary so that this test does not fail for - // developers without kvm-ok. - tmpDir := c.MkDir() - err := os.WriteFile(filepath.Join(tmpDir, "kvm-ok"), []byte("#!/bin/bash"), 0777) - c.Check(err, jc.ErrorIsNil) - s.PatchEnvironment("PATH", tmpDir) - - supported, err := kvm.IsKVMSupported() - c.Check(supported, jc.IsTrue) - c.Assert(err, jc.ErrorIsNil) -} - -func (s *KVMSuite) TestKVMPathIsCorrect(c *gc.C) { - c.Assert(*kvm.KVMPath, gc.Equals, "/usr/sbin") -} diff --git a/internal/container/kvm/libvirt/doc.go b/internal/container/kvm/libvirt/doc.go deleted file mode 100644 index 581d86d22f0..00000000000 --- a/internal/container/kvm/libvirt/doc.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2016 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -// libvirt provides types and functions for interacting with libvirt in kvm -// containers. - -package libvirt diff --git a/internal/container/kvm/libvirt/domainxml.go b/internal/container/kvm/libvirt/domainxml.go deleted file mode 100644 index 00034fc013a..00000000000 --- a/internal/container/kvm/libvirt/domainxml.go +++ /dev/null @@ -1,427 +0,0 @@ -// Copyright 2016 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package libvirt - -import ( - "encoding/xml" - "fmt" - - "github.com/juju/errors" - - "github.com/juju/juju/core/arch" -) - -// Details of the domain XML format are at: https://libvirt.org/formatdomain.html -// We only use a subset, just enough to create instances in the pool. We don't -// check any argument types here. We expect incoming params to be validate-able -// by a function on the incoming domainParams. - -// DiskInfo represents the type and location of a libvirt pool image. -type DiskInfo interface { - // Source is the path to the disk image. - Source() string - // Driver is the type of disk, qcow, vkmd, raw, etc... - Driver() string -} - -// InterfaceInfo represents network interface parameters for a kvm domain. -type InterfaceInfo interface { - // MAC returns the interfaces MAC address. - MACAddress() string - // ParentInterfaceName returns the interface's parent device name. - ParentInterfaceName() string - // ParentVirtualPortType returns the type of the virtual port for this - // interface's parent (e.g. for bridging to an OVS-managed device) or - // an empty value if no virtual port is used. - ParentVirtualPortType() string - // InterfaceName returns the interface's device name. - InterfaceName() string -} - -type domainParams interface { - // Arch returns the arch for which we want to generate the domainXML. - Arch() string - // CPUs returns the number of CPUs to use. - CPUs() uint64 - // DiskInfo returns the disk information for the domain. - DiskInfo() []DiskInfo - // Host returns the host name. - Host() string - // Loader returns the path to the EFI firmware blob to UEFI boot into an - // image. This is a read-only "pflash" drive. - Loader() string - // NetworkInfo contains the network interfaces to create in the domain. - NetworkInfo() []InterfaceInfo - // RAM returns the amount of RAM to use. - RAM() uint64 - // ValidateDomainParams returns nil if the domainParams are valid. - ValidateDomainParams() error -} - -// NewDomain returns a guest domain suitable for unmarshaling (as XML) onto the -// target host. -func NewDomain(p domainParams) (Domain, error) { - if err := p.ValidateDomainParams(); err != nil { - return Domain{}, errors.Trace(err) - } - - d := Domain{ - Type: "kvm", - Name: p.Host(), - VCPU: p.CPUs(), - CurrentMemory: Memory{Unit: "MiB", Text: p.RAM()}, - Memory: Memory{Unit: "MiB", Text: p.RAM()}, - OS: generateOSElement(p), - Features: generateFeaturesElement(p), - CPU: generateCPU(p), - Disk: []Disk{}, - Interface: []Interface{}, - Serial: Serial{ - Type: "pty", - Source: SerialSource{ - Path: "/dev/pts/2", - }, - Target: SerialTarget{ - Port: 0, - }, - }, - Console: []Console{ - { - Type: "pty", - TTY: "/dev/pts/2", - Target: ConsoleTarget{ - Port: 0, - }, - Source: ConsoleSource{ - Path: "/dev/pts/2", - }, - }, - }, - } - for i, diskInfo := range p.DiskInfo() { - devID, err := deviceID(i) - if err != nil { - return Domain{}, errors.Trace(err) - } - switch diskInfo.Driver() { - case "raw": - d.Disk = append(d.Disk, Disk{ - Device: "disk", - Type: "file", - Driver: DiskDriver{Type: diskInfo.Driver(), Name: "qemu"}, - Source: DiskSource{File: diskInfo.Source()}, - Target: DiskTarget{Dev: devID}, - }) - case "qcow2": - d.Disk = append(d.Disk, Disk{ - Device: "disk", - Type: "file", - Driver: DiskDriver{Type: diskInfo.Driver(), Name: "qemu"}, - Source: DiskSource{File: diskInfo.Source()}, - Target: DiskTarget{Dev: devID}, - }) - default: - return Domain{}, errors.Errorf( - "unsupported disk type %q", diskInfo.Driver()) - } - } - for _, iface := range p.NetworkInfo() { - virtIf := Interface{ - Type: "bridge", - MAC: InterfaceMAC{Address: iface.MACAddress()}, - Model: Model{Type: "virtio"}, - Source: InterfaceSource{Bridge: iface.ParentInterfaceName()}, - Guest: InterfaceGuest{Dev: iface.InterfaceName()}, - } - if vpType := iface.ParentVirtualPortType(); vpType != "" { - virtIf.VirtualPortType = &InterfaceVirtualPort{ - Type: vpType, - } - } - d.Interface = append(d.Interface, virtIf) - } - return d, nil -} - -// generateOSElement creates the architecture appropriate element details. -func generateOSElement(p domainParams) OS { - switch p.Arch() { - case arch.ARM64: - return OS{ - Type: OSType{ - Arch: "aarch64", - Machine: "virt", - Text: "hvm", - }, - - Loader: &NVRAMCode{ - Text: p.Loader(), - ReadOnly: "yes", - Type: "pflash", - }, - } - default: - return OS{Type: OSType{Text: "hvm"}} - } -} - -// generateFeaturesElement generates the appropriate features element based on -// the architecture. -func generateFeaturesElement(p domainParams) *Features { - f := new(Features) - if p.Arch() == arch.ARM64 { - f.GIC = &GIC{Version: "host"} - } - return f -} - -// generateCPU infor generates any model/fallback related settings. These are -// typically to allow for better compatibility across versions of libvirt/qemu AFAIU. -func generateCPU(p domainParams) *CPU { - return &CPU{ - Mode: "host-passthrough", - Check: "none", - } -} - -// deviceID generates a device id from and int. The limit of 26 is arbitrary, -// but it seems unlikely we'll need more than a couple for our use case. -func deviceID(i int) (string, error) { - if i < 0 || i > 25 { - return "", errors.Errorf("got %d but only support devices 0-25", i) - } - return fmt.Sprintf("vd%s", string(rune('a'+i))), nil -} - -// Domain describes a libvirt domain. A domain is an instance of an operating -// system running on a virtualized machine. -// See: https://libvirt.org/formatdomain.html where we only care about kvm -// specific details. -type Domain struct { - XMLName xml.Name `xml:"domain"` - Type string `xml:"type,attr"` - Name string `xml:"name"` - VCPU uint64 `xml:"vcpu"` - CurrentMemory Memory `xml:"currentMemory"` - Memory Memory `xml:"memory"` - OS OS `xml:"os"` - Features *Features `xml:"features,omitempty"` - CPU *CPU `xml:"cpu,omitempty"` - Disk []Disk `xml:"devices>disk"` - Interface []Interface `xml:"devices>interface"` - Serial Serial `xml:"devices>serial,omitempty"` - Console []Console `xml:"devices>console"` -} - -// OS is static. We generate a default value (kvm) for it. -// See: https://libvirt.org/formatdomain.html#elementsOSBIOS -// See also: https://libvirt.org/formatcaps.html#elementGuest -type OS struct { - Type OSType `xml:"type"` - // Loader is a pointer so it is omitted if empty. - Loader *NVRAMCode `xml:"loader,omitempty"` -} - -// OSType provides details that are required on certain architectures, e.g. -// ARM64. -// See: https://libvirt.org/formatdomain.html#elementsOS -type OSType struct { - Text string `xml:",chardata"` - Arch string `xml:"arch,attr,omitempty"` - Machine string `xml:"machine,attr,omitempty"` -} - -// NVRAMCode represents the "firmware blob". In our case that is the UEFI code -// which is of type pflash. -// See: https://libvirt.org/formatdomain.html#elementsOS -type NVRAMCode struct { - Text string `xml:",chardata"` - ReadOnly string `xml:"readonly,attr,omitempty"` - Type string `xml:"type,attr,omitempty"` -} - -// Features allows us to request one or more hypervisor features to be toggled -// on/off. See: https://libvirt.org/formatdomain.html#elementsFeatures -type Features struct { - GIC *GIC `xml:"gic,omitempty"` - ACPI string `xml:"acpi"` -} - -// GIC is the Generic Interrupt Controller and is required to UEFI boot on -// ARM64. -// -// NB: Dann Frazier (irc:dannf) reports: -// To deploy trusty, we'll either need to use a GICv2 host, or use the HWE -// kernel in your guest. There are no official cloud images w/ HWE kernel -// preinstalled AFAIK. -// The systems we have in our #hyperscale lab are GICv3 (requiring an HWE -// kernel) - but the system Juju QA has had for a while (McDivitt) is GICv2, so -// it should be able to boot a standard trusty EFI cloud image. Either way, -// you'll need a xenial *host*, at least to have a new enough version of -// qemu-efi and so libvirt can parse the gic_version=host xml. -// -// TODO(ro) 2017-01-20 Determine if we can provide details to reliably boot -// trusty, or if we should exit on error if we are trying to boot trusty on -// arm64. -// -// See: https://libvirt.org/formatdomain.html#elementsFeatures -type GIC struct { - Version string `xml:"version,attr,omitempty"` -} - -// CPU defines CPU topology and model requirements. -// See: https://libvirt.org/formatdomain.html#elementsCPU -type CPU struct { - Mode string `xml:"mode,attr,omitempty"` - Match string `xml:"match,attr,omitempty"` - Check string `xml:"check,attr,omitempty"` -} - -// Address is static. We generate a default value for it. -// See: Controller, Video -type Address struct { - Type string `xml:"type,attr,omitepmty"` - Domain string `xml:"domain,attr,omitempty"` - Bus string `xml:"bus,attr,omitempty"` - Slot string `xml:"slot,attr,omitempty"` - Function string `xml:"function,attr,omitempty"` -} - -// Console is static. We generate a default value for it. -// See: https://libvirt.org/formatdomain.html#elementsConsole -type Console struct { - Type string `xml:"type,attr"` - TTY string `xml:"tty,attr,omitempty"` - Source ConsoleSource `xml:"source,omitempty"` - Target ConsoleTarget `xml:"target,omitempty"` -} - -// ConsoleTarget is static. We generate a default value for it. -// See: Console -type ConsoleTarget struct { - Type string `xml:"type,attr,omitempty"` - Port int `xml:"port,attr"` - Path string `xml:"path,attr,omitempty"` -} - -// ConsoleSource is static. We generate a default value for it. -// See: Console -type ConsoleSource struct { - Path string `xml:"path,attr,omitempty"` -} - -// Serial is static. This was added specifically to create a functional console -// for troubleshooting vms as they boot. You can attach to this console with -// `virsh console `. -// See: https://libvirt.org/formatdomain.html#elementsConsole -type Serial struct { - Type string `xml:"type,attr"` - Source SerialSource `xml:"source"` - Target SerialTarget `xml:"target"` -} - -// SerialSource is static. We generate a default value for it. -// See: Serial -type SerialSource struct { - Path string `xml:"path,attr"` -} - -// SerialTarget is static. We generate a default value for it. -// See: Serial -type SerialTarget struct { - Port int `xml:"port,attr"` -} - -// Interface is dynamic. It represents a network interface. We generate it from -// an incoming argument. -// See: https://libvirt.org/formatdomain.html#elementsNICSBridge -type Interface struct { - Type string `xml:"type,attr"` - MAC InterfaceMAC `xml:"mac"` - Model Model `xml:"model"` - Source InterfaceSource `xml:"source"` - Guest InterfaceGuest `xml:"guest"` - VirtualPortType *InterfaceVirtualPort `xml:"virtualport,omitempty"` -} - -// InterfaceVirtualPort provides additional configuration data to be forwarded -// to a vepa (802.1Qbg) or 802.1Qbh compliant switch, or to an Open vSwitch -// virtual switch. -type InterfaceVirtualPort struct { - Type string `xml:"type,attr"` -} - -// InterfaceMAC is the MAC address for an Interface. -// See: Interface -type InterfaceMAC struct { - Address string `xml:"address,attr"` -} - -// InterfaceSource it the host bridge to the network. -// See: Interface -type InterfaceSource struct { - Bridge string `xml:"bridge,attr"` -} - -// InterfaceGuest is the guests network device. -// See: Interface -type InterfaceGuest struct { - Dev string `xml:"dev,attr"` -} - -// Disk is dynamic. We create it with paths to the user data source and disk. -// See: https://libvirt.org/formatdomain.html#elementsDisks -type Disk struct { - Device string `xml:"device,attr"` - Type string `xml:"type,attr"` - Driver DiskDriver `xml:"driver"` - Source DiskSource `xml:"source"` - Target DiskTarget `xml:"target"` -} - -// DiskDriver is the type of virtual disk. We generate it dynamically. -// See: Disk -type DiskDriver struct { - Type string `xml:"type,attr"` - Name string `xml:"name,attr"` -} - -// DiskSource is the location of the disk image. In our case the path to the -// necessary images. -// See: Disk -type DiskSource struct { - File string `xml:"file,attr"` -} - -// DiskTarget is the target device on the guest. We generate these. -type DiskTarget struct { - Dev string `xml:"dev,attr"` -} - -// CurrentMemory is the actual allocation of memory for the guest. It appears -// we historically set this the same as Memory, which is also the default -// behavior of libvirt. Constraints.Value.Mem documents this as "megabytes". -// Interpreting that here as MiB. -// See: Memory, github.com/juju/juju/core/constraints/constraints.Value.Mem -type CurrentMemory struct { - Unit string `xml:"unit,attr"` - Text uint64 `xml:",chardata"` -} - -// Memory is dynamic. We take an argument to set it. Unit is magnitude of -// memory: b, k or KiB, KB, M or MiB, MB, etc... The libvirt default is KiB. We want to -// set MiB so we default to that. -// See: https://libvirt.org/formatdomain.html#elementsMemoryAllocation -type Memory struct { - Unit string `xml:"unit,attr,omitempty"` - Text uint64 `xml:",chardata"` -} - -// Model is used as an element in CPU and Interface. -// See: CPU, Interface -type Model struct { - Fallback string `xml:"fallback,attr,omitempty"` - Text string `xml:",chardata"` - Type string `xml:"type,attr,omitempty"` -} diff --git a/internal/container/kvm/libvirt/domainxml_internal_test.go b/internal/container/kvm/libvirt/domainxml_internal_test.go deleted file mode 100644 index 236793adeca..00000000000 --- a/internal/container/kvm/libvirt/domainxml_internal_test.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2016 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package libvirt - -import ( - "github.com/juju/testing" - jc "github.com/juju/testing/checkers" - gc "gopkg.in/check.v1" -) - -// gocheck boilerplate. -type domainXMLInternalSuite struct { - testing.IsolationSuite -} - -var _ = gc.Suite(&domainXMLInternalSuite{}) - -func (domainXMLInternalSuite) TestDeviceID(c *gc.C) { - table := []struct { - in int - want string - errMsg string - }{ - {0, "vda", ""}, - {4, "vde", ""}, - {15, "vdp", ""}, - {25, "vdz", ""}, - {-1, "", "got -1 but only support devices 0-25"}, - {26, "", "got 26 but only support devices 0-25"}, - {120, "", "got 120 but only support devices 0-25"}, - } - for i, test := range table { - c.Logf("test %d for input %d", i+1, test.in) - got, err := deviceID(test.in) - if err != nil { - c.Check(err, gc.ErrorMatches, test.errMsg) - c.Check(got, gc.Equals, "") - continue - } - c.Check(got, gc.Equals, test.want) - c.Check(err, jc.ErrorIsNil) - } -} diff --git a/internal/container/kvm/libvirt/domainxml_test.go b/internal/container/kvm/libvirt/domainxml_test.go deleted file mode 100644 index 3a475b3e490..00000000000 --- a/internal/container/kvm/libvirt/domainxml_test.go +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright 2016 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package libvirt_test - -import ( - "encoding/xml" - - "github.com/juju/errors" - "github.com/juju/testing" - jc "github.com/juju/testing/checkers" - gc "gopkg.in/check.v1" - - "github.com/juju/juju/internal/container/kvm/libvirt" -) - -// gocheck boilerplate. -type domainXMLSuite struct { - testing.IsolationSuite -} - -var _ = gc.Suite(&domainXMLSuite{}) - -var amd64DomainStr = ` - - juju-someid - 2 - 1024 - 1024 - - hvm - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -`[1:] - -var arm64DomainStr = ` - - juju-someid - 2 - 1024 - 1024 - - hvm - /shared/readonly.fd - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -`[1:] - -var amd64WithOvsBridgeDomainStr = ` - - juju-someid - 2 - 1024 - 1024 - - hvm - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -`[1:] - -func (domainXMLSuite) TestNewDomain(c *gc.C) { - table := []struct { - arch, want string - }{ - {"amd64", amd64DomainStr}, - {"arm64", arm64DomainStr}, - } - for i, test := range table { - c.Logf("TestNewDomain: test #%d for %s", i+1, test.arch) - ifaces := []libvirt.InterfaceInfo{ - dummyInterface{ - mac: "00:00:00:00:00:00", - parent: "parent-dev", - name: "device-name"}} - disks := []libvirt.DiskInfo{ - dummyDisk{driver: "qcow2", source: "/some/path"}, - dummyDisk{driver: "raw", source: "/another/path"}, - } - params := dummyParams{ifaceInfo: ifaces, diskInfo: disks, memory: 1024, cpuCores: 2, hostname: "juju-someid", arch: test.arch} - - if test.arch == "arm64" { - params.loader = "/shared/readonly.fd" - } - - d, err := libvirt.NewDomain(params) - c.Check(err, jc.ErrorIsNil) - ml, err := xml.MarshalIndent(&d, "", " ") - c.Check(err, jc.ErrorIsNil) - c.Assert(string(ml), jc.DeepEquals, test.want) - } -} - -func (domainXMLSuite) TestNewDomainWithOvsBridge(c *gc.C) { - ifaces := []libvirt.InterfaceInfo{ - dummyInterface{ - mac: "00:00:00:00:00:00", - parent: "parent-dev", - name: "device-name", - parentVirtualPortType: "openvswitch", - }, - } - disks := []libvirt.DiskInfo{ - dummyDisk{driver: "qcow2", source: "/some/path"}, - dummyDisk{driver: "raw", source: "/another/path"}, - } - params := dummyParams{ifaceInfo: ifaces, diskInfo: disks, memory: 1024, cpuCores: 2, hostname: "juju-someid", arch: "amd64"} - - d, err := libvirt.NewDomain(params) - c.Check(err, jc.ErrorIsNil) - ml, err := xml.MarshalIndent(&d, "", " ") - c.Check(err, jc.ErrorIsNil) - c.Assert(string(ml), jc.DeepEquals, amd64WithOvsBridgeDomainStr) -} - -func (domainXMLSuite) TestNewDomainError(c *gc.C) { - d, err := libvirt.NewDomain(dummyParams{err: errors.Errorf("boom")}) - c.Check(d, jc.DeepEquals, libvirt.Domain{}) - c.Check(err, gc.ErrorMatches, "boom") -} - -type dummyParams struct { - err error - arch string - cpuCores uint64 - diskInfo []libvirt.DiskInfo - hostname string - ifaceInfo []libvirt.InterfaceInfo - loader string - memory uint64 - nvram string -} - -func (p dummyParams) Arch() string { return p.arch } -func (p dummyParams) CPUs() uint64 { return p.cpuCores } -func (p dummyParams) DiskInfo() []libvirt.DiskInfo { return p.diskInfo } -func (p dummyParams) Host() string { return p.hostname } -func (p dummyParams) Loader() string { return p.loader } -func (p dummyParams) NVRAM() string { return p.nvram } -func (p dummyParams) NetworkInfo() []libvirt.InterfaceInfo { return p.ifaceInfo } -func (p dummyParams) RAM() uint64 { return p.memory } -func (p dummyParams) ValidateDomainParams() error { return p.err } - -type dummyDisk struct { - source string - driver string -} - -func (d dummyDisk) Driver() string { return d.driver } -func (d dummyDisk) Source() string { return d.source } - -type dummyInterface struct { - mac, parent, parentVirtualPortType, name string -} - -func (i dummyInterface) InterfaceName() string { return i.name } -func (i dummyInterface) MACAddress() string { return i.mac } -func (i dummyInterface) ParentInterfaceName() string { return i.parent } -func (i dummyInterface) ParentVirtualPortType() string { return i.parentVirtualPortType } diff --git a/internal/container/kvm/libvirt/package_test.go b/internal/container/kvm/libvirt/package_test.go deleted file mode 100644 index 3780d4ce6a2..00000000000 --- a/internal/container/kvm/libvirt/package_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2016 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package libvirt_test - -import ( - "runtime" - "testing" - - gc "gopkg.in/check.v1" -) - -func Test(t *testing.T) { - if runtime.GOOS != "linux" || !supportedArch() { - t.Skip("KVM is currently only supported on linux architectures amd64, arm64, and ppc64el") - } - gc.TestingT(t) -} - -func supportedArch() bool { - for _, arch := range []string{"amd64", "arm64", "ppc64el"} { - if runtime.GOARCH == arch { - return true - } - } - return false -} diff --git a/internal/container/kvm/libvirt/ssh_test.go b/internal/container/kvm/libvirt/ssh_test.go deleted file mode 100644 index 6284c709dcb..00000000000 --- a/internal/container/kvm/libvirt/ssh_test.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2018 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package libvirt - -import ( - jc "github.com/juju/testing/checkers" - gc "gopkg.in/check.v1" -) - -// libvirtSSHSuite is gocheck boilerplate -type libvirtSSHSuite struct{} - -// gocheck boilerplate -var _ = gc.Suite(libvirtSSHSuite{}) - -func (libvirtSSHSuite) TestKeepTheImports(c *gc.C) { - c.Assert(true, jc.IsTrue) -} diff --git a/internal/container/kvm/live_test.go b/internal/container/kvm/live_test.go deleted file mode 100644 index 74ac1c724db..00000000000 --- a/internal/container/kvm/live_test.go +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2013 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package kvm_test - -import ( - "context" - "fmt" - "os" - "os/exec" - "runtime" - - "github.com/juju/loggo/v2" - jc "github.com/juju/testing/checkers" - "github.com/juju/version/v2" - gc "gopkg.in/check.v1" - - "github.com/juju/juju/core/arch" - corebase "github.com/juju/juju/core/base" - "github.com/juju/juju/core/constraints" - "github.com/juju/juju/core/network" - "github.com/juju/juju/core/status" - "github.com/juju/juju/environs/config" - "github.com/juju/juju/environs/imagemetadata" - "github.com/juju/juju/environs/instances" - "github.com/juju/juju/internal/cloudconfig/instancecfg" - "github.com/juju/juju/internal/container" - "github.com/juju/juju/internal/container/kvm" - "github.com/juju/juju/internal/tools" - jujutesting "github.com/juju/juju/juju/testing" - coretesting "github.com/juju/juju/testing" - jujuversion "github.com/juju/juju/version" -) - -type LiveSuite struct { - coretesting.BaseSuite - ContainerDir string - RemovedDir string -} - -var _ = gc.Suite(&LiveSuite{}) - -func (s *LiveSuite) SetUpTest(c *gc.C) { - s.BaseSuite.SetUpTest(c) - // Skip if not linux - if runtime.GOOS != "linux" { - c.Skip("not running linux") - } - // Skip if virsh is not installed. - if _, err := exec.LookPath("virsh"); err != nil { - c.Skip("virsh not found") - } - // Skip if not running as root. - if os.Getuid() != 0 { - c.Skip("not running as root") - } - s.ContainerDir = c.MkDir() - s.PatchValue(&container.ContainerDir, s.ContainerDir) - s.RemovedDir = c.MkDir() - s.PatchValue(&container.RemovedContainerDir, s.RemovedDir) - loggo.GetLogger("juju.container").SetLogLevel(loggo.TRACE) -} - -func (s *LiveSuite) newManager(c *gc.C, name string) container.Manager { - manager, err := kvm.NewContainerManager( - container.ManagerConfig{ - container.ConfigModelUUID: coretesting.ModelTag.Id(), - container.ConfigLogDir: c.MkDir(), - }) - c.Assert(err, jc.ErrorIsNil) - return manager -} - -func assertNumberOfContainers(c *gc.C, manager container.Manager, count int) { - containers, err := manager.ListContainers() - c.Assert(err, jc.ErrorIsNil) - c.Assert(containers, gc.HasLen, count) -} - -func (s *LiveSuite) TestNoInitialContainers(c *gc.C) { - manager := s.newManager(c, "test") - assertNumberOfContainers(c, manager, 0) -} - -func shutdownMachines(manager container.Manager) func(*gc.C) { - return func(c *gc.C) { - instances, err := manager.ListContainers() - c.Assert(err, jc.ErrorIsNil) - for _, instance := range instances { - err := manager.DestroyContainer(instance.Id()) - c.Check(err, jc.ErrorIsNil) - } - } -} - -func createContainer(c *gc.C, manager container.Manager, machineId string) instances.Instance { - machineNonce := "fake-nonce" - apiInfo := jujutesting.FakeAPIInfo(machineId) - instanceConfig, err := instancecfg.NewInstanceConfig(coretesting.ControllerTag, machineId, machineNonce, - imagemetadata.ReleasedStream, corebase.MakeDefaultBase("ubuntu", "22.04"), apiInfo) - c.Assert(err, jc.ErrorIsNil) - - nics := network.InterfaceInfos{{ - InterfaceName: "eth0", - InterfaceType: network.EthernetDevice, - ConfigType: network.ConfigDHCP, - }} - net := container.BridgeNetworkConfig(0, nics) - - err = instanceConfig.SetTools(tools.List{ - &tools.Tools{ - Version: version.MustParseBinary("2.3.4-foo-bar"), - URL: "http://tools.testing.invalid/2.3.4-foo-bar.tgz", - }, - }) - c.Assert(err, jc.ErrorIsNil) - environConfig := dummyConfig(c) - err = instancecfg.FinishInstanceConfig(instanceConfig, environConfig) - c.Assert(err, jc.ErrorIsNil) - callback := func(settableStatus status.Status, info string, data map[string]interface{}) error { return nil } - inst, hardware, err := manager.CreateContainer(context.Background(), instanceConfig, constraints.Value{}, corebase.MakeDefaultBase("ubuntu", "12.04"), net, nil, callback) - c.Assert(err, jc.ErrorIsNil) - c.Assert(hardware, gc.NotNil) - expected := fmt.Sprintf("arch=%s cores=1 mem=512M root-disk=8192M", arch.HostArch()) - c.Assert(hardware.String(), gc.Equals, expected) - return inst -} - -func (s *LiveSuite) TestShutdownMachines(c *gc.C) { - manager := s.newManager(c, "test") - createContainer(c, manager, "1/kvm/0") - createContainer(c, manager, "1/kvm/1") - assertNumberOfContainers(c, manager, 2) - - shutdownMachines(manager)(c) - assertNumberOfContainers(c, manager, 0) -} - -func (s *LiveSuite) TestManagerIsolation(c *gc.C) { - firstManager := s.newManager(c, "first") - s.AddCleanup(shutdownMachines(firstManager)) - - createContainer(c, firstManager, "1/kvm/0") - createContainer(c, firstManager, "1/kvm/1") - - secondManager := s.newManager(c, "second") - s.AddCleanup(shutdownMachines(secondManager)) - - createContainer(c, secondManager, "1/kvm/0") - - assertNumberOfContainers(c, firstManager, 2) - assertNumberOfContainers(c, secondManager, 1) -} - -func dummyConfig(c *gc.C) *config.Config { - testConfig, err := config.New(config.UseDefaults, coretesting.FakeConfig()) - c.Assert(err, jc.ErrorIsNil) - testConfig, err = testConfig.Apply(map[string]interface{}{ - "type": "dummy", - "controller": false, - "agent-version": jujuversion.Current.String(), - }) - c.Assert(err, jc.ErrorIsNil) - return testConfig -} diff --git a/internal/container/kvm/mock/mock-kvm.go b/internal/container/kvm/mock/mock-kvm.go deleted file mode 100644 index 19a8d08f6e6..00000000000 --- a/internal/container/kvm/mock/mock-kvm.go +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2013 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package mock - -import ( - "fmt" - "sync" - - "github.com/juju/juju/internal/container/kvm" -) - -// This file provides a mock implementation of the kvm interfaces -// ContainerFactory and Container. - -type Action int - -const ( - // A container has been started. - Started Action = iota - // A container has been stopped. - Stopped -) - -func (action Action) String() string { - switch action { - case Started: - return "Started" - case Stopped: - return "Stopped" - } - return "unknown" -} - -type Event struct { - Action Action - InstanceId string -} - -type ContainerFactory interface { - kvm.ContainerFactory - - AddListener(chan<- Event) - RemoveListener(chan<- Event) - HasListener(chan<- Event) bool -} - -type mockFactory struct { - instances map[string]kvm.Container - listeners []chan<- Event - mu sync.Mutex -} - -func MockFactory() ContainerFactory { - return &mockFactory{ - instances: make(map[string]kvm.Container), - } -} - -type MockContainer struct { - StartParams kvm.StartParams - - factory *mockFactory - name string - started bool -} - -// Name returns the name of the container. -func (mock *MockContainer) Name() string { - return mock.name -} - -// imageCacheCalls does nothing more than get incremented by calls to the -// mocked EnsureCachedImage method below. -// However, it will cause the race checker to fail if such calls are not made -// in a Goroutine safe manner. -var imageCacheCalls int //nolint:unused - -// EnsureCachedImage is the first supply of start-params to the container. -// We set it here for subsequent test assertions. -// Start is called by the manager immediately after, with the same argument. -func (mock *MockContainer) EnsureCachedImage(params kvm.StartParams) error { - imageCacheCalls++ - mock.StartParams = params - return nil -} - -func (mock *MockContainer) Start(params kvm.StartParams) error { - if mock.started { - return fmt.Errorf("container is already running") - } - mock.started = true - mock.factory.notify(Started, mock.name) - return nil -} - -// Stop terminates the running container. -func (mock *MockContainer) Stop() error { - if !mock.started { - return fmt.Errorf("container is not running") - } - mock.started = false - mock.factory.notify(Stopped, mock.name) - return nil -} - -func (mock *MockContainer) IsRunning() bool { - return mock.started -} - -// String returns information about the container. -func (mock *MockContainer) String() string { - return fmt.Sprintf("", mock.name) -} - -func (mock *mockFactory) String() string { - return fmt.Sprintf("") -} - -func (mock *mockFactory) New(name string) kvm.Container { - mock.mu.Lock() - defer mock.mu.Unlock() - - container, ok := mock.instances[name] - if ok { - return container - } - container = &MockContainer{ - factory: mock, - name: name, - } - mock.instances[name] = container - return container -} - -func (mock *mockFactory) List() (result []kvm.Container, err error) { - for _, container := range mock.instances { - result = append(result, container) - } - return -} - -func (mock *mockFactory) notify(action Action, instanceId string) { - event := Event{action, instanceId} - for _, c := range mock.listeners { - c <- event - } -} - -func (mock *mockFactory) AddListener(listener chan<- Event) { - mock.listeners = append(mock.listeners, listener) -} - -func (mock *mockFactory) RemoveListener(listener chan<- Event) { - pos := 0 - for i, c := range mock.listeners { - if c == listener { - pos = i - } - } - mock.listeners = append(mock.listeners[:pos], mock.listeners[pos+1:]...) -} - -func (mock *mockFactory) HasListener(listener chan<- Event) bool { - for _, c := range mock.listeners { - if c == listener { - return true - } - } - return false -} diff --git a/internal/container/kvm/mock/mock-kvm_test.go b/internal/container/kvm/mock/mock-kvm_test.go deleted file mode 100644 index dc158d35636..00000000000 --- a/internal/container/kvm/mock/mock-kvm_test.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2013 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package mock_test - -import ( - jc "github.com/juju/testing/checkers" - gc "gopkg.in/check.v1" - - "github.com/juju/juju/internal/container/kvm" - "github.com/juju/juju/internal/container/kvm/mock" - "github.com/juju/juju/testing" -) - -type MockSuite struct { - testing.BaseSuite -} - -var _ = gc.Suite(&MockSuite{}) - -func (*MockSuite) TestListInitiallyEmpty(c *gc.C) { - factory := mock.MockFactory() - containers, err := factory.List() - c.Assert(err, jc.ErrorIsNil) - c.Assert(containers, gc.HasLen, 0) -} - -func (*MockSuite) TestNewContainersInList(c *gc.C) { - factory := mock.MockFactory() - added := []kvm.Container{} - added = append(added, factory.New("first")) - added = append(added, factory.New("second")) - containers, err := factory.List() - c.Assert(err, jc.ErrorIsNil) - c.Assert(containers, jc.SameContents, added) -} - -func (*MockSuite) TestContainers(c *gc.C) { - factory := mock.MockFactory() - container := factory.New("first") - c.Assert(container.Name(), gc.Equals, "first") - c.Assert(container.IsRunning(), jc.IsFalse) -} - -func (*MockSuite) TestContainerStoppingStoppedErrors(c *gc.C) { - factory := mock.MockFactory() - container := factory.New("first") - err := container.Stop() - c.Assert(err, gc.ErrorMatches, "container is not running") -} - -func (*MockSuite) TestContainerStartStarts(c *gc.C) { - factory := mock.MockFactory() - container := factory.New("first") - err := container.Start(kvm.StartParams{}) - c.Assert(err, jc.ErrorIsNil) - c.Assert(container.IsRunning(), jc.IsTrue) -} - -func (*MockSuite) TestContainerStartingRunningErrors(c *gc.C) { - factory := mock.MockFactory() - container := factory.New("first") - err := container.Start(kvm.StartParams{}) - c.Assert(err, jc.ErrorIsNil) - err = container.Start(kvm.StartParams{}) - c.Assert(err, gc.ErrorMatches, "container is already running") -} - -func (*MockSuite) TestContainerStoppingRunningStops(c *gc.C) { - factory := mock.MockFactory() - container := factory.New("first") - err := container.Start(kvm.StartParams{}) - c.Assert(err, jc.ErrorIsNil) - err = container.Stop() - c.Assert(err, jc.ErrorIsNil) - c.Assert(container.IsRunning(), jc.IsFalse) -} - -func (*MockSuite) TestAddListener(c *gc.C) { - listener := make(chan mock.Event) - factory := mock.MockFactory() - factory.AddListener(listener) - c.Assert(factory.HasListener(listener), jc.IsTrue) -} - -func (*MockSuite) TestRemoveFirstListener(c *gc.C) { - factory := mock.MockFactory() - first := make(chan mock.Event) - factory.AddListener(first) - second := make(chan mock.Event) - factory.AddListener(second) - third := make(chan mock.Event) - factory.AddListener(third) - factory.RemoveListener(first) - c.Assert(factory.HasListener(first), jc.IsFalse) - c.Assert(factory.HasListener(second), jc.IsTrue) - c.Assert(factory.HasListener(third), jc.IsTrue) -} - -func (*MockSuite) TestRemoveMiddleListener(c *gc.C) { - factory := mock.MockFactory() - first := make(chan mock.Event) - factory.AddListener(first) - second := make(chan mock.Event) - factory.AddListener(second) - third := make(chan mock.Event) - factory.AddListener(third) - factory.RemoveListener(second) - c.Assert(factory.HasListener(first), jc.IsTrue) - c.Assert(factory.HasListener(second), jc.IsFalse) - c.Assert(factory.HasListener(third), jc.IsTrue) -} - -func (*MockSuite) TestRemoveLastListener(c *gc.C) { - factory := mock.MockFactory() - first := make(chan mock.Event) - factory.AddListener(first) - second := make(chan mock.Event) - factory.AddListener(second) - third := make(chan mock.Event) - factory.AddListener(third) - factory.RemoveListener(third) - c.Assert(factory.HasListener(first), jc.IsTrue) - c.Assert(factory.HasListener(second), jc.IsTrue) - c.Assert(factory.HasListener(third), jc.IsFalse) -} - -func (*MockSuite) TestEvents(c *gc.C) { - factory := mock.MockFactory() - listener := make(chan mock.Event, 5) - factory.AddListener(listener) - - first := factory.New("first") - second := factory.New("second") - first.Start(kvm.StartParams{}) - second.Start(kvm.StartParams{}) - second.Stop() - first.Stop() - - c.Assert(<-listener, gc.Equals, mock.Event{mock.Started, "first"}) - c.Assert(<-listener, gc.Equals, mock.Event{mock.Started, "second"}) - c.Assert(<-listener, gc.Equals, mock.Event{mock.Stopped, "second"}) - c.Assert(<-listener, gc.Equals, mock.Event{mock.Stopped, "first"}) -} - -func (*MockSuite) TestEventsGoToAllListeners(c *gc.C) { - factory := mock.MockFactory() - first := make(chan mock.Event, 5) - factory.AddListener(first) - second := make(chan mock.Event, 5) - factory.AddListener(second) - - container := factory.New("container") - container.Start(kvm.StartParams{}) - container.Stop() - - c.Assert(<-first, gc.Equals, mock.Event{mock.Started, "container"}) - c.Assert(<-second, gc.Equals, mock.Event{mock.Started, "container"}) - c.Assert(<-first, gc.Equals, mock.Event{mock.Stopped, "container"}) - c.Assert(<-second, gc.Equals, mock.Event{mock.Stopped, "container"}) -} diff --git a/internal/container/kvm/mock/package_test.go b/internal/container/kvm/mock/package_test.go deleted file mode 100644 index 3c39d819b14..00000000000 --- a/internal/container/kvm/mock/package_test.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2013 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package mock_test - -import ( - "testing" - - gc "gopkg.in/check.v1" -) - -func Test(t *testing.T) { - gc.TestingT(t) -} diff --git a/internal/container/kvm/package_test.go b/internal/container/kvm/package_test.go deleted file mode 100644 index 73ecc2bd6cf..00000000000 --- a/internal/container/kvm/package_test.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2013 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package kvm_test - -import ( - "runtime" - "testing" - - gc "gopkg.in/check.v1" - - "github.com/juju/juju/core/arch" -) - -func Test(t *testing.T) { - if runtime.GOOS != "linux" || !supportedArch() { - t.Skip("KVM is currently only supported on linux architectures amd64, arm64, and ppc64el") - } - gc.TestingT(t) -} - -func supportedArch() bool { - for _, a := range []string{arch.AMD64, arch.ARM64, arch.PPC64EL} { - if runtime.GOARCH == a { - return true - } - } - return false -} diff --git a/internal/container/kvm/run.go b/internal/container/kvm/run.go deleted file mode 100644 index a12196b9866..00000000000 --- a/internal/container/kvm/run.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2016 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package kvm - -import ( - "os/exec" -) - -// This is the user on ubuntu. I don't know what the user would be on other -// linux distros. At the time of this writing we are only supporting ubuntu on -// ubuntu for kvm containers in Juju. -const libvirtUser = "libvirt-qemu" - -// runFunc provides the signature for running an external -// command and returning the combined output. -// The first parameter, if non-empty will use the input -// path as the working directory for the command. -// NOTE: if changing runFunc, remember to edit BOTH copies of -// runAsLibvirt() in run_other.go and run_linux.go. One doesn't -// compile on linux, thus easily missed. -type runFunc func(string, string, ...string) (string, error) - -// run the command and return the combined output. -func run(dir, command string, args ...string) (string, error) { - logger.Debugf("(%s) %s %v", dir, command, args) - - cmd := exec.Command(command, args...) - if dir != "" { - cmd.Dir = dir - } - - out, err := cmd.CombinedOutput() - output := string(out) - - logger.Debugf("output: %v", output) - return output, err -} diff --git a/internal/container/kvm/run_linux.go b/internal/container/kvm/run_linux.go deleted file mode 100644 index 0424df53be6..00000000000 --- a/internal/container/kvm/run_linux.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2016 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -//go:build linux - -package kvm - -import ( - "os" - "os/exec" - "os/user" - "strconv" - "syscall" - - "github.com/juju/errors" -) - -// Run the command as user libvirt-qemu and return the combined output. -// If dir is non-empty, use it as the working directory. -func runAsLibvirt(dir, command string, args ...string) (string, error) { - uid, gid, err := getUserUIDGID(libvirtUser) - if err != nil { - return "", errors.Trace(err) - } - - cmd := exec.Command(command, args...) - if dir != "" { - cmd.Dir = dir - } - - if dir == "" { - dir, _ = os.Getwd() - } - logger.Debugf("running: %s %v from %s", command, args, dir) - logger.Debugf("running as uid: %d, gid: %d\n", uid, gid) - - cmd.SysProcAttr = &syscall.SysProcAttr{} - cmd.SysProcAttr.Credential = &syscall.Credential{ - Uid: uint32(uid), - Gid: uint32(gid), - } - - out, err := cmd.CombinedOutput() - output := string(out) - logger.Debugf("output: %v", output) - - return output, err - -} - -// getUserUIDGID returns integer vals for uid and gid for the user. It returns -// -1 when there's an error so no one accidentally thinks 0 is the appropriate -// uid/gid when there's an error. -func getUserUIDGID(_ string) (int, int, error) { - u, err := user.Lookup(libvirtUser) - if err != nil { - return -1, -1, errors.Trace(err) - } - uid, err := strconv.ParseUint(u.Uid, 10, 32) - if err != nil { - return -1, -1, errors.Trace(err) - } - gid, err := strconv.ParseUint(u.Gid, 10, 32) - if err != nil { - return -1, -1, errors.Trace(err) - } - return int(uid), int(gid), nil -} diff --git a/internal/container/kvm/run_other.go b/internal/container/kvm/run_other.go deleted file mode 100644 index 864ee555fc0..00000000000 --- a/internal/container/kvm/run_other.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2016 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -//go:build !linux - -package kvm - -import "github.com/juju/errors" - -func runAsLibvirt(_, _ string, _ ...string) (string, error) { - return "", errors.New("kvm is only supported on linux amd64, arm64, and ppc64el") -} - -func getUserUIDGID(_ string) (int, int, error) { - return -1, -1, errors.New("kvm is only supported on linux amd64, arm64, and ppc64el") -} diff --git a/internal/container/kvm/sync.go b/internal/container/kvm/sync.go deleted file mode 100644 index 69e0feaa082..00000000000 --- a/internal/container/kvm/sync.go +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright 2016 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package kvm - -import ( - "context" - "crypto/sha256" - "fmt" - "io" - "net/http" - "os" - "path" - "path/filepath" - "time" - - "github.com/dustin/go-humanize" - "github.com/juju/clock" - "github.com/juju/errors" - - "github.com/juju/juju/core/paths" - "github.com/juju/juju/environs/imagedownloads" - "github.com/juju/juju/environs/imagemetadata" - "github.com/juju/juju/environs/simplestreams" -) - -// DiskImageType is the file type we want to fetch and use for kvm instances. -const DiskImageType = "disk1.img" - -// Oner gets the one matching item from simplestreams. -type Oner interface { - One() (*imagedownloads.Metadata, error) -} - -// syncParams conveys the information necessary for calling imagedownloads.One. -type syncParams struct { - fetcher imagemetadata.SimplestreamsFetcher - arch, version, stream, fType string - srcFunc func() simplestreams.DataSource -} - -// One implements Oner. -func (p syncParams) One() (*imagedownloads.Metadata, error) { - if err := p.exists(); err != nil { - return nil, errors.Trace(err) - } - return imagedownloads.One(context.TODO(), p.fetcher, p.arch, p.version, p.stream, p.fType, p.srcFunc) -} - -func (p syncParams) exists() error { - fName := backingFileName(p.version, p.arch) - baseDir := paths.DataDir(paths.CurrentOS()) - imagePath := filepath.Join(baseDir, kvm, guestDir, fName) - - if _, err := os.Stat(imagePath); err == nil { - return errors.AlreadyExistsf("%q %q image for exists at %q", p.version, p.arch, imagePath) - } - return nil -} - -// Validate that our types fulfill their implementations. -var _ Oner = (*syncParams)(nil) -var _ Fetcher = (*fetcher)(nil) - -// Fetcher is an interface to permit faking input in tests. The default -// implementation is updater, defined in this file. -type Fetcher interface { - Fetch() error - Close() -} - -type fetcher struct { - metadata *imagedownloads.Metadata - req *http.Request - client *http.Client - image *Image - imageDownloadURL string -} - -// Fetch implements Fetcher. It fetches the image file from simplestreams and -// delegates writing it out and creating the qcow3 backing file to Image.write. -func (f *fetcher) Fetch() error { - resp, err := f.client.Do(f.req) - if err != nil { - return errors.Trace(err) - } - - defer func() { - err = resp.Body.Close() - if err != nil { - logger.Debugf("failed defer %q", errors.Trace(err)) - } - }() - - if resp.StatusCode != 200 { - f.image.cleanup() - return errors.NotFoundf( - "got %d fetching image %q", resp.StatusCode, path.Base( - f.req.URL.String())) - } - err = f.image.write(resp.Body, f.metadata, f.imageDownloadURL) - if err != nil { - return errors.Trace(err) - } - return nil -} - -// Close calls images cleanup method for deferred closing of the image tmpFile. -func (f *fetcher) Close() { - f.image.cleanup() -} - -type ProgressCallback func(message string) - -// Sync updates the local cached images by reading the simplestreams data and -// caching if an image matching the constraints doesn't exist. It retrieves -// metadata information from Oner and updates local cache via Fetcher. -// A ProgressCallback can optionally be passed which will get update messages -// as data is copied. -func Sync(o Oner, f Fetcher, imageDownloadURL string, progress ProgressCallback) error { - md, err := o.One() - if err != nil { - if errors.Is(err, errors.AlreadyExists) { - // We've already got a backing file for this series/architecture. - return nil - } - return errors.Trace(err) - } - if f == nil { - f, err = newDefaultFetcher(md, imageDownloadURL, paths.DataDir, progress) - if err != nil { - return errors.Trace(err) - } - defer f.Close() - } - err = f.Fetch() - if err != nil { - return errors.Trace(err) - } - return nil -} - -// Image represents a server image. -type Image struct { - FilePath string - progress ProgressCallback - tmpFile *os.File - runCmd runFunc -} - -type progressWriter struct { - callback ProgressCallback - url string - total uint64 - maxBytes uint64 - startTime *time.Time - lastPercent int - clock clock.Clock -} - -var _ io.Writer = (*progressWriter)(nil) - -func (p *progressWriter) Write(content []byte) (n int, err error) { - if p.clock == nil { - p.clock = clock.WallClock - } - p.total += uint64(len(content)) - if p.startTime == nil { - now := p.clock.Now() - p.startTime = &now - return len(content), nil - } - if p.callback != nil { - elapsed := p.clock.Now().Sub(*p.startTime) - // Avoid measurements that aren't interesting - if elapsed > time.Millisecond { - percent := (float64(p.total) * 100.0) / float64(p.maxBytes) - intPercent := int(percent + 0.5) - if p.lastPercent != intPercent { - bps := uint64((float64(p.total) / elapsed.Seconds()) + 0.5) - p.callback(fmt.Sprintf("copying %s %d%% (%s/s)", p.url, intPercent, humanize.Bytes(bps))) - p.lastPercent = intPercent - } - } - } - return len(content), nil -} - -// write saves the stream to disk and updates the metadata file. -func (i *Image) write(r io.Reader, md *imagedownloads.Metadata, imageDownloadURL string) error { - tmpPath := i.tmpFile.Name() - defer func() { - err := i.tmpFile.Close() - if err != nil { - logger.Errorf("failed to close %q %s", tmpPath, err) - } - err = os.Remove(tmpPath) - if err != nil { - logger.Errorf("failed to remove %q after use %s", tmpPath, err) - } - - }() - - hash := sha256.New() - var writer io.Writer - if i.progress == nil { - writer = io.MultiWriter(i.tmpFile, hash) - } else { - dlURL, _ := md.DownloadURL(imageDownloadURL) - progWriter := &progressWriter{ - url: dlURL.String(), - callback: i.progress, - maxBytes: uint64(md.Size), - total: 0, - } - writer = io.MultiWriter(i.tmpFile, hash, progWriter) - } - _, err := io.Copy(writer, r) - if err != nil { - i.cleanup() - return errors.Trace(err) - } - - result := fmt.Sprintf("%x", hash.Sum(nil)) - if result != md.SHA256 { - i.cleanup() - return errors.Errorf( - "hash sum mismatch for %s: %s != %s", i.tmpFile.Name(), result, md.SHA256) - } - - // TODO(jam): 2017-03-19 If this is slow, maybe we want to add a progress step for it, rather than only - // indicating download progress. - output, err := i.runCmd( - "", "qemu-img", "convert", "-f", "qcow2", tmpPath, i.FilePath) - logger.Debugf("qemu-image convert output: %s", output) - if err != nil { - i.cleanupAll() - return errors.Trace(err) - } - return nil -} - -// cleanup attempts to close and remove the tempfile download image. It can be -// called if things don't work out. E.g. sha256 mismatch, incorrect size... -func (i *Image) cleanup() { - if err := i.tmpFile.Close(); err != nil { - logger.Debugf("%s", err.Error()) - } - - if err := os.Remove(i.tmpFile.Name()); err != nil { - logger.Debugf("got %q removing %q", err.Error(), i.tmpFile.Name()) - } -} - -// cleanupAll cleans up the possible backing file as well. -func (i *Image) cleanupAll() { - i.cleanup() - err := os.Remove(i.FilePath) - if err != nil { - logger.Debugf("got %q removing %q", err.Error(), i.FilePath) - } -} - -func newDefaultFetcher(md *imagedownloads.Metadata, imageDownloadURL string, pathfinder pathfinderFunc, callback ProgressCallback) (*fetcher, error) { - i, err := newImage(md, imageDownloadURL, pathfinder, callback) - if err != nil { - return nil, errors.Trace(err) - } - dlURL, err := md.DownloadURL(imageDownloadURL) - if err != nil { - return nil, errors.Trace(err) - } - req, err := http.NewRequest("GET", dlURL.String(), nil) - if err != nil { - return nil, errors.Trace(err) - } - client := &http.Client{} - return &fetcher{metadata: md, image: i, client: client, req: req, imageDownloadURL: imageDownloadURL}, nil -} - -func newImage(md *imagedownloads.Metadata, imageDownloadURL string, pathfinder pathfinderFunc, callback ProgressCallback) (*Image, error) { - // Setup names and paths. - dlURL, err := md.DownloadURL(imageDownloadURL) - if err != nil { - return nil, errors.Trace(err) - } - baseDir := pathfinder(paths.CurrentOS()) - - // Closing this is deferred in Image.write. - fh, err := os.CreateTemp("", fmt.Sprintf("juju-kvm-%s-", path.Base(dlURL.String()))) - if err != nil { - return nil, errors.Trace(err) - } - - return &Image{ - FilePath: filepath.Join( - baseDir, kvm, guestDir, backingFileName(md.Version, md.Arch)), - tmpFile: fh, - runCmd: run, - progress: callback, - }, nil -} - -func backingFileName(version, arch string) string { - return fmt.Sprintf("%s-%s-backing-file.qcow", version, arch) -} diff --git a/internal/container/kvm/sync_internal_test.go b/internal/container/kvm/sync_internal_test.go deleted file mode 100644 index 6dc84cf40c9..00000000000 --- a/internal/container/kvm/sync_internal_test.go +++ /dev/null @@ -1,312 +0,0 @@ -// Copyright 2016 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package kvm - -import ( - "fmt" - "io" - "net/http" - "net/http/httptest" - "os" - "path" - "strings" - "time" - - "github.com/juju/clock/testclock" - "github.com/juju/errors" - "github.com/juju/testing" - jc "github.com/juju/testing/checkers" - gc "gopkg.in/check.v1" - - "github.com/juju/juju/core/paths" - "github.com/juju/juju/environs/imagedownloads" -) - -// syncInternalSuite is gocheck boilerplate. -type syncInternalSuite struct { - testing.IsolationSuite -} - -var _ = gc.Suite(&syncInternalSuite{}) - -const imageContents = "fake img file" - -func (syncInternalSuite) TestFetcher(c *gc.C) { - ts := newTestServer() - defer ts.Close() - - md := newTestMetadata() - - tmpdir, pathfinder, ok := newTmpdir() - if !ok { - c.Fatal("failed to setup temp dir in test") - } - defer func() { - err := os.RemoveAll(tmpdir) - if err != nil { - c.Errorf("got error %q when removing tmpdir %q", - err.Error(), - tmpdir) - } - }() - - fetcher, err := newDefaultFetcher(md, ts.URL, pathfinder, nil) - c.Assert(err, jc.ErrorIsNil) - - // setup a fake command runner. - stub := runStub{} - fetcher.image.runCmd = stub.Run - - err = fetcher.Fetch() - c.Assert(err, jc.ErrorIsNil) - - _, err = os.Stat(fetcher.image.tmpFile.Name()) - c.Check(os.IsNotExist(err), jc.IsTrue) - - // Check that our call was made as expected. - c.Assert(stub.Calls(), gc.HasLen, 1) - c.Assert(stub.Calls()[0], gc.Matches, " qemu-img convert -f qcow2 .*/juju-kvm-server.img-.* .*/guests/version-archless-backing-file.qcow") - -} - -func (syncInternalSuite) TestFetcherWriteFails(c *gc.C) { - ts := newTestServer() - defer ts.Close() - - md := newTestMetadata() - - tmpdir, pathfinder, ok := newTmpdir() - if !ok { - c.Fatal("failed to setup temp dir in test") - } - defer func() { - err := os.RemoveAll(tmpdir) - if err != nil { - c.Errorf("got error %q when removing tmpdir %q", - err.Error(), - tmpdir) - } - }() - - fetcher, err := newDefaultFetcher(md, ts.URL, pathfinder, nil) - c.Assert(err, jc.ErrorIsNil) - - // setup a fake command runner. - stub := runStub{err: errors.Errorf("boom")} - fetcher.image.runCmd = stub.Run - - // Make sure we got the error. - err = fetcher.Fetch() - c.Assert(err, gc.ErrorMatches, "boom") - - // Check that our call was made as expected. - c.Assert(stub.Calls(), gc.HasLen, 1) - c.Assert(stub.Calls()[0], gc.Matches, " qemu-img convert -f qcow2 .*/juju-kvm-server.img-.* .*/guests/version-archless-backing-file.qcow") - -} - -func (syncInternalSuite) TestFetcherInvalidSHA(c *gc.C) { - ts := newTestServer() - defer ts.Close() - - md := newTestMetadata() - md.SHA256 = "invalid" - - tmpdir, pathfinder, ok := newTmpdir() - if !ok { - c.Fatal("failed to setup temp dir in test") - } - defer func() { - err := os.RemoveAll(tmpdir) - if err != nil { - c.Errorf("got error %q when removing tmpdir %q", - err.Error(), - tmpdir) - } - }() - - fetcher, err := newDefaultFetcher(md, ts.URL, pathfinder, nil) - c.Assert(err, jc.ErrorIsNil) - - err = fetcher.Fetch() - c.Assert(err, gc.ErrorMatches, "hash sum mismatch for /tmp/juju-kvm-.*") -} - -func (syncInternalSuite) TestFetcherNotFound(c *gc.C) { - ts := newTestServer() - defer ts.Close() - - md := newTestMetadata() - md.Path = "not-there" - - tmpdir, pathfinder, ok := newTmpdir() - if !ok { - c.Fatal("failed to setup temp dir in test") - } - defer func() { - err := os.RemoveAll(tmpdir) - if err != nil { - c.Errorf("got error %q when removing tmpdir %q", - err.Error(), - tmpdir) - } - }() - - fetcher, err := newDefaultFetcher(md, ts.URL, pathfinder, nil) - c.Assert(err, jc.ErrorIsNil) - - err = fetcher.Fetch() - c.Check(err, jc.ErrorIs, errors.NotFound) - c.Assert(err, gc.ErrorMatches, `got 404 fetching image "not-there" not found`) -} - -func newTestMetadata() *imagedownloads.Metadata { - return &imagedownloads.Metadata{ - Arch: "archless", - Release: "spammy", - Version: "version", - FType: "ftype", - SHA256: "5e8467e6732923e74de52ef60134ba747aeeb283812c60f69b67f4f79aca1475", - Path: "server.img", - Size: int64(len(imageContents)), - } -} - -func newTestServer() *httptest.Server { - mtime := time.Unix(1000, 0).UTC() - imageFile := &fakeFileInfo{ - basename: "series-image.img", - modtime: mtime, - contents: imageContents, - } - fs := fakeFS{ - "/": &fakeFileInfo{ - dir: true, - ents: []*fakeFileInfo{imageFile}, - }, - "/server.img": imageFile, - } - return httptest.NewServer(http.FileServer(fs)) -} - -// newTmpdir creates a tmpdir and returns pathfinder func that returns the -// tmpdir. -func newTmpdir() (string, pathfinderFunc, bool) { - td, err := os.MkdirTemp("", "juju-test-kvm-internalSuite") - if err != nil { - return "", nil, false - } - pathfinder := func(_ paths.OS) string { return td } - return td, pathfinder, true -} - -type fakeFileInfo struct { - dir bool - basename string - modtime time.Time - ents []*fakeFileInfo - contents string - err error -} - -func (f *fakeFileInfo) Name() string { return f.basename } -func (f *fakeFileInfo) Sys() interface{} { return nil } -func (f *fakeFileInfo) ModTime() time.Time { return f.modtime } -func (f *fakeFileInfo) IsDir() bool { return f.dir } -func (f *fakeFileInfo) Size() int64 { return int64(len(f.contents)) } -func (f *fakeFileInfo) Mode() os.FileMode { - if f.dir { - return 0755 | os.ModeDir - } - return 0644 -} - -type fakeFile struct { - io.ReadSeeker - fi *fakeFileInfo - path string // as opened - entpos int -} - -func (f *fakeFile) Close() error { return nil } -func (f *fakeFile) Stat() (os.FileInfo, error) { return f.fi, nil } -func (f *fakeFile) Readdir(count int) ([]os.FileInfo, error) { - if !f.fi.dir { - return nil, os.ErrInvalid - } - var fis []os.FileInfo - - limit := f.entpos + count - if count <= 0 || limit > len(f.fi.ents) { - limit = len(f.fi.ents) - } - for ; f.entpos < limit; f.entpos++ { - fis = append(fis, f.fi.ents[f.entpos]) - } - - if len(fis) == 0 && count > 0 { - return fis, io.EOF - } - return fis, nil -} - -type fakeFS map[string]*fakeFileInfo - -func (fs fakeFS) Open(name string) (http.File, error) { - name = path.Clean(name) - f, ok := fs[name] - if !ok { - return nil, os.ErrNotExist - } - if f.err != nil { - return nil, f.err - } - return &fakeFile{ReadSeeker: strings.NewReader(f.contents), fi: f, path: name}, nil -} - -type progressWriterSuite struct { - testing.IsolationSuite -} - -var _ = gc.Suite(&progressWriterSuite{}) - -func (s *progressWriterSuite) TestOnlyPercentChanges(c *gc.C) { - cbLog := []string{} - loggingCB := func(msg string) { - cbLog = append(cbLog, msg) - } - clock := testclock.NewClock(time.Date(2007, 1, 1, 10, 20, 30, 1234, time.UTC)) - // We are using clock to actually measure time, not trigger an event, which - // causes the testclock.Clock to think we're doing something wrong, so we - // just create one waiter that we'll otherwise ignore. - ignored := clock.After(10 * time.Second) - _ = ignored - writer := progressWriter{ - callback: loggingCB, - url: "http://host/path", - total: 0, - maxBytes: 100 * 1024 * 1024, // 100 MB - clock: clock, - } - content := make([]byte, 50*1024) - // Start the clock before the first tick, that way every tick represents - // exactly 1ms and 50kiB written. - now := clock.Now() - writer.startTime = &now - for i := 0; i < 2048; i++ { - clock.Advance(time.Millisecond) - n, err := writer.Write(content) - c.Assert(err, jc.ErrorIsNil) - c.Check(n, gc.Equals, len(content)) - } - expectedCB := []string{} - for i := 1; i <= 100; i++ { - // We tick every 1ms and add 50kiB each time, which is - // 50*1024 *1000/ 1000/1000 = 51MB/s - expectedCB = append(expectedCB, fmt.Sprintf("copying http://host/path %d%% (51 MB/s)", i)) - } - // There are 2048 calls to Write, but there should only be 100 calls to progress update - c.Check(len(cbLog), gc.Equals, 100) - c.Check(cbLog, gc.DeepEquals, expectedCB) -} diff --git a/internal/container/kvm/sync_test.go b/internal/container/kvm/sync_test.go deleted file mode 100644 index abfe4cfe31f..00000000000 --- a/internal/container/kvm/sync_test.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2016 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package kvm_test - -import ( - "github.com/juju/errors" - "github.com/juju/testing" - jc "github.com/juju/testing/checkers" - gc "gopkg.in/check.v1" - - "github.com/juju/juju/environs/imagedownloads" - "github.com/juju/juju/internal/container/kvm" -) - -// cacheSuite is gocheck boilerplate. -type cacheSuite struct { - testing.IsolationSuite -} - -// _ is gocheck boilerplate. -var _ = gc.Suite(&cacheSuite{}) - -func (cacheSuite) TestSyncOnerErrors(c *gc.C) { - o := fakeParams{FakeData: nil, Err: errors.New("oner failed")} - u := fakeFetcher{} - got := kvm.Sync(o, u, "", nil) - c.Assert(got, gc.ErrorMatches, "oner failed") -} - -func (cacheSuite) TestSyncOnerExists(c *gc.C) { - o := fakeParams{ - FakeData: nil, - Err: errors.AlreadyExistsf("exists")} - u := fakeFetcher{} - got := kvm.Sync(o, u, "", nil) - c.Assert(got, jc.ErrorIsNil) -} - -func (cacheSuite) TestSyncUpdaterErrors(c *gc.C) { - o := fakeParams{FakeData: &imagedownloads.Metadata{}, Err: nil} - u := fakeFetcher{Err: errors.New("updater failed")} - got := kvm.Sync(o, u, "", nil) - c.Assert(got, gc.ErrorMatches, "updater failed") -} - -func (cacheSuite) TestSyncSucceeds(c *gc.C) { - o := fakeParams{FakeData: &imagedownloads.Metadata{}} - u := fakeFetcher{} - got := kvm.Sync(o, u, "", nil) - c.Assert(got, jc.ErrorIsNil) -} - -type fakeParams struct { - FakeData *imagedownloads.Metadata - Err error -} - -func (f fakeParams) One() (*imagedownloads.Metadata, error) { - if f.Err != nil { - return nil, f.Err - } - return f.FakeData, nil -} - -type fakeFetcher struct { - // Used to return an error - Err error -} - -func (f fakeFetcher) Fetch() error { - if f.Err != nil { - return f.Err - } - return nil -} - -func (f fakeFetcher) Close() { - return -} diff --git a/internal/container/kvm/testing/test.go b/internal/container/kvm/testing/test.go deleted file mode 100644 index 19633f2c36a..00000000000 --- a/internal/container/kvm/testing/test.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2013 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -// Functions defined in this file should *ONLY* be used for testing. These -// functions are exported for testing purposes only, and shouldn't be called -// from code that isn't in a test file. - -package testing - -import ( - gc "gopkg.in/check.v1" - - "github.com/juju/juju/internal/container" - "github.com/juju/juju/internal/container/kvm" - "github.com/juju/juju/internal/container/kvm/mock" - "github.com/juju/juju/testing" -) - -// TestSuite replaces the kvm factory that the manager uses with a mock -// implementation. -type TestSuite struct { - testing.BaseSuite - ContainerFactory mock.ContainerFactory - ContainerDir string - RemovedDir string -} - -func (s *TestSuite) SetUpTest(c *gc.C) { - s.BaseSuite.SetUpTest(c) - s.ContainerDir = c.MkDir() - s.PatchValue(&container.ContainerDir, s.ContainerDir) - s.RemovedDir = c.MkDir() - s.PatchValue(&container.RemovedContainerDir, s.RemovedDir) - s.ContainerFactory = mock.MockFactory() - s.PatchValue(&kvm.KVMObjectFactory, s.ContainerFactory) -} diff --git a/internal/container/kvm/wrappedcmds.go b/internal/container/kvm/wrappedcmds.go deleted file mode 100644 index d2177ebe178..00000000000 --- a/internal/container/kvm/wrappedcmds.go +++ /dev/null @@ -1,505 +0,0 @@ -// Copyright 2013-2016 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package kvm - -// This file contains wrappers around the following executables: -// genisoimage -// qemu-img -// virsh -// Those executables are found in the following packages: -// genisoimage -// libvirt-bin -// qemu-utils -// -// These executables provide Juju's interface to dealing with kvm containers. -// They are the means by which we start, stop and list running containers on -// the host - -import ( - "encoding/xml" - "fmt" - "os" - "path/filepath" - "regexp" - "strings" - - "github.com/juju/errors" - "gopkg.in/yaml.v2" - - "github.com/juju/juju/core/arch" - "github.com/juju/juju/core/paths" - "github.com/juju/juju/internal/container/kvm/libvirt" - "github.com/juju/juju/internal/uuid" -) - -const ( - virsh = "virsh" - guestDir = "guests" - poolName = "juju-pool" - kvm = "kvm" - metadata = "meta-data" - userdata = "user-data" - networkconfig = "network-config" - - // This path is only valid on ubuntu, and xenial at this point. - // TODO(ro) 2017-01-20 Determine if we will support trusty and update this - // as necessary if so. It seems it will require some serious acrobatics to - // get trusty to work properly and that may be out of scope for juju. - nvramCode = "/usr/share/AAVMF/AAVMF_CODE.fd" -) - -var ( - // The regular expression for breaking up the results of 'virsh list' - // (?m) - specify that this is a multi-line regex - // first part is the opaque identifier we don't care about - // then the hostname, and lastly the status. - machineListPattern = regexp.MustCompile(`(?m)^\s+\d+\s+(?P[-\w]+)\s+(?P.+)\s*$`) -) - -// CreateMachineParams Implements libvirt.domainParams. -type CreateMachineParams struct { - Hostname string - Version string - UserDataFile string - NetworkConfigData string - Memory uint64 - CpuCores uint64 - RootDisk uint64 - Interfaces []libvirt.InterfaceInfo - - disks []libvirt.DiskInfo - findPath pathfinderFunc - - runCmd runFunc - runCmdAsRoot runFunc - arch string -} - -// Arch returns the architecture to be used. -func (p CreateMachineParams) Arch() string { - if p.arch != "" { - return p.arch - } - return arch.HostArch() -} - -// Loader is the path to the binary firmware blob used in UEFI booting. At the -// time of this writing only ARM64 requires this to run. -func (p CreateMachineParams) Loader() string { - return nvramCode -} - -// Host implements libvirt.domainParams. -func (p CreateMachineParams) Host() string { - return p.Hostname -} - -// CPUs implements libvirt.domainParams. -func (p CreateMachineParams) CPUs() uint64 { - if p.CpuCores == 0 { - return 1 - } - return p.CpuCores -} - -// DiskInfo implements libvirt.domainParams. -func (p CreateMachineParams) DiskInfo() []libvirt.DiskInfo { - return p.disks -} - -// RAM implements libvirt.domainParams. -func (p CreateMachineParams) RAM() uint64 { - if p.Memory == 0 { - return 512 - } - return p.Memory -} - -// NetworkInfo implements libvirt.domainParams. -func (p CreateMachineParams) NetworkInfo() []libvirt.InterfaceInfo { - return p.Interfaces -} - -// ValidateDomainParams implements libvirt.domainParams. -func (p CreateMachineParams) ValidateDomainParams() error { - if p.Hostname == "" { - return errors.Errorf("missing required hostname") - } - if len(p.disks) < 2 { - // We need at least the drive and the data source disk. - return errors.Errorf("got %d disks, need at least 2", len(p.disks)) - } - var ds, fs bool - for _, d := range p.disks { - if d.Driver() == "qcow2" { - fs = true - } - if d.Driver() == "raw" { - ds = true - } - } - if !ds { - return errors.Trace(errors.Errorf("missing data source disk")) - } - if !fs { - return errors.Trace(errors.Errorf("missing system disk")) - } - return nil -} - -// diskInfo is type for implementing libvirt.DiskInfo. -type diskInfo struct { - driver, source string -} - -// Driver implements libvirt.DiskInfo. -func (d diskInfo) Driver() string { - return d.driver -} - -// Source implements libvirt.Source. -func (d diskInfo) Source() string { - return d.source -} - -// CreateMachine creates a virtual machine and starts it. -func CreateMachine(params CreateMachineParams) error { - if params.Hostname == "" { - return fmt.Errorf("hostname is required") - } - - setDefaults(¶ms) - - templateDir := filepath.Dir(params.UserDataFile) - - err := writeMetadata(templateDir) - if err != nil { - return errors.Annotate(err, "failed to write instance metadata") - } - - dsPath, err := writeDataSourceVolume(params) - if err != nil { - return errors.Annotatef(err, "failed to write data source volume for %q", params.Host()) - } - - imgPath, err := writeRootDisk(params) - if err != nil { - return errors.Annotatef(err, "failed to write root volume for %q", params.Host()) - } - - params.disks = append(params.disks, diskInfo{source: imgPath, driver: "qcow2"}) - params.disks = append(params.disks, diskInfo{source: dsPath, driver: "raw"}) - - domainPath, err := writeDomainXML(templateDir, params) - if err != nil { - return errors.Annotatef(err, "failed to write domain xml for %q", params.Host()) - } - - out, err := params.runCmdAsRoot("", virsh, "define", domainPath) - if err != nil { - return errors.Annotatef(err, "failed to define the domain for %q from %s:%s", params.Host(), domainPath, out) - } - logger.Debugf("created domain: %s", out) - - out, err = params.runCmdAsRoot("", virsh, "start", params.Host()) - if err != nil { - return errors.Annotatef(err, "failed to start domain %q:%s", params.Host(), out) - } - logger.Debugf("started domain: %s", out) - - return err -} - -// Setup the default values for params. -func setDefaults(p *CreateMachineParams) { - if p.findPath == nil { - p.findPath = paths.DataDir - } - if p.runCmd == nil { - p.runCmd = runAsLibvirt - } - if p.runCmdAsRoot == nil { - p.runCmdAsRoot = run - } -} - -// DestroyMachine destroys the virtual machine represented by the kvmContainer. -func DestroyMachine(c *kvmContainer) error { - if c.runCmd == nil { - c.runCmd = run - } - if c.pathfinder == nil { - c.pathfinder = paths.DataDir - } - - // We don't return errors for virsh commands because it is possible that we - // didn't succeed in creating the domain. Additionally, we want all the - // commands to run. If any fail it is certainly because the thing we're - // trying to remove wasn't created. However, we still want to try removing - // all the parts. The exception here is getting the guestBase, if that - // fails we return the error because we cannot continue without it. - - _, err := c.runCmd("", virsh, "destroy", c.Name()) - if err != nil { - logger.Infof("`%s destroy %s` failed: %q", virsh, c.Name(), err) - } - - // The nvram flag here removes the pflash drive for us. There is also a - // `remove-all-storage` flag, but it is unclear if that would also remove - // the backing store which we don't want to do. So we remove those manually - // after undefining. - _, err = c.runCmd("", virsh, "undefine", "--nvram", c.Name()) - if err != nil { - logger.Infof("`%s undefine --nvram %s` failed: %q", virsh, c.Name(), err) - } - guestBase, err := guestPath(c.pathfinder) - if err != nil { - return errors.Trace(err) - } - err = os.Remove(filepath.Join(guestBase, fmt.Sprintf("%s.qcow", c.Name()))) - if err != nil { - logger.Errorf("failed to remove system disk for %q: %s", c.Name(), err) - } - err = os.Remove(filepath.Join(guestBase, fmt.Sprintf("%s-ds.iso", c.Name()))) - if err != nil { - logger.Errorf("failed to remove cloud-init data disk for %q: %s", c.Name(), err) - } - - return nil -} - -// AutostartMachine indicates that the virtual machines should automatically -// restart when the host restarts. -func AutostartMachine(c *kvmContainer) error { - if c.runCmd == nil { - c.runCmd = run - } - _, err := c.runCmd("", virsh, "autostart", c.Name()) - return errors.Annotatef(err, "failed to autostart domain %q", c.Name()) -} - -// ListMachines returns a map of machine name to state, where state is one of: -// running, idle, paused, shutdown, shut off, crashed, dying, pmsuspended. -func ListMachines(runCmd runFunc) (map[string]string, error) { - if runCmd == nil { - runCmd = run - } - - output, err := runCmd("", virsh, "-q", "list", "--all") - if err != nil { - return nil, err - } - // Split the output into lines. - // Regex matching is the easiest way to match the lines. - // id hostname status - // separated by whitespace, with whitespace at the start too. - result := make(map[string]string) - for _, s := range machineListPattern.FindAllStringSubmatchIndex(output, -1) { - hostnameAndStatus := machineListPattern.ExpandString(nil, "$hostname $status", output, s) - parts := strings.SplitN(string(hostnameAndStatus), " ", 2) - result[parts[0]] = parts[1] - } - return result, nil -} - -// guestPath returns the path to the guest directory from the given -// pathfinder. -func guestPath(pathfinder pathfinderFunc) (string, error) { - baseDir := pathfinder(paths.CurrentOS()) - return filepath.Join(baseDir, kvm, guestDir), nil -} - -// writeDataSourceVolume creates a data source image for cloud init. -func writeDataSourceVolume(params CreateMachineParams) (string, error) { - templateDir := filepath.Dir(params.UserDataFile) - - if err := writeMetadata(templateDir); err != nil { - return "", errors.Trace(err) - } - - if err := writeNetworkConfig(params, templateDir); err != nil { - return "", errors.Trace(err) - } - - // Creating a working DS volume was a bit troublesome for me. I finally - // found the details in the docs. - // http://cloudinit.readthedocs.io/en/latest/topics/datasources/nocloud.html - // - // The arguments passed to create the DS volume for NoCloud must be - // `user-data` and `meta-data`. So the `cloud-init` file we generate won't - // work. Also, they must be exactly `user-data` and `meta-data` with no - // path beforehand, so `$JUJUDIR/containers/juju-someid-0/user-data` also - // fails. - // - // Furthermore, symlinks aren't followed by NoCloud. So we rename our - // cloud-init file to user-data. We could change the output name in - // juju/cloudconfig/containerinit/container_userdata.go:WriteUserData but - // who knows what that will break. - userDataPath := filepath.Join(templateDir, userdata) - if err := os.Rename(params.UserDataFile, userDataPath); err != nil { - return "", errors.Trace(err) - } - - // Create data the source volume outputting the iso image to the guests - // (AKA libvirt storage pool) directory. - guestBase, err := guestPath(params.findPath) - if err != nil { - return "", errors.Trace(err) - } - dsPath := filepath.Join(guestBase, fmt.Sprintf("%s-ds.iso", params.Host())) - - // Use the template path as the working directory. - // This allows us to run the command with user-data and meta-data as - // relative paths to appease the NoCloud script. - out, err := params.runCmd( - templateDir, - "genisoimage", - "-output", dsPath, - "-volid", "cidata", - "-joliet", "-rock", - userdata, - metadata, - networkconfig) - if err != nil { - return "", errors.Trace(err) - } - logger.Debugf("create ds image: %s", out) - - return dsPath, nil -} - -// writeDomainXML writes out the configuration required to create a new guest -// domain. -func writeDomainXML(templateDir string, p CreateMachineParams) (string, error) { - domainPath := filepath.Join(templateDir, fmt.Sprintf("%s.xml", p.Host())) - dom, err := libvirt.NewDomain(p) - if err != nil { - return "", errors.Trace(err) - } - - ml, err := xml.MarshalIndent(&dom, "", " ") - if err != nil { - return "", errors.Trace(err) - } - - f, err := os.Create(domainPath) - if err != nil { - return "", errors.Trace(err) - } - defer func() { - err = f.Close() - if err != nil { - logger.Debugf("failed defer %q", errors.Trace(err)) - } - }() - - _, err = f.Write(ml) - if err != nil { - return "", errors.Trace(err) - } - - return domainPath, nil -} - -// writeMetadata writes out a metadata file with an UUID instance-id. The -// meta-data file is used in the data source image along with user-data nee -// cloud-init. `instance-id` is a required field in meta-data. It is what is -// used to determine if this is the first boot, thereby whether or not to run -// cloud-init. -// See: http://cloudinit.readthedocs.io/en/latest/topics/datasources/nocloud.html -func writeMetadata(dir string) error { - data := fmt.Sprintf(`{"instance-id": "%s"}`, uuid.MustNewUUID()) - f, err := os.Create(filepath.Join(dir, metadata)) - if err != nil { - return errors.Trace(err) - } - defer func() { - if err = f.Close(); err != nil { - logger.Errorf("failed to close %q %s", f.Name(), err) - } - }() - _, err = f.WriteString(data) - if err != nil { - return errors.Trace(err) - } - return nil -} - -func writeNetworkConfig(params CreateMachineParams, dir string) error { - f, err := os.Create(filepath.Join(dir, networkconfig)) - if err != nil { - return errors.Trace(err) - } - defer func() { - if err = f.Close(); err != nil { - logger.Errorf("failed to close %q %s", f.Name(), err) - } - }() - _, err = f.WriteString(params.NetworkConfigData) - if err != nil { - return errors.Trace(err) - } - return nil -} - -// writeRootDisk writes out the root disk for the container. This creates a -// system disk backed by our shared series/arch backing store. -func writeRootDisk(params CreateMachineParams) (string, error) { - guestBase, err := guestPath(params.findPath) - if err != nil { - return "", errors.Trace(err) - } - imgPath := filepath.Join(guestBase, fmt.Sprintf("%s.qcow", params.Host())) - backingPath := filepath.Join( - guestBase, - backingFileName(params.Version, params.Arch())) - - cmdArgs := []string{ - "create", - "-b", backingPath, - } - - // Contrary to their extension, the backing files fetched via - // simple stream are raw and not qcow2 images. - cmdArgs = append(cmdArgs, "-F", "raw") - - cmdArgs = append(cmdArgs, - "-f", "qcow2", - imgPath, - fmt.Sprintf("%dG", params.RootDisk), - ) - - out, err := params.runCmd("", "qemu-img", cmdArgs...) - logger.Debugf("create root image: %s", out) - if err != nil { - return "", errors.Trace(err) - } - - return imgPath, nil -} - -// pool info parses and returns the output of `virsh pool-info `. -func poolInfo(runCmd runFunc) (*libvirtPool, error) { - output, err := runCmd("", virsh, "pool-info", poolName) - if err != nil { - logger.Debugf("pool %q doesn't appear to exist: %s", poolName, err) - return nil, nil - } - - p := &libvirtPool{} - err = yaml.Unmarshal([]byte(output), p) - if err != nil { - logger.Errorf("failed to unmarshal info %s", err) - return nil, errors.Trace(err) - } - return p, nil -} - -// libvirtPool represents the guest pool information we care about. Additional -// fields are available but ignored here. -type libvirtPool struct { - Name string `yaml:"Name"` - State string `yaml:"State"` - Autostart string `yaml:"Autostart"` -} diff --git a/internal/container/kvm/wrappedcmds_internal_test.go b/internal/container/kvm/wrappedcmds_internal_test.go deleted file mode 100644 index 647982bf1c5..00000000000 --- a/internal/container/kvm/wrappedcmds_internal_test.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2016 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package kvm - -import ( - "errors" - "os" - "path/filepath" - - "github.com/juju/testing" - jc "github.com/juju/testing/checkers" - gc "gopkg.in/check.v1" - - "github.com/juju/juju/internal/container/kvm/libvirt" -) - -type libvirtInternalSuite struct { - testing.IsolationSuite -} - -var _ = gc.Suite(&libvirtInternalSuite{}) - -func (libvirtInternalSuite) TestWriteMetadata(c *gc.C) { - d := c.MkDir() - - err := writeMetadata(d) - c.Check(err, jc.ErrorIsNil) - b, err := os.ReadFile(filepath.Join(d, metadata)) - c.Check(err, jc.ErrorIsNil) - c.Assert(string(b), gc.Matches, `{"instance-id": ".*-.*-.*-.*"}`) -} - -func (libvirtInternalSuite) TestWriteDomainXMLSucceeds(c *gc.C) { - d := c.MkDir() - - stub := &runStub{} - - p := CreateMachineParams{ - Hostname: "host00", - runCmd: stub.Run, - disks: []libvirt.DiskInfo{ - diskInfo{ - source: "/path-ds", - driver: "raw"}, - diskInfo{ - source: "/path", - driver: "qcow2"}, - }, - } - - got, err := writeDomainXML(d, p) - c.Assert(err, jc.ErrorIsNil) - c.Assert(got, gc.Matches, `/tmp/check-.*/\d+/host00.xml`) -} - -func (libvirtInternalSuite) TestWriteDomainXMLMissingValidSystemDisk(c *gc.C) { - d := c.MkDir() - - stub := &runStub{} - - p := CreateMachineParams{ - Hostname: "host00", - runCmd: stub.Run, - disks: []libvirt.DiskInfo{ - diskInfo{ - source: "/path-ds", - driver: "raw"}, - diskInfo{ - source: "/path", - driver: "raw"}, - }, - } - - got, err := writeDomainXML(d, p) - c.Assert(err, gc.ErrorMatches, "missing system disk") - c.Assert(got, gc.Matches, "") -} - -func (libvirtInternalSuite) TestWriteDomainXMLMissingOneDisk(c *gc.C) { - d := c.MkDir() - - stub := &runStub{} - - p := CreateMachineParams{ - Hostname: "host00", - runCmd: stub.Run, - disks: []libvirt.DiskInfo{ - diskInfo{ - source: "/path-ds", - driver: "raw"}, - }, - } - - got, err := writeDomainXML(d, p) - c.Assert(err, gc.ErrorMatches, "got 1 disks, need at least 2") - c.Assert(got, gc.Matches, "") -} - -func (libvirtInternalSuite) TestWriteDomainXMLMissingBothDisk(c *gc.C) { - d := c.MkDir() - - stub := &runStub{} - - p := CreateMachineParams{ - Hostname: "host00", - runCmd: stub.Run, - disks: []libvirt.DiskInfo{}, - } - - got, err := writeDomainXML(d, p) - c.Assert(err, gc.ErrorMatches, "got 0 disks, need at least 2") - c.Assert(got, gc.Matches, "") -} - -func (libvirtInternalSuite) TestWriteDomainXMLNoHostname(c *gc.C) { - d := c.MkDir() - - stub := &runStub{} - - p := CreateMachineParams{ - runCmd: stub.Run, - disks: []libvirt.DiskInfo{ - diskInfo{ - source: "/path-ds", - driver: "raw"}, - diskInfo{ - source: "/path", - driver: "qcow"}, - }, - } - - got, err := writeDomainXML(d, p) - c.Assert(err, gc.ErrorMatches, "missing required hostname") - c.Assert(got, gc.Matches, "") -} - -func (libvirtInternalSuite) TestPoolInfoSuccess(c *gc.C) { - output := ` -Name: juju-pool -UUID: 06ebee2d-6bd0-4f47-a7dc-dea555fdaa3b -State: running -Persistent: yes -Autostart: yes -Capacity: 35.31 GiB -Allocation: 3.54 GiB -Available: 31.77 GiB -` - stub := runStub{output: output} - got, err := poolInfo(stub.Run) - c.Check(err, jc.ErrorIsNil) - c.Assert(got, jc.DeepEquals, &libvirtPool{Name: "juju-pool", Autostart: "yes", State: "running"}) - -} - -func (libvirtInternalSuite) TestPoolInfoNoPool(c *gc.C) { - stub := runStub{err: errors.New("boom")} - got, err := poolInfo(stub.Run) - c.Assert(err, jc.ErrorIsNil) - c.Assert(got, gc.IsNil) -} diff --git a/internal/container/kvm/wrappedcmds_test.go b/internal/container/kvm/wrappedcmds_test.go deleted file mode 100644 index 84ced81353c..00000000000 --- a/internal/container/kvm/wrappedcmds_test.go +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright 2014-2016 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package kvm_test - -import ( - "errors" - "os" - "path/filepath" - - "github.com/juju/testing" - jc "github.com/juju/testing/checkers" - gc "gopkg.in/check.v1" - - "github.com/juju/juju/core/paths" - "github.com/juju/juju/environs/imagedownloads" - "github.com/juju/juju/environs/simplestreams" - sstesting "github.com/juju/juju/environs/simplestreams/testing" - "github.com/juju/juju/internal/container/kvm" - coretesting "github.com/juju/juju/testing" -) - -type LibVertSuite struct { - coretesting.BaseSuite - ContainerDir string - RemovedDir string -} - -var _ = gc.Suite(&LibVertSuite{}) - -func (s *LibVertSuite) SetUpTest(c *gc.C) { - s.BaseSuite.SetUpTest(c) -} - -type testSyncParams struct { - arch, series string - srcFunc func() simplestreams.DataSource - onevalErr error - success bool -} - -func (p testSyncParams) One() (*imagedownloads.Metadata, error) { - if p.success { - return &imagedownloads.Metadata{ - Arch: p.arch, - Release: p.series, - }, nil - } - return nil, p.onevalErr -} - -func (p testSyncParams) sourceURL() (string, error) { - return p.srcFunc().URL("") -} - -// Test that the call to SyncImages utilizes the defined source -func (s *LibVertSuite) TestSyncImagesUtilizesSimpleStreamsSource(c *gc.C) { - - const ( - series = "mocked-series" - arch = "mocked-arch" - source = "mocked-url" - ) - p := testSyncParams{ - arch: arch, - series: series, - srcFunc: func() simplestreams.DataSource { - ss := simplestreams.NewSimpleStreams(sstesting.TestDataSourceFactory()) - return imagedownloads.NewDataSource(ss, source) - }, - success: true, - } - err := kvm.Sync(p, fakeFetcher{}, source, nil) - c.Assert(err, jc.ErrorIsNil) - - url, err := p.sourceURL() - c.Check(err, jc.ErrorIsNil) - c.Check(url, jc.DeepEquals, source+"/") - - res, err := p.One() - c.Check(err, jc.ErrorIsNil) - - c.Check(res.Arch, jc.DeepEquals, arch) - c.Check(res.Release, jc.DeepEquals, series) -} - -// gocheck boilerplate. -type commandWrapperSuite struct { - testing.IsolationSuite -} - -var _ = gc.Suite(&commandWrapperSuite{}) - -func (commandWrapperSuite) TestCreateNoHostname(c *gc.C) { - stub := kvm.NewRunStub("exit before this", nil) - p := kvm.CreateMachineParams{} - err := kvm.CreateMachine(p) - c.Assert(len(stub.Calls()) == 0, jc.IsTrue) - c.Assert(err, gc.ErrorMatches, "hostname is required") -} - -func (s *commandWrapperSuite) TestCreateMachineSuccessOnFocal(c *gc.C) { - tmpDir, err := os.MkdirTemp("", "juju-libvirtSuite-") - c.Check(err, jc.ErrorIsNil) - - want := []string{ - tmpDir + ` genisoimage -output \/tmp\/juju-libvirtSuite-\d+\/kvm\/guests\/host00-ds\.iso -volid cidata -joliet -rock user-data meta-data network-config`, - // On focal, the backing image format must be explicitly specified - // hence the '-F raw' - ` qemu-img create -b \/tmp/juju-libvirtSuite-\d+\/kvm\/guests\/20.04-arm64-backing-file.qcow -F raw -f qcow2 \/tmp\/juju-libvirtSuite-\d+\/kvm\/guests\/host00.qcow 8G`, - ` virsh define \/tmp\/juju-libvirtSuite-\d+\/host00.xml`, - " virsh start host00", - } - - assertCreateMachineSuccess(c, tmpDir, want) -} -func assertCreateMachineSuccess(c *gc.C, tmpDir string, expCommands []string) { - stub := kvm.NewRunStub("success", nil) - - err := os.MkdirAll(filepath.Join(tmpDir, "kvm", "guests"), 0755) - c.Check(err, jc.ErrorIsNil) - cloudInitPath := filepath.Join(tmpDir, "cloud-init") - userDataPath := filepath.Join(tmpDir, "user-data") - networkConfigPath := filepath.Join(tmpDir, "network-config") - err = os.WriteFile(cloudInitPath, []byte("#cloud-init\nEOF\n"), 0755) - c.Assert(err, jc.ErrorIsNil) - - defer func() { - err := os.RemoveAll(tmpDir) - if err != nil { - c.Errorf("failed removing %q in test %s", tmpDir, err) - } - }() - pathfinder := func(_ paths.OS) string { - return tmpDir - } - - hostname := "host00" - params := kvm.CreateMachineParams{ - Hostname: hostname, - Version: "20.04", - UserDataFile: cloudInitPath, - NetworkConfigData: "this-is-network-config", - CpuCores: 1, - RootDisk: 8, - } - - kvm.MakeCreateMachineParamsTestable(¶ms, pathfinder, stub.Run, "arm64") - err = kvm.CreateMachine(params) - c.Assert(err, jc.ErrorIsNil) - - _, err = os.Stat(cloudInitPath) - c.Assert(os.IsNotExist(err), jc.IsTrue) - - b, err := os.ReadFile(userDataPath) - c.Assert(err, jc.ErrorIsNil) - c.Assert(string(b), jc.Contains, "#cloud-init") - - b, err = os.ReadFile(networkConfigPath) - c.Assert(err, jc.ErrorIsNil) - c.Assert(string(b), gc.Equals, "this-is-network-config") - - c.Check(len(stub.Calls()), gc.Equals, len(expCommands)) - for i, cmd := range stub.Calls() { - c.Check(cmd, gc.Matches, expCommands[i]) - } -} - -func (commandWrapperSuite) TestDestroyMachineSuccess(c *gc.C) { - tmpDir, err := os.MkdirTemp("", "juju-libvirtSuite-") - c.Check(err, jc.ErrorIsNil) - guestBase := filepath.Join(tmpDir, "kvm", "guests") - err = os.MkdirAll(guestBase, 0700) - c.Check(err, jc.ErrorIsNil) - - err = os.WriteFile(filepath.Join(guestBase, "aname.qcow"), []byte("diskcontents"), 0700) - c.Check(err, jc.ErrorIsNil) - err = os.WriteFile(filepath.Join(guestBase, "aname-ds.iso"), []byte("diskcontents"), 0700) - c.Check(err, jc.ErrorIsNil) - - pathfinder := func(_ paths.OS) string { - return tmpDir - } - - stub := kvm.NewRunStub("success", nil) - container := kvm.NewTestContainer("aname", stub.Run, pathfinder) - err = kvm.DestroyMachine(container) - c.Assert(err, jc.ErrorIsNil) - c.Assert(stub.Calls(), jc.DeepEquals, []string{ - " virsh destroy aname", - " virsh undefine --nvram aname", - }) -} - -func (commandWrapperSuite) TestDestroyMachineFails(c *gc.C) { - stub := kvm.NewRunStub("", errors.New("Boom")) - container := kvm.NewTestContainer("aname", stub.Run, nil) - err := kvm.DestroyMachine(container) - c.Check(stub.Calls(), jc.DeepEquals, []string{ - " virsh destroy aname", - " virsh undefine --nvram aname", - }) - log := c.GetTestLog() - c.Check(log, jc.Contains, "`virsh destroy aname` failed") - c.Check(log, jc.Contains, "`virsh undefine --nvram aname` failed") - c.Assert(err, jc.ErrorIsNil) - -} - -func (commandWrapperSuite) TestAutostartMachineSuccess(c *gc.C) { - stub := kvm.NewRunStub("success", nil) - container := kvm.NewTestContainer("aname", stub.Run, nil) - err := kvm.AutostartMachine(container) - c.Assert(stub.Calls(), jc.DeepEquals, []string{" virsh autostart aname"}) - c.Assert(err, jc.ErrorIsNil) -} - -func (commandWrapperSuite) TestAutostartMachineFails(c *gc.C) { - stub := kvm.NewRunStub("", errors.New("Boom")) - container := kvm.NewTestContainer("aname", stub.Run, nil) - err := kvm.AutostartMachine(container) - c.Assert(stub.Calls(), jc.DeepEquals, []string{" virsh autostart aname"}) - c.Check(err, gc.ErrorMatches, `failed to autostart domain "aname": Boom`) -} - -func (commandWrapperSuite) TestListMachinesSuccess(c *gc.C) { - output := ` - Id Name State ----------------------------------------------------- - 0 Domain-0 running - 2 ubuntu paused -`[1:] - stub := kvm.NewRunStub(output, nil) - got, err := kvm.ListMachines(stub.Run) - - c.Check(err, jc.ErrorIsNil) - c.Check(stub.Calls(), jc.DeepEquals, []string{" virsh -q list --all"}) - c.Assert(got, jc.DeepEquals, map[string]string{ - "Domain-0": "running", - "ubuntu": "paused", - }) - -} - -func (commandWrapperSuite) TestListMachinesFails(c *gc.C) { - stub := kvm.NewRunStub("", errors.New("Boom")) - got, err := kvm.ListMachines(stub.Run) - c.Check(err, gc.ErrorMatches, "Boom") - c.Check(stub.Calls(), jc.DeepEquals, []string{" virsh -q list --all"}) - c.Assert(got, gc.IsNil) -} diff --git a/internal/database/node.go b/internal/database/node.go index f345f842608..adb2bcb295f 100644 --- a/internal/database/node.go +++ b/internal/database/node.go @@ -248,7 +248,6 @@ func (m *NodeManager) WithPreferredCloudLocalAddressOption(source corenetwork.Co name := nic.Name() if nic.Type() == corenetwork.LoopbackDevice || name == network.DefaultLXDBridge || - name == network.DefaultKVMBridge || name == network.DefaultDockerBridge { continue } diff --git a/internal/network/containerizer/bridgepolicy.go b/internal/network/containerizer/bridgepolicy.go index 319629d7774..5f26a193ff2 100644 --- a/internal/network/containerizer/bridgepolicy.go +++ b/internal/network/containerizer/bridgepolicy.go @@ -13,7 +13,6 @@ import ( "github.com/juju/errors" "github.com/juju/loggo/v2" - "github.com/juju/juju/core/instance" corenetwork "github.com/juju/juju/core/network" "github.com/juju/juju/environs" "github.com/juju/juju/internal/network" @@ -24,7 +23,6 @@ var logger = loggo.GetLogger("juju.network.containerizer") var skippedDeviceNames = set.NewStrings( network.DefaultLXDBridge, - network.DefaultKVMBridge, ) // namedNICsBySpace is a type alias for a map of link-layer devices @@ -529,9 +527,6 @@ func (p *BridgePolicy) PopulateContainerLinkLayerDevices( p.containerNetworkingMethod == "local" { localBridgeName := network.DefaultLXDBridge - if guest.ContainerType() == instance.KVM { - localBridgeName = network.DefaultKVMBridge - } for _, hostDevice := range devicesPerSpace[corenetwork.AlphaSpaceId] { name := hostDevice.Name() diff --git a/internal/network/containerizer/bridgepolicy_test.go b/internal/network/containerizer/bridgepolicy_test.go index ac0ae76050a..bc3c5a7ebac 100644 --- a/internal/network/containerizer/bridgepolicy_test.go +++ b/internal/network/containerizer/bridgepolicy_test.go @@ -87,8 +87,6 @@ func (s *bridgePolicySuite) expectAllDefaultDevices(c *gc.C, ctrl *gomock.Contro s.expectLoopbackNIC(ctrl) // container.DefaultLxdBridge s.expectBridgeDeviceWithIP(ctrl, "lxdbr0", corenetwork.AlphaSpaceId) - // container.DefaultKvmBridge - s.expectBridgeDeviceWithIP(ctrl, "virbr0", corenetwork.AlphaSpaceId) } func (s *bridgePolicySuite) policy() *BridgePolicy { diff --git a/internal/network/network.go b/internal/network/network.go index 1a050017d5d..2c0e728b9f7 100644 --- a/internal/network/network.go +++ b/internal/network/network.go @@ -23,10 +23,6 @@ const UnknownId = "" // DefaultLXDBridge is the bridge that gets used for LXD containers const DefaultLXDBridge = "lxdbr0" -// DefaultKVMBridge is the bridge that is set up by installing libvirt-bin -// Note: we don't import this from 'container' to avoid import loops -const DefaultKVMBridge = "virbr0" - // DefaultDockerBridge is the bridge that is set up by Docker. const DefaultDockerBridge = "docker0" @@ -155,7 +151,6 @@ func gatherBridgeAddresses(bridgeName string, toRemove map[string][]string) { func FilterBridgeAddresses(addresses corenetwork.ProviderAddresses) corenetwork.ProviderAddresses { addressesToRemove := make(map[string][]string) gatherBridgeAddresses(DefaultLXDBridge, addressesToRemove) - gatherBridgeAddresses(DefaultKVMBridge, addressesToRemove) filtered := filterAddrs(addresses, addressesToRemove) logger.Debugf("addresses after filtering: %v", filtered) return filtered diff --git a/internal/network/network_test.go b/internal/network/network_test.go index b23a8c94037..6de119937a3 100644 --- a/internal/network/network_test.go +++ b/internal/network/network_test.go @@ -114,10 +114,6 @@ func (s *NetworkSuite) TestFilterBridgeAddresses(c *gc.C) { "10.0.4.1", "10.0.5.1/24", }, nil - } else if name == network.DefaultKVMBridge { - return []string{ - "192.168.122.1", - }, nil } c.Fatalf("unknown bridge name: %q", name) return nil, nil @@ -140,6 +136,7 @@ func (s *NetworkSuite) TestFilterBridgeAddresses(c *gc.C) { "2001:db8::1", "10.0.0.1", "10.0.6.10", + "192.168.122.1", "192.168.123.42", "localhost", "252.16.134.1", diff --git a/internal/packaging/dependency/kvm.go b/internal/packaging/dependency/kvm.go deleted file mode 100644 index 90c624c3dbb..00000000000 --- a/internal/packaging/dependency/kvm.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2019 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package dependency - -import ( - "github.com/juju/errors" - - "github.com/juju/juju/core/arch" - "github.com/juju/juju/core/base" - "github.com/juju/juju/internal/packaging" -) - -// KVM returns a dependency instance for installing KVM support. -func KVM(arch string) packaging.Dependency { - return &kvmDependency{arch: arch} -} - -type kvmDependency struct { - arch string -} - -// PackageList implements packaging.Dependency. -func (dep kvmDependency) PackageList(series string) ([]packaging.Package, error) { - if series == base.Centos7.String() || series == base.Centos9.String() { - return nil, errors.NotSupportedf("installing kvm on series %q", series) - } - - var pkgList []string - if dep.arch == arch.ARM64 { - // ARM64 doesn't support legacy BIOS so it requires Extensible Firmware - // Interface. - pkgList = append(pkgList, "qemu-efi") - } - - pkgList = append(pkgList, - "qemu-kvm", - "qemu-utils", - "genisoimage", - ) - - switch series { - case "bionic": - pkgList = append(pkgList, "libvirt-bin") - default: - // On focal+ virsh is provided by libvirt-clients; also we need - // to install the daemon package separately. - pkgList = append(pkgList, - "libvirt-daemon-system", - "libvirt-clients", - ) - } - - return packaging.MakePackageList(packaging.AptPackageManager, "", pkgList...), nil -} diff --git a/internal/provider/azure/environ_test.go b/internal/provider/azure/environ_test.go index 47d267432ce..fab0fafc1c8 100644 --- a/internal/provider/azure/environ_test.go +++ b/internal/provider/azure/environ_test.go @@ -1869,7 +1869,7 @@ func (s *environSuite) TestStopInstancesDeploymentNotFound(c *gc.C) { func (s *environSuite) TestConstraintsValidatorUnsupported(c *gc.C) { validator := s.constraintsValidator(c) unsupported, err := validator.Validate(constraints.MustParse( - "arch=amd64 tags=foo cpu-power=100 virt-type=kvm", + "arch=amd64 tags=foo cpu-power=100 virt-type=lxd", )) c.Assert(err, jc.ErrorIsNil) c.Assert(unsupported, jc.SameContents, []string{"tags", "cpu-power", "virt-type"}) diff --git a/internal/provider/ec2/local_test.go b/internal/provider/ec2/local_test.go index 38ca0660e0e..95e3c0d7e6a 100644 --- a/internal/provider/ec2/local_test.go +++ b/internal/provider/ec2/local_test.go @@ -1719,7 +1719,7 @@ func (t *localServerSuite) TestConstraintsValidatorUnsupported(c *gc.C) { env := t.Prepare(c) validator, err := env.ConstraintsValidator(t.callCtx) c.Assert(err, jc.ErrorIsNil) - cons := constraints.MustParse("arch=amd64 tags=foo virt-type=kvm") + cons := constraints.MustParse("arch=amd64 tags=foo virt-type=lxd") unsupported, err := validator.Validate(cons) c.Assert(err, jc.ErrorIsNil) c.Assert(unsupported, jc.SameContents, []string{"tags", "virt-type"}) diff --git a/internal/worker/apiaddressupdater/apiaddressupdater_test.go b/internal/worker/apiaddressupdater/apiaddressupdater_test.go index 17c6c77a3c5..b87cc677f2f 100644 --- a/internal/worker/apiaddressupdater/apiaddressupdater_test.go +++ b/internal/worker/apiaddressupdater/apiaddressupdater_test.go @@ -182,10 +182,6 @@ func (s *APIAddressUpdaterSuite) TestBridgeAddressesFiltering(c *gc.C) { "10.0.4.1", "10.0.4.4", }, nil - } else if name == network.DefaultKVMBridge { - return []string{ - "192.168.122.1", - }, nil } c.Fatalf("unknown bridge in testing: %v", name) return nil, nil @@ -237,8 +233,9 @@ func (s *APIAddressUpdaterSuite) TestBridgeAddressesFiltering(c *gc.C) { expServerInit := corenetwork.ProviderHostPorts{ corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewMachineAddress("10.0.3.3").AsProviderAddress(), NetPort: 4321}, corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewMachineAddress("10.0.4.2").AsProviderAddress(), NetPort: 4321}, + corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewMachineAddress("192.168.122.1").AsProviderAddress(), NetPort: 4321}, }.HostPorts() - c.Assert(servers, jc.DeepEquals, []corenetwork.HostPorts{expServer1, expServerInit}) + c.Check(servers, jc.DeepEquals, []corenetwork.HostPorts{expServer1, expServerInit}) } client.EXPECT().APIHostPorts().Return(updatedServers, nil) @@ -254,7 +251,7 @@ func (s *APIAddressUpdaterSuite) TestBridgeAddressesFiltering(c *gc.C) { expServerUpd := corenetwork.ProviderHostPorts{ corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewMachineAddress("10.0.3.3").AsProviderAddress(), NetPort: 4001}, }.HostPorts() - c.Assert(servers, jc.DeepEquals, []corenetwork.HostPorts{expServer1, expServerUpd}) + c.Check(servers, jc.DeepEquals, []corenetwork.HostPorts{expServer1, expServerUpd}) } } diff --git a/internal/worker/containerbroker/broker_test.go b/internal/worker/containerbroker/broker_test.go index 9ddc3b1b833..85347595412 100644 --- a/internal/worker/containerbroker/broker_test.go +++ b/internal/worker/containerbroker/broker_test.go @@ -212,18 +212,6 @@ func (s *trackerSuite) TestNewTrackerWithNoDeterminedContainers(c *gc.C) { c.Assert(err, gc.ErrorMatches, "no container types determined") } -func (s *trackerSuite) TestNewTrackerWithKVMContainers(c *gc.C) { - defer s.setup(c).Finish() - - _, err := s.withScenario(c, - nil, - s.expectMachineTag, - s.expectMachines, - s.expectKVMSupportedContainers, - ) - c.Assert(err, gc.ErrorMatches, "resource permanently unavailable") -} - func (s *trackerSuite) withScenario(c *gc.C, expected *broker.Config, behaviours ...func()) (*containerbroker.Tracker, error) { for _, b := range behaviours { b() @@ -285,12 +273,6 @@ func (s *trackerSuite) expectNoDeterminedSupportedContainers() { }, false, nil) } -func (s *trackerSuite) expectKVMSupportedContainers() { - s.machine.EXPECT().SupportedContainers().Return([]instance.ContainerType{ - instance.KVM, - }, true, nil) -} - func (s *trackerSuite) expectContainerConfig() { s.state.EXPECT().ContainerManagerConfig(params.ContainerManagerConfigParams{ Type: instance.LXD, diff --git a/internal/worker/instancemutater/mutater.go b/internal/worker/instancemutater/mutater.go index e3e9f17a4a8..2afa9aaa444 100644 --- a/internal/worker/instancemutater/mutater.go +++ b/internal/worker/instancemutater/mutater.go @@ -83,13 +83,13 @@ func (m *mutater) startMachines(ctx context.Context, tags []names.MachineTag) er } id := api.Tag().Id() - // Ensure we do not watch any KVM containers. + // Ensure we do not watch any containers that aren't LXD. containerType, err := api.ContainerType(ctx) if err != nil { return errors.Trace(err) } - if containerType == instance.KVM { - m.logger.Tracef("ignoring KVM container machine-%s", id) + if containerType != instance.LXD { + m.logger.Tracef("ignoring %q container machine-%s", containerType, id) continue } diff --git a/internal/worker/instancemutater/worker_test.go b/internal/worker/instancemutater/worker_test.go index a8b64d05d0a..31917913b5b 100644 --- a/internal/worker/instancemutater/worker_test.go +++ b/internal/worker/instancemutater/worker_test.go @@ -200,6 +200,7 @@ func (s *workerEnvironSuite) TestFullWorkflow(c *gc.C) { s.notifyMachines([][]string{{"0"}}) s.expectFacadeMachineTag(0) + s.expectContainerType() s.notifyMachineAppLXDProfile(0, 1) s.expectMachineCharmProfilingInfo(0, 3) s.expectLXDProfileNamesTrue() @@ -216,6 +217,7 @@ func (s *workerEnvironSuite) TestVerifyCurrentProfilesTrue(c *gc.C) { s.notifyMachines([][]string{{"0"}}) s.expectFacadeMachineTag(0) + s.expectContainerType() s.notifyMachineAppLXDProfile(0, 1) s.expectAliveAndSetModificationStatusIdle(0) s.expectMachineCharmProfilingInfo(0, 2) @@ -230,6 +232,7 @@ func (s *workerEnvironSuite) TestRemoveAllCharmProfiles(c *gc.C) { s.notifyMachines([][]string{{"0"}}) s.expectFacadeMachineTag(0) + s.expectContainerType() s.notifyMachineAppLXDProfile(0, 1) s.expectAliveAndSetModificationStatusIdle(0) s.expectCharmProfilingInfoRemove(0) @@ -250,6 +253,7 @@ func (s *workerEnvironSuite) TestMachineNotifyTwice(c *gc.C) { s.notifyMachinesWaitGroup([][]string{{"0", "1"}, {"0"}}, &group) s.expectFacadeMachineTag(0) s.expectFacadeMachineTag(1) + s.expectContainerType() s.notifyMachineAppLXDProfile(0, 1) s.notifyMachineAppLXDProfile(1, 1) s.expectAliveAndSetModificationStatusIdle(1) @@ -267,6 +271,7 @@ func (s *workerEnvironSuite) TestNoChangeFoundOne(c *gc.C) { s.notifyMachines([][]string{{"0"}}) s.expectFacadeMachineTag(0) + s.expectContainerType() s.notifyMachineAppLXDProfile(0, 1) s.expectCharmProfilingInfoSimpleNoChange(0) @@ -288,6 +293,7 @@ func (s *workerEnvironSuite) TestCharmProfilingInfoNotProvisioned(c *gc.C) { s.notifyMachines([][]string{{"0"}}) s.expectFacadeMachineTag(0) + s.expectContainerType() s.notifyMachineAppLXDProfile(0, 1) s.expectCharmProfileInfoNotProvisioned(0) @@ -299,6 +305,7 @@ func (s *workerEnvironSuite) TestCharmProfilingInfoError(c *gc.C) { s.notifyMachines([][]string{{"0"}}) s.expectFacadeMachineTag(0) + s.expectContainerType() s.notifyMachineAppLXDProfile(0, 1) s.expectCharmProfileInfoError(0) s.expectContextKillError() @@ -307,11 +314,22 @@ func (s *workerEnvironSuite) TestCharmProfilingInfoError(c *gc.C) { c.Assert(err, jc.Satisfies, params.IsCodeNotSupported) } +func (s *workerEnvironSuite) TestMachineContainerTypeNotSupported(c *gc.C) { + defer s.setup(c, 1).Finish() + + s.notifyMachines([][]string{{"0"}}) + s.expectFacadeMachineTag(0) + s.expectContainerTypeNone() + + s.cleanKill(c, s.workerForScenario(c)) +} + func (s *workerEnvironSuite) TestMachineNotSupported(c *gc.C) { defer s.setup(c, 1).Finish() s.notifyMachines([][]string{{"0"}}) s.expectFacadeMachineTag(0) + s.expectContainerType() // We need another sync point here, because the worker can be killed // before this method is called. @@ -342,7 +360,6 @@ func (s *workerSuite) setup(c *gc.C, machineCount int) *gomock.Controller { s.appLXDProfileWorker[i] = workermocks.NewMockWorker(ctrl) } - s.expectContainerTypeNone() return ctrl } @@ -398,6 +415,12 @@ func (s *workerSuite) expectFacadeReturnsNoMachine() { s.facade.EXPECT().Machine(gomock.Any(), s.machineTag).Return(nil, errors.NewNotFound(nil, "machine")).Do(do) } +func (s *workerSuite) expectContainerType() { + for _, m := range s.machine { + m.EXPECT().ContainerType(gomock.Any()).Return(instance.LXD, nil).AnyTimes() + } +} + func (s *workerSuite) expectContainerTypeNone() { for _, m := range s.machine { m.EXPECT().ContainerType(gomock.Any()).Return(instance.NONE, nil).AnyTimes() @@ -663,9 +686,7 @@ type workerContainerSuite struct { workerSuite lxdContainerTag names.Tag - kvmContainerTag names.Tag lxdContainer *mocks.MockMutaterMachine - kvmContainer *mocks.MockMutaterMachine } var _ = gc.Suite(&workerContainerSuite{}) @@ -674,7 +695,6 @@ func (s *workerContainerSuite) SetUpTest(c *gc.C) { s.workerSuite.SetUpTest(c) s.lxdContainerTag = names.NewMachineTag("0/lxd/0") - s.kvmContainerTag = names.NewMachineTag("0/kvm/0") s.newWorkerFunc = instancemutater.NewContainerTestWorker s.getRequiredLXDProfiles = func(modelName string) []string { return []string{"default"} @@ -687,7 +707,7 @@ func (s *workerContainerSuite) SetUpTest(c *gc.C) { func (s *workerContainerSuite) TestFullWorkflow(c *gc.C) { defer s.setup(c).Finish() - s.notifyContainers(0, [][]string{{"0/lxd/0", "0/kvm/0"}}) + s.notifyContainers(0, [][]string{{"0/lxd/0"}}) s.expectFacadeMachineTag(0) s.expectFacadeContainerTags() s.expectContainerTypes() @@ -705,20 +725,16 @@ func (s *workerContainerSuite) TestFullWorkflow(c *gc.C) { func (s *workerContainerSuite) setup(c *gc.C) *gomock.Controller { ctrl := s.workerSuite.setup(c, 1) s.lxdContainer = mocks.NewMockMutaterMachine(ctrl) - s.kvmContainer = mocks.NewMockMutaterMachine(ctrl) return ctrl } func (s *workerContainerSuite) expectFacadeContainerTags() { s.facade.EXPECT().Machine(gomock.Any(), s.lxdContainerTag).Return(s.lxdContainer, nil).AnyTimes() s.lxdContainer.EXPECT().Tag().Return(s.lxdContainerTag).AnyTimes() - s.facade.EXPECT().Machine(gomock.Any(), s.kvmContainerTag).Return(s.kvmContainer, nil).AnyTimes() - s.kvmContainer.EXPECT().Tag().Return(s.kvmContainerTag).AnyTimes() } func (s *workerContainerSuite) expectContainerTypes() { s.lxdContainer.EXPECT().ContainerType(gomock.Any()).Return(instance.LXD, nil).AnyTimes() - s.kvmContainer.EXPECT().ContainerType(gomock.Any()).Return(instance.KVM, nil).AnyTimes() } func (s *workerContainerSuite) expectContainerCharmProfilingInfo(rev int) { diff --git a/internal/worker/machiner/machiner_test.go b/internal/worker/machiner/machiner_test.go index 5821646c45e..f0b887626e8 100644 --- a/internal/worker/machiner/machiner_test.go +++ b/internal/worker/machiner/machiner_test.go @@ -376,10 +376,6 @@ func (s *MachinerSuite) TestSetMachineAddresses(c *gc.C) { "10.0.4.1", "10.0.4.4", }, nil - } else if name == network.DefaultKVMBridge { - return []string{ - "192.168.122.1", - }, nil } c.Fatalf("unknown bridge in testing: %v", name) return nil, nil diff --git a/internal/worker/provisioner/container_initialisation.go b/internal/worker/provisioner/container_initialisation.go index ea42ffb23a1..c47e0c6126d 100644 --- a/internal/worker/provisioner/container_initialisation.go +++ b/internal/worker/provisioner/container_initialisation.go @@ -17,7 +17,6 @@ import ( "github.com/juju/juju/environs/config" "github.com/juju/juju/internal/container" "github.com/juju/juju/internal/container/broker" - "github.com/juju/juju/internal/container/kvm" "github.com/juju/juju/internal/container/lxd" workercommon "github.com/juju/juju/internal/worker/common" "github.com/juju/juju/rpc/params" @@ -94,11 +93,14 @@ func (cs *ContainerSetup) initContainerDependencies(abort <-chan struct{}, manag snapChannels := map[string]string{ "lxd": managerCfg.PopValue(config.LXDSnapChannel), } - initialiser := getContainerInitialiser( + initialiser, err := getContainerInitialiser( cs.containerType, snapChannels, managerCfg.PopValue(config.ContainerNetworkingMethod), ) + if err != nil { + return errors.Annotate(err, "initialising container infrastructure on host machine") + } releaser, err := cs.acquireLock(abort, fmt.Sprintf("%s container initialisation", cs.containerType)) if err != nil { @@ -145,12 +147,11 @@ var getContainerInitialiser = func( ct instance.ContainerType, snapChannels map[string]string, containerNetworkingMethod string, -) container.Initialiser { - - if ct == instance.LXD { - return lxd.NewContainerInitialiser(snapChannels["lxd"], containerNetworkingMethod) +) (container.Initialiser, error) { + if ct != instance.LXD { + return nil, errors.NotSupportedf("container type %q", ct) } - return kvm.NewContainerInitialiser() + return lxd.NewContainerInitialiser(snapChannels["lxd"], containerNetworkingMethod), nil } func (cs *ContainerSetup) initialiseContainerProvisioner() (Provisioner, error) { diff --git a/internal/worker/provisioner/container_initialisation_test.go b/internal/worker/provisioner/container_initialisation_test.go index f45212ede35..72907e09b49 100644 --- a/internal/worker/provisioner/container_initialisation_test.go +++ b/internal/worker/provisioner/container_initialisation_test.go @@ -53,10 +53,6 @@ func (s *containerSetupSuite) SetUpTest(c *gc.C) { var _ = gc.Suite(&containerSetupSuite{}) -func (s *containerSetupSuite) TestInitialiseContainersKVM(c *gc.C) { - s.testInitialiseContainers(c, instance.KVM) -} - func (s *containerSetupSuite) TestInitialiseContainersLXD(c *gc.C) { s.testInitialiseContainers(c, instance.LXD) } @@ -80,10 +76,6 @@ func (s *containerSetupSuite) testInitialiseContainers(c *gc.C, containerType in c.Assert(err, jc.ErrorIsNil) } -func (s *containerSetupSuite) TestInitialiseContainerProvisionerKVM(c *gc.C) { - s.testInitialiseContainers(c, instance.KVM) -} - func (s *containerSetupSuite) TestInitialiseContainerProvisonerLXD(c *gc.C) { s.testInitialiseContainers(c, instance.LXD) } @@ -148,8 +140,8 @@ func (s *containerSetupSuite) patch(c *gc.C) *gomock.Controller { s.machine.EXPECT().MachineTag().Return(names.NewMachineTag("0")).AnyTimes() - s.PatchValue(GetContainerInitialiser, func(instance.ContainerType, map[string]string, string) container.Initialiser { - return s.initialiser + s.PatchValue(GetContainerInitialiser, func(instance.ContainerType, map[string]string, string) (container.Initialiser, error) { + return s.initialiser, nil }) return ctrl diff --git a/internal/worker/provisioner/containerprovisioner_test.go b/internal/worker/provisioner/containerprovisioner_test.go index 972e2851488..22c3b831e01 100644 --- a/internal/worker/provisioner/containerprovisioner_test.go +++ b/internal/worker/provisioner/containerprovisioner_test.go @@ -29,15 +29,15 @@ import ( jujuversion "github.com/juju/juju/version" ) -type kvmProvisionerSuite struct { +type lxdProvisionerSuite struct { CommonProvisionerSuite containersCh chan []string } -var _ = gc.Suite(&kvmProvisionerSuite{}) +var _ = gc.Suite(&lxdProvisionerSuite{}) -func (s *kvmProvisionerSuite) newKvmProvisioner(c *gc.C, ctrl *gomock.Controller) provisioner.Provisioner { +func (s *lxdProvisionerSuite) newLXDProvisioner(c *gc.C, ctrl *gomock.Controller) provisioner.Provisioner { mTag := names.NewMachineTag("0") defaultPaths := agent.DefaultPaths defaultPaths.DataDir = c.MkDir() @@ -63,7 +63,7 @@ func (s *kvmProvisionerSuite) newKvmProvisioner(c *gc.C, ctrl *gomock.Controller toolsFinder := &mockToolsFinder{} w, err := provisioner.NewContainerProvisioner( - instance.KVM, s.controllerAPI, s.machinesAPI, loggo.GetLogger("test"), + instance.LXD, s.controllerAPI, s.machinesAPI, loggo.GetLogger("test"), cfg, s.broker, toolsFinder, &mockDistributionGroupFinder{}, &credentialAPIForTest{}) c.Assert(err, jc.ErrorIsNil) @@ -72,19 +72,19 @@ func (s *kvmProvisionerSuite) newKvmProvisioner(c *gc.C, ctrl *gomock.Controller return w } -func (s *kvmProvisionerSuite) TestProvisionerStartStop(c *gc.C) { +func (s *lxdProvisionerSuite) TestProvisionerStartStop(c *gc.C) { ctrl := s.setUpMocks(c) defer ctrl.Finish() - p := s.newKvmProvisioner(c, ctrl) + p := s.newLXDProvisioner(c, ctrl) workertest.CleanKill(c, p) } -func (s *kvmProvisionerSuite) TestDoesNotHaveRetryWatcher(c *gc.C) { +func (s *lxdProvisionerSuite) TestDoesNotHaveRetryWatcher(c *gc.C) { ctrl := s.setUpMocks(c) defer ctrl.Finish() - p := s.newKvmProvisioner(c, ctrl) + p := s.newLXDProvisioner(c, ctrl) defer workertest.CleanKill(c, p) w, err := provisioner.GetRetryWatcher(p) @@ -92,7 +92,7 @@ func (s *kvmProvisionerSuite) TestDoesNotHaveRetryWatcher(c *gc.C) { c.Assert(err, jc.ErrorIs, errors.NotImplemented) } -func (s *kvmProvisionerSuite) sendMachineContainersChange(c *gc.C, ids ...string) { +func (s *lxdProvisionerSuite) sendMachineContainersChange(c *gc.C, ids ...string) { select { case s.containersCh <- ids: case <-time.After(coretesting.LongWait): @@ -100,16 +100,16 @@ func (s *kvmProvisionerSuite) sendMachineContainersChange(c *gc.C, ids ...string } } -func (s *kvmProvisionerSuite) TestContainerStartedAndStopped(c *gc.C) { +func (s *lxdProvisionerSuite) TestContainerStartedAndStopped(c *gc.C) { ctrl := s.setUpMocks(c) defer ctrl.Finish() - p := s.newKvmProvisioner(c, ctrl) + p := s.newLXDProvisioner(c, ctrl) defer workertest.CleanKill(c, p) - cTag := names.NewMachineTag("0/kvm/666") + cTag := names.NewMachineTag("0/lxd/666") - c666 := &testMachine{id: "0/kvm/666"} + c666 := &testMachine{id: "0/lxd/666"} s.broker.EXPECT().AllRunningInstances(gomock.Any()).Return([]instances.Instance{&testInstance{id: "inst-666"}}, nil).Times(2) s.machinesAPI.EXPECT().Machines(gomock.Any(), cTag).Return([]apiprovisioner.MachineResult{{ Machine: c666, @@ -143,21 +143,21 @@ func (s *kvmProvisionerSuite) TestContainerStartedAndStopped(c *gc.C) { s.waitForRemovalMark(c, c666) } -func (s *kvmProvisionerSuite) TestKVMProvisionerObservesConfigChanges(c *gc.C) { +func (s *lxdProvisionerSuite) TestKVMProvisionerObservesConfigChanges(c *gc.C) { ctrl := s.setUpMocks(c) defer ctrl.Finish() - p := s.newKvmProvisioner(c, ctrl) + p := s.newLXDProvisioner(c, ctrl) defer workertest.CleanKill(c, p) s.assertProvisionerObservesConfigChanges(c, p, true) } -func (s *kvmProvisionerSuite) TestKVMProvisionerObservesConfigChangesWorkerCount(c *gc.C) { +func (s *lxdProvisionerSuite) TestKVMProvisionerObservesConfigChangesWorkerCount(c *gc.C) { ctrl := s.setUpMocks(c) defer ctrl.Finish() - p := s.newKvmProvisioner(c, ctrl) + p := s.newLXDProvisioner(c, ctrl) defer workertest.CleanKill(c, p) s.assertProvisionerObservesConfigChangesWorkerCount(c, p, true) diff --git a/internal/worker/provisioner/containerworker_test.go b/internal/worker/provisioner/containerworker_test.go index 41c62079a9f..f58c340edb3 100644 --- a/internal/worker/provisioner/containerworker_test.go +++ b/internal/worker/provisioner/containerworker_test.go @@ -202,8 +202,8 @@ func (s *containerWorkerSuite) patch(c *gc.C) *gomock.Controller { s.machine.EXPECT().Id().Return("0").AnyTimes() s.machine.EXPECT().MachineTag().Return(names.NewMachineTag("0")).AnyTimes() - s.PatchValue(provisioner.GetContainerInitialiser, func(instance.ContainerType, map[string]string, string) container.Initialiser { - return s.initialiser + s.PatchValue(provisioner.GetContainerInitialiser, func(instance.ContainerType, map[string]string, string) (container.Initialiser, error) { + return s.initialiser, nil }) s.manager.EXPECT().ListContainers().Return(nil, nil).AnyTimes() diff --git a/state/constraintsvalidation_test.go b/state/constraintsvalidation_test.go index 91d404cd064..54ed7001377 100644 --- a/state/constraintsvalidation_test.go +++ b/state/constraintsvalidation_test.go @@ -166,7 +166,7 @@ var setConstraintsTests = []struct { effectiveMachineCons: "cpu-power= tags= spaces=bar", }, { about: "container type can only be used for deployment, not provisioning", - consToSet: "container=kvm arch=amd64", + consToSet: "container=lxd arch=amd64", consFallback: "container=lxd mem=8G", // application deployment constraints are transformed into machine @@ -175,33 +175,33 @@ var setConstraintsTests = []struct { // when merging application/model deployment constraints into // effective machine provisioning constraints. effectiveModelCons: "container=lxd mem=8G", - effectiveApplicationCons: "container=kvm arch=amd64", - effectiveUnitCons: "container=kvm mem=8G arch=amd64", + effectiveApplicationCons: "container=lxd arch=amd64", + effectiveUnitCons: "container=lxd mem=8G arch=amd64", effectiveMachineCons: "mem=8G arch=amd64", }, { about: "specify image virt-type when deploying applications on multi-hypervisor aware openstack", - consToSet: "virt-type=kvm", + consToSet: "virt-type=lxd", consFallback: "", // application deployment constraints are transformed into machine // provisioning constraints. Unit constraints must also have virt-type set // to ensure consistency in scalability. effectiveModelCons: "", - effectiveApplicationCons: "virt-type=kvm", - effectiveUnitCons: "arch=amd64 virt-type=kvm", - effectiveMachineCons: "virt-type=kvm", + effectiveApplicationCons: "virt-type=lxd", + effectiveUnitCons: "arch=amd64 virt-type=lxd", + effectiveMachineCons: "virt-type=lxd", }, { about: "ensure model and application constraints are separate", - consToSet: "virt-type=kvm", + consToSet: "virt-type=lxd", consFallback: "mem=2G", // application deployment constraints are transformed into machine // provisioning constraints. Unit constraints must also have virt-type set // to ensure consistency in scalability. effectiveModelCons: "mem=2G", - effectiveApplicationCons: "virt-type=kvm", - effectiveUnitCons: "arch=amd64 mem=2G virt-type=kvm", - effectiveMachineCons: "mem=2G virt-type=kvm", + effectiveApplicationCons: "virt-type=lxd", + effectiveUnitCons: "arch=amd64 mem=2G virt-type=lxd", + effectiveMachineCons: "mem=2G virt-type=lxd", }} func (s *constraintsValidationSuite) TestMachineConstraints(c *gc.C) { @@ -275,7 +275,7 @@ func (s *applicationConstraintsSuite) SetUpTest(c *gc.C) { s.ConnSuite.SetUpTest(c) s.policy.GetConstraintsValidator = func() (constraints.Validator, error) { validator := constraints.NewValidator() - validator.RegisterVocabulary(constraints.VirtType, []string{"kvm"}) + validator.RegisterVocabulary(constraints.VirtType, []string{"lxd"}) return validator, nil } s.applicationName = "wordpress" @@ -293,11 +293,11 @@ func (s *applicationConstraintsSuite) TestAddApplicationInvalidConstraints(c *gc Charm: s.testCharm, Constraints: cons, }, state.NewObjectStore(c, s.State.ModelUUID())) - c.Assert(errors.Cause(err), gc.ErrorMatches, regexp.QuoteMeta("invalid constraint value: virt-type=blah\nvalid values are: [kvm]")) + c.Assert(errors.Cause(err), gc.ErrorMatches, regexp.QuoteMeta("invalid constraint value: virt-type=blah\nvalid values are: [lxd]")) } func (s *applicationConstraintsSuite) TestAddApplicationValidConstraints(c *gc.C) { - cons := constraints.MustParse("virt-type=kvm") + cons := constraints.MustParse("virt-type=lxd") application, err := s.State.AddApplication(defaultInstancePrechecker, state.AddApplicationArgs{ Name: s.applicationName, CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{ diff --git a/state/linklayerdevices_test.go b/state/linklayerdevices_test.go index 86f35e3ad03..e8244e53f58 100644 --- a/state/linklayerdevices_test.go +++ b/state/linklayerdevices_test.go @@ -930,7 +930,6 @@ func (s *linkLayerDevicesStateSuite) TestSetLinkLayerDevicesAllowsParentBridgeDe // Add default bridges per container type to ensure they will be skipped // when deciding which host bridges to use for the container NICs. s.addParentBridgeDeviceWithContainerDevicesAsChildren(c, network.DefaultLXDBridge, "vethX", 1) - s.addParentBridgeDeviceWithContainerDevicesAsChildren(c, network.DefaultKVMBridge, "vethY", 1) parentDevice, _ := s.addParentBridgeDeviceWithContainerDevicesAsChildren(c, "br-eth1.250", "eth", 1) childDevice, err := s.containerMachine.LinkLayerDevice("eth0") c.Assert(err, jc.ErrorIsNil) diff --git a/state/machine_test.go b/state/machine_test.go index c6d16b3ffea..ec6e7b17142 100644 --- a/state/machine_test.go +++ b/state/machine_test.go @@ -2536,92 +2536,6 @@ func (s *MachineSuite) TestSetSupportedContainersSame(c *gc.C) { assertSupportedContainers(c, machine, []instance.ContainerType{instance.LXD}) } -func (s *MachineSuite) TestSetSupportedContainersNew(c *gc.C) { - machine := s.addMachineWithSupportedContainer(c, instance.LXD) - - err := machine.SetSupportedContainers([]instance.ContainerType{instance.LXD, instance.KVM}) - c.Assert(err, jc.ErrorIsNil) - assertSupportedContainers(c, machine, []instance.ContainerType{instance.LXD, instance.KVM}) -} - -func (s *MachineSuite) TestSetSupportedContainersMultipeNew(c *gc.C) { - machine, err := s.State.AddMachine(defaultInstancePrechecker, state.UbuntuBase("12.10"), state.JobHostUnits) - c.Assert(err, jc.ErrorIsNil) - - err = machine.SetSupportedContainers([]instance.ContainerType{instance.LXD, instance.KVM}) - c.Assert(err, jc.ErrorIsNil) - assertSupportedContainers(c, machine, []instance.ContainerType{instance.LXD, instance.KVM}) -} - -func (s *MachineSuite) TestSetSupportedContainersMultipleExisting(c *gc.C) { - machine := s.addMachineWithSupportedContainer(c, instance.LXD) - - err := machine.SetSupportedContainers([]instance.ContainerType{instance.LXD, instance.KVM}) - c.Assert(err, jc.ErrorIsNil) - assertSupportedContainers(c, machine, []instance.ContainerType{instance.LXD, instance.KVM}) - // Setting it again will be a no-op - defer state.SetFailIfTransaction(c, s.State).Check() - err = machine.SetSupportedContainers([]instance.ContainerType{instance.LXD, instance.KVM}) - c.Assert(err, jc.ErrorIsNil) - assertSupportedContainers(c, machine, []instance.ContainerType{instance.LXD, instance.KVM}) -} - -func (s *MachineSuite) TestSetSupportedContainersMultipleExistingInvertedOrder(c *gc.C) { - machine := s.addMachineWithSupportedContainer(c, instance.LXD) - - err := machine.SetSupportedContainers([]instance.ContainerType{instance.KVM, instance.LXD}) - c.Assert(err, jc.ErrorIsNil) - assertSupportedContainers(c, machine, []instance.ContainerType{instance.KVM, instance.LXD}) - // Setting it again will be a no-op - defer state.SetFailIfTransaction(c, s.State).Check() - err = machine.SetSupportedContainers([]instance.ContainerType{instance.KVM, instance.LXD}) - c.Assert(err, jc.ErrorIsNil) - assertSupportedContainers(c, machine, []instance.ContainerType{instance.KVM, instance.LXD}) -} - -func (s *MachineSuite) TestSetSupportedContainersMultipleExistingWithDifferentInstanceType(c *gc.C) { - machine := s.addMachineWithSupportedContainer(c, instance.LXD) - - err := machine.SetSupportedContainers([]instance.ContainerType{instance.LXD, instance.KVM}) - c.Assert(err, jc.ErrorIsNil) - assertSupportedContainers(c, machine, []instance.ContainerType{instance.LXD, instance.KVM}) - // Setting it again will be a no-op - err = machine.SetSupportedContainers([]instance.ContainerType{instance.LXD, instance.ContainerType("FOO")}) - c.Assert(err, jc.ErrorIsNil) -} - -func (s *MachineSuite) TestSetSupportedContainersSetsUnknownToError(c *gc.C) { - // Create a machine and add lxd and kvm containers prior to calling SetSupportedContainers - machine, err := s.State.AddMachine(defaultInstancePrechecker, state.UbuntuBase("12.10"), state.JobHostUnits) - c.Assert(err, jc.ErrorIsNil) - template := state.MachineTemplate{ - Base: state.UbuntuBase("12.10"), - Jobs: []state.MachineJob{state.JobHostUnits}, - } - container, err := s.State.AddMachineInsideMachine(template, machine.Id(), instance.LXD) - c.Assert(err, jc.ErrorIsNil) - supportedContainer, err := s.State.AddMachineInsideMachine(template, machine.Id(), instance.KVM) - c.Assert(err, jc.ErrorIsNil) - err = machine.SetSupportedContainers([]instance.ContainerType{instance.KVM}) - c.Assert(err, jc.ErrorIsNil) - - // A supported (kvm) container will have a pending status. - err = supportedContainer.Refresh() - c.Assert(err, jc.ErrorIsNil) - statusInfo, err := supportedContainer.Status() - c.Assert(err, jc.ErrorIsNil) - c.Assert(statusInfo.Status, gc.Equals, status.Pending) - - // An unsupported (lxd) container will have an error status. - err = container.Refresh() - c.Assert(err, jc.ErrorIsNil) - statusInfo, err = container.Status() - c.Assert(err, jc.ErrorIsNil) - c.Assert(statusInfo.Status, gc.Equals, status.Error) - c.Assert(statusInfo.Message, gc.Equals, "unsupported container") - c.Assert(statusInfo.Data, gc.DeepEquals, map[string]interface{}{"type": "lxd"}) -} - func (s *MachineSuite) TestSupportsNoContainersSetsAllToError(c *gc.C) { // Create a machine and add all container types prior to calling SupportsNoContainers machine, err := s.State.AddMachine(defaultInstancePrechecker, state.UbuntuBase("12.10"), state.JobHostUnits) diff --git a/state/migration_export_test.go b/state/migration_export_test.go index 282115baf4b..986f9bc2183 100644 --- a/state/migration_export_test.go +++ b/state/migration_export_test.go @@ -257,7 +257,7 @@ func (s *MigrationExportSuite) TestMachines(c *gc.C) { } func (s *MigrationExportSuite) TestMachinesWithVirtConstraint(c *gc.C) { - s.assertMachinesMigrated(c, constraints.MustParse("arch=amd64 mem=8G virt-type=kvm")) + s.assertMachinesMigrated(c, constraints.MustParse("arch=amd64 mem=8G virt-type=lxd")) } func (s *MigrationExportSuite) TestMachinesWithRootDiskSourceConstraint(c *gc.C) { @@ -350,7 +350,7 @@ func (s *MigrationExportSuite) TestCAASSidecarApplications(c *gc.C) { } func (s *MigrationExportSuite) TestApplicationsWithVirtConstraint(c *gc.C) { - s.assertMigrateApplications(c, s.State, constraints.MustParse("arch=amd64 mem=8G virt-type=kvm")) + s.assertMigrateApplications(c, s.State, constraints.MustParse("arch=amd64 mem=8G virt-type=lxd")) } func (s *MigrationExportSuite) TestApplicationsWithRootDiskSourceConstraint(c *gc.C) { @@ -366,8 +366,7 @@ func (s *MigrationExportSuite) assertMigrateApplications(c *gc.C, st *state.Stat if dbModel.Type() == state.ModelTypeCAAS { series = "focal" } - var ch *state.Charm - ch = f.MakeCharmV2(c, &factory.CharmParams{ + ch := f.MakeCharmV2(c, &factory.CharmParams{ Name: "snappass-test", Series: series, }) diff --git a/state/migration_import_test.go b/state/migration_import_test.go index a8f3ed776a3..c245bc263ea 100644 --- a/state/migration_import_test.go +++ b/state/migration_import_test.go @@ -1051,7 +1051,7 @@ func (s *MigrationImportSuite) TestCAASUnits(c *gc.C) { } func (s *MigrationImportSuite) TestUnitsWithVirtConstraint(c *gc.C) { - cons := constraints.MustParse("arch=amd64 mem=8G virt-type=kvm") + cons := constraints.MustParse("arch=amd64 mem=8G virt-type=lxd") f := factory.NewFactory(s.State, s.StatePool) exported, pwd := f.MakeUnitReturningPassword(c, &factory.UnitParams{ Constraints: cons, diff --git a/state/state_test.go b/state/state_test.go index 9ec83ddcf7e..bc4932b840a 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -330,7 +330,7 @@ func (s *MultiModelStateSuite) TestWatchTwoModels(c *gc.C) { Jobs: []state.MachineJob{state.JobHostUnits}, }, m.Id(), - instance.KVM, + instance.LXD, ) c.Assert(err, jc.ErrorIsNil) }, @@ -1122,30 +1122,30 @@ func (s *StateSuite) TestAddContainerToMachineWithKnownSupportedContainers(c *gc oneJob := []state.MachineJob{state.JobHostUnits} host, err := s.State.AddMachine(defaultInstancePrechecker, state.UbuntuBase("12.10"), oneJob...) c.Assert(err, jc.ErrorIsNil) - err = host.SetSupportedContainers([]instance.ContainerType{instance.KVM}) + err = host.SetSupportedContainers([]instance.ContainerType{instance.LXD}) c.Assert(err, jc.ErrorIsNil) m, err := s.State.AddMachineInsideMachine(state.MachineTemplate{ Base: state.UbuntuBase("12.10"), Jobs: []state.MachineJob{state.JobHostUnits}, - }, "0", instance.KVM) + }, "0", instance.LXD) c.Assert(err, jc.ErrorIsNil) - c.Assert(m.Id(), gc.Equals, "0/kvm/0") - s.assertMachineContainers(c, host, []string{"0/kvm/0"}) + c.Assert(m.Id(), gc.Equals, "0/lxd/0") + s.assertMachineContainers(c, host, []string{"0/lxd/0"}) } func (s *StateSuite) TestAddInvalidContainerToMachineWithKnownSupportedContainers(c *gc.C) { oneJob := []state.MachineJob{state.JobHostUnits} host, err := s.State.AddMachine(defaultInstancePrechecker, state.UbuntuBase("12.10"), oneJob...) c.Assert(err, jc.ErrorIsNil) - err = host.SetSupportedContainers([]instance.ContainerType{instance.KVM}) + err = host.SetSupportedContainers([]instance.ContainerType{instance.LXD}) c.Assert(err, jc.ErrorIsNil) _, err = s.State.AddMachineInsideMachine(state.MachineTemplate{ Base: state.UbuntuBase("12.10"), Jobs: []state.MachineJob{state.JobHostUnits}, - }, "0", instance.LXD) - c.Assert(err, gc.ErrorMatches, "cannot add a new machine: machine 0 cannot host lxd containers") + }, "0", instance.ContainerType("abc")) + c.Assert(err, gc.ErrorMatches, "cannot add a new machine: machine 0 cannot host abc containers") s.assertMachineContainers(c, host, nil) } @@ -1351,13 +1351,13 @@ func (s *StateSuite) TestAddMachineCanOnlyAddControllerForMachine0(c *gc.C) { c.Assert(controllerIds, gc.DeepEquals, []string{"0"}) const errCannotAdd = "cannot add a new machine: controller jobs specified but not allowed" - m, err = s.State.AddOneMachine(defaultInstancePrechecker, template) + _, err = s.State.AddOneMachine(defaultInstancePrechecker, template) c.Assert(err, gc.ErrorMatches, errCannotAdd) - m, err = s.State.AddMachineInsideMachine(template, "0", instance.LXD) + _, err = s.State.AddMachineInsideMachine(template, "0", instance.LXD) c.Assert(err, gc.ErrorMatches, errCannotAdd) - m, err = s.State.AddMachineInsideNewMachine(defaultInstancePrechecker, template, template, instance.LXD) + _, err = s.State.AddMachineInsideNewMachine(defaultInstancePrechecker, template, template, instance.LXD) c.Assert(err, gc.ErrorMatches, errCannotAdd) } @@ -1386,7 +1386,7 @@ func (s *StateSuite) TestMachineIdLessThan(c *gc.C) { c.Assert(state.MachineIdLessThan("1", "0/lxd/0"), jc.IsFalse) c.Assert(state.MachineIdLessThan("0/lxd/0", "1"), jc.IsTrue) c.Assert(state.MachineIdLessThan("0/lxd/0/lxd/1", "0/lxd/0"), jc.IsFalse) - c.Assert(state.MachineIdLessThan("0/kvm/0", "0/lxd/0"), jc.IsTrue) + c.Assert(state.MachineIdLessThan("0/lxd/0", "0/lxd/0"), jc.IsFalse) } func (s *StateSuite) TestAllMachines(c *gc.C) { @@ -2941,14 +2941,6 @@ func (s *StateSuite) TestWatchContainerLifecycle(c *gc.C) { wcAll.AssertChange("0/lxd/0") wcAll.AssertNoChange() - // Add a container of a different type: not reported. - m1, err := s.State.AddMachineInsideMachine(template, machine.Id(), instance.KVM) - c.Assert(err, jc.ErrorIsNil) - wc.AssertNoChange() - // But reported by the all watcher. - wcAll.AssertChange("0/kvm/0") - wcAll.AssertNoChange() - // Add a nested container of the right type: not reported. mchild, err := s.State.AddMachineInsideMachine(template, m.Id(), instance.LXD) c.Assert(err, jc.ErrorIsNil) @@ -2956,7 +2948,7 @@ func (s *StateSuite) TestWatchContainerLifecycle(c *gc.C) { wcAll.AssertNoChange() // Add a container of a different machine: not reported. - m2, err := s.State.AddMachineInsideMachine(template, otherMachine.Id(), instance.LXD) + m1, err := s.State.AddMachineInsideMachine(template, otherMachine.Id(), instance.LXD) c.Assert(err, jc.ErrorIsNil) wc.AssertNoChange() workertest.CleanKill(c, w) @@ -2971,7 +2963,7 @@ func (s *StateSuite) TestWatchContainerLifecycle(c *gc.C) { wcAll = statetesting.NewStringsWatcherC(c, wAll) wc.AssertChange("0/lxd/0") wc.AssertNoChange() - wcAll.AssertChange("0/kvm/0", "0/lxd/0") + wcAll.AssertChange("0/lxd/0") wcAll.AssertNoChange() // Make the container Dying: cannot because of nested container. @@ -2994,12 +2986,7 @@ func (s *StateSuite) TestWatchContainerLifecycle(c *gc.C) { // Make the other containers Dying: not reported. err = m1.Destroy(state.NewObjectStore(c, s.State.ModelUUID())) c.Assert(err, jc.ErrorIsNil) - err = m2.Destroy(state.NewObjectStore(c, s.State.ModelUUID())) - c.Assert(err, jc.ErrorIsNil) wc.AssertNoChange() - // But reported by the all watcher. - wcAll.AssertChange("0/kvm/0") - wcAll.AssertNoChange() // Make the container Dead: reported. err = m.EnsureDead() @@ -3012,12 +2999,7 @@ func (s *StateSuite) TestWatchContainerLifecycle(c *gc.C) { // Make the other containers Dead: not reported. err = m1.EnsureDead() c.Assert(err, jc.ErrorIsNil) - err = m2.EnsureDead() - c.Assert(err, jc.ErrorIsNil) wc.AssertNoChange() - // But reported by the all watcher. - wcAll.AssertChange("0/kvm/0") - wcAll.AssertNoChange() // Remove the container: not reported. err = m.Remove(state.NewObjectStore(c, s.State.ModelUUID()))