github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/environs/jujutest/livetests.go (about)

     1  // Copyright 2011, 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package jujutest
     5  
     6  import (
     7  	stdcontext "context"
     8  	"fmt"
     9  	"path/filepath"
    10  	"strconv"
    11  	"time"
    12  
    13  	"github.com/juju/charm/v12"
    14  	"github.com/juju/errors"
    15  	gitjujutesting "github.com/juju/testing"
    16  	jc "github.com/juju/testing/checkers"
    17  	"github.com/juju/utils/v3"
    18  	"github.com/juju/version/v2"
    19  	gc "gopkg.in/check.v1"
    20  
    21  	"github.com/juju/juju/api"
    22  	apiclient "github.com/juju/juju/api/client/client"
    23  	"github.com/juju/juju/cloud"
    24  	"github.com/juju/juju/cloudconfig/instancecfg"
    25  	"github.com/juju/juju/core/arch"
    26  	corebase "github.com/juju/juju/core/base"
    27  	"github.com/juju/juju/core/constraints"
    28  	"github.com/juju/juju/core/instance"
    29  	"github.com/juju/juju/core/network"
    30  	"github.com/juju/juju/core/network/firewall"
    31  	"github.com/juju/juju/core/status"
    32  	"github.com/juju/juju/environs"
    33  	"github.com/juju/juju/environs/bootstrap"
    34  	environscloudspec "github.com/juju/juju/environs/cloudspec"
    35  	"github.com/juju/juju/environs/config"
    36  	"github.com/juju/juju/environs/context"
    37  	"github.com/juju/juju/environs/filestorage"
    38  	"github.com/juju/juju/environs/instances"
    39  	"github.com/juju/juju/environs/models"
    40  	"github.com/juju/juju/environs/simplestreams"
    41  	sstesting "github.com/juju/juju/environs/simplestreams/testing"
    42  	"github.com/juju/juju/environs/storage"
    43  	"github.com/juju/juju/environs/sync"
    44  	envtesting "github.com/juju/juju/environs/testing"
    45  	envtools "github.com/juju/juju/environs/tools"
    46  	envtoolstesting "github.com/juju/juju/environs/tools/testing"
    47  	"github.com/juju/juju/juju/keys"
    48  	jujutesting "github.com/juju/juju/juju/testing"
    49  	"github.com/juju/juju/jujuclient"
    50  	"github.com/juju/juju/provider/dummy"
    51  	"github.com/juju/juju/state"
    52  	stateerrors "github.com/juju/juju/state/errors"
    53  	statetesting "github.com/juju/juju/state/testing"
    54  	"github.com/juju/juju/testcharms"
    55  	coretesting "github.com/juju/juju/testing"
    56  	coretools "github.com/juju/juju/tools"
    57  	jujuversion "github.com/juju/juju/version"
    58  )
    59  
    60  const (
    61  	AdminSecret = "admin-secret"
    62  )
    63  
    64  // LiveTests contains tests that are designed to run against a live server
    65  // (e.g. Amazon EC2).  The Environ is opened once only for all the tests
    66  // in the suite, stored in Env, and Destroyed after the suite has completed.
    67  type LiveTests struct {
    68  	gitjujutesting.CleanupSuite
    69  
    70  	envtesting.ToolsFixture
    71  	sstesting.TestDataSuite
    72  
    73  	// TestConfig contains the configuration attributes for opening an environment.
    74  	TestConfig coretesting.Attrs
    75  
    76  	// Credential contains the credential for preparing an environment for
    77  	// bootstrapping. If this is unset, empty credentials will be used.
    78  	Credential cloud.Credential
    79  
    80  	// CloudRegion contains the cloud region name to create resources in.
    81  	CloudRegion string
    82  
    83  	// CloudEndpoint contains the cloud API endpoint to communicate with.
    84  	CloudEndpoint string
    85  
    86  	// Attempt holds a strategy for waiting until the environment
    87  	// becomes logically consistent.
    88  	//
    89  	// TODO(katco): 2016-08-09: lp:1611427
    90  	Attempt utils.AttemptStrategy
    91  
    92  	// CanOpenState should be true if the testing environment allows
    93  	// the state to be opened after bootstrapping.
    94  	CanOpenState bool
    95  
    96  	// HasProvisioner should be true if the environment has
    97  	// a provisioning agent.
    98  	HasProvisioner bool
    99  
   100  	// Env holds the currently opened environment.
   101  	// This is set by PrepareOnce and BootstrapOnce.
   102  	Env environs.Environ
   103  
   104  	// ControllerStore holds the controller related information
   105  	// such as controllers, accounts, etc., used when preparing
   106  	// the environment. This is initialized by SetUpSuite.
   107  	ControllerStore jujuclient.ClientStore
   108  
   109  	// ControllerUUID is the uuid of the bootstrapped controller.
   110  	ControllerUUID string
   111  
   112  	// ProviderCallContext holds the context to be used to make
   113  	// calls to a cloud provider.
   114  	ProviderCallContext context.ProviderCallContext
   115  
   116  	// BootstrapContext holds the context to bootstrap a test environment.
   117  	BootstrapContext environs.BootstrapContext
   118  
   119  	prepared     bool
   120  	bootstrapped bool
   121  	toolsStorage storage.Storage
   122  }
   123  
   124  func (t *LiveTests) SetUpSuite(c *gc.C) {
   125  	t.CleanupSuite.SetUpSuite(c)
   126  	t.TestDataSuite.SetUpSuite(c)
   127  	t.ControllerStore = jujuclient.NewMemStore()
   128  	t.PatchValue(&keys.JujuPublicKey, sstesting.SignedMetadataPublicKey)
   129  
   130  	// Setup the simplestreams bootstrap context.
   131  	ss := simplestreams.NewSimpleStreams(sstesting.TestDataSourceFactory())
   132  
   133  	ctx := stdcontext.TODO()
   134  	ctx = stdcontext.WithValue(ctx, bootstrap.SimplestreamsFetcherContextKey, ss)
   135  
   136  	t.BootstrapContext = envtesting.BootstrapContext(ctx, c)
   137  	t.ProviderCallContext = context.NewCloudCallContext(ctx)
   138  }
   139  
   140  func (t *LiveTests) SetUpTest(c *gc.C) {
   141  	t.CleanupSuite.SetUpTest(c)
   142  	t.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber)
   143  	storageDir := c.MkDir()
   144  	baseURLPath := filepath.Join(storageDir, "tools")
   145  	t.DefaultBaseURL = utils.MakeFileURL(baseURLPath)
   146  	t.ToolsFixture.SetUpTest(c)
   147  	stor, err := filestorage.NewFileStorageWriter(storageDir)
   148  	c.Assert(err, jc.ErrorIsNil)
   149  	t.UploadFakeTools(c, stor, "released", "released")
   150  	t.toolsStorage = stor
   151  	t.CleanupSuite.PatchValue(&envtools.BundleTools, envtoolstesting.GetMockBundleTools(coretesting.FakeVersionNumber))
   152  }
   153  
   154  func (t *LiveTests) TearDownSuite(c *gc.C) {
   155  	t.Destroy(c)
   156  	t.TestDataSuite.TearDownSuite(c)
   157  	t.CleanupSuite.TearDownSuite(c)
   158  }
   159  
   160  func (t *LiveTests) TearDownTest(c *gc.C) {
   161  	t.ToolsFixture.TearDownTest(c)
   162  	t.CleanupSuite.TearDownTest(c)
   163  }
   164  
   165  // PrepareOnce ensures that the environment is
   166  // available and prepared. It sets t.Env appropriately.
   167  func (t *LiveTests) PrepareOnce(c *gc.C) {
   168  	if t.prepared {
   169  		return
   170  	}
   171  
   172  	args := t.prepareForBootstrapParams(c)
   173  	e, err := bootstrap.PrepareController(false, t.BootstrapContext, t.ControllerStore, args)
   174  	c.Assert(err, gc.IsNil, gc.Commentf("preparing environ %#v", t.TestConfig))
   175  	c.Assert(e, gc.NotNil)
   176  	t.Env = e.(environs.Environ)
   177  	t.prepared = true
   178  	t.ControllerUUID = coretesting.FakeControllerConfig().ControllerUUID()
   179  }
   180  
   181  func (t *LiveTests) prepareForBootstrapParams(c *gc.C) bootstrap.PrepareParams {
   182  	credential := t.Credential
   183  	if credential.AuthType() == "" {
   184  		credential = cloud.NewEmptyCredential()
   185  	}
   186  	return bootstrap.PrepareParams{
   187  		ControllerConfig: coretesting.FakeControllerConfig(),
   188  		ModelConfig:      t.TestConfig,
   189  		Cloud: environscloudspec.CloudSpec{
   190  			Type:       t.TestConfig["type"].(string),
   191  			Name:       t.TestConfig["type"].(string),
   192  			Region:     t.CloudRegion,
   193  			Endpoint:   t.CloudEndpoint,
   194  			Credential: &credential,
   195  		},
   196  		ControllerName: t.TestConfig["name"].(string),
   197  		AdminSecret:    AdminSecret,
   198  	}
   199  }
   200  
   201  func (t *LiveTests) bootstrapParams() bootstrap.BootstrapParams {
   202  	credential := t.Credential
   203  	if credential.AuthType() == "" {
   204  		credential = cloud.NewEmptyCredential()
   205  	}
   206  	var regions []cloud.Region
   207  	if t.CloudRegion != "" {
   208  		regions = []cloud.Region{{
   209  			Name:     t.CloudRegion,
   210  			Endpoint: t.CloudEndpoint,
   211  		}}
   212  	}
   213  	return bootstrap.BootstrapParams{
   214  		ControllerConfig: coretesting.FakeControllerConfig(),
   215  		Cloud: cloud.Cloud{
   216  			Name:      t.TestConfig["type"].(string),
   217  			Type:      t.TestConfig["type"].(string),
   218  			AuthTypes: []cloud.AuthType{credential.AuthType()},
   219  			Regions:   regions,
   220  			Endpoint:  t.CloudEndpoint,
   221  		},
   222  		CloudRegion:             t.CloudRegion,
   223  		CloudCredential:         &credential,
   224  		CloudCredentialName:     "credential",
   225  		AdminSecret:             AdminSecret,
   226  		CAPrivateKey:            coretesting.CAKey,
   227  		SupportedBootstrapBases: coretesting.FakeSupportedJujuBases,
   228  	}
   229  }
   230  
   231  func (t *LiveTests) BootstrapOnce(c *gc.C) {
   232  	if t.bootstrapped {
   233  		return
   234  	}
   235  
   236  	t.PrepareOnce(c)
   237  	// We only build and upload tools if there will be a state agent that
   238  	// we could connect to (actual live tests, rather than local-only)
   239  	cons := constraints.MustParse("mem=2G")
   240  	if t.CanOpenState {
   241  		ss := simplestreams.NewSimpleStreams(sstesting.TestDataSourceFactory())
   242  		_, err := sync.Upload(ss, t.toolsStorage, "released", nil)
   243  		c.Assert(err, jc.ErrorIsNil)
   244  	}
   245  	args := t.bootstrapParams()
   246  	args.BootstrapConstraints = cons
   247  	args.ModelConstraints = cons
   248  
   249  	err := bootstrap.Bootstrap(t.BootstrapContext, t.Env, t.ProviderCallContext, args)
   250  	c.Assert(err, jc.ErrorIsNil)
   251  	t.bootstrapped = true
   252  }
   253  
   254  func (t *LiveTests) Destroy(c *gc.C) {
   255  	if t.Env == nil {
   256  		return
   257  	}
   258  	err := environs.Destroy(t.Env.Config().Name(), t.Env, t.ProviderCallContext, t.ControllerStore)
   259  	c.Assert(err, jc.ErrorIsNil)
   260  	t.bootstrapped = false
   261  	t.prepared = false
   262  	t.ControllerUUID = ""
   263  	t.Env = nil
   264  }
   265  
   266  func (t *LiveTests) TestPrechecker(c *gc.C) {
   267  	// All implementations of InstancePrechecker should
   268  	// return nil for empty constraints (excluding the
   269  	// manual provider).
   270  	t.PrepareOnce(c)
   271  	err := t.Env.PrecheckInstance(t.ProviderCallContext,
   272  		environs.PrecheckInstanceParams{
   273  			Base: corebase.MakeDefaultBase("ubuntu", "22.04"),
   274  		})
   275  	c.Assert(err, jc.ErrorIsNil)
   276  }
   277  
   278  // TestStartStop is similar to Tests.TestStartStop except
   279  // that it does not assume a pristine environment.
   280  func (t *LiveTests) TestStartStop(c *gc.C) {
   281  	t.BootstrapOnce(c)
   282  
   283  	inst, _ := jujutesting.AssertStartInstance(c, t.Env, t.ProviderCallContext, t.ControllerUUID, "0")
   284  	c.Assert(inst, gc.NotNil)
   285  	id0 := inst.Id()
   286  
   287  	insts, err := t.Env.Instances(t.ProviderCallContext, []instance.Id{id0, id0})
   288  	c.Assert(err, jc.ErrorIsNil)
   289  	c.Assert(insts, gc.HasLen, 2)
   290  	c.Assert(insts[0].Id(), gc.Equals, id0)
   291  	c.Assert(insts[1].Id(), gc.Equals, id0)
   292  
   293  	// Asserting on the return of AllInstances makes the test fragile,
   294  	// as even comparing the before and after start values can be thrown
   295  	// off if other instances have been created or destroyed in the same
   296  	// time frame. Instead, just check the instance we created exists.
   297  	insts, err = t.Env.AllInstances(t.ProviderCallContext)
   298  	c.Assert(err, jc.ErrorIsNil)
   299  	found := false
   300  	for _, inst := range insts {
   301  		if inst.Id() == id0 {
   302  			c.Assert(found, gc.Equals, false, gc.Commentf("%v", insts))
   303  			found = true
   304  		}
   305  	}
   306  	c.Assert(found, gc.Equals, true, gc.Commentf("expected %v in %v", inst, insts))
   307  
   308  	addresses, err := jujutesting.WaitInstanceAddresses(t.Env, t.ProviderCallContext, inst.Id())
   309  	c.Assert(err, jc.ErrorIsNil)
   310  	c.Assert(addresses, gc.Not(gc.HasLen), 0)
   311  
   312  	insts, err = t.Env.Instances(t.ProviderCallContext, []instance.Id{id0, ""})
   313  	c.Assert(err, gc.Equals, environs.ErrPartialInstances)
   314  	c.Assert(insts, gc.HasLen, 2)
   315  	c.Check(insts[0].Id(), gc.Equals, id0)
   316  	c.Check(insts[1], gc.IsNil)
   317  
   318  	err = t.Env.StopInstances(t.ProviderCallContext, inst.Id())
   319  	c.Assert(err, jc.ErrorIsNil)
   320  
   321  	// The machine may not be marked as shutting down
   322  	// immediately. Repeat a few times to ensure we get the error.
   323  	for a := t.Attempt.Start(); a.Next(); {
   324  		insts, err = t.Env.Instances(t.ProviderCallContext, []instance.Id{id0})
   325  		if err != nil {
   326  			break
   327  		}
   328  	}
   329  	c.Assert(err, gc.Equals, environs.ErrNoInstances)
   330  	c.Assert(insts, gc.HasLen, 0)
   331  }
   332  
   333  func (t *LiveTests) TestPorts(c *gc.C) {
   334  	t.BootstrapOnce(c)
   335  
   336  	inst1, _ := jujutesting.AssertStartInstance(c, t.Env, t.ProviderCallContext, t.ControllerUUID, "1")
   337  	c.Assert(inst1, gc.NotNil)
   338  	defer func() { _ = t.Env.StopInstances(t.ProviderCallContext, inst1.Id()) }()
   339  	fwInst1, ok := inst1.(instances.InstanceFirewaller)
   340  	c.Assert(ok, gc.Equals, true)
   341  
   342  	rules, err := fwInst1.IngressRules(t.ProviderCallContext, "1")
   343  	c.Assert(err, jc.ErrorIsNil)
   344  	c.Assert(rules, gc.HasLen, 0)
   345  
   346  	inst2, _ := jujutesting.AssertStartInstance(c, t.Env, t.ProviderCallContext, t.ControllerUUID, "2")
   347  	c.Assert(inst2, gc.NotNil)
   348  	fwInst2, ok := inst2.(instances.InstanceFirewaller)
   349  	c.Assert(ok, gc.Equals, true)
   350  	rules, err = fwInst2.IngressRules(t.ProviderCallContext, "2")
   351  	c.Assert(err, jc.ErrorIsNil)
   352  	c.Assert(rules, gc.HasLen, 0)
   353  	defer func() { _ = t.Env.StopInstances(t.ProviderCallContext, inst2.Id()) }()
   354  
   355  	// Open some ports and check they're there.
   356  	err = fwInst1.OpenPorts(t.ProviderCallContext,
   357  		"1", firewall.IngressRules{
   358  			firewall.NewIngressRule(network.MustParsePortRange("67/udp")),
   359  			firewall.NewIngressRule(network.MustParsePortRange("45/tcp")),
   360  			firewall.NewIngressRule(network.MustParsePortRange("80-100/tcp")),
   361  		})
   362  
   363  	c.Assert(err, jc.ErrorIsNil)
   364  	rules, err = fwInst1.IngressRules(t.ProviderCallContext, "1")
   365  	c.Assert(err, jc.ErrorIsNil)
   366  	c.Assert(
   367  		rules, jc.DeepEquals,
   368  		firewall.IngressRules{
   369  			firewall.NewIngressRule(network.MustParsePortRange("45/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   370  			firewall.NewIngressRule(network.MustParsePortRange("80-100/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   371  			firewall.NewIngressRule(network.MustParsePortRange("67/udp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   372  		},
   373  	)
   374  	rules, err = fwInst2.IngressRules(t.ProviderCallContext, "2")
   375  	c.Assert(err, jc.ErrorIsNil)
   376  	c.Assert(rules, gc.HasLen, 0)
   377  
   378  	err = fwInst2.OpenPorts(t.ProviderCallContext,
   379  		"2", firewall.IngressRules{
   380  			firewall.NewIngressRule(network.MustParsePortRange("89/tcp")),
   381  			firewall.NewIngressRule(network.MustParsePortRange("45/tcp")),
   382  			firewall.NewIngressRule(network.MustParsePortRange("20-30/tcp")),
   383  		})
   384  	c.Assert(err, jc.ErrorIsNil)
   385  
   386  	// Check there's no crosstalk to another machine
   387  	rules, err = fwInst2.IngressRules(t.ProviderCallContext, "2")
   388  	c.Assert(err, jc.ErrorIsNil)
   389  	c.Assert(
   390  		rules, jc.DeepEquals,
   391  		firewall.IngressRules{
   392  			firewall.NewIngressRule(network.MustParsePortRange("20-30/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   393  			firewall.NewIngressRule(network.MustParsePortRange("45/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   394  			firewall.NewIngressRule(network.MustParsePortRange("89/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   395  		},
   396  	)
   397  	rules, err = fwInst1.IngressRules(t.ProviderCallContext, "1")
   398  	c.Assert(err, jc.ErrorIsNil)
   399  	c.Assert(
   400  		rules, jc.DeepEquals,
   401  		firewall.IngressRules{
   402  			firewall.NewIngressRule(network.MustParsePortRange("45/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   403  			firewall.NewIngressRule(network.MustParsePortRange("80-100/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   404  			firewall.NewIngressRule(network.MustParsePortRange("67/udp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   405  		},
   406  	)
   407  
   408  	// Check that opening the same port again is ok.
   409  	oldRules, err := fwInst2.IngressRules(t.ProviderCallContext, "2")
   410  	c.Assert(err, jc.ErrorIsNil)
   411  	err = fwInst2.OpenPorts(t.ProviderCallContext,
   412  		"2", firewall.IngressRules{
   413  			firewall.NewIngressRule(network.MustParsePortRange("45/tcp")),
   414  		})
   415  	c.Assert(err, jc.ErrorIsNil)
   416  	err = fwInst2.OpenPorts(t.ProviderCallContext,
   417  		"2", firewall.IngressRules{
   418  			firewall.NewIngressRule(network.MustParsePortRange("20-30/tcp")),
   419  		})
   420  	c.Assert(err, jc.ErrorIsNil)
   421  	rules, err = fwInst2.IngressRules(t.ProviderCallContext, "2")
   422  	c.Assert(err, jc.ErrorIsNil)
   423  	c.Assert(rules, jc.DeepEquals, oldRules)
   424  
   425  	// Check that opening the same port again and another port is ok.
   426  	err = fwInst2.OpenPorts(t.ProviderCallContext,
   427  		"2", firewall.IngressRules{
   428  			firewall.NewIngressRule(network.MustParsePortRange("45/tcp")),
   429  			firewall.NewIngressRule(network.MustParsePortRange("99/tcp")),
   430  		})
   431  	c.Assert(err, jc.ErrorIsNil)
   432  	rules, err = fwInst2.IngressRules(t.ProviderCallContext, "2")
   433  	c.Assert(err, jc.ErrorIsNil)
   434  	c.Assert(
   435  		rules, jc.DeepEquals,
   436  		firewall.IngressRules{
   437  			firewall.NewIngressRule(network.MustParsePortRange("20-30/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   438  			firewall.NewIngressRule(network.MustParsePortRange("45/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   439  			firewall.NewIngressRule(network.MustParsePortRange("89/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   440  			firewall.NewIngressRule(network.MustParsePortRange("99/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   441  		},
   442  	)
   443  	err = fwInst2.ClosePorts(t.ProviderCallContext,
   444  		"2", firewall.IngressRules{
   445  			firewall.NewIngressRule(network.MustParsePortRange("45/tcp")),
   446  			firewall.NewIngressRule(network.MustParsePortRange("99/tcp")),
   447  			firewall.NewIngressRule(network.MustParsePortRange("20-30/tcp")),
   448  		})
   449  	c.Assert(err, jc.ErrorIsNil)
   450  
   451  	// Check that we can close ports and that there's no crosstalk.
   452  	rules, err = fwInst2.IngressRules(t.ProviderCallContext, "2")
   453  	c.Assert(err, jc.ErrorIsNil)
   454  	c.Assert(
   455  		rules, jc.DeepEquals,
   456  		firewall.IngressRules{
   457  			firewall.NewIngressRule(network.MustParsePortRange("89/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   458  		},
   459  	)
   460  	rules, err = fwInst1.IngressRules(t.ProviderCallContext, "1")
   461  	c.Assert(err, jc.ErrorIsNil)
   462  	c.Assert(
   463  		rules, jc.DeepEquals,
   464  		firewall.IngressRules{
   465  			firewall.NewIngressRule(network.MustParsePortRange("45/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   466  			firewall.NewIngressRule(network.MustParsePortRange("80-100/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   467  			firewall.NewIngressRule(network.MustParsePortRange("67/udp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   468  		},
   469  	)
   470  
   471  	// Check that we can close multiple ports.
   472  	err = fwInst1.ClosePorts(t.ProviderCallContext,
   473  		"1", firewall.IngressRules{
   474  			firewall.NewIngressRule(network.MustParsePortRange("45/tcp")),
   475  			firewall.NewIngressRule(network.MustParsePortRange("67/udp")),
   476  			firewall.NewIngressRule(network.MustParsePortRange("80-100/tcp")),
   477  		})
   478  	c.Assert(err, jc.ErrorIsNil)
   479  	rules, err = fwInst1.IngressRules(t.ProviderCallContext, "1")
   480  	c.Assert(err, jc.ErrorIsNil)
   481  	c.Assert(rules, gc.HasLen, 0)
   482  
   483  	// Check that we can close ports that aren't there.
   484  	err = fwInst2.ClosePorts(t.ProviderCallContext,
   485  		"2", firewall.IngressRules{
   486  			firewall.NewIngressRule(network.MustParsePortRange("111/tcp")),
   487  			firewall.NewIngressRule(network.MustParsePortRange("222/udp")),
   488  			firewall.NewIngressRule(network.MustParsePortRange("600-700/tcp")),
   489  		})
   490  	c.Assert(err, jc.ErrorIsNil)
   491  	rules, err = fwInst2.IngressRules(t.ProviderCallContext, "2")
   492  	c.Assert(err, jc.ErrorIsNil)
   493  	c.Assert(
   494  		rules, jc.DeepEquals,
   495  		firewall.IngressRules{
   496  			firewall.NewIngressRule(network.MustParsePortRange("89/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   497  		},
   498  	)
   499  
   500  	// Check errors when acting on environment.
   501  	fwEnv, ok := t.Env.(environs.Firewaller)
   502  	c.Assert(ok, gc.Equals, true)
   503  	err = fwEnv.OpenPorts(t.ProviderCallContext, firewall.IngressRules{firewall.NewIngressRule(network.MustParsePortRange("80/tcp"))})
   504  	c.Assert(err, gc.ErrorMatches, `invalid firewall mode "instance" for opening ports on model`)
   505  
   506  	err = fwEnv.ClosePorts(t.ProviderCallContext, firewall.IngressRules{firewall.NewIngressRule(network.MustParsePortRange("80/tcp"))})
   507  	c.Assert(err, gc.ErrorMatches, `invalid firewall mode "instance" for closing ports on model`)
   508  
   509  	_, err = fwEnv.IngressRules(t.ProviderCallContext)
   510  	c.Assert(err, gc.ErrorMatches, `invalid firewall mode "instance" for retrieving ingress rules from model`)
   511  }
   512  
   513  func (t *LiveTests) TestGlobalPorts(c *gc.C) {
   514  	t.BootstrapOnce(c)
   515  
   516  	// Change configuration.
   517  	oldConfig := t.Env.Config()
   518  	defer func() {
   519  		err := t.Env.SetConfig(oldConfig)
   520  		c.Assert(err, jc.ErrorIsNil)
   521  	}()
   522  
   523  	attrs := t.Env.Config().AllAttrs()
   524  	attrs["firewall-mode"] = config.FwGlobal
   525  	newConfig, err := t.Env.Config().Apply(attrs)
   526  	c.Assert(err, jc.ErrorIsNil)
   527  	err = t.Env.SetConfig(newConfig)
   528  	c.Assert(err, jc.ErrorIsNil)
   529  
   530  	// Create instances and check open ports on both instances.
   531  	inst1, _ := jujutesting.AssertStartInstance(c, t.Env, t.ProviderCallContext, t.ControllerUUID, "1")
   532  	defer func() { _ = t.Env.StopInstances(t.ProviderCallContext, inst1.Id()) }()
   533  
   534  	fwEnv, ok := t.Env.(environs.Firewaller)
   535  	c.Assert(ok, gc.Equals, true)
   536  
   537  	rules, err := fwEnv.IngressRules(t.ProviderCallContext)
   538  	c.Assert(err, jc.ErrorIsNil)
   539  	c.Assert(rules, gc.HasLen, 0)
   540  
   541  	inst2, _ := jujutesting.AssertStartInstance(c, t.Env, t.ProviderCallContext, t.ControllerUUID, "2")
   542  	rules, err = fwEnv.IngressRules(t.ProviderCallContext)
   543  	c.Assert(err, jc.ErrorIsNil)
   544  	c.Assert(rules, gc.HasLen, 0)
   545  	defer func() { _ = t.Env.StopInstances(t.ProviderCallContext, inst2.Id()) }()
   546  
   547  	err = fwEnv.OpenPorts(t.ProviderCallContext,
   548  		firewall.IngressRules{
   549  			firewall.NewIngressRule(network.MustParsePortRange("67/udp")),
   550  			firewall.NewIngressRule(network.MustParsePortRange("45/tcp")),
   551  			firewall.NewIngressRule(network.MustParsePortRange("89/tcp")),
   552  			firewall.NewIngressRule(network.MustParsePortRange("99/tcp")),
   553  			firewall.NewIngressRule(network.MustParsePortRange("100-110/tcp")),
   554  		})
   555  	c.Assert(err, jc.ErrorIsNil)
   556  
   557  	rules, err = fwEnv.IngressRules(t.ProviderCallContext)
   558  	c.Assert(err, jc.ErrorIsNil)
   559  	c.Assert(
   560  		rules, jc.DeepEquals,
   561  		firewall.IngressRules{
   562  			firewall.NewIngressRule(network.MustParsePortRange("45/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   563  			firewall.NewIngressRule(network.MustParsePortRange("89/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   564  			firewall.NewIngressRule(network.MustParsePortRange("99/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   565  			firewall.NewIngressRule(network.MustParsePortRange("100-110/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   566  			firewall.NewIngressRule(network.MustParsePortRange("67/udp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   567  		},
   568  	)
   569  
   570  	// Check closing some ports.
   571  	err = fwEnv.ClosePorts(t.ProviderCallContext,
   572  		firewall.IngressRules{
   573  			firewall.NewIngressRule(network.MustParsePortRange("99/tcp")),
   574  			firewall.NewIngressRule(network.MustParsePortRange("67/udp")),
   575  		})
   576  	c.Assert(err, jc.ErrorIsNil)
   577  
   578  	rules, err = fwEnv.IngressRules(t.ProviderCallContext)
   579  	c.Assert(err, jc.ErrorIsNil)
   580  	c.Assert(
   581  		rules, jc.DeepEquals,
   582  		firewall.IngressRules{
   583  			firewall.NewIngressRule(network.MustParsePortRange("45/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   584  			firewall.NewIngressRule(network.MustParsePortRange("89/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   585  			firewall.NewIngressRule(network.MustParsePortRange("100-110/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   586  		},
   587  	)
   588  
   589  	// Check that we can close ports that aren't there.
   590  	err = fwEnv.ClosePorts(t.ProviderCallContext,
   591  		firewall.IngressRules{
   592  			firewall.NewIngressRule(network.MustParsePortRange("111/tcp")),
   593  			firewall.NewIngressRule(network.MustParsePortRange("222/udp")),
   594  			firewall.NewIngressRule(network.MustParsePortRange("2000-2500/tcp")),
   595  		})
   596  	c.Assert(err, jc.ErrorIsNil)
   597  
   598  	rules, err = fwEnv.IngressRules(t.ProviderCallContext)
   599  	c.Assert(err, jc.ErrorIsNil)
   600  	c.Assert(
   601  		rules, jc.DeepEquals,
   602  		firewall.IngressRules{
   603  			firewall.NewIngressRule(network.MustParsePortRange("45/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   604  			firewall.NewIngressRule(network.MustParsePortRange("89/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   605  			firewall.NewIngressRule(network.MustParsePortRange("100-110/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   606  		},
   607  	)
   608  
   609  	fwInst1, ok := inst1.(instances.InstanceFirewaller)
   610  	c.Assert(ok, gc.Equals, true)
   611  	// Check errors when acting on instances.
   612  	err = fwInst1.OpenPorts(t.ProviderCallContext,
   613  		"1", firewall.IngressRules{firewall.NewIngressRule(network.MustParsePortRange("80/tcp"))})
   614  	c.Assert(err, gc.ErrorMatches, `invalid firewall mode "global" for opening ports on instance`)
   615  
   616  	err = fwInst1.ClosePorts(t.ProviderCallContext,
   617  		"1", firewall.IngressRules{firewall.NewIngressRule(network.MustParsePortRange("80/tcp"))})
   618  	c.Assert(err, gc.ErrorMatches, `invalid firewall mode "global" for closing ports on instance`)
   619  
   620  	_, err = fwInst1.IngressRules(t.ProviderCallContext, "1")
   621  	c.Assert(err, gc.ErrorMatches, `invalid firewall mode "global" for retrieving ingress rules from instance`)
   622  }
   623  
   624  func (t *LiveTests) TestModelPorts(c *gc.C) {
   625  	t.BootstrapOnce(c)
   626  
   627  	fwModelEnv, ok := t.Env.(models.ModelFirewaller)
   628  	c.Assert(ok, gc.Equals, true)
   629  
   630  	rules, err := fwModelEnv.ModelIngressRules(t.ProviderCallContext)
   631  	c.Assert(err, jc.ErrorIsNil)
   632  	c.Assert(rules, jc.SameContents, firewall.IngressRules{
   633  		firewall.NewIngressRule(network.MustParsePortRange("22/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   634  		// TODO: extend tests to check the api port isn't on hosted models.
   635  		firewall.NewIngressRule(network.MustParsePortRange(strconv.Itoa(coretesting.FakeControllerConfig().APIPort())), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   636  	})
   637  
   638  	err = fwModelEnv.OpenModelPorts(t.ProviderCallContext,
   639  		firewall.IngressRules{
   640  			firewall.NewIngressRule(network.MustParsePortRange("67/udp")),
   641  			firewall.NewIngressRule(network.MustParsePortRange("45/tcp")),
   642  			firewall.NewIngressRule(network.MustParsePortRange("100-110/tcp")),
   643  		})
   644  	c.Assert(err, jc.ErrorIsNil)
   645  
   646  	rules, err = fwModelEnv.ModelIngressRules(t.ProviderCallContext)
   647  	c.Assert(err, jc.ErrorIsNil)
   648  	c.Assert(rules, jc.SameContents, firewall.IngressRules{
   649  		firewall.NewIngressRule(network.MustParsePortRange("22/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   650  		// TODO: extend tests to check the api port isn't on hosted models.
   651  		firewall.NewIngressRule(network.MustParsePortRange(strconv.Itoa(coretesting.FakeControllerConfig().APIPort())), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   652  		firewall.NewIngressRule(network.MustParsePortRange("45/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   653  		firewall.NewIngressRule(network.MustParsePortRange("100-110/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   654  		firewall.NewIngressRule(network.MustParsePortRange("67/udp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   655  	})
   656  
   657  	// Check closing some ports.
   658  	err = fwModelEnv.CloseModelPorts(t.ProviderCallContext,
   659  		firewall.IngressRules{
   660  			firewall.NewIngressRule(network.MustParsePortRange("45/tcp")),
   661  			firewall.NewIngressRule(network.MustParsePortRange("67/udp")),
   662  		})
   663  	c.Assert(err, jc.ErrorIsNil)
   664  
   665  	rules, err = fwModelEnv.ModelIngressRules(t.ProviderCallContext)
   666  	c.Assert(err, jc.ErrorIsNil)
   667  	c.Assert(rules, jc.SameContents, firewall.IngressRules{
   668  		firewall.NewIngressRule(network.MustParsePortRange("22/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   669  		// TODO: extend tests to check the api port isn't on hosted models.
   670  		firewall.NewIngressRule(network.MustParsePortRange(strconv.Itoa(coretesting.FakeControllerConfig().APIPort())), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   671  		firewall.NewIngressRule(network.MustParsePortRange("100-110/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   672  	})
   673  
   674  	// Check that we can close ports that aren't there.
   675  	err = fwModelEnv.CloseModelPorts(t.ProviderCallContext,
   676  		firewall.IngressRules{
   677  			firewall.NewIngressRule(network.MustParsePortRange("111/tcp")),
   678  			firewall.NewIngressRule(network.MustParsePortRange("222/udp")),
   679  			firewall.NewIngressRule(network.MustParsePortRange("2000-2500/tcp")),
   680  		})
   681  	c.Assert(err, jc.ErrorIsNil)
   682  
   683  	rules, err = fwModelEnv.ModelIngressRules(t.ProviderCallContext)
   684  	c.Assert(err, jc.ErrorIsNil)
   685  	c.Assert(rules, jc.SameContents, firewall.IngressRules{
   686  		firewall.NewIngressRule(network.MustParsePortRange("22/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   687  		// TODO: extend tests to check the api port isn't on hosted models.
   688  		firewall.NewIngressRule(network.MustParsePortRange(strconv.Itoa(coretesting.FakeControllerConfig().APIPort())), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   689  		firewall.NewIngressRule(network.MustParsePortRange("100-110/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   690  	})
   691  
   692  	// Cleanup
   693  	err = fwModelEnv.CloseModelPorts(t.ProviderCallContext, firewall.IngressRules{
   694  		firewall.NewIngressRule(network.MustParsePortRange("100-110/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
   695  	})
   696  	c.Assert(err, jc.ErrorIsNil)
   697  }
   698  
   699  func (t *LiveTests) TestBootstrapMultiple(c *gc.C) {
   700  	// bootstrap.Bootstrap no longer raises errors if the environment is
   701  	// already up, this has been moved into the bootstrap command.
   702  	t.BootstrapOnce(c)
   703  
   704  	c.Logf("destroy env")
   705  	env := t.Env
   706  	t.Destroy(c)
   707  	err := env.Destroy(t.ProviderCallContext) // Again, should work fine and do nothing.
   708  	c.Assert(err, jc.ErrorIsNil)
   709  
   710  	// check that we can bootstrap after destroy
   711  	t.BootstrapOnce(c)
   712  }
   713  
   714  func (t *LiveTests) TestBootstrapAndDeploy(c *gc.C) {
   715  	if !t.CanOpenState || !t.HasProvisioner {
   716  		c.Skip(fmt.Sprintf("skipping provisioner test, CanOpenState: %v, HasProvisioner: %v", t.CanOpenState, t.HasProvisioner))
   717  	}
   718  	t.BootstrapOnce(c)
   719  
   720  	// TODO(niemeyer): Stop growing this kitchen sink test and split it into proper parts.
   721  
   722  	c.Logf("opening state")
   723  	st := t.Env.(jujutesting.GetStater).GetStateInAPIServer()
   724  
   725  	model, err := st.Model()
   726  	c.Assert(err, jc.ErrorIsNil)
   727  	owner := model.Owner()
   728  
   729  	c.Logf("opening API connection")
   730  	controllerCfg, err := st.ControllerConfig()
   731  	c.Assert(err, jc.ErrorIsNil)
   732  	caCert, _ := controllerCfg.CACert()
   733  	apiInfo, err := environs.APIInfo(t.ProviderCallContext, model.Tag().Id(), model.Tag().Id(), caCert, controllerCfg.APIPort(), t.Env)
   734  	c.Assert(err, jc.ErrorIsNil)
   735  	apiInfo.Tag = owner
   736  	apiInfo.Password = AdminSecret
   737  	apiState, err := api.Open(apiInfo, api.DefaultDialOpts())
   738  	c.Assert(err, jc.ErrorIsNil)
   739  	defer apiState.Close()
   740  
   741  	// Check that the agent version has made it through the
   742  	// bootstrap process (it's optional in the config.Config)
   743  	cfg, err := model.ModelConfig()
   744  	c.Assert(err, jc.ErrorIsNil)
   745  	agentVersion, ok := cfg.AgentVersion()
   746  	c.Check(ok, jc.IsTrue)
   747  	c.Check(agentVersion, gc.Equals, jujuversion.Current)
   748  
   749  	// Check that the constraints have been set in the environment.
   750  	cons, err := st.ModelConstraints()
   751  	c.Assert(err, jc.ErrorIsNil)
   752  	c.Assert(cons.String(), gc.Equals, "mem=2048M")
   753  
   754  	// Wait for machine agent to come up on the bootstrap
   755  	// machine and find the deployed series from that.
   756  	m0, err := st.Machine("0")
   757  	c.Assert(err, jc.ErrorIsNil)
   758  
   759  	instId0, err := m0.InstanceId()
   760  	c.Assert(err, jc.ErrorIsNil)
   761  
   762  	// Check that the API connection is working.
   763  	status, err := apiclient.NewClient(apiState, coretesting.NoopLogger{}).Status(nil)
   764  	c.Assert(err, jc.ErrorIsNil)
   765  	c.Assert(status.Machines["0"].InstanceId, gc.Equals, string(instId0))
   766  
   767  	mw0 := newMachineToolWaiter(m0)
   768  	defer func() { _ = mw0.Stop() }()
   769  
   770  	// Controllers always run on Ubuntu.
   771  	expectedVersion := version.Binary{
   772  		Number:  jujuversion.Current,
   773  		Arch:    arch.HostArch(),
   774  		Release: "ubuntu",
   775  	}
   776  
   777  	mtools0 := waitAgentTools(c, mw0, expectedVersion)
   778  
   779  	// Create a new application and deploy a unit of it.
   780  	c.Logf("deploying application")
   781  	ch := testcharms.Repo.ClonedDir(c.MkDir(), "dummy")
   782  	sch, err := jujutesting.PutCharm(st, charm.MustParseURL("local:dummy"), ch)
   783  	c.Assert(err, jc.ErrorIsNil)
   784  	svc, err := st.AddApplication(state.AddApplicationArgs{Name: "dummy", Charm: sch})
   785  	c.Assert(err, jc.ErrorIsNil)
   786  	unit, err := svc.AddUnit(state.AddUnitParams{})
   787  	c.Assert(err, jc.ErrorIsNil)
   788  	err = st.AssignUnit(unit, state.AssignCleanEmpty)
   789  	c.Assert(err, jc.ErrorIsNil)
   790  
   791  	// Wait for the unit's machine and associated agent to come up
   792  	// and announce itself.
   793  	mid1, err := unit.AssignedMachineId()
   794  	c.Assert(err, jc.ErrorIsNil)
   795  	m1, err := st.Machine(mid1)
   796  	c.Assert(err, jc.ErrorIsNil)
   797  	mw1 := newMachineToolWaiter(m1)
   798  	defer func() { _ = mw1.Stop() }()
   799  	waitAgentTools(c, mw1, mtools0.Version)
   800  
   801  	err = m1.Refresh()
   802  	c.Assert(err, jc.ErrorIsNil)
   803  	instId1, err := m1.InstanceId()
   804  	c.Assert(err, jc.ErrorIsNil)
   805  	uw := newUnitToolWaiter(unit)
   806  	defer func() { _ = uw.Stop() }()
   807  	utools := waitAgentTools(c, uw, expectedVersion)
   808  
   809  	// Check that we can upgrade the environment.
   810  	newVersion := utools.Version
   811  	newVersion.Patch++
   812  	t.checkUpgrade(c, st, newVersion, mw0, mw1, uw)
   813  
   814  	// BUG(niemeyer): Logic below is very much wrong. Must be:
   815  	//
   816  	// 1. EnsureDying on the unit and EnsureDying on the machine
   817  	// 2. Unit dies by itself
   818  	// 3. Machine removes dead unit
   819  	// 4. Machine dies by itself
   820  	// 5. Provisioner removes dead machine
   821  	//
   822  
   823  	// Now remove the unit and its assigned machine and
   824  	// check that the PA removes it.
   825  	c.Logf("removing unit")
   826  	err = unit.Destroy()
   827  	c.Assert(err, jc.ErrorIsNil)
   828  
   829  	// Wait until unit is dead
   830  	uwatch := unit.Watch()
   831  	defer func() { _ = uwatch.Stop() }()
   832  	for unit.Life() != state.Dead {
   833  		c.Logf("waiting for unit change")
   834  		<-uwatch.Changes()
   835  		err := unit.Refresh()
   836  		c.Logf("refreshed; err %v", err)
   837  		if errors.IsNotFound(err) {
   838  			c.Logf("unit has been removed")
   839  			break
   840  		}
   841  		c.Assert(err, jc.ErrorIsNil)
   842  	}
   843  	for {
   844  		c.Logf("destroying machine")
   845  		err := m1.Destroy()
   846  		if err == nil {
   847  			break
   848  		}
   849  		c.Assert(errors.Is(err, stateerrors.HasAssignedUnitsError), jc.IsTrue)
   850  		time.Sleep(5 * time.Second)
   851  		err = m1.Refresh()
   852  		if errors.IsNotFound(err) {
   853  			break
   854  		}
   855  		c.Assert(err, jc.ErrorIsNil)
   856  	}
   857  	c.Logf("waiting for instance to be removed")
   858  	t.assertStopInstance(c, t.Env, instId1)
   859  }
   860  
   861  type tooler interface {
   862  	Life() state.Life
   863  	AgentTools() (*coretools.Tools, error)
   864  	Refresh() error
   865  	String() string
   866  }
   867  
   868  type watcher interface {
   869  	Stop() error
   870  	Err() error
   871  }
   872  
   873  type toolsWaiter struct {
   874  	lastTools *coretools.Tools
   875  	// changes is a chan of struct{} so that it can
   876  	// be used with different kinds of entity watcher.
   877  	changes chan struct{}
   878  	watcher watcher
   879  	tooler  tooler
   880  }
   881  
   882  func newMachineToolWaiter(m *state.Machine) *toolsWaiter {
   883  	w := m.Watch()
   884  	waiter := &toolsWaiter{
   885  		changes: make(chan struct{}, 1),
   886  		watcher: w,
   887  		tooler:  m,
   888  	}
   889  	go func() {
   890  		for range w.Changes() {
   891  			waiter.changes <- struct{}{}
   892  		}
   893  		close(waiter.changes)
   894  	}()
   895  	return waiter
   896  }
   897  
   898  func newUnitToolWaiter(u *state.Unit) *toolsWaiter {
   899  	w := u.Watch()
   900  	waiter := &toolsWaiter{
   901  		changes: make(chan struct{}, 1),
   902  		watcher: w,
   903  		tooler:  u,
   904  	}
   905  	go func() {
   906  		for range w.Changes() {
   907  			waiter.changes <- struct{}{}
   908  		}
   909  		close(waiter.changes)
   910  	}()
   911  	return waiter
   912  }
   913  
   914  func (w *toolsWaiter) Stop() error {
   915  	return w.watcher.Stop()
   916  }
   917  
   918  // NextTools returns the next changed tools, waiting
   919  // until the tools are actually set.
   920  func (w *toolsWaiter) NextTools(c *gc.C) (*coretools.Tools, error) {
   921  	for range w.changes {
   922  		err := w.tooler.Refresh()
   923  		if err != nil {
   924  			return nil, fmt.Errorf("cannot refresh: %v", err)
   925  		}
   926  		if w.tooler.Life() == state.Dead {
   927  			return nil, fmt.Errorf("object is dead")
   928  		}
   929  		tools, err := w.tooler.AgentTools()
   930  		if errors.IsNotFound(err) {
   931  			c.Logf("tools not yet set")
   932  			continue
   933  		}
   934  		if err != nil {
   935  			return nil, err
   936  		}
   937  		changed := w.lastTools == nil || *tools != *w.lastTools
   938  		w.lastTools = tools
   939  		if changed {
   940  			return tools, nil
   941  		}
   942  		c.Logf("found same tools")
   943  	}
   944  	return nil, fmt.Errorf("watcher closed prematurely: %v", w.watcher.Err())
   945  }
   946  
   947  // waitAgentTools waits for the given agent
   948  // to start and returns the tools that it is running.
   949  func waitAgentTools(c *gc.C, w *toolsWaiter, expect version.Binary) *coretools.Tools {
   950  	c.Logf("waiting for %v to signal agent version", w.tooler.String())
   951  	tools, err := w.NextTools(c)
   952  	c.Assert(err, jc.ErrorIsNil)
   953  	c.Check(tools.Version, gc.Equals, expect)
   954  	return tools
   955  }
   956  
   957  // checkUpgrade sets the environment agent version and checks that
   958  // all the provided watchers upgrade to the requested version.
   959  func (t *LiveTests) checkUpgrade(c *gc.C, st *state.State, newVersion version.Binary, waiters ...*toolsWaiter) {
   960  	c.Logf("putting testing version of juju tools")
   961  	ss := simplestreams.NewSimpleStreams(sstesting.TestDataSourceFactory())
   962  	upgradeTools, err := sync.Upload(
   963  		ss, t.toolsStorage, "released",
   964  		func(version.Number) version.Number { return newVersion.Number },
   965  	)
   966  	c.Assert(err, jc.ErrorIsNil)
   967  
   968  	// Check that the put version really is the version we expect.
   969  	c.Assert(upgradeTools.Version, gc.Equals, newVersion)
   970  	err = statetesting.SetAgentVersion(st, newVersion.Number)
   971  	c.Assert(err, jc.ErrorIsNil)
   972  
   973  	for i, w := range waiters {
   974  		c.Logf("waiting for upgrade of %d: %v", i, w.tooler.String())
   975  
   976  		waitAgentTools(c, w, newVersion)
   977  		c.Logf("upgrade %d successful", i)
   978  	}
   979  }
   980  
   981  // TODO(katco): 2016-08-09: lp:1611427
   982  var waitAgent = utils.AttemptStrategy{
   983  	Total: 30 * time.Second,
   984  	Delay: 1 * time.Second,
   985  }
   986  
   987  func (t *LiveTests) assertStopInstance(c *gc.C, env environs.Environ, instId instance.Id) {
   988  	var err error
   989  	for a := waitAgent.Start(); a.Next(); {
   990  		_, err = t.Env.Instances(t.ProviderCallContext, []instance.Id{instId})
   991  		if err == nil {
   992  			continue
   993  		}
   994  		if err == environs.ErrNoInstances {
   995  			return
   996  		}
   997  		c.Logf("error from Instances: %v", err)
   998  	}
   999  	c.Fatalf("provisioner failed to stop machine after %v", waitAgent.Total)
  1000  }
  1001  
  1002  func (t *LiveTests) TestStartInstanceWithEmptyNonceFails(c *gc.C) {
  1003  	// Check that we get a consistent error when asking for an instance without
  1004  	// a valid machine config.
  1005  	machineId := "4"
  1006  	apiInfo := jujutesting.FakeAPIInfo(machineId)
  1007  	instanceConfig, err := instancecfg.NewInstanceConfig(coretesting.ControllerTag, machineId, "",
  1008  		"released", corebase.MakeDefaultBase("ubuntu", "22.04"), apiInfo)
  1009  	c.Assert(err, jc.ErrorIsNil)
  1010  
  1011  	t.PrepareOnce(c)
  1012  	possibleTools := coretools.List(envtesting.AssertUploadFakeToolsVersions(
  1013  		c, t.toolsStorage, "released", "released", version.MustParseBinary("5.4.5-ubuntu-amd64"),
  1014  	))
  1015  	fakeCallback := func(_ status.Status, _ string, _ map[string]interface{}) error {
  1016  		return nil
  1017  	}
  1018  	params := environs.StartInstanceParams{
  1019  		ControllerUUID: coretesting.ControllerTag.Id(),
  1020  		Tools:          possibleTools,
  1021  		InstanceConfig: instanceConfig,
  1022  		StatusCallback: fakeCallback,
  1023  	}
  1024  	err = jujutesting.SetImageMetadata(
  1025  		t.Env,
  1026  		simplestreams.NewSimpleStreams(sstesting.TestDataSourceFactory()),
  1027  		[]string{"22.04"},
  1028  		[]string{"amd64"},
  1029  		&params.ImageMetadata,
  1030  	)
  1031  	c.Check(err, jc.ErrorIsNil)
  1032  	result, err := t.Env.StartInstance(t.ProviderCallContext, params)
  1033  	if result != nil && result.Instance != nil {
  1034  		err := t.Env.StopInstances(t.ProviderCallContext, result.Instance.Id())
  1035  		c.Check(err, jc.ErrorIsNil)
  1036  	}
  1037  	c.Assert(result, gc.IsNil)
  1038  	c.Assert(err, gc.ErrorMatches, ".*missing machine nonce")
  1039  }
  1040  
  1041  func (t *LiveTests) TestBootstrapWithDefaultSeries(c *gc.C) {
  1042  	if !t.HasProvisioner {
  1043  		c.Skip("HasProvisioner is false; cannot test deployment")
  1044  	}
  1045  
  1046  	current := coretesting.CurrentVersion()
  1047  	other := current
  1048  	other.Release = "quantal"
  1049  
  1050  	dummyCfg := dummy.SampleConfig().Merge(coretesting.Attrs{
  1051  		"controller": false,
  1052  		"name":       "dummy storage",
  1053  	})
  1054  	args := t.prepareForBootstrapParams(c)
  1055  	args.ModelConfig = dummyCfg
  1056  	e, err := bootstrap.PrepareController(false, t.BootstrapContext,
  1057  		jujuclient.NewMemStore(),
  1058  		args,
  1059  	)
  1060  	c.Assert(err, jc.ErrorIsNil)
  1061  	defer func() { _ = e.(environs.Environ).Destroy(t.ProviderCallContext) }()
  1062  
  1063  	t.Destroy(c)
  1064  
  1065  	attrs := t.TestConfig.Merge(coretesting.Attrs{
  1066  		"name":           "livetests",
  1067  		"default-series": "quantal",
  1068  	})
  1069  	args.ModelConfig = attrs
  1070  	env, err := bootstrap.PrepareController(false, t.BootstrapContext,
  1071  		t.ControllerStore,
  1072  		args)
  1073  	c.Assert(err, jc.ErrorIsNil)
  1074  	defer func() { _ = environs.Destroy("livetests", env, t.ProviderCallContext, t.ControllerStore) }()
  1075  
  1076  	err = bootstrap.Bootstrap(t.BootstrapContext, env, t.ProviderCallContext, t.bootstrapParams())
  1077  	c.Assert(err, jc.ErrorIsNil)
  1078  
  1079  	st := t.Env.(jujutesting.GetStater).GetStateInAPIServer()
  1080  	// Wait for machine agent to come up on the bootstrap
  1081  	// machine and ensure it deployed the proper series.
  1082  	m0, err := st.Machine("0")
  1083  	c.Assert(err, jc.ErrorIsNil)
  1084  	mw0 := newMachineToolWaiter(m0)
  1085  	defer func() { _ = mw0.Stop() }()
  1086  
  1087  	waitAgentTools(c, mw0, other)
  1088  }
  1089  
  1090  func (t *LiveTests) TestIngressRulesWithPartiallyMatchingCIDRs(c *gc.C) {
  1091  	t.BootstrapOnce(c)
  1092  
  1093  	inst1, _ := jujutesting.AssertStartInstance(c, t.Env, t.ProviderCallContext, t.ControllerUUID, "1")
  1094  	c.Assert(inst1, gc.NotNil)
  1095  	defer func() { _ = t.Env.StopInstances(t.ProviderCallContext, inst1.Id()) }()
  1096  	fwInst1, ok := inst1.(instances.InstanceFirewaller)
  1097  	c.Assert(ok, gc.Equals, true)
  1098  
  1099  	rules, err := fwInst1.IngressRules(t.ProviderCallContext, "1")
  1100  	c.Assert(err, jc.ErrorIsNil)
  1101  	c.Assert(rules, gc.HasLen, 0)
  1102  
  1103  	// Open ports with different CIDRs. Check that rules with same port range
  1104  	// get merged.
  1105  	err = fwInst1.OpenPorts(t.ProviderCallContext,
  1106  		"1", firewall.IngressRules{
  1107  			firewall.NewIngressRule(network.MustParsePortRange("42/tcp"), firewall.AllNetworksIPV4CIDR),
  1108  			firewall.NewIngressRule(network.MustParsePortRange("42/tcp"), "10.0.0.0/24"),
  1109  			firewall.NewIngressRule(network.MustParsePortRange("80/tcp")), // open to 0.0.0.0/0
  1110  		})
  1111  
  1112  	c.Assert(err, jc.ErrorIsNil)
  1113  	rules, err = fwInst1.IngressRules(t.ProviderCallContext, "1")
  1114  	c.Assert(err, jc.ErrorIsNil)
  1115  	c.Assert(
  1116  		rules, jc.DeepEquals,
  1117  		firewall.IngressRules{
  1118  			firewall.NewIngressRule(network.MustParsePortRange("42/tcp"), firewall.AllNetworksIPV4CIDR, "10.0.0.0/24"),
  1119  			firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
  1120  		},
  1121  	)
  1122  
  1123  	// Open same port with different CIDRs and check that the CIDR gets
  1124  	// appended to the existing rule's CIDR list.
  1125  	err = fwInst1.OpenPorts(t.ProviderCallContext,
  1126  		"1", firewall.IngressRules{
  1127  			firewall.NewIngressRule(network.MustParsePortRange("42/tcp"), "192.168.0.0/24"),
  1128  		})
  1129  
  1130  	c.Assert(err, jc.ErrorIsNil)
  1131  	rules, err = fwInst1.IngressRules(t.ProviderCallContext, "1")
  1132  	c.Assert(err, jc.ErrorIsNil)
  1133  	c.Assert(
  1134  		rules, jc.DeepEquals,
  1135  		firewall.IngressRules{
  1136  			firewall.NewIngressRule(network.MustParsePortRange("42/tcp"), firewall.AllNetworksIPV4CIDR, "10.0.0.0/24", "192.168.0.0/24"),
  1137  			firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
  1138  		},
  1139  	)
  1140  
  1141  	// Close port on a subset of the CIDRs and ensure that that CIDR gets
  1142  	// removed from the ingress rules
  1143  	err = fwInst1.ClosePorts(t.ProviderCallContext,
  1144  		"1", firewall.IngressRules{
  1145  			firewall.NewIngressRule(network.MustParsePortRange("42/tcp"), "192.168.0.0/24"),
  1146  		})
  1147  
  1148  	c.Assert(err, jc.ErrorIsNil)
  1149  	rules, err = fwInst1.IngressRules(t.ProviderCallContext, "1")
  1150  	c.Assert(err, jc.ErrorIsNil)
  1151  	c.Assert(
  1152  		rules, jc.DeepEquals,
  1153  		firewall.IngressRules{
  1154  			firewall.NewIngressRule(network.MustParsePortRange("42/tcp"), firewall.AllNetworksIPV4CIDR, "10.0.0.0/24"),
  1155  			firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
  1156  		},
  1157  	)
  1158  
  1159  	// Remove all CIDRs from the rule and check that rules without CIDRs
  1160  	// get dropped.
  1161  	err = fwInst1.ClosePorts(t.ProviderCallContext,
  1162  		"1", firewall.IngressRules{
  1163  			firewall.NewIngressRule(network.MustParsePortRange("42/tcp"), firewall.AllNetworksIPV4CIDR, "10.0.0.0/24"),
  1164  		})
  1165  
  1166  	c.Assert(err, jc.ErrorIsNil)
  1167  	rules, err = fwInst1.IngressRules(t.ProviderCallContext, "1")
  1168  	c.Assert(err, jc.ErrorIsNil)
  1169  	c.Assert(
  1170  		rules, jc.DeepEquals,
  1171  		firewall.IngressRules{
  1172  			firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR, firewall.AllNetworksIPV6CIDR),
  1173  		},
  1174  	)
  1175  }