github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/firewaller/firewaller_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package firewaller_test
     5  
     6  import (
     7  	"fmt"
     8  	"strconv"
     9  	"sync/atomic"
    10  	"time"
    11  
    12  	"github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery"
    13  	"github.com/juju/charm/v12"
    14  	"github.com/juju/clock"
    15  	"github.com/juju/clock/testclock"
    16  	"github.com/juju/errors"
    17  	"github.com/juju/loggo"
    18  	"github.com/juju/mgo/v3/txn"
    19  	"github.com/juju/names/v5"
    20  	jc "github.com/juju/testing/checkers"
    21  	"github.com/juju/utils/v3"
    22  	"github.com/juju/worker/v3"
    23  	gc "gopkg.in/check.v1"
    24  
    25  	"github.com/juju/juju/api"
    26  	"github.com/juju/juju/api/agent/credentialvalidator"
    27  	basetesting "github.com/juju/juju/api/base/testing"
    28  	"github.com/juju/juju/api/controller/crossmodelrelations"
    29  	apifirewaller "github.com/juju/juju/api/controller/firewaller"
    30  	"github.com/juju/juju/api/controller/remoterelations"
    31  	apitesting "github.com/juju/juju/api/testing"
    32  	"github.com/juju/juju/core/crossmodel"
    33  	"github.com/juju/juju/core/network"
    34  	"github.com/juju/juju/core/network/firewall"
    35  	"github.com/juju/juju/core/status"
    36  	"github.com/juju/juju/core/watcher"
    37  	"github.com/juju/juju/core/watcher/watchertest"
    38  	"github.com/juju/juju/environs"
    39  	"github.com/juju/juju/environs/config"
    40  	"github.com/juju/juju/environs/context"
    41  	"github.com/juju/juju/environs/instances"
    42  	"github.com/juju/juju/environs/models"
    43  	jujutesting "github.com/juju/juju/juju/testing"
    44  	"github.com/juju/juju/provider/dummy"
    45  	"github.com/juju/juju/rpc/params"
    46  	"github.com/juju/juju/state"
    47  	statetesting "github.com/juju/juju/state/testing"
    48  	"github.com/juju/juju/testing"
    49  	coretesting "github.com/juju/juju/testing"
    50  	"github.com/juju/juju/testing/factory"
    51  	"github.com/juju/juju/worker/firewaller"
    52  )
    53  
    54  const allEndpoints = ""
    55  
    56  // firewallerBaseSuite implements common functionality for embedding
    57  // into each of the other per-mode suites.
    58  type firewallerBaseSuite struct {
    59  	jujutesting.JujuConnSuite
    60  	charm              *state.Charm
    61  	controllerMachine  *state.Machine
    62  	controllerPassword string
    63  
    64  	st                   api.Connection
    65  	firewaller           firewaller.FirewallerAPI
    66  	remoteRelations      *remoterelations.Client
    67  	crossmodelFirewaller *crossmodelrelations.Client
    68  	clock                testclock.AdvanceableClock
    69  
    70  	callCtx           context.ProviderCallContext
    71  	credentialsFacade *credentialvalidator.Facade
    72  }
    73  
    74  func (s *firewallerBaseSuite) SetUpTest(c *gc.C) {
    75  	s.JujuConnSuite.SetUpTest(c)
    76  
    77  	s.callCtx = context.NewEmptyCloudCallContext()
    78  }
    79  
    80  var _ worker.Worker = (*firewaller.Firewaller)(nil)
    81  
    82  func (s *firewallerBaseSuite) setUpTest(c *gc.C, firewallMode string) {
    83  	add := map[string]interface{}{"firewall-mode": firewallMode}
    84  	s.DummyConfig = dummy.SampleConfig().Merge(add).Delete("admin-secret")
    85  
    86  	s.JujuConnSuite.SetUpTest(c)
    87  	s.charm = s.AddTestingCharm(c, "wordpress")
    88  
    89  	// Create a manager machine and login to the API.
    90  	var err error
    91  	s.controllerMachine, err = s.State.AddMachine(state.UbuntuBase("12.10"), state.JobManageModel)
    92  	c.Assert(err, jc.ErrorIsNil)
    93  	s.controllerPassword, err = utils.RandomPassword()
    94  	c.Assert(err, jc.ErrorIsNil)
    95  	err = s.controllerMachine.SetPassword(s.controllerPassword)
    96  	c.Assert(err, jc.ErrorIsNil)
    97  	err = s.controllerMachine.SetProvisioned("i-manager", "", "fake_nonce", nil)
    98  	c.Assert(err, jc.ErrorIsNil)
    99  	s.st = s.OpenAPIAsMachine(c, s.controllerMachine.Tag(), s.controllerPassword, "fake_nonce")
   100  	c.Assert(s.st, gc.NotNil)
   101  
   102  	// Create the API facades.
   103  	firewallerClient, err := apifirewaller.NewClient(s.st)
   104  	c.Assert(err, jc.ErrorIsNil)
   105  	s.firewaller = firewallerClient
   106  	s.remoteRelations = remoterelations.NewClient(s.st)
   107  	c.Assert(s.remoteRelations, gc.NotNil)
   108  
   109  	s.credentialsFacade = credentialvalidator.NewFacade(s.st)
   110  }
   111  
   112  // assertIngressRules retrieves the ingress rules from the provided instance
   113  // and compares them to the expected value.
   114  func (s *firewallerBaseSuite) assertIngressRules(c *gc.C, inst instances.Instance, machineId string,
   115  	expected firewall.IngressRules) {
   116  	fwInst, ok := inst.(instances.InstanceFirewaller)
   117  	c.Assert(ok, gc.Equals, true)
   118  
   119  	start := time.Now()
   120  	for {
   121  		// Make it more likely for the dust to have settled (there still may
   122  		// be rare cases where a test passes when it shouldn't if expected
   123  		// is nil, which is the initial value).
   124  		time.Sleep(coretesting.ShortWait)
   125  
   126  		got, err := fwInst.IngressRules(s.callCtx, machineId)
   127  		if err != nil {
   128  			c.Fatal(err)
   129  		}
   130  		if got.EqualTo(expected) {
   131  			c.Succeed()
   132  			return
   133  		}
   134  		if time.Since(start) > coretesting.LongWait {
   135  			c.Fatalf("timed out: expected %q; got %q", expected, got)
   136  		}
   137  		time.Sleep(coretesting.ShortWait)
   138  	}
   139  }
   140  
   141  // assertEnvironPorts retrieves the open ports of environment and compares them
   142  // to the expected.
   143  func (s *firewallerBaseSuite) assertEnvironPorts(c *gc.C, expected firewall.IngressRules) {
   144  	fwEnv, ok := s.Environ.(environs.Firewaller)
   145  	c.Assert(ok, gc.Equals, true)
   146  
   147  	start := time.Now()
   148  	for {
   149  		got, err := fwEnv.IngressRules(s.callCtx)
   150  		if err != nil {
   151  			c.Fatal(err)
   152  		}
   153  		if got.EqualTo(expected) {
   154  			c.Succeed()
   155  			return
   156  		}
   157  		if time.Since(start) > coretesting.LongWait {
   158  			c.Fatalf("timed out: expected %q; got %q", expected, got)
   159  		}
   160  		time.Sleep(coretesting.ShortWait)
   161  	}
   162  }
   163  
   164  // assertModelIngressRules retrieves the ingress rules from the model firewall
   165  // and compares them to the expected value
   166  func (s *firewallerBaseSuite) assertModelIngressRules(c *gc.C, expected firewall.IngressRules) {
   167  	fwEnv, ok := s.Environ.(models.ModelFirewaller)
   168  	c.Assert(ok, gc.Equals, true)
   169  
   170  	start := time.Now()
   171  	for {
   172  		got, err := fwEnv.ModelIngressRules(s.callCtx)
   173  		if err != nil && !errors.IsNotFound(err) {
   174  			c.Fatal(err)
   175  		}
   176  		if got.EqualTo(expected) {
   177  			c.Succeed()
   178  			return
   179  		}
   180  		if time.Since(start) > coretesting.LongWait {
   181  			c.Fatalf("timed out: expected %q; got %q", expected, got)
   182  		}
   183  		time.Sleep(coretesting.ShortWait)
   184  	}
   185  }
   186  
   187  func (s *firewallerBaseSuite) addUnit(c *gc.C, app *state.Application) (*state.Unit, *state.Machine) {
   188  	u, err := app.AddUnit(state.AddUnitParams{})
   189  	c.Assert(err, jc.ErrorIsNil)
   190  	err = s.State.AssignUnit(u, state.AssignCleanEmpty)
   191  	c.Assert(err, jc.ErrorIsNil)
   192  	id, err := u.AssignedMachineId()
   193  	c.Assert(err, jc.ErrorIsNil)
   194  	m, err := s.State.Machine(id)
   195  	c.Assert(err, jc.ErrorIsNil)
   196  	return u, m
   197  }
   198  
   199  // startInstance starts a new instance for the given machine.
   200  func (s *firewallerBaseSuite) startInstance(c *gc.C, m *state.Machine) instances.Instance {
   201  	inst, hc := jujutesting.AssertStartInstance(c, s.Environ, s.callCtx, s.ControllerConfig.ControllerUUID(), m.Id())
   202  	err := m.SetProvisioned(inst.Id(), "", "fake_nonce", hc)
   203  	c.Assert(err, jc.ErrorIsNil)
   204  	return inst
   205  }
   206  
   207  type InstanceModeSuite struct {
   208  	firewallerBaseSuite
   209  	watchMachineNotify func(tag names.MachineTag)
   210  	flushModelNotify   func()
   211  }
   212  
   213  var _ = gc.Suite(&InstanceModeSuite{})
   214  
   215  func (s *InstanceModeSuite) SetUpTest(c *gc.C) {
   216  	s.firewallerBaseSuite.setUpTest(c, config.FwInstance)
   217  	s.watchMachineNotify = nil
   218  	s.flushModelNotify = nil
   219  	s.clock = testclock.NewDilatedWallClock(testing.ShortWait)
   220  }
   221  
   222  func (s *InstanceModeSuite) newFirewaller(c *gc.C) worker.Worker {
   223  	return s.newFirewallerWithIPV6CIDRSupport(c, true)
   224  }
   225  
   226  func (s *InstanceModeSuite) newFirewallerWithIPV6CIDRSupport(c *gc.C, ipv6CIDRSupport bool) worker.Worker {
   227  	fwEnv, ok := s.Environ.(environs.Firewaller)
   228  	c.Assert(ok, gc.Equals, true)
   229  
   230  	modelFwEnv, ok := s.Environ.(models.ModelFirewaller)
   231  	c.Assert(ok, gc.Equals, true)
   232  
   233  	cfg := firewaller.Config{
   234  		ModelUUID:              s.State.ModelUUID(),
   235  		Mode:                   config.FwInstance,
   236  		EnvironFirewaller:      fwEnv,
   237  		EnvironModelFirewaller: modelFwEnv,
   238  		EnvironInstances:       s.Environ,
   239  		EnvironIPV6CIDRSupport: ipv6CIDRSupport,
   240  		FirewallerAPI:          s.firewaller,
   241  		RemoteRelationsApi:     s.remoteRelations,
   242  		NewCrossModelFacadeFunc: func(*api.Info) (firewaller.CrossModelFirewallerFacadeCloser, error) {
   243  			return s.crossmodelFirewaller, nil
   244  		},
   245  		Clock:              s.clock,
   246  		Logger:             loggo.GetLogger("test"),
   247  		CredentialAPI:      s.credentialsFacade,
   248  		WatchMachineNotify: s.watchMachineNotify,
   249  		FlushModelNotify:   s.flushModelNotify,
   250  	}
   251  	fw, err := firewaller.NewFirewaller(cfg)
   252  	c.Assert(err, jc.ErrorIsNil)
   253  	return fw
   254  }
   255  
   256  func (s *InstanceModeSuite) newFirewallerWithoutModelFirewaller(c *gc.C) worker.Worker {
   257  	fwEnv, ok := s.Environ.(environs.Firewaller)
   258  	c.Assert(ok, gc.Equals, true)
   259  
   260  	cfg := firewaller.Config{
   261  		ModelUUID:              s.State.ModelUUID(),
   262  		Mode:                   config.FwInstance,
   263  		EnvironFirewaller:      fwEnv,
   264  		EnvironInstances:       s.Environ,
   265  		EnvironIPV6CIDRSupport: true,
   266  		FirewallerAPI:          s.firewaller,
   267  		RemoteRelationsApi:     s.remoteRelations,
   268  		NewCrossModelFacadeFunc: func(*api.Info) (firewaller.CrossModelFirewallerFacadeCloser, error) {
   269  			return s.crossmodelFirewaller, nil
   270  		},
   271  		Clock:              s.clock,
   272  		Logger:             loggo.GetLogger("test"),
   273  		CredentialAPI:      s.credentialsFacade,
   274  		WatchMachineNotify: s.watchMachineNotify,
   275  		FlushModelNotify:   s.flushModelNotify,
   276  	}
   277  	fw, err := firewaller.NewFirewaller(cfg)
   278  	c.Assert(err, jc.ErrorIsNil)
   279  	return fw
   280  }
   281  
   282  func (s *InstanceModeSuite) TestStartStop(c *gc.C) {
   283  	fw := s.newFirewaller(c)
   284  	statetesting.AssertKillAndWait(c, fw)
   285  }
   286  
   287  func (s *InstanceModeSuite) TestStartStopWithoutModelFirewaller(c *gc.C) {
   288  	fw := s.newFirewallerWithoutModelFirewaller(c)
   289  	statetesting.AssertKillAndWait(c, fw)
   290  }
   291  
   292  func (s *InstanceModeSuite) testNotExposedApplication(c *gc.C, fw worker.Worker) {
   293  	app := s.AddTestingApplication(c, "wordpress", s.charm)
   294  	u, m := s.addUnit(c, app)
   295  	inst := s.startInstance(c, m)
   296  
   297  	mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{
   298  		network.MustParsePortRange("80/tcp"),
   299  		network.MustParsePortRange("8080/tcp"),
   300  	})
   301  
   302  	s.assertIngressRules(c, inst, m.Id(), nil)
   303  
   304  	mustClosePortRanges(c, s.State, u, allEndpoints, []network.PortRange{
   305  		network.MustParsePortRange("80/tcp"),
   306  	})
   307  
   308  	s.assertIngressRules(c, inst, m.Id(), nil)
   309  }
   310  
   311  func (s *InstanceModeSuite) TestNotExposedApplication(c *gc.C) {
   312  	fw := s.newFirewaller(c)
   313  	defer statetesting.AssertKillAndWait(c, fw)
   314  	s.testNotExposedApplication(c, fw)
   315  }
   316  
   317  func (s *InstanceModeSuite) TestNotExposedApplicationWithoutModelFirewaller(c *gc.C) {
   318  	fw := s.newFirewallerWithoutModelFirewaller(c)
   319  	defer statetesting.AssertKillAndWait(c, fw)
   320  	s.testNotExposedApplication(c, fw)
   321  }
   322  
   323  func (s *InstanceModeSuite) TestExposedApplication(c *gc.C) {
   324  	fw := s.newFirewaller(c)
   325  	defer statetesting.AssertKillAndWait(c, fw)
   326  
   327  	app := s.AddTestingApplication(c, "wordpress", s.charm)
   328  
   329  	err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{
   330  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
   331  	})
   332  	c.Assert(err, jc.ErrorIsNil)
   333  	u, m := s.addUnit(c, app)
   334  	inst := s.startInstance(c, m)
   335  
   336  	mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{
   337  		network.MustParsePortRange("80-90/tcp"),
   338  		network.MustParsePortRange("8080/tcp"),
   339  	})
   340  
   341  	s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{
   342  		firewall.NewIngressRule(network.MustParsePortRange("80-90/tcp"), firewall.AllNetworksIPV4CIDR),
   343  		firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR),
   344  	})
   345  
   346  	mustClosePortRanges(c, s.State, u, allEndpoints, []network.PortRange{
   347  		network.MustParsePortRange("80-90/tcp"),
   348  	})
   349  
   350  	s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{
   351  		firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR),
   352  	})
   353  }
   354  
   355  func (s *InstanceModeSuite) TestMultipleExposedApplications(c *gc.C) {
   356  	fw := s.newFirewaller(c)
   357  	defer statetesting.AssertKillAndWait(c, fw)
   358  
   359  	app1 := s.AddTestingApplication(c, "wordpress", s.charm)
   360  	err := app1.MergeExposeSettings(map[string]state.ExposedEndpoint{
   361  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
   362  	})
   363  	c.Assert(err, jc.ErrorIsNil)
   364  
   365  	u1, m1 := s.addUnit(c, app1)
   366  	inst1 := s.startInstance(c, m1)
   367  	mustOpenPortRanges(c, s.State, u1, allEndpoints, []network.PortRange{
   368  		network.MustParsePortRange("80/tcp"),
   369  		network.MustParsePortRange("8080/tcp"),
   370  	})
   371  
   372  	app2 := s.AddTestingApplication(c, "mysql", s.charm)
   373  	c.Assert(err, jc.ErrorIsNil)
   374  	err = app2.MergeExposeSettings(map[string]state.ExposedEndpoint{
   375  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
   376  	})
   377  	c.Assert(err, jc.ErrorIsNil)
   378  
   379  	u2, m2 := s.addUnit(c, app2)
   380  	inst2 := s.startInstance(c, m2)
   381  	mustOpenPortRanges(c, s.State, u2, allEndpoints, []network.PortRange{
   382  		network.MustParsePortRange("3306/tcp"),
   383  	})
   384  
   385  	s.assertIngressRules(c, inst1, m1.Id(), firewall.IngressRules{
   386  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR),
   387  		firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR),
   388  	})
   389  	s.assertIngressRules(c, inst2, m2.Id(), firewall.IngressRules{
   390  		firewall.NewIngressRule(network.MustParsePortRange("3306/tcp"), firewall.AllNetworksIPV4CIDR),
   391  	})
   392  
   393  	mustClosePortRanges(c, s.State, u1, allEndpoints, []network.PortRange{
   394  		network.MustParsePortRange("80/tcp"),
   395  	})
   396  	mustClosePortRanges(c, s.State, u2, allEndpoints, []network.PortRange{
   397  		network.MustParsePortRange("3306/tcp"),
   398  	})
   399  
   400  	s.assertIngressRules(c, inst1, m1.Id(), firewall.IngressRules{
   401  		firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR),
   402  	})
   403  	s.assertIngressRules(c, inst2, m2.Id(), nil)
   404  }
   405  
   406  func (s *InstanceModeSuite) TestMachineWithoutInstanceId(c *gc.C) {
   407  	fw := s.newFirewaller(c)
   408  	defer statetesting.AssertKillAndWait(c, fw)
   409  
   410  	app := s.AddTestingApplication(c, "wordpress", s.charm)
   411  	err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{
   412  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
   413  	})
   414  	c.Assert(err, jc.ErrorIsNil)
   415  	// add a unit but don't start its instance yet.
   416  	u1, m1 := s.addUnit(c, app)
   417  
   418  	// add another unit and start its instance, so that
   419  	// we're sure the firewaller has seen the first instance.
   420  	u2, m2 := s.addUnit(c, app)
   421  	inst2 := s.startInstance(c, m2)
   422  	mustOpenPortRanges(c, s.State, u2, allEndpoints, []network.PortRange{
   423  		network.MustParsePortRange("80/tcp"),
   424  	})
   425  	s.assertIngressRules(c, inst2, m2.Id(), firewall.IngressRules{
   426  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR),
   427  	})
   428  
   429  	inst1 := s.startInstance(c, m1)
   430  	mustOpenPortRanges(c, s.State, u1, allEndpoints, []network.PortRange{
   431  		network.MustParsePortRange("8080/tcp"),
   432  	})
   433  	s.assertIngressRules(c, inst1, m1.Id(), firewall.IngressRules{
   434  		firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR),
   435  	})
   436  }
   437  
   438  func (s *InstanceModeSuite) TestMultipleUnits(c *gc.C) {
   439  	fw := s.newFirewaller(c)
   440  	defer statetesting.AssertKillAndWait(c, fw)
   441  
   442  	app := s.AddTestingApplication(c, "wordpress", s.charm)
   443  	err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{
   444  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
   445  	})
   446  	c.Assert(err, jc.ErrorIsNil)
   447  
   448  	u1, m1 := s.addUnit(c, app)
   449  	inst1 := s.startInstance(c, m1)
   450  	mustOpenPortRanges(c, s.State, u1, allEndpoints, []network.PortRange{
   451  		network.MustParsePortRange("80/tcp"),
   452  	})
   453  
   454  	u2, m2 := s.addUnit(c, app)
   455  	inst2 := s.startInstance(c, m2)
   456  	mustOpenPortRanges(c, s.State, u2, allEndpoints, []network.PortRange{
   457  		network.MustParsePortRange("80/tcp"),
   458  	})
   459  
   460  	s.assertIngressRules(c, inst1, m1.Id(), firewall.IngressRules{
   461  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR),
   462  	})
   463  	s.assertIngressRules(c, inst2, m2.Id(), firewall.IngressRules{
   464  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR),
   465  	})
   466  
   467  	mustClosePortRanges(c, s.State, u1, allEndpoints, []network.PortRange{
   468  		network.MustParsePortRange("80/tcp"),
   469  	})
   470  	mustClosePortRanges(c, s.State, u2, allEndpoints, []network.PortRange{
   471  		network.MustParsePortRange("80/tcp"),
   472  	})
   473  
   474  	s.assertIngressRules(c, inst1, m1.Id(), nil)
   475  	s.assertIngressRules(c, inst2, m2.Id(), nil)
   476  }
   477  
   478  func (s *InstanceModeSuite) TestStartWithState(c *gc.C) {
   479  	app := s.AddTestingApplication(c, "wordpress", s.charm)
   480  	err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{
   481  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
   482  	})
   483  	c.Assert(err, jc.ErrorIsNil)
   484  	u, m := s.addUnit(c, app)
   485  	inst := s.startInstance(c, m)
   486  
   487  	mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{
   488  		network.MustParsePortRange("80/tcp"),
   489  		network.MustParsePortRange("8080/tcp"),
   490  	})
   491  
   492  	// Nothing open without firewaller.
   493  	s.assertIngressRules(c, inst, m.Id(), nil)
   494  
   495  	// Starting the firewaller opens the ports.
   496  	fw := s.newFirewaller(c)
   497  	defer statetesting.AssertKillAndWait(c, fw)
   498  
   499  	s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{
   500  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR),
   501  		firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR),
   502  	})
   503  
   504  	err = app.MergeExposeSettings(nil)
   505  	c.Assert(err, jc.ErrorIsNil)
   506  }
   507  
   508  func (s *InstanceModeSuite) TestStartWithPartialState(c *gc.C) {
   509  	m, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   510  	c.Assert(err, jc.ErrorIsNil)
   511  	inst := s.startInstance(c, m)
   512  
   513  	app := s.AddTestingApplication(c, "wordpress", s.charm)
   514  	err = app.MergeExposeSettings(map[string]state.ExposedEndpoint{
   515  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
   516  	})
   517  	c.Assert(err, jc.ErrorIsNil)
   518  
   519  	// Starting the firewaller, no open ports.
   520  	fw := s.newFirewaller(c)
   521  	defer statetesting.AssertKillAndWait(c, fw)
   522  
   523  	s.assertIngressRules(c, inst, m.Id(), nil)
   524  
   525  	// Complete steps to open port.
   526  	u, err := app.AddUnit(state.AddUnitParams{})
   527  	c.Assert(err, jc.ErrorIsNil)
   528  	err = u.AssignToMachine(m)
   529  	c.Assert(err, jc.ErrorIsNil)
   530  	mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{
   531  		network.MustParsePortRange("80/tcp"),
   532  	})
   533  
   534  	s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{
   535  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR),
   536  	})
   537  }
   538  
   539  func (s *InstanceModeSuite) TestStartWithUnexposedApplication(c *gc.C) {
   540  	m, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   541  	c.Assert(err, jc.ErrorIsNil)
   542  	inst := s.startInstance(c, m)
   543  
   544  	app := s.AddTestingApplication(c, "wordpress", s.charm)
   545  	u, err := app.AddUnit(state.AddUnitParams{})
   546  	c.Assert(err, jc.ErrorIsNil)
   547  	err = u.AssignToMachine(m)
   548  	c.Assert(err, jc.ErrorIsNil)
   549  	mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{
   550  		network.MustParsePortRange("80/tcp"),
   551  	})
   552  
   553  	// Starting the firewaller, no open ports.
   554  	fw := s.newFirewaller(c)
   555  	defer statetesting.AssertKillAndWait(c, fw)
   556  
   557  	s.assertIngressRules(c, inst, m.Id(), nil)
   558  
   559  	// Expose service.
   560  	err = app.MergeExposeSettings(map[string]state.ExposedEndpoint{
   561  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
   562  	})
   563  	c.Assert(err, jc.ErrorIsNil)
   564  	s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{
   565  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR),
   566  	})
   567  }
   568  
   569  func (s *InstanceModeSuite) TestStartMachineWithManualMachine(c *gc.C) {
   570  	watching := make(chan names.MachineTag, 10) // buffer to ensure test never blocks
   571  	s.watchMachineNotify = func(tag names.MachineTag) {
   572  		watching <- tag
   573  	}
   574  	assertWatching := func(expected names.MachineTag) {
   575  		select {
   576  		case got := <-watching:
   577  			c.Assert(got, gc.Equals, expected)
   578  		case <-time.After(coretesting.LongWait):
   579  			c.Fatalf("timed out waiting to watch machine %v", expected.Id())
   580  		}
   581  	}
   582  
   583  	fw := s.newFirewaller(c)
   584  	defer statetesting.AssertKillAndWait(c, fw)
   585  
   586  	// Wait for manager machine (started by setUpTest)
   587  	assertWatching(names.NewMachineTag("0"))
   588  
   589  	_, err := s.State.AddOneMachine(state.MachineTemplate{
   590  		Base:       state.UbuntuBase("12.10"),
   591  		Jobs:       []state.MachineJob{state.JobHostUnits},
   592  		InstanceId: "2",
   593  		Nonce:      "manual:",
   594  	})
   595  	c.Assert(err, jc.ErrorIsNil)
   596  	select {
   597  	case tag := <-watching:
   598  		c.Fatalf("shouldn't be watching manual machine %v", tag)
   599  	case <-time.After(coretesting.ShortWait):
   600  	}
   601  
   602  	m, err := s.State.AddOneMachine(state.MachineTemplate{
   603  		Base: state.UbuntuBase("12.10"),
   604  		Jobs: []state.MachineJob{state.JobHostUnits},
   605  	})
   606  	c.Assert(err, jc.ErrorIsNil)
   607  	assertWatching(m.MachineTag())
   608  }
   609  
   610  // TODO: remove once JujuConnSuite is gone.
   611  type firewallerAPIShim struct {
   612  	firewaller.FirewallerAPI
   613  	newModelMachineWatcher func() (watcher.StringsWatcher, error)
   614  }
   615  
   616  func (a *firewallerAPIShim) WatchModelMachines() (watcher.StringsWatcher, error) {
   617  	return a.newModelMachineWatcher()
   618  }
   619  
   620  func (s *InstanceModeSuite) TestFlushModelAfterFirstMachineOnly(c *gc.C) {
   621  	machinesChan := make(chan []string, 1)
   622  	machinesChan <- []string{}
   623  	s.firewaller = &firewallerAPIShim{
   624  		FirewallerAPI: s.firewaller,
   625  		newModelMachineWatcher: func() (watcher.StringsWatcher, error) {
   626  			return watchertest.NewMockStringsWatcher(machinesChan), nil
   627  		},
   628  	}
   629  
   630  	flush := make(chan struct{}, 10) // buffer to ensure test never blocks
   631  	s.flushModelNotify = func() {
   632  		flush <- struct{}{}
   633  	}
   634  
   635  	fw := s.newFirewaller(c)
   636  	defer statetesting.AssertKillAndWait(c, fw)
   637  
   638  	// Initial event from model config watcher
   639  	select {
   640  	case <-flush:
   641  	case <-time.After(coretesting.LongWait):
   642  		c.Fatalf("timed out waiting for initial event")
   643  	}
   644  
   645  	m1, err := s.State.AddOneMachine(state.MachineTemplate{
   646  		Base: state.UbuntuBase("12.10"),
   647  		Jobs: []state.MachineJob{state.JobHostUnits},
   648  	})
   649  	c.Assert(err, jc.ErrorIsNil)
   650  	machinesChan <- []string{m1.Id()}
   651  
   652  	select {
   653  	case <-flush:
   654  	case <-time.After(coretesting.LongWait):
   655  		c.Fatalf("timed out waiting for first machine event")
   656  	}
   657  
   658  	m2, err := s.State.AddOneMachine(state.MachineTemplate{
   659  		Base: state.UbuntuBase("12.10"),
   660  		Jobs: []state.MachineJob{state.JobHostUnits},
   661  	})
   662  	c.Assert(err, jc.ErrorIsNil)
   663  	machinesChan <- []string{m2.Id()}
   664  
   665  	// Since the initial event successfully configured the model firewall
   666  	// the next machine shouldn't trigger a model flush
   667  	select {
   668  	case <-flush:
   669  		c.Fatalf("unexpected model flush creating machine")
   670  	case <-time.After(coretesting.ShortWait):
   671  	}
   672  }
   673  
   674  func (s *InstanceModeSuite) TestSetClearExposedApplication(c *gc.C) {
   675  	fw := s.newFirewaller(c)
   676  	defer statetesting.AssertKillAndWait(c, fw)
   677  
   678  	app := s.AddTestingApplication(c, "wordpress", s.charm)
   679  
   680  	u, m := s.addUnit(c, app)
   681  	inst := s.startInstance(c, m)
   682  	mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{
   683  		network.MustParsePortRange("80/tcp"),
   684  		network.MustParsePortRange("8080/tcp"),
   685  	})
   686  
   687  	// Not exposed service, so no open port.
   688  	s.assertIngressRules(c, inst, m.Id(), nil)
   689  
   690  	// SeExposed opens the ports.
   691  	err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{
   692  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
   693  	})
   694  	c.Assert(err, jc.ErrorIsNil)
   695  
   696  	s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{
   697  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR),
   698  		firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR),
   699  	})
   700  
   701  	// ClearExposed closes the ports again.
   702  	err = app.ClearExposed()
   703  	c.Assert(err, jc.ErrorIsNil)
   704  
   705  	s.assertIngressRules(c, inst, m.Id(), nil)
   706  }
   707  
   708  func (s *InstanceModeSuite) TestRemoveUnit(c *gc.C) {
   709  	fw := s.newFirewaller(c)
   710  	defer statetesting.AssertKillAndWait(c, fw)
   711  
   712  	app := s.AddTestingApplication(c, "wordpress", s.charm)
   713  	err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{
   714  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
   715  	})
   716  	c.Assert(err, jc.ErrorIsNil)
   717  
   718  	u1, m1 := s.addUnit(c, app)
   719  	inst1 := s.startInstance(c, m1)
   720  	mustOpenPortRanges(c, s.State, u1, allEndpoints, []network.PortRange{
   721  		network.MustParsePortRange("80/tcp"),
   722  	})
   723  
   724  	u2, m2 := s.addUnit(c, app)
   725  	inst2 := s.startInstance(c, m2)
   726  	mustOpenPortRanges(c, s.State, u2, allEndpoints, []network.PortRange{
   727  		network.MustParsePortRange("80/tcp"),
   728  	})
   729  
   730  	s.assertIngressRules(c, inst1, m1.Id(), firewall.IngressRules{
   731  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR),
   732  	})
   733  	s.assertIngressRules(c, inst2, m2.Id(), firewall.IngressRules{
   734  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR),
   735  	})
   736  
   737  	// Remove unit.
   738  	err = u1.EnsureDead()
   739  	c.Assert(err, jc.ErrorIsNil)
   740  	err = u1.Remove()
   741  	c.Assert(err, jc.ErrorIsNil)
   742  
   743  	s.assertIngressRules(c, inst1, m1.Id(), nil)
   744  	s.assertIngressRules(c, inst2, m2.Id(), firewall.IngressRules{
   745  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR),
   746  	})
   747  }
   748  
   749  func (s *InstanceModeSuite) TestRemoveApplication(c *gc.C) {
   750  	fw := s.newFirewaller(c)
   751  	defer statetesting.AssertKillAndWait(c, fw)
   752  
   753  	app := s.AddTestingApplication(c, "wordpress", s.charm)
   754  	err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{
   755  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
   756  	})
   757  	c.Assert(err, jc.ErrorIsNil)
   758  
   759  	u, m := s.addUnit(c, app)
   760  	inst := s.startInstance(c, m)
   761  	mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{
   762  		network.MustParsePortRange("80/tcp"),
   763  	})
   764  
   765  	s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{
   766  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR),
   767  	})
   768  
   769  	// Remove application.
   770  	err = u.EnsureDead()
   771  	c.Assert(err, jc.ErrorIsNil)
   772  	err = u.Remove()
   773  	c.Assert(err, jc.ErrorIsNil)
   774  	err = app.Destroy()
   775  	c.Assert(err, jc.ErrorIsNil)
   776  	s.assertIngressRules(c, inst, m.Id(), nil)
   777  }
   778  
   779  func (s *InstanceModeSuite) TestRemoveMultipleApplications(c *gc.C) {
   780  	fw := s.newFirewaller(c)
   781  	defer statetesting.AssertKillAndWait(c, fw)
   782  
   783  	app1 := s.AddTestingApplication(c, "wordpress", s.charm)
   784  	err := app1.MergeExposeSettings(map[string]state.ExposedEndpoint{
   785  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
   786  	})
   787  	c.Assert(err, jc.ErrorIsNil)
   788  
   789  	u1, m1 := s.addUnit(c, app1)
   790  	inst1 := s.startInstance(c, m1)
   791  	mustOpenPortRanges(c, s.State, u1, allEndpoints, []network.PortRange{
   792  		network.MustParsePortRange("80/tcp"),
   793  	})
   794  
   795  	app2 := s.AddTestingApplication(c, "mysql", s.charm)
   796  	err = app2.MergeExposeSettings(map[string]state.ExposedEndpoint{
   797  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
   798  	})
   799  	c.Assert(err, jc.ErrorIsNil)
   800  
   801  	u2, m2 := s.addUnit(c, app2)
   802  	inst2 := s.startInstance(c, m2)
   803  	mustOpenPortRanges(c, s.State, u2, allEndpoints, []network.PortRange{
   804  		network.MustParsePortRange("3306/tcp"),
   805  	})
   806  
   807  	s.assertIngressRules(c, inst1, m1.Id(), firewall.IngressRules{
   808  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR),
   809  	})
   810  	s.assertIngressRules(c, inst2, m2.Id(), firewall.IngressRules{
   811  		firewall.NewIngressRule(network.MustParsePortRange("3306/tcp"), firewall.AllNetworksIPV4CIDR),
   812  	})
   813  
   814  	// Remove applications.
   815  	err = u2.EnsureDead()
   816  	c.Assert(err, jc.ErrorIsNil)
   817  	err = u2.Remove()
   818  	c.Assert(err, jc.ErrorIsNil)
   819  	err = app2.Destroy()
   820  	c.Assert(err, jc.ErrorIsNil)
   821  
   822  	err = u1.EnsureDead()
   823  	c.Assert(err, jc.ErrorIsNil)
   824  	err = u1.Remove()
   825  	c.Assert(err, jc.ErrorIsNil)
   826  	err = app1.Destroy()
   827  	c.Assert(err, jc.ErrorIsNil)
   828  
   829  	s.assertIngressRules(c, inst1, m1.Id(), nil)
   830  	s.assertIngressRules(c, inst2, m2.Id(), nil)
   831  }
   832  
   833  func (s *InstanceModeSuite) TestDeadMachine(c *gc.C) {
   834  	fw := s.newFirewaller(c)
   835  	defer statetesting.AssertKillAndWait(c, fw)
   836  
   837  	app := s.AddTestingApplication(c, "wordpress", s.charm)
   838  	err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{
   839  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
   840  	})
   841  	c.Assert(err, jc.ErrorIsNil)
   842  
   843  	u, m := s.addUnit(c, app)
   844  	inst := s.startInstance(c, m)
   845  	mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{
   846  		network.MustParsePortRange("80/tcp"),
   847  	})
   848  
   849  	s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{
   850  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR),
   851  	})
   852  
   853  	// Remove unit and application, also tested without. Has no effect.
   854  	err = u.EnsureDead()
   855  	c.Assert(err, jc.ErrorIsNil)
   856  	err = u.Remove()
   857  	c.Assert(err, jc.ErrorIsNil)
   858  	err = app.Destroy()
   859  	c.Assert(err, jc.ErrorIsNil)
   860  
   861  	// Kill machine.
   862  	err = m.Refresh()
   863  	c.Assert(err, jc.ErrorIsNil)
   864  	err = m.EnsureDead()
   865  	c.Assert(err, jc.ErrorIsNil)
   866  
   867  	s.assertIngressRules(c, inst, m.Id(), nil)
   868  }
   869  
   870  func (s *InstanceModeSuite) TestRemoveMachine(c *gc.C) {
   871  	fw := s.newFirewaller(c)
   872  
   873  	app := s.AddTestingApplication(c, "wordpress", s.charm)
   874  	err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{
   875  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
   876  	})
   877  	c.Assert(err, jc.ErrorIsNil)
   878  
   879  	u, m := s.addUnit(c, app)
   880  	inst := s.startInstance(c, m)
   881  	mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{
   882  		network.MustParsePortRange("80/tcp"),
   883  	})
   884  
   885  	s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{
   886  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR),
   887  	})
   888  
   889  	// Remove unit.
   890  	err = u.EnsureDead()
   891  	c.Assert(err, jc.ErrorIsNil)
   892  	err = u.Remove()
   893  	c.Assert(err, jc.ErrorIsNil)
   894  
   895  	// Remove machine. Nothing bad should happen, but can't
   896  	// assert port state since the machine must have been
   897  	// destroyed and we lost its reference.
   898  	err = m.Refresh()
   899  	c.Assert(err, jc.ErrorIsNil)
   900  	err = m.EnsureDead()
   901  	c.Assert(err, jc.ErrorIsNil)
   902  	err = m.Remove()
   903  	c.Assert(err, jc.ErrorIsNil)
   904  
   905  	// TODO (manadart 2019-02-01): This fails intermittently with a "not found"
   906  	// error for the machine. This is not a huge problem in production, as the
   907  	// worker will restart and proceed happily thereafter.
   908  	// That error is detected here for expediency, but the ideal mitigation is
   909  	// a refactoring of the worker logic as per LP:1814277.
   910  	fw.Kill()
   911  	err = fw.Wait()
   912  	c.Assert(err == nil || params.IsCodeNotFound(err), jc.IsTrue)
   913  }
   914  
   915  func (s *InstanceModeSuite) TestStartWithStateOpenPortsBroken(c *gc.C) {
   916  	app := s.AddTestingApplication(c, "wordpress", s.charm)
   917  	err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{
   918  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
   919  	})
   920  	c.Assert(err, jc.ErrorIsNil)
   921  	u, m := s.addUnit(c, app)
   922  	inst := s.startInstance(c, m)
   923  
   924  	mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{
   925  		network.MustParsePortRange("80/tcp"),
   926  	})
   927  
   928  	// Nothing open without firewaller.
   929  	s.assertIngressRules(c, inst, m.Id(), nil)
   930  	dummy.SetInstanceBroken(inst, "OpenPorts")
   931  
   932  	// Starting the firewaller should attempt to open the ports,
   933  	// and fail due to the method being broken.
   934  	fw := s.newFirewaller(c)
   935  
   936  	errc := make(chan error, 1)
   937  	go func() { errc <- fw.Wait() }()
   938  	select {
   939  	case err := <-errc:
   940  		c.Assert(err, gc.ErrorMatches,
   941  			`cannot respond to units changes for "machine-1", \"deadbeef-0bad-400d-8000-4b1d0d06f00d\": dummyInstance.OpenPorts is broken`)
   942  	case <-time.After(coretesting.LongWait):
   943  		fw.Kill()
   944  		fw.Wait()
   945  		c.Fatal("timed out waiting for firewaller to stop")
   946  	}
   947  }
   948  
   949  func (s *InstanceModeSuite) TestDefaultModelFirewall(c *gc.C) {
   950  	fw := s.newFirewaller(c)
   951  	defer statetesting.AssertKillAndWait(c, fw)
   952  
   953  	ctrlCfg, err := s.State.ControllerConfig()
   954  	c.Assert(err, jc.ErrorIsNil)
   955  	apiPort := ctrlCfg.APIPort()
   956  
   957  	s.assertModelIngressRules(c, firewall.IngressRules{
   958  		firewall.NewIngressRule(network.MustParsePortRange("22"), "0.0.0.0/0", "::/0"),
   959  		firewall.NewIngressRule(network.MustParsePortRange(strconv.Itoa(apiPort)), "0.0.0.0/0", "::/0"),
   960  	})
   961  }
   962  
   963  func (s *InstanceModeSuite) TestConfigureModelFirewall(c *gc.C) {
   964  	fw := s.newFirewaller(c)
   965  	defer statetesting.AssertKillAndWait(c, fw)
   966  
   967  	model, err := s.State.Model()
   968  	c.Assert(err, jc.ErrorIsNil)
   969  
   970  	ctrlCfg, err := s.State.ControllerConfig()
   971  	c.Assert(err, jc.ErrorIsNil)
   972  	apiPort := ctrlCfg.APIPort()
   973  
   974  	s.assertModelIngressRules(c, firewall.IngressRules{
   975  		firewall.NewIngressRule(network.MustParsePortRange("22"), "0.0.0.0/0", "::/0"),
   976  		firewall.NewIngressRule(network.MustParsePortRange(strconv.Itoa(apiPort)), "0.0.0.0/0", "::/0"),
   977  	})
   978  
   979  	err = model.UpdateModelConfig(map[string]interface{}{
   980  		config.SSHAllowKey: "192.168.0.0/24",
   981  	}, nil)
   982  	c.Assert(err, jc.ErrorIsNil)
   983  
   984  	s.assertModelIngressRules(c, firewall.IngressRules{
   985  		firewall.NewIngressRule(network.MustParsePortRange("22"), "192.168.0.0/24"),
   986  		firewall.NewIngressRule(network.MustParsePortRange(strconv.Itoa(apiPort)), "0.0.0.0/0", "::/0"),
   987  	})
   988  }
   989  
   990  func (s *InstanceModeSuite) setupRemoteRelationRequirerRoleConsumingSide(
   991  	c *gc.C, published chan bool, shouldErr func() bool, ingressRequired *bool, clock clock.Clock,
   992  ) (worker.Worker, *state.RelationUnit) {
   993  	// Set up the consuming model - create the local app.
   994  	wordpress := s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
   995  	// Set up the consuming model - create the remote app.
   996  	offeringModelTag := names.NewModelTag(utils.MustNewUUID().String())
   997  	// Create the external controller info.
   998  	ec := state.NewExternalControllers(s.State)
   999  	_, err := ec.Save(crossmodel.ControllerInfo{
  1000  		ControllerTag: coretesting.ControllerTag,
  1001  		Addrs:         []string{"1.2.3.4:1234"},
  1002  		CACert:        coretesting.CACert}, offeringModelTag.Id())
  1003  	c.Assert(err, jc.ErrorIsNil)
  1004  
  1005  	mac, err := apitesting.NewMacaroon("apimac")
  1006  	c.Assert(err, jc.ErrorIsNil)
  1007  	var relToken string
  1008  	apiCaller := basetesting.APICallerFunc(func(objType string, version int, id, request string,
  1009  		arg, result interface{}) error {
  1010  		c.Check(objType, gc.Equals, "CrossModelRelations")
  1011  		c.Check(version, gc.Equals, 0)
  1012  		c.Check(id, gc.Equals, "")
  1013  		c.Check(request, gc.Equals, "PublishIngressNetworkChanges")
  1014  		expected := params.IngressNetworksChanges{
  1015  			Changes: []params.IngressNetworksChangeEvent{{
  1016  				RelationToken: relToken,
  1017  			}},
  1018  		}
  1019  
  1020  		// Extract macaroons so we can compare them separately
  1021  		// (as they can't be compared using DeepEquals due to 'UnmarshaledAs')
  1022  		expectedMacs := arg.(params.IngressNetworksChanges).Changes[0].Macaroons
  1023  		arg.(params.IngressNetworksChanges).Changes[0].Macaroons = nil
  1024  		c.Assert(len(expectedMacs), gc.Equals, 1)
  1025  		apitesting.MacaroonEquals(c, expectedMacs[0], mac)
  1026  
  1027  		// Networks may be empty or not, depending on coalescing of watcher events.
  1028  		// We may get an initial empty event followed by an event with a network.
  1029  		// Or we may get just the event with a network.
  1030  		// Set the arg networks to empty and compare below.
  1031  		changes := arg.(params.IngressNetworksChanges)
  1032  		argNetworks := changes.Changes[0].Networks
  1033  		argIngressRequired := changes.Changes[0].IngressRequired
  1034  
  1035  		changes.Changes[0].Networks = nil
  1036  		expected.Changes[0].IngressRequired = argIngressRequired
  1037  		expected.Changes[0].BakeryVersion = bakery.LatestVersion
  1038  		c.Check(arg, gc.DeepEquals, expected)
  1039  
  1040  		if !*ingressRequired {
  1041  			c.Assert(changes.Changes[0].Networks, gc.HasLen, 0)
  1042  		}
  1043  		if *ingressRequired && len(argNetworks) > 0 {
  1044  			c.Assert(argIngressRequired, jc.IsTrue)
  1045  			c.Assert(argNetworks, jc.DeepEquals, []string{"10.0.0.4/32"})
  1046  		}
  1047  		changes.Changes[0].Macaroons = expectedMacs
  1048  		c.Assert(result, gc.FitsTypeOf, &params.ErrorResults{})
  1049  		*(result.(*params.ErrorResults)) = params.ErrorResults{
  1050  			Results: []params.ErrorResult{{}},
  1051  		}
  1052  		if shouldErr() {
  1053  			return errors.New("fail")
  1054  		}
  1055  		if !*ingressRequired || len(argNetworks) > 0 {
  1056  			published <- true
  1057  		}
  1058  		return nil
  1059  	})
  1060  
  1061  	s.crossmodelFirewaller = crossmodelrelations.NewClient(apiCaller)
  1062  	c.Assert(s.crossmodelFirewaller, gc.NotNil)
  1063  
  1064  	// Create the firewaller facade on the consuming model.
  1065  	fw := s.newFirewaller(c)
  1066  
  1067  	_, err = s.State.AddRemoteApplication(state.AddRemoteApplicationParams{
  1068  		Name: "mysql", SourceModel: offeringModelTag,
  1069  		Endpoints: []charm.Relation{{Name: "database", Interface: "mysql", Role: "provider", Scope: "global"}},
  1070  	})
  1071  	c.Assert(err, jc.ErrorIsNil)
  1072  	eps, err := s.State.InferEndpoints("wordpress", "mysql")
  1073  	c.Assert(err, jc.ErrorIsNil)
  1074  	rel, err := s.State.AddRelation(eps...)
  1075  	c.Assert(err, jc.ErrorIsNil)
  1076  
  1077  	// Export the relation details so the firewaller knows it's ready to be processed.
  1078  	re := s.State.RemoteEntities()
  1079  	relToken, err = re.ExportLocalEntity(rel.Tag())
  1080  	c.Assert(err, jc.ErrorIsNil)
  1081  	err = re.SaveMacaroon(rel.Tag(), mac)
  1082  	c.Assert(err, jc.ErrorIsNil)
  1083  
  1084  	// We should not have published any ingress events yet - no unit has entered scope.
  1085  	select {
  1086  	case <-time.After(coretesting.ShortWait):
  1087  	case <-published:
  1088  		c.Fatal("unexpected ingress change to be published")
  1089  	}
  1090  
  1091  	// Add a public address to the consuming unit so the firewaller can use it.
  1092  	wpm := s.Factory.MakeMachine(c, &factory.MachineParams{
  1093  		Addresses: network.SpaceAddresses{network.NewSpaceAddress("10.0.0.4")},
  1094  	})
  1095  	u, err := wordpress.AddUnit(state.AddUnitParams{})
  1096  	c.Assert(err, jc.ErrorIsNil)
  1097  	err = u.AssignToMachine(wpm)
  1098  	c.Assert(err, jc.ErrorIsNil)
  1099  	ru, err := rel.Unit(u)
  1100  	c.Assert(err, jc.ErrorIsNil)
  1101  	return fw, ru
  1102  }
  1103  
  1104  func (s *InstanceModeSuite) TestRemoteRelationRequirerRoleConsumingSide(c *gc.C) {
  1105  	published := make(chan bool)
  1106  	ingressRequired := true
  1107  	apiErr := func() bool {
  1108  		return false
  1109  	}
  1110  	fw, ru := s.setupRemoteRelationRequirerRoleConsumingSide(c, published, apiErr, &ingressRequired, s.clock)
  1111  	defer statetesting.AssertKillAndWait(c, fw)
  1112  
  1113  	// Add a unit on the consuming app and have it enter the relation scope.
  1114  	// This will trigger the firewaller to publish the changes.
  1115  	err := ru.EnterScope(map[string]interface{}{})
  1116  	c.Assert(err, jc.ErrorIsNil)
  1117  	select {
  1118  	case <-time.After(coretesting.LongWait):
  1119  		c.Fatal("time out waiting for ingress change to be published on enter scope")
  1120  	case <-published:
  1121  	}
  1122  
  1123  	// Change should be sent when unit leaves scope.
  1124  	ingressRequired = false
  1125  	err = ru.LeaveScope()
  1126  	c.Assert(err, jc.ErrorIsNil)
  1127  	select {
  1128  	case <-time.After(coretesting.LongWait):
  1129  		c.Fatal("time out waiting for ingress change to be published on leave scope")
  1130  	case <-published:
  1131  	}
  1132  }
  1133  
  1134  func (s *InstanceModeSuite) TestRemoteRelationWorkerError(c *gc.C) {
  1135  	published := make(chan bool, 1)
  1136  	ingressRequired := true
  1137  
  1138  	apiCalled := make(chan struct{}, 1)
  1139  	callCount := 0
  1140  	apiErr := func() bool {
  1141  		select {
  1142  		case apiCalled <- struct{}{}:
  1143  		case <-time.After(coretesting.ShortWait):
  1144  		}
  1145  		callCount += 1
  1146  		return callCount == 1
  1147  	}
  1148  	fw, ru := s.setupRemoteRelationRequirerRoleConsumingSide(c, published, apiErr, &ingressRequired, s.clock)
  1149  	defer statetesting.AssertKillAndWait(c, fw)
  1150  
  1151  	// Add a unit on the consuming app and have it enter the relation scope.
  1152  	// This will trigger the firewaller to try and publish the changes.
  1153  	err := ru.EnterScope(map[string]interface{}{})
  1154  	c.Assert(err, jc.ErrorIsNil)
  1155  
  1156  	select {
  1157  	case <-apiCalled:
  1158  	case <-time.After(coretesting.LongWait):
  1159  		c.Fatal("time out waiting for api to be called")
  1160  	}
  1161  
  1162  	// We should not have published any ingress events yet - no changed published.
  1163  	select {
  1164  	case <-time.After(coretesting.ShortWait):
  1165  	case <-published:
  1166  		c.Fatal("unexpected ingress change to be published")
  1167  	}
  1168  
  1169  	s.clock.Advance(time.Minute)
  1170  
  1171  	// Give the worker time to restart and try again.
  1172  	select {
  1173  	case <-time.After(coretesting.LongWait):
  1174  		c.Fatal("time out waiting for ingress change to be published on enter scope")
  1175  	case <-published:
  1176  	}
  1177  }
  1178  
  1179  func (s *InstanceModeSuite) TestRemoteRelationProviderRoleConsumingSide(c *gc.C) {
  1180  	// Set up the consuming model - create the local app.
  1181  	s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql"))
  1182  	// Set up the consuming model - create the remote app.
  1183  	offeringModelTag := names.NewModelTag(utils.MustNewUUID().String())
  1184  	appToken := utils.MustNewUUID().String()
  1185  	app, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{
  1186  		Name: "wordpress", SourceModel: offeringModelTag,
  1187  		Endpoints: []charm.Relation{{Name: "database", Interface: "mysql", Role: "requirer", Scope: "global"}},
  1188  	})
  1189  	c.Assert(err, jc.ErrorIsNil)
  1190  	// Create the external controller info.
  1191  	ec := state.NewExternalControllers(s.State)
  1192  	_, err = ec.Save(crossmodel.ControllerInfo{
  1193  		ControllerTag: coretesting.ControllerTag,
  1194  		Addrs:         []string{"1.2.3.4:1234"},
  1195  		CACert:        coretesting.CACert}, offeringModelTag.Id())
  1196  	c.Assert(err, jc.ErrorIsNil)
  1197  
  1198  	mac, err := apitesting.NewMacaroon("apimac")
  1199  	c.Assert(err, jc.ErrorIsNil)
  1200  	watched := make(chan bool)
  1201  	var relToken string
  1202  	callCount := int32(0)
  1203  	apiCaller := basetesting.APICallerFunc(func(objType string, version int, id, request string,
  1204  		arg, result interface{}) error {
  1205  		switch atomic.LoadInt32(&callCount) {
  1206  		case 0:
  1207  			c.Check(objType, gc.Equals, "CrossModelRelations")
  1208  			c.Check(version, gc.Equals, 0)
  1209  			c.Check(id, gc.Equals, "")
  1210  			c.Check(request, gc.Equals, "WatchEgressAddressesForRelations")
  1211  
  1212  			rArgs := arg.(params.RemoteEntityArgs).Args
  1213  			c.Assert(rArgs, gc.HasLen, 1)
  1214  			c.Check(rArgs[0].Token, gc.Equals, relToken)
  1215  
  1216  			macs := rArgs[0].Macaroons
  1217  			c.Assert(macs, gc.HasLen, 1)
  1218  			c.Assert(macs[0], gc.NotNil)
  1219  			apitesting.MacaroonEquals(c, macs[0], mac)
  1220  
  1221  			c.Assert(result, gc.FitsTypeOf, &params.StringsWatchResults{})
  1222  			*(result.(*params.StringsWatchResults)) = params.StringsWatchResults{
  1223  				Results: []params.StringsWatchResult{{StringsWatcherId: "1"}},
  1224  			}
  1225  			watched <- true
  1226  		default:
  1227  			c.Check(objType, gc.Equals, "StringsWatcher")
  1228  		}
  1229  		atomic.AddInt32(&callCount, 1)
  1230  		return nil
  1231  	})
  1232  
  1233  	s.crossmodelFirewaller = crossmodelrelations.NewClient(apiCaller)
  1234  	c.Assert(s.crossmodelFirewaller, gc.NotNil)
  1235  
  1236  	// Create the firewaller facade on the consuming model.
  1237  	fw := s.newFirewaller(c)
  1238  	defer statetesting.AssertKillAndWait(c, fw)
  1239  
  1240  	eps, err := s.State.InferEndpoints("wordpress", "mysql")
  1241  	c.Assert(err, jc.ErrorIsNil)
  1242  	rel, err := s.State.AddRelation(eps...)
  1243  	c.Assert(err, jc.ErrorIsNil)
  1244  
  1245  	// Export the relation details so the firewaller knows it's ready to be processed.
  1246  	re := s.State.RemoteEntities()
  1247  	relToken, err = re.ExportLocalEntity(rel.Tag())
  1248  	c.Assert(err, jc.ErrorIsNil)
  1249  	err = re.SaveMacaroon(rel.Tag(), mac)
  1250  	c.Assert(err, jc.ErrorIsNil)
  1251  	err = re.ImportRemoteEntity(app.Tag(), appToken)
  1252  	c.Assert(err, jc.ErrorIsNil)
  1253  
  1254  	select {
  1255  	case <-time.After(coretesting.LongWait):
  1256  		c.Fatal("time out waiting for watcher call")
  1257  	case <-watched:
  1258  	}
  1259  }
  1260  
  1261  func (s *InstanceModeSuite) TestRemoteRelationIngressRejected(c *gc.C) {
  1262  	// Set up the consuming model - create the local app.
  1263  	wordpress := s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
  1264  	// Set up the consuming model - create the remote app.
  1265  	offeringModelTag := names.NewModelTag(utils.MustNewUUID().String())
  1266  	appToken := utils.MustNewUUID().String()
  1267  
  1268  	app, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{
  1269  		Name: "mysql", SourceModel: offeringModelTag,
  1270  		Endpoints: []charm.Relation{{Name: "database", Interface: "mysql", Role: "provider", Scope: "global"}},
  1271  	})
  1272  	c.Assert(err, jc.ErrorIsNil)
  1273  	// Create the external controller info.
  1274  	ec := state.NewExternalControllers(s.State)
  1275  	_, err = ec.Save(crossmodel.ControllerInfo{
  1276  		ControllerTag: coretesting.ControllerTag,
  1277  		Addrs:         []string{"1.2.3.4:1234"},
  1278  		CACert:        coretesting.CACert}, offeringModelTag.Id())
  1279  	c.Assert(err, jc.ErrorIsNil)
  1280  
  1281  	mac, err := apitesting.NewMacaroon("apimac")
  1282  	c.Assert(err, jc.ErrorIsNil)
  1283  
  1284  	published := make(chan bool)
  1285  	done := false
  1286  	apiCaller := basetesting.APICallerFunc(func(objType string, version int, id, request string,
  1287  		arg, result interface{}) error {
  1288  		c.Assert(result, gc.FitsTypeOf, &params.ErrorResults{})
  1289  		*(result.(*params.ErrorResults)) = params.ErrorResults{
  1290  			Results: []params.ErrorResult{{Error: &params.Error{Code: params.CodeForbidden, Message: "error"}}},
  1291  		}
  1292  		// We can get more than one api call depending on the
  1293  		// granularity of watcher events.
  1294  		if !done {
  1295  			done = true
  1296  			published <- true
  1297  		}
  1298  		return nil
  1299  	})
  1300  
  1301  	s.crossmodelFirewaller = crossmodelrelations.NewClient(apiCaller)
  1302  	c.Assert(s.crossmodelFirewaller, gc.NotNil)
  1303  
  1304  	// Create the firewaller facade on the consuming model.
  1305  	fw := s.newFirewaller(c)
  1306  	defer statetesting.AssertKillAndWait(c, fw)
  1307  
  1308  	eps, err := s.State.InferEndpoints("wordpress", "mysql")
  1309  	c.Assert(err, jc.ErrorIsNil)
  1310  	rel, err := s.State.AddRelation(eps...)
  1311  	c.Assert(err, jc.ErrorIsNil)
  1312  
  1313  	// Export the relation details so the firewaller knows it's ready to be processed.
  1314  	re := s.State.RemoteEntities()
  1315  	_, err = re.ExportLocalEntity(rel.Tag())
  1316  	c.Assert(err, jc.ErrorIsNil)
  1317  	err = re.SaveMacaroon(rel.Tag(), mac)
  1318  	c.Assert(err, jc.ErrorIsNil)
  1319  	err = re.ImportRemoteEntity(app.Tag(), appToken)
  1320  	c.Assert(err, jc.ErrorIsNil)
  1321  
  1322  	// Add a public address to the consuming unit so the firewaller can use it.
  1323  	wpm := s.Factory.MakeMachine(c, &factory.MachineParams{
  1324  		Addresses: network.SpaceAddresses{network.NewSpaceAddress("10.0.0.4")},
  1325  	})
  1326  	u, err := wordpress.AddUnit(state.AddUnitParams{})
  1327  	c.Assert(err, jc.ErrorIsNil)
  1328  	err = u.AssignToMachine(wpm)
  1329  	c.Assert(err, jc.ErrorIsNil)
  1330  	ru, err := rel.Unit(u)
  1331  	c.Assert(err, jc.ErrorIsNil)
  1332  
  1333  	// Add a unit on the consuming app and have it enter the relation scope.
  1334  	// This will trigger the firewaller to publish the changes.
  1335  	err = ru.EnterScope(map[string]interface{}{})
  1336  	c.Assert(err, jc.ErrorIsNil)
  1337  	select {
  1338  	case <-time.After(coretesting.LongWait):
  1339  		c.Fatal("time out waiting for ingress change to be published on enter scope")
  1340  	case <-published:
  1341  	}
  1342  
  1343  	// Check that the relation status is set to error
  1344  	for attempt := coretesting.LongAttempt.Start(); attempt.Next(); {
  1345  		relStatus, err := rel.Status()
  1346  		c.Check(err, jc.ErrorIsNil)
  1347  		if relStatus.Status != status.Error {
  1348  			continue
  1349  		}
  1350  		c.Check(relStatus.Message, gc.Equals, "error")
  1351  		return
  1352  	}
  1353  	c.Fatal("time out waiting for relation status to be updated")
  1354  }
  1355  
  1356  func (s *InstanceModeSuite) assertIngressCidrs(c *gc.C, ingress []string, expected []string) {
  1357  	// Set up the offering model - create the local app.
  1358  	mysql := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql"))
  1359  	u, m := s.addUnit(c, mysql)
  1360  	inst := s.startInstance(c, m)
  1361  
  1362  	mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{
  1363  		network.MustParsePortRange("3306/tcp"),
  1364  	})
  1365  
  1366  	// Set up the offering model - create the remote app.
  1367  	consumingModelTag := names.NewModelTag(utils.MustNewUUID().String())
  1368  	relToken := utils.MustNewUUID().String()
  1369  	appToken := utils.MustNewUUID().String()
  1370  	app, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{
  1371  		Name: "wordpress", SourceModel: consumingModelTag, IsConsumerProxy: true,
  1372  		Endpoints: []charm.Relation{{Name: "db", Interface: "mysql", Role: "requirer", Scope: "global"}},
  1373  	})
  1374  	c.Assert(err, jc.ErrorIsNil)
  1375  	s.WaitForModelWatchersIdle(c, s.State.ModelUUID())
  1376  
  1377  	// Create the firewaller facade on the offering model.
  1378  	fw := s.newFirewaller(c)
  1379  	defer statetesting.AssertKillAndWait(c, fw)
  1380  
  1381  	eps, err := s.State.InferEndpoints("wordpress", "mysql")
  1382  	c.Assert(err, jc.ErrorIsNil)
  1383  	rel, err := s.State.AddRelation(eps...)
  1384  	c.Assert(err, jc.ErrorIsNil)
  1385  
  1386  	// Export the relation details so the firewaller knows it's ready to be processed.
  1387  	re := s.State.RemoteEntities()
  1388  	err = re.ImportRemoteEntity(rel.Tag(), relToken)
  1389  	c.Assert(err, jc.ErrorIsNil)
  1390  	err = re.ImportRemoteEntity(app.Tag(), appToken)
  1391  	c.Assert(err, jc.ErrorIsNil)
  1392  
  1393  	// No port changes yet.
  1394  	s.assertIngressRules(c, inst, m.Id(), nil)
  1395  
  1396  	// Save a new ingress network against the relation.
  1397  	rin := state.NewRelationIngressNetworks(s.State)
  1398  	_, err = rin.Save(rel.Tag().Id(), false, ingress)
  1399  	c.Assert(err, jc.ErrorIsNil)
  1400  
  1401  	// Ports opened.
  1402  	s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{
  1403  		firewall.NewIngressRule(network.MustParsePortRange("3306/tcp"), expected...),
  1404  	})
  1405  
  1406  	// Change should be sent when ingress networks disappear.
  1407  	_, err = rin.Save(rel.Tag().Id(), false, nil)
  1408  	c.Assert(err, jc.ErrorIsNil)
  1409  	s.assertIngressRules(c, inst, m.Id(), nil)
  1410  
  1411  	_, err = rin.Save(rel.Tag().Id(), false, ingress)
  1412  	c.Assert(err, jc.ErrorIsNil)
  1413  	s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{
  1414  		firewall.NewIngressRule(network.MustParsePortRange("3306/tcp"), expected...),
  1415  	})
  1416  
  1417  	// And again when relation is suspended.
  1418  	err = rel.SetSuspended(true, "")
  1419  	c.Assert(err, jc.ErrorIsNil)
  1420  	s.assertIngressRules(c, inst, m.Id(), nil)
  1421  
  1422  	// And again when relation is resumed.
  1423  	err = rel.SetSuspended(false, "")
  1424  	c.Assert(err, jc.ErrorIsNil)
  1425  	s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{
  1426  		firewall.NewIngressRule(network.MustParsePortRange("3306/tcp"), expected...),
  1427  	})
  1428  
  1429  	// And again when relation is destroyed.
  1430  	err = rel.Destroy()
  1431  	c.Assert(err, jc.ErrorIsNil)
  1432  	s.assertIngressRules(c, inst, m.Id(), nil)
  1433  }
  1434  
  1435  func (s *InstanceModeSuite) TestRemoteRelationProviderRoleOffering(c *gc.C) {
  1436  	s.assertIngressCidrs(c, []string{"10.0.0.4/16"}, []string{"10.0.0.4/16"})
  1437  }
  1438  
  1439  func (s *InstanceModeSuite) TestRemoteRelationIngressFallbackToWhitelist(c *gc.C) {
  1440  	m, err := s.State.Model()
  1441  	c.Assert(err, jc.ErrorIsNil)
  1442  	m.UpdateModelConfig(map[string]interface{}{
  1443  		config.SAASIngressAllowKey: "192.168.1.0/16",
  1444  	}, nil)
  1445  	var ingress []string
  1446  	for i := 1; i < 30; i++ {
  1447  		ingress = append(ingress, fmt.Sprintf("10.%d.0.1/32", i))
  1448  	}
  1449  	s.assertIngressCidrs(c, ingress, []string{"192.168.1.0/16"})
  1450  }
  1451  
  1452  func (s *InstanceModeSuite) TestRemoteRelationIngressMergesCIDRS(c *gc.C) {
  1453  	ingress := []string{
  1454  		"192.0.1.254/31",
  1455  		"192.0.2.0/28",
  1456  		"192.0.2.16/28",
  1457  		"192.0.2.32/28",
  1458  		"192.0.2.48/28",
  1459  		"192.0.2.64/28",
  1460  		"192.0.2.80/28",
  1461  		"192.0.2.96/28",
  1462  		"192.0.2.112/28",
  1463  		"192.0.2.128/28",
  1464  		"192.0.2.144/28",
  1465  		"192.0.2.160/28",
  1466  		"192.0.2.176/28",
  1467  		"192.0.2.192/28",
  1468  		"192.0.2.208/28",
  1469  		"192.0.2.224/28",
  1470  		"192.0.2.240/28",
  1471  		"192.0.3.0/28",
  1472  		"192.0.4.0/28",
  1473  		"192.0.5.0/28",
  1474  		"192.0.6.0/28",
  1475  	}
  1476  	expected := []string{
  1477  		"192.0.1.254/31",
  1478  		"192.0.2.0/24",
  1479  		"192.0.3.0/28",
  1480  		"192.0.4.0/28",
  1481  		"192.0.5.0/28",
  1482  		"192.0.6.0/28",
  1483  	}
  1484  	s.assertIngressCidrs(c, ingress, expected)
  1485  }
  1486  
  1487  func (s *InstanceModeSuite) TestExposedApplicationWithExposedEndpoints(c *gc.C) {
  1488  	// Create a spaces and add a subnet to it
  1489  	sp1, err := s.State.AddSpace("space1", network.Id("sp-1"), nil, false)
  1490  	c.Assert(err, jc.ErrorIsNil)
  1491  	_, err = s.State.AddSubnet(network.SubnetInfo{
  1492  		ID:        "subnet-1",
  1493  		CIDR:      "42.42.0.0/16",
  1494  		SpaceID:   sp1.Id(),
  1495  		SpaceName: sp1.Name(),
  1496  		IsPublic:  false,
  1497  	})
  1498  	c.Assert(err, jc.ErrorIsNil)
  1499  
  1500  	fw := s.newFirewaller(c)
  1501  	defer statetesting.AssertKillAndWait(c, fw)
  1502  
  1503  	app := s.AddTestingApplication(c, "wordpress", s.charm)
  1504  
  1505  	u, m := s.addUnit(c, app)
  1506  	inst := s.startInstance(c, m)
  1507  
  1508  	mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{
  1509  		network.MustParsePortRange("80/tcp"),
  1510  	})
  1511  	mustOpenPortRanges(c, s.State, u, "url", []network.PortRange{
  1512  		network.MustParsePortRange("1337/tcp"),
  1513  		network.MustParsePortRange("1337/udp"),
  1514  	})
  1515  
  1516  	err = app.MergeExposeSettings(map[string]state.ExposedEndpoint{
  1517  		allEndpoints: {
  1518  			ExposeToCIDRs: []string{"10.0.0.0/24"},
  1519  		},
  1520  		"url": {
  1521  			ExposeToCIDRs:    []string{"192.168.0.0/24", "192.168.1.0/24"},
  1522  			ExposeToSpaceIDs: []string{sp1.Id()},
  1523  		},
  1524  	})
  1525  	c.Assert(err, jc.ErrorIsNil)
  1526  
  1527  	s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{
  1528  		// We have opened port 80 for ALL endpoints (including "url"),
  1529  		// then exposed ALL endpoints to 10.0.0.0/24 and the "url"
  1530  		// endpoint to 192.168.{0,1}.0/24 and 42.42.0.0/16 (subnet
  1531  		// of space-1).
  1532  		//
  1533  		// We expect to see port 80 use all three CIDRs as valid input sources
  1534  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), "10.0.0.0/24", "192.168.0.0/24", "192.168.1.0/24",
  1535  			"42.42.0.0/16"),
  1536  		//
  1537  		// The 1337/{tcp,udp} ports have only been opened for the "url"
  1538  		// endpoint and the "url" endpoint has been exposed to 192.168.{0,1}.0/24
  1539  		// and 42.42.0.0/16 (the subnet for space-1).
  1540  		//
  1541  		// The ports should only be reachable from these CIDRs. Note
  1542  		// that the expose for the wildcard ("") endpoint is ignored
  1543  		// here as the expose settings for the "url" endpoint must
  1544  		// supersede it.
  1545  		firewall.NewIngressRule(network.MustParsePortRange("1337/tcp"), "192.168.0.0/24", "192.168.1.0/24",
  1546  			"42.42.0.0/16"),
  1547  		firewall.NewIngressRule(network.MustParsePortRange("1337/udp"), "192.168.0.0/24", "192.168.1.0/24",
  1548  			"42.42.0.0/16"),
  1549  	})
  1550  
  1551  	// Change the expose settings and remove the entry for the wildcard endpoint
  1552  	err = app.UnsetExposeSettings([]string{allEndpoints})
  1553  	c.Assert(err, jc.ErrorIsNil)
  1554  
  1555  	s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{
  1556  		// We unexposed the wildcard endpoint so only the "url" endpoint
  1557  		// remains exposed. This endpoint has ports 1337/{tcp,udp}
  1558  		// explicitly open as well as port 80 which is opened for ALL
  1559  		// endpoints. These three ports should be exposed to the
  1560  		// CIDRs used when the "url" endpoint was exposed
  1561  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), "192.168.0.0/24", "192.168.1.0/24",
  1562  			"42.42.0.0/16"),
  1563  		firewall.NewIngressRule(network.MustParsePortRange("1337/tcp"), "192.168.0.0/24", "192.168.1.0/24",
  1564  			"42.42.0.0/16"),
  1565  		firewall.NewIngressRule(network.MustParsePortRange("1337/udp"), "192.168.0.0/24", "192.168.1.0/24",
  1566  			"42.42.0.0/16"),
  1567  	})
  1568  }
  1569  
  1570  func (s *InstanceModeSuite) TestExposedApplicationWithExposedEndpointsWhenSpaceTopologyChanges(c *gc.C) {
  1571  	// Create two spaces and add a subnet to each one
  1572  	sp1, err := s.State.AddSpace("space1", network.Id("sp-1"), nil, false)
  1573  	c.Assert(err, jc.ErrorIsNil)
  1574  	_, err = s.State.AddSubnet(network.SubnetInfo{
  1575  		ID:        "subnet-1",
  1576  		CIDR:      "192.168.0.0/24",
  1577  		SpaceID:   sp1.Id(),
  1578  		SpaceName: sp1.Name(),
  1579  		IsPublic:  false,
  1580  	})
  1581  	c.Assert(err, jc.ErrorIsNil)
  1582  
  1583  	sp2, err := s.State.AddSpace("space2", network.Id("sp-2"), nil, true)
  1584  	c.Assert(err, jc.ErrorIsNil)
  1585  	sub2, err := s.State.AddSubnet(network.SubnetInfo{
  1586  		ID:        "subnet-2",
  1587  		CIDR:      "192.168.1.0/24",
  1588  		SpaceID:   sp2.Id(),
  1589  		SpaceName: sp2.Name(),
  1590  		IsPublic:  false,
  1591  	})
  1592  	c.Assert(err, jc.ErrorIsNil)
  1593  
  1594  	fw := s.newFirewaller(c)
  1595  	defer statetesting.AssertKillAndWait(c, fw)
  1596  
  1597  	app := s.AddTestingApplication(c, "wordpress", s.charm)
  1598  
  1599  	u, m := s.addUnit(c, app)
  1600  	inst := s.startInstance(c, m)
  1601  	// Open port 80 for all endpoints
  1602  	mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{
  1603  		network.MustParsePortRange("80/tcp"),
  1604  	})
  1605  
  1606  	// Expose app to space-1
  1607  	err = app.MergeExposeSettings(map[string]state.ExposedEndpoint{
  1608  		allEndpoints: {
  1609  			ExposeToSpaceIDs: []string{sp1.Id()},
  1610  		},
  1611  	})
  1612  	c.Assert(err, jc.ErrorIsNil)
  1613  
  1614  	s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{
  1615  		// We expect to see port 80 use the subnet-1 CIDR
  1616  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), "192.168.0.0/24"),
  1617  	})
  1618  
  1619  	// Trigger a space topology change by moving subnet-2 into space 1
  1620  	moveOps := s.State.UpdateSubnetSpaceOps(sub2.ID(), sp1.Id())
  1621  	c.Assert(s.State.ApplyOperation(modelOp{moveOps}), jc.ErrorIsNil)
  1622  
  1623  	// Check that worker picked up the change and updated the rules
  1624  	s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{
  1625  		// We expect to see port 80 use subnet-{1,2} CIDRs
  1626  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), "192.168.0.0/24", "192.168.1.0/24"),
  1627  	})
  1628  }
  1629  
  1630  func (s *InstanceModeSuite) TestExposedApplicationWithExposedEndpointsWhenSpaceDeleted(c *gc.C) {
  1631  	// Create two spaces and add a subnet to each one
  1632  	sp1, err := s.State.AddSpace("space1", "sp-1", nil, false)
  1633  	c.Assert(err, jc.ErrorIsNil)
  1634  	sub1, err := s.State.AddSubnet(network.SubnetInfo{
  1635  		ID:        "subnet-1",
  1636  		CIDR:      "192.168.0.0/24",
  1637  		SpaceID:   sp1.Id(),
  1638  		SpaceName: sp1.Name(),
  1639  		IsPublic:  false,
  1640  	})
  1641  	c.Assert(err, jc.ErrorIsNil)
  1642  
  1643  	sp2, err := s.State.AddSpace("space2", "sp-2", nil, true)
  1644  	c.Assert(err, jc.ErrorIsNil)
  1645  	_, err = s.State.AddSubnet(network.SubnetInfo{
  1646  		ID:        "subnet-2",
  1647  		CIDR:      "192.168.1.0/24",
  1648  		SpaceID:   sp2.Id(),
  1649  		SpaceName: sp2.Name(),
  1650  		IsPublic:  false,
  1651  	})
  1652  	c.Assert(err, jc.ErrorIsNil)
  1653  
  1654  	fw := s.newFirewaller(c)
  1655  	defer statetesting.AssertKillAndWait(c, fw)
  1656  
  1657  	app := s.AddTestingApplication(c, "wordpress", s.charm)
  1658  
  1659  	u, m := s.addUnit(c, app)
  1660  	inst := s.startInstance(c, m)
  1661  	// Open port 80 for all endpoints
  1662  	mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{
  1663  		network.MustParsePortRange("80/tcp"),
  1664  	})
  1665  
  1666  	// Expose app to space-1
  1667  	err = app.MergeExposeSettings(map[string]state.ExposedEndpoint{
  1668  		allEndpoints: {
  1669  			ExposeToSpaceIDs: []string{sp1.Id()},
  1670  		},
  1671  	})
  1672  	c.Assert(err, jc.ErrorIsNil)
  1673  
  1674  	s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{
  1675  		// We expect to see port 80 use the subnet-1 CIDR
  1676  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), "192.168.0.0/24"),
  1677  	})
  1678  
  1679  	// Simulate the deletion of a space, with subnets moving back to alpha.
  1680  	moveOps := s.State.UpdateSubnetSpaceOps(sub1.ID(), network.AlphaSpaceId)
  1681  	deleteOps, err := sp1.RemoveSpaceOps()
  1682  	c.Assert(err, jc.ErrorIsNil)
  1683  	c.Assert(s.State.ApplyOperation(modelOp{append(moveOps, deleteOps...)}), jc.ErrorIsNil)
  1684  
  1685  	// We expect to see NO ingress rules as the referenced space does not exist.
  1686  	s.assertIngressRules(c, inst, m.Id(), nil)
  1687  }
  1688  
  1689  func (s *InstanceModeSuite) TestExposedApplicationWithExposedEndpointsWhenSpaceHasNoSubnets(c *gc.C) {
  1690  	// Create a space with a single subnet
  1691  	sp1, err := s.State.AddSpace("space1", "sp-1", nil, false)
  1692  	c.Assert(err, jc.ErrorIsNil)
  1693  	sub1, err := s.State.AddSubnet(network.SubnetInfo{
  1694  		ID:        "subnet-1",
  1695  		CIDR:      "192.168.0.0/24",
  1696  		SpaceID:   sp1.Id(),
  1697  		SpaceName: sp1.Name(),
  1698  		IsPublic:  false,
  1699  	})
  1700  	c.Assert(err, jc.ErrorIsNil)
  1701  
  1702  	fw := s.newFirewaller(c)
  1703  	defer statetesting.AssertKillAndWait(c, fw)
  1704  
  1705  	app := s.AddTestingApplication(c, "wordpress", s.charm)
  1706  
  1707  	u, m := s.addUnit(c, app)
  1708  	inst := s.startInstance(c, m)
  1709  	// Open port 80 for all endpoints and 1337 for the "url" endpoint
  1710  	mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{
  1711  		network.MustParsePortRange("80/tcp"),
  1712  	})
  1713  	mustOpenPortRanges(c, s.State, u, "url", []network.PortRange{
  1714  		network.MustParsePortRange("1337/tcp"),
  1715  	})
  1716  
  1717  	// Expose app to space-1
  1718  	err = app.MergeExposeSettings(map[string]state.ExposedEndpoint{
  1719  		allEndpoints: {
  1720  			ExposeToSpaceIDs: []string{sp1.Id()},
  1721  		},
  1722  		"url": {
  1723  			ExposeToSpaceIDs: []string{sp1.Id()},
  1724  		},
  1725  	})
  1726  	c.Assert(err, jc.ErrorIsNil)
  1727  
  1728  	s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{
  1729  		// We expect to see port 80 and 1337 use the subnet-1 CIDR
  1730  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), "192.168.0.0/24"),
  1731  		firewall.NewIngressRule(network.MustParsePortRange("1337/tcp"), "192.168.0.0/24"),
  1732  	})
  1733  
  1734  	// Move endpoint back to alpha space. This will leave space-1 with no
  1735  	// endpoints.
  1736  	moveOps := s.State.UpdateSubnetSpaceOps(sub1.ID(), network.AlphaSpaceId)
  1737  	c.Assert(s.State.ApplyOperation(modelOp{moveOps}), jc.ErrorIsNil)
  1738  
  1739  	// We expect to see NO ingress rules (and warnings in the logs) as
  1740  	// there are no CIDRs to access the exposed application.
  1741  	s.assertIngressRules(c, inst, m.Id(), nil)
  1742  }
  1743  
  1744  func (s *InstanceModeSuite) TestExposeToIPV6CIDRsOnIPV4OnlyProvider(c *gc.C) {
  1745  	supportsIPV6CIDRs := false
  1746  	fw := s.newFirewallerWithIPV6CIDRSupport(c, supportsIPV6CIDRs)
  1747  	defer statetesting.AssertKillAndWait(c, fw)
  1748  
  1749  	app := s.AddTestingApplication(c, "wordpress", s.charm)
  1750  
  1751  	u, m := s.addUnit(c, app)
  1752  	inst := s.startInstance(c, m)
  1753  
  1754  	mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{
  1755  		network.MustParsePortRange("80/tcp"),
  1756  	})
  1757  
  1758  	err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{
  1759  		allEndpoints: {
  1760  			ExposeToCIDRs: []string{"10.0.0.0/24", "2002::1234:abcd:ffff:c0a8:101/64"},
  1761  		},
  1762  	})
  1763  	c.Assert(err, jc.ErrorIsNil)
  1764  
  1765  	// Since the provider only supports IPV4 CIDRs, the firewall worker
  1766  	// will filter the IPV6 CIDRs when opening ports.
  1767  	s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{
  1768  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), "10.0.0.0/24"),
  1769  	})
  1770  }
  1771  
  1772  type modelOp struct {
  1773  	ops []txn.Op
  1774  }
  1775  
  1776  func (m modelOp) Build(_ int) ([]txn.Op, error) { return m.ops, nil }
  1777  func (m modelOp) Done(err error) error          { return err }
  1778  
  1779  type GlobalModeSuite struct {
  1780  	firewallerBaseSuite
  1781  }
  1782  
  1783  var _ = gc.Suite(&GlobalModeSuite{})
  1784  
  1785  func (s *GlobalModeSuite) SetUpTest(c *gc.C) {
  1786  	s.firewallerBaseSuite.setUpTest(c, config.FwGlobal)
  1787  }
  1788  
  1789  func (s *GlobalModeSuite) TearDownTest(c *gc.C) {
  1790  	s.firewallerBaseSuite.JujuConnSuite.TearDownTest(c)
  1791  }
  1792  
  1793  func (s *GlobalModeSuite) newFirewaller(c *gc.C) worker.Worker {
  1794  	return s.newFirewallerWithIPV6CIDRSupport(c, true)
  1795  }
  1796  
  1797  func (s *GlobalModeSuite) newFirewallerWithIPV6CIDRSupport(c *gc.C, supportIPV6CIDRs bool) worker.Worker {
  1798  	fwEnv, ok := s.Environ.(environs.Firewaller)
  1799  	c.Assert(ok, gc.Equals, true)
  1800  
  1801  	modelFwEnv, ok := s.Environ.(models.ModelFirewaller)
  1802  	c.Assert(ok, gc.Equals, true)
  1803  
  1804  	cfg := firewaller.Config{
  1805  		ModelUUID:              s.State.ModelUUID(),
  1806  		Mode:                   config.FwGlobal,
  1807  		EnvironFirewaller:      fwEnv,
  1808  		EnvironModelFirewaller: modelFwEnv,
  1809  		EnvironInstances:       s.Environ,
  1810  		EnvironIPV6CIDRSupport: supportIPV6CIDRs,
  1811  		FirewallerAPI:          s.firewaller,
  1812  		RemoteRelationsApi:     s.remoteRelations,
  1813  		NewCrossModelFacadeFunc: func(*api.Info) (firewaller.CrossModelFirewallerFacadeCloser, error) {
  1814  			return s.crossmodelFirewaller, nil
  1815  		},
  1816  		Logger:        loggo.GetLogger("test"),
  1817  		CredentialAPI: s.credentialsFacade,
  1818  	}
  1819  	fw, err := firewaller.NewFirewaller(cfg)
  1820  	c.Assert(err, jc.ErrorIsNil)
  1821  	return fw
  1822  }
  1823  
  1824  func (s *GlobalModeSuite) TestStartStop(c *gc.C) {
  1825  	fw := s.newFirewaller(c)
  1826  	statetesting.AssertKillAndWait(c, fw)
  1827  }
  1828  
  1829  func (s *GlobalModeSuite) TestGlobalMode(c *gc.C) {
  1830  	// Start firewaller and open ports.
  1831  	fw := s.newFirewaller(c)
  1832  	defer statetesting.AssertKillAndWait(c, fw)
  1833  
  1834  	app1 := s.AddTestingApplication(c, "wordpress", s.charm)
  1835  	err := app1.MergeExposeSettings(map[string]state.ExposedEndpoint{
  1836  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
  1837  	})
  1838  	c.Assert(err, jc.ErrorIsNil)
  1839  
  1840  	u1, m1 := s.addUnit(c, app1)
  1841  	s.startInstance(c, m1)
  1842  	mustOpenPortRanges(c, s.State, u1, allEndpoints, []network.PortRange{
  1843  		network.MustParsePortRange("80-90/tcp"),
  1844  		network.MustParsePortRange("8080/tcp"),
  1845  	})
  1846  
  1847  	app2 := s.AddTestingApplication(c, "moinmoin", s.charm)
  1848  	c.Assert(err, jc.ErrorIsNil)
  1849  	err = app2.MergeExposeSettings(map[string]state.ExposedEndpoint{
  1850  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
  1851  	})
  1852  	c.Assert(err, jc.ErrorIsNil)
  1853  
  1854  	u2, m2 := s.addUnit(c, app2)
  1855  	s.startInstance(c, m2)
  1856  	mustOpenPortRanges(c, s.State, u2, allEndpoints, []network.PortRange{
  1857  		network.MustParsePortRange("80-90/tcp"),
  1858  	})
  1859  
  1860  	s.assertEnvironPorts(c, firewall.IngressRules{
  1861  		firewall.NewIngressRule(network.MustParsePortRange("80-90/tcp"), firewall.AllNetworksIPV4CIDR),
  1862  		firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR),
  1863  	})
  1864  
  1865  	// Closing a port opened by a different unit won't touch the environment.
  1866  	mustClosePortRanges(c, s.State, u1, allEndpoints, []network.PortRange{
  1867  		network.MustParsePortRange("80-90/tcp"),
  1868  	})
  1869  	s.assertEnvironPorts(c, firewall.IngressRules{
  1870  		firewall.NewIngressRule(network.MustParsePortRange("80-90/tcp"), firewall.AllNetworksIPV4CIDR),
  1871  		firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR),
  1872  	})
  1873  
  1874  	// Closing a port used just once changes the environment.
  1875  	mustClosePortRanges(c, s.State, u1, allEndpoints, []network.PortRange{
  1876  		network.MustParsePortRange("8080/tcp"),
  1877  	})
  1878  	s.assertEnvironPorts(c, firewall.IngressRules{
  1879  		firewall.NewIngressRule(network.MustParsePortRange("80-90/tcp"), firewall.AllNetworksIPV4CIDR),
  1880  	})
  1881  
  1882  	// Closing the last port also modifies the environment.
  1883  	mustClosePortRanges(c, s.State, u2, allEndpoints, []network.PortRange{
  1884  		network.MustParsePortRange("80-90/tcp"),
  1885  	})
  1886  	s.assertEnvironPorts(c, nil)
  1887  }
  1888  
  1889  func (s *GlobalModeSuite) TestStartWithUnexposedApplication(c *gc.C) {
  1890  	m, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
  1891  	c.Assert(err, jc.ErrorIsNil)
  1892  	s.startInstance(c, m)
  1893  
  1894  	app := s.AddTestingApplication(c, "wordpress", s.charm)
  1895  	u, err := app.AddUnit(state.AddUnitParams{})
  1896  	c.Assert(err, jc.ErrorIsNil)
  1897  	err = u.AssignToMachine(m)
  1898  	c.Assert(err, jc.ErrorIsNil)
  1899  	mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{
  1900  		network.MustParsePortRange("80/tcp"),
  1901  	})
  1902  
  1903  	// Starting the firewaller, no open ports.
  1904  	fw := s.newFirewaller(c)
  1905  	defer statetesting.AssertKillAndWait(c, fw)
  1906  
  1907  	s.assertEnvironPorts(c, nil)
  1908  
  1909  	// Expose application.
  1910  	err = app.MergeExposeSettings(map[string]state.ExposedEndpoint{
  1911  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
  1912  	})
  1913  	c.Assert(err, jc.ErrorIsNil)
  1914  	s.assertEnvironPorts(c, firewall.IngressRules{
  1915  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR),
  1916  	})
  1917  }
  1918  
  1919  func (s *GlobalModeSuite) TestRestart(c *gc.C) {
  1920  	// Start firewaller and open ports.
  1921  	fw := s.newFirewaller(c)
  1922  
  1923  	app := s.AddTestingApplication(c, "wordpress", s.charm)
  1924  	err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{
  1925  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
  1926  	})
  1927  	c.Assert(err, jc.ErrorIsNil)
  1928  
  1929  	u, m := s.addUnit(c, app)
  1930  	s.startInstance(c, m)
  1931  	mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{
  1932  		network.MustParsePortRange("80-90/tcp"),
  1933  		network.MustParsePortRange("8080/tcp"),
  1934  	})
  1935  	c.Assert(err, jc.ErrorIsNil)
  1936  
  1937  	s.assertEnvironPorts(c, firewall.IngressRules{
  1938  		firewall.NewIngressRule(network.MustParsePortRange("80-90/tcp"), firewall.AllNetworksIPV4CIDR),
  1939  		firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR),
  1940  	})
  1941  
  1942  	// Stop firewaller and close one and open a different port.
  1943  	err = worker.Stop(fw)
  1944  	c.Assert(err, jc.ErrorIsNil)
  1945  
  1946  	mustClosePortRanges(c, s.State, u, allEndpoints, []network.PortRange{
  1947  		network.MustParsePortRange("8080/tcp"),
  1948  	})
  1949  	mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{
  1950  		network.MustParsePortRange("8888/tcp"),
  1951  	})
  1952  
  1953  	// Start firewaller and check port.
  1954  	fw = s.newFirewaller(c)
  1955  	defer statetesting.AssertKillAndWait(c, fw)
  1956  
  1957  	s.assertEnvironPorts(c, firewall.IngressRules{
  1958  		firewall.NewIngressRule(network.MustParsePortRange("80-90/tcp"), firewall.AllNetworksIPV4CIDR),
  1959  		firewall.NewIngressRule(network.MustParsePortRange("8888/tcp"), firewall.AllNetworksIPV4CIDR),
  1960  	})
  1961  }
  1962  
  1963  func (s *GlobalModeSuite) TestRestartUnexposedApplication(c *gc.C) {
  1964  	// Start firewaller and open ports.
  1965  	fw := s.newFirewaller(c)
  1966  
  1967  	app := s.AddTestingApplication(c, "wordpress", s.charm)
  1968  	err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{
  1969  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
  1970  	})
  1971  	c.Assert(err, jc.ErrorIsNil)
  1972  
  1973  	u, m := s.addUnit(c, app)
  1974  	s.startInstance(c, m)
  1975  	mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{
  1976  		network.MustParsePortRange("80/tcp"),
  1977  		network.MustParsePortRange("8080/tcp"),
  1978  	})
  1979  
  1980  	s.assertEnvironPorts(c, firewall.IngressRules{
  1981  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR),
  1982  		firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR),
  1983  	})
  1984  
  1985  	// Stop firewaller and clear exposed flag on application.
  1986  	err = worker.Stop(fw)
  1987  	c.Assert(err, jc.ErrorIsNil)
  1988  
  1989  	err = app.ClearExposed()
  1990  	c.Assert(err, jc.ErrorIsNil)
  1991  
  1992  	// Start firewaller and check port.
  1993  	fw = s.newFirewaller(c)
  1994  	defer statetesting.AssertKillAndWait(c, fw)
  1995  
  1996  	s.assertEnvironPorts(c, nil)
  1997  }
  1998  
  1999  func (s *GlobalModeSuite) TestRestartPortCount(c *gc.C) {
  2000  	// Start firewaller and open ports.
  2001  	fw := s.newFirewaller(c)
  2002  
  2003  	app1 := s.AddTestingApplication(c, "wordpress", s.charm)
  2004  	err := app1.MergeExposeSettings(map[string]state.ExposedEndpoint{
  2005  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
  2006  	})
  2007  	c.Assert(err, jc.ErrorIsNil)
  2008  
  2009  	u1, m1 := s.addUnit(c, app1)
  2010  	s.startInstance(c, m1)
  2011  	mustOpenPortRanges(c, s.State, u1, allEndpoints, []network.PortRange{
  2012  		network.MustParsePortRange("80/tcp"),
  2013  		network.MustParsePortRange("8080/tcp"),
  2014  	})
  2015  
  2016  	s.assertEnvironPorts(c, firewall.IngressRules{
  2017  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR),
  2018  		firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR),
  2019  	})
  2020  
  2021  	// Stop firewaller and add another application using the port.
  2022  	err = worker.Stop(fw)
  2023  	c.Assert(err, jc.ErrorIsNil)
  2024  
  2025  	app2 := s.AddTestingApplication(c, "moinmoin", s.charm)
  2026  	err = app2.MergeExposeSettings(map[string]state.ExposedEndpoint{
  2027  		allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}},
  2028  	})
  2029  	c.Assert(err, jc.ErrorIsNil)
  2030  
  2031  	u2, m2 := s.addUnit(c, app2)
  2032  	s.startInstance(c, m2)
  2033  	mustOpenPortRanges(c, s.State, u2, allEndpoints, []network.PortRange{
  2034  		network.MustParsePortRange("80/tcp"),
  2035  	})
  2036  
  2037  	// Start firewaller and check port.
  2038  	fw = s.newFirewaller(c)
  2039  	defer statetesting.AssertKillAndWait(c, fw)
  2040  
  2041  	s.assertEnvironPorts(c, firewall.IngressRules{
  2042  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR),
  2043  		firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR),
  2044  	})
  2045  
  2046  	// Closing a port opened by a different unit won't touch the environment.
  2047  	mustClosePortRanges(c, s.State, u1, allEndpoints, []network.PortRange{
  2048  		network.MustParsePortRange("80/tcp"),
  2049  	})
  2050  	s.assertEnvironPorts(c, firewall.IngressRules{
  2051  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR),
  2052  		firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR),
  2053  	})
  2054  
  2055  	// Closing a port used just once changes the environment.
  2056  	mustClosePortRanges(c, s.State, u1, allEndpoints, []network.PortRange{
  2057  		network.MustParsePortRange("8080/tcp"),
  2058  	})
  2059  	s.assertEnvironPorts(c, firewall.IngressRules{
  2060  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR),
  2061  	})
  2062  
  2063  	// Closing the last port also modifies the environment.
  2064  	mustClosePortRanges(c, s.State, u2, allEndpoints, []network.PortRange{
  2065  		network.MustParsePortRange("80/tcp"),
  2066  	})
  2067  	s.assertEnvironPorts(c, nil)
  2068  }
  2069  
  2070  func (s *GlobalModeSuite) TestExposeToIPV6CIDRsOnIPV4OnlyProvider(c *gc.C) {
  2071  	supportsIPV6CIDRs := false
  2072  	fw := s.newFirewallerWithIPV6CIDRSupport(c, supportsIPV6CIDRs)
  2073  	defer statetesting.AssertKillAndWait(c, fw)
  2074  
  2075  	app := s.AddTestingApplication(c, "wordpress", s.charm)
  2076  	u, _ := s.addUnit(c, app)
  2077  
  2078  	mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{
  2079  		network.MustParsePortRange("80/tcp"),
  2080  	})
  2081  
  2082  	err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{
  2083  		allEndpoints: {
  2084  			ExposeToCIDRs: []string{"10.0.0.0/24", "2002::1234:abcd:ffff:c0a8:101/64"},
  2085  		},
  2086  	})
  2087  	c.Assert(err, jc.ErrorIsNil)
  2088  
  2089  	// Since the provider only supports IPV4 CIDRs, the firewall worker
  2090  	// will filter the IPV6 CIDRs when opening ports.
  2091  	s.assertEnvironPorts(c, firewall.IngressRules{
  2092  		firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), "10.0.0.0/24"),
  2093  	})
  2094  }
  2095  
  2096  type NoneModeSuite struct {
  2097  	firewallerBaseSuite
  2098  }
  2099  
  2100  var _ = gc.Suite(&NoneModeSuite{})
  2101  
  2102  func (s *NoneModeSuite) SetUpTest(c *gc.C) {
  2103  	s.firewallerBaseSuite.setUpTest(c, config.FwNone)
  2104  }
  2105  
  2106  func (s *NoneModeSuite) TestStopImmediately(c *gc.C) {
  2107  	fwEnv, ok := s.Environ.(environs.Firewaller)
  2108  	c.Assert(ok, gc.Equals, true)
  2109  
  2110  	cfg := firewaller.Config{
  2111  		ModelUUID:          s.State.ModelUUID(),
  2112  		Mode:               config.FwNone,
  2113  		EnvironFirewaller:  fwEnv,
  2114  		EnvironInstances:   s.Environ,
  2115  		FirewallerAPI:      s.firewaller,
  2116  		RemoteRelationsApi: s.remoteRelations,
  2117  		NewCrossModelFacadeFunc: func(*api.Info) (firewaller.CrossModelFirewallerFacadeCloser, error) {
  2118  			return s.crossmodelFirewaller, nil
  2119  		},
  2120  		Logger:        loggo.GetLogger("test"),
  2121  		CredentialAPI: s.credentialsFacade,
  2122  	}
  2123  	_, err := firewaller.NewFirewaller(cfg)
  2124  	c.Assert(err, gc.ErrorMatches, `invalid firewall-mode "none"`)
  2125  }
  2126  
  2127  func mustOpenPortRanges(c *gc.C, st *state.State, u *state.Unit, endpointName string, portRanges []network.PortRange) {
  2128  	unitPortRanges, err := u.OpenedPortRanges()
  2129  	c.Assert(err, jc.ErrorIsNil)
  2130  
  2131  	for _, pr := range portRanges {
  2132  		unitPortRanges.Open(endpointName, pr)
  2133  	}
  2134  
  2135  	c.Assert(st.ApplyOperation(unitPortRanges.Changes()), jc.ErrorIsNil)
  2136  }
  2137  
  2138  func mustClosePortRanges(c *gc.C, st *state.State, u *state.Unit, endpointName string, portRanges []network.PortRange) {
  2139  	unitPortRanges, err := u.OpenedPortRanges()
  2140  	c.Assert(err, jc.ErrorIsNil)
  2141  
  2142  	for _, pr := range portRanges {
  2143  		unitPortRanges.Close(endpointName, pr)
  2144  	}
  2145  
  2146  	c.Assert(st.ApplyOperation(unitPortRanges.Changes()), jc.ErrorIsNil)
  2147  }