github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/worker/discoverspaces/worker_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package discoverspaces_test
     5  
     6  import (
     7  	"sync/atomic"
     8  	"time"
     9  
    10  	"github.com/juju/names"
    11  	jc "github.com/juju/testing/checkers"
    12  	gc "gopkg.in/check.v1"
    13  
    14  	"github.com/juju/juju/api"
    15  	apidiscoverspaces "github.com/juju/juju/api/discoverspaces"
    16  	"github.com/juju/juju/apiserver/params"
    17  	"github.com/juju/juju/environs"
    18  	"github.com/juju/juju/juju/testing"
    19  	"github.com/juju/juju/network"
    20  	"github.com/juju/juju/provider/dummy"
    21  	"github.com/juju/juju/state"
    22  	coretesting "github.com/juju/juju/testing"
    23  	"github.com/juju/juju/worker"
    24  	"github.com/juju/juju/worker/discoverspaces"
    25  	"github.com/juju/juju/worker/gate"
    26  	"github.com/juju/juju/worker/workertest"
    27  )
    28  
    29  type WorkerSuite struct {
    30  	// TODO(fwereade): we *really* should not be using
    31  	// JujuConnSuite in new code.
    32  	testing.JujuConnSuite
    33  
    34  	APIConnection api.Connection
    35  	API           *checkingFacade
    36  
    37  	numCreateSpaceCalls uint32
    38  	numAddSubnetsCalls  uint32
    39  }
    40  
    41  type checkingFacade struct {
    42  	apidiscoverspaces.API
    43  
    44  	createSpacesCallback func()
    45  	addSubnetsCallback   func()
    46  }
    47  
    48  func (cf *checkingFacade) CreateSpaces(args params.CreateSpacesParams) (results params.ErrorResults, err error) {
    49  	if cf.createSpacesCallback != nil {
    50  		cf.createSpacesCallback()
    51  	}
    52  	return cf.API.CreateSpaces(args)
    53  }
    54  
    55  func (cf *checkingFacade) AddSubnets(args params.AddSubnetsParams) (params.ErrorResults, error) {
    56  	if cf.addSubnetsCallback != nil {
    57  		cf.addSubnetsCallback()
    58  	}
    59  	return cf.API.AddSubnets(args)
    60  }
    61  
    62  var _ = gc.Suite(&WorkerSuite{})
    63  
    64  func (s *WorkerSuite) SetUpTest(c *gc.C) {
    65  	s.JujuConnSuite.SetUpTest(c)
    66  
    67  	// Unbreak dummy provider methods.
    68  	s.AssertConfigParameterUpdated(c, "broken", "")
    69  
    70  	s.APIConnection, _ = s.OpenAPIAsNewMachine(c, state.JobManageModel)
    71  
    72  	realAPI := s.APIConnection.DiscoverSpaces()
    73  	s.API = &checkingFacade{
    74  		API: *realAPI,
    75  		createSpacesCallback: func() {
    76  			atomic.AddUint32(&s.numCreateSpaceCalls, 1)
    77  		},
    78  		addSubnetsCallback: func() {
    79  			atomic.AddUint32(&s.numAddSubnetsCalls, 1)
    80  		},
    81  	}
    82  }
    83  
    84  func (s *WorkerSuite) TearDownTest(c *gc.C) {
    85  	if s.APIConnection != nil {
    86  		c.Check(s.APIConnection.Close(), jc.ErrorIsNil)
    87  	}
    88  	s.JujuConnSuite.TearDownTest(c)
    89  }
    90  
    91  func (s *WorkerSuite) TestSupportsSpaceDiscoveryBroken(c *gc.C) {
    92  	s.AssertConfigParameterUpdated(c, "broken", "SupportsSpaceDiscovery")
    93  
    94  	worker, lock := s.startWorker(c)
    95  	err := workertest.CheckKilled(c, worker)
    96  	c.Assert(err, gc.ErrorMatches, "dummy.SupportsSpaceDiscovery is broken")
    97  
    98  	select {
    99  	case <-time.After(coretesting.ShortWait):
   100  	case <-lock.Unlocked():
   101  		c.Fatalf("gate unlocked despite worker failure")
   102  	}
   103  }
   104  
   105  func (s *WorkerSuite) TestSpacesBroken(c *gc.C) {
   106  	dummy.SetSupportsSpaceDiscovery(true)
   107  	s.AssertConfigParameterUpdated(c, "broken", "Spaces")
   108  
   109  	worker, lock := s.startWorker(c)
   110  	err := workertest.CheckKilled(c, worker)
   111  	c.Assert(err, gc.ErrorMatches, "dummy.Spaces is broken")
   112  
   113  	select {
   114  	case <-time.After(coretesting.ShortWait):
   115  	case <-lock.Unlocked():
   116  		c.Fatalf("gate unlocked despite worker failure")
   117  	}
   118  }
   119  
   120  func (s *WorkerSuite) TestWorkerSupportsNetworkingFalse(c *gc.C) {
   121  	// We set SupportsSpaceDiscovery to true so that spaces *would* be
   122  	// discovered if networking was supported. So we know that if they're
   123  	// discovered it must be because networking is not supported.
   124  	dummy.SetSupportsSpaceDiscovery(true)
   125  
   126  	// TODO(fwereade): monkey-patching remote packages is even worse
   127  	// than monkey-patching local packages, please don't do it.
   128  	noNetworking := func(environs.Environ) (environs.NetworkingEnviron, bool) {
   129  		return nil, false
   130  	}
   131  	s.PatchValue(&environs.SupportsNetworking, noNetworking)
   132  
   133  	s.unlockCheck(c, s.assertDiscoveredNoSpaces)
   134  }
   135  
   136  func (s *WorkerSuite) TestWorkerSupportsSpaceDiscoveryFalse(c *gc.C) {
   137  	s.unlockCheck(c, s.assertDiscoveredNoSpaces)
   138  }
   139  
   140  func (s *WorkerSuite) TestWorkerDiscoversSpaces(c *gc.C) {
   141  	dummy.SetSupportsSpaceDiscovery(true)
   142  	s.unlockCheck(c, func(*gc.C) {
   143  		s.assertDiscoveredSpaces(c)
   144  		s.assertNumCalls(c, 1, 1)
   145  	})
   146  }
   147  
   148  func (s *WorkerSuite) TestWorkerIdempotent(c *gc.C) {
   149  	dummy.SetSupportsSpaceDiscovery(true)
   150  	s.unlockCheck(c, s.assertDiscoveredSpaces)
   151  	s.unlockCheck(c, func(*gc.C) {
   152  		s.assertDiscoveredSpaces(c)
   153  		s.assertNumCalls(c, 2, 2)
   154  	})
   155  }
   156  
   157  func (s *WorkerSuite) TestWorkerIgnoresExistingSpacesAndSubnets(c *gc.C) {
   158  	dummy.SetSupportsSpaceDiscovery(true)
   159  	spaceTag := names.NewSpaceTag("foo")
   160  	args := params.CreateSpacesParams{
   161  		Spaces: []params.CreateSpaceParams{{
   162  			Public:     false,
   163  			SpaceTag:   spaceTag.String(),
   164  			ProviderId: "foo",
   165  		}}}
   166  	result, err := s.API.CreateSpaces(args)
   167  	c.Assert(err, jc.ErrorIsNil)
   168  	c.Assert(result.Results, gc.HasLen, 1)
   169  	c.Assert(result.Results[0].Error, gc.IsNil)
   170  
   171  	subnetArgs := params.AddSubnetsParams{
   172  		Subnets: []params.AddSubnetParams{{
   173  			SubnetProviderId: "1",
   174  			SpaceTag:         spaceTag.String(),
   175  			Zones:            []string{"zone1"},
   176  		}}}
   177  	subnetResult, err := s.API.AddSubnets(subnetArgs)
   178  	c.Assert(err, jc.ErrorIsNil)
   179  	c.Assert(subnetResult.Results, gc.HasLen, 1)
   180  	c.Assert(subnetResult.Results[0].Error, gc.IsNil)
   181  
   182  	s.unlockCheck(c, func(c *gc.C) {
   183  		spaces, err := s.State.AllSpaces()
   184  		c.Assert(err, jc.ErrorIsNil)
   185  		c.Assert(spaces, gc.HasLen, 5)
   186  	})
   187  }
   188  
   189  func (s *WorkerSuite) startWorker(c *gc.C) (worker.Worker, gate.Lock) {
   190  	// create fresh environ to see any injected broken-ness
   191  	config, err := s.State.ModelConfig()
   192  	c.Assert(err, jc.ErrorIsNil)
   193  	environ, err := environs.New(config)
   194  	c.Assert(err, jc.ErrorIsNil)
   195  
   196  	lock := gate.NewLock()
   197  	worker, err := discoverspaces.NewWorker(discoverspaces.Config{
   198  		Facade:   s.API,
   199  		Environ:  environ,
   200  		NewName:  network.ConvertSpaceName,
   201  		Unlocker: lock,
   202  	})
   203  	c.Assert(err, jc.ErrorIsNil)
   204  	return worker, lock
   205  }
   206  
   207  func (s *WorkerSuite) unlockCheck(c *gc.C, check func(c *gc.C)) {
   208  	worker, lock := s.startWorker(c)
   209  	defer workertest.CleanKill(c, worker)
   210  	select {
   211  	case <-time.After(coretesting.LongWait):
   212  		c.Fatalf("discovery never completed")
   213  	case <-lock.Unlocked():
   214  		check(c)
   215  	}
   216  	workertest.CheckAlive(c, worker)
   217  }
   218  
   219  func (s *WorkerSuite) assertDiscoveredNoSpaces(c *gc.C) {
   220  	spaces, err := s.State.AllSpaces()
   221  	c.Assert(err, jc.ErrorIsNil)
   222  	c.Check(spaces, gc.HasLen, 0)
   223  }
   224  
   225  func (s *WorkerSuite) assertDiscoveredSpaces(c *gc.C) {
   226  	spaces, err := s.State.AllSpaces()
   227  	c.Assert(err, jc.ErrorIsNil)
   228  	c.Assert(spaces, gc.HasLen, 4)
   229  	expectedSpaces := []network.SpaceInfo{{
   230  		Name:       "foo",
   231  		ProviderId: network.Id("0"),
   232  		Subnets: []network.SubnetInfo{{
   233  			ProviderId:        network.Id("1"),
   234  			CIDR:              "192.168.1.0/24",
   235  			AvailabilityZones: []string{"zone1"},
   236  		}, {
   237  			ProviderId:        network.Id("2"),
   238  			CIDR:              "192.168.2.0/24",
   239  			AvailabilityZones: []string{"zone1"},
   240  		}}}, {
   241  		Name:       "another-foo-99",
   242  		ProviderId: network.Id("1"),
   243  		Subnets: []network.SubnetInfo{{
   244  			ProviderId:        network.Id("3"),
   245  			CIDR:              "192.168.3.0/24",
   246  			AvailabilityZones: []string{"zone1"},
   247  		}}}, {
   248  		Name:       "foo-2",
   249  		ProviderId: network.Id("2"),
   250  		Subnets: []network.SubnetInfo{{
   251  			ProviderId:        network.Id("4"),
   252  			CIDR:              "192.168.4.0/24",
   253  			AvailabilityZones: []string{"zone1"},
   254  		}}}, {
   255  		Name:       "empty",
   256  		ProviderId: network.Id("3"),
   257  		Subnets: []network.SubnetInfo{{
   258  			ProviderId:        network.Id("5"),
   259  			CIDR:              "192.168.5.0/24",
   260  			AvailabilityZones: []string{"zone1"},
   261  		}}}}
   262  	expectedSpaceMap := make(map[string]network.SpaceInfo)
   263  	for _, space := range expectedSpaces {
   264  		expectedSpaceMap[space.Name] = space
   265  	}
   266  	for _, space := range spaces {
   267  		expected, ok := expectedSpaceMap[space.Name()]
   268  		if !c.Check(ok, jc.IsTrue) {
   269  			continue
   270  		}
   271  		c.Check(space.ProviderId(), gc.Equals, expected.ProviderId)
   272  		subnets, err := space.Subnets()
   273  		if !c.Check(err, jc.ErrorIsNil) {
   274  			continue
   275  		}
   276  		if !c.Check(len(subnets), gc.Equals, len(expected.Subnets)) {
   277  			continue
   278  		}
   279  		for i, subnet := range subnets {
   280  			expectedSubnet := expected.Subnets[i]
   281  			c.Check(subnet.ProviderId(), gc.Equals, expectedSubnet.ProviderId)
   282  			c.Check([]string{subnet.AvailabilityZone()}, jc.DeepEquals, expectedSubnet.AvailabilityZones)
   283  			c.Check(subnet.CIDR(), gc.Equals, expectedSubnet.CIDR)
   284  		}
   285  	}
   286  }
   287  
   288  func (s *WorkerSuite) assertNumCalls(c *gc.C, expectedNumCreateSpaceCalls, expectedNumAddSubnetsCalls int) {
   289  	c.Check(atomic.LoadUint32(&s.numCreateSpaceCalls), gc.Equals, uint32(expectedNumCreateSpaceCalls))
   290  	c.Check(atomic.LoadUint32(&s.numAddSubnetsCalls), gc.Equals, uint32(expectedNumAddSubnetsCalls))
   291  }