<commit_before>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package state_test

import (
	"fmt"
	"sort"
	"sync"
	"sync/atomic"

	"github.com/juju/errors"
	jc "github.com/juju/testing/checkers"
	gc "gopkg.in/check.v1"

	"github.com/juju/juju/network"
	"github.com/juju/juju/state"
)

type SubnetSuite struct {
	ConnSuite
}

var _ = gc.Suite(&SubnetSuite{})

func (s *SubnetSuite) TestAddSubnetSucceedsWithFullyPopulatedInfo(c *gc.C) {
	subnetInfo := state.SubnetInfo{
		ProviderId:        "foo",
		CIDR:              "192.168.1.0/24",
		VLANTag:           79,
		AllocatableIPLow:  "192.168.1.0",
		AllocatableIPHigh: "192.168.1.1",
		AvailabilityZone:  "Timbuktu",
		SpaceName:         "foo",
	}

	subnet, err := s.State.AddSubnet(subnetInfo)
	c.Assert(err, jc.ErrorIsNil)
	s.assertSubnetMatchesInfo(c, subnet, subnetInfo)

	// check it's been stored in state by fetching it back again
	subnetFromDB, err := s.State.Subnet("192.168.1.0/24")
	c.Assert(err, jc.ErrorIsNil)
	s.assertSubnetMatchesInfo(c, subnetFromDB, subnetInfo)
}

func (s *SubnetSuite) assertSubnetMatchesInfo(c *gc.C, subnet *state.Subnet, info state.SubnetInfo) {
	c.Assert(subnet.ProviderId(), gc.Equals, info.ProviderId)
	c.Assert(subnet.CIDR(), gc.Equals, info.CIDR)
	c.Assert(subnet.VLANTag(), gc.Equals, info.VLANTag)
	c.Assert(subnet.AllocatableIPLow(), gc.Equals, info.AllocatableIPLow)
	c.Assert(subnet.AllocatableIPHigh(), gc.Equals, info.AllocatableIPHigh)
	c.Assert(subnet.AvailabilityZone(), gc.Equals, info.AvailabilityZone)
	c.Assert(subnet.String(), gc.Equals, info.CIDR)
	c.Assert(subnet.GoString(), gc.Equals, info.CIDR)
	c.Assert(subnet.SpaceName(), gc.Equals, info.SpaceName)
}

func (s *SubnetSuite) TestAddSubnetFailsWithEmptyCIDR(c *gc.C) {
	subnetInfo := state.SubnetInfo{}
	s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, "missing CIDR")
}

func (s *SubnetSuite) assertAddSubnetForInfoFailsWithSuffix(c *gc.C, subnetInfo state.SubnetInfo, errorSuffix string) error {
	subnet, err := s.State.AddSubnet(subnetInfo)
	errorMessage := fmt.Sprintf("adding subnet %q: %s", subnetInfo.CIDR, errorSuffix)
	c.Assert(err, gc.ErrorMatches, errorMessage)
	c.Assert(subnet, gc.IsNil)
	return err
}

func (s *SubnetSuite) TestAddSubnetFailsWithInvalidCIDR(c *gc.C) {
	subnetInfo := state.SubnetInfo{CIDR: "foobar"}
	s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, "invalid CIDR address: foobar")
}

func (s *SubnetSuite) TestAddSubnetFailsWithOutOfRangeVLANTag(c *gc.C) {
	subnetInfo := state.SubnetInfo{CIDR: "192.168.0.1/24", VLANTag: 4095}
	s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, "invalid VLAN tag 4095: must be between 0 and 4094")
}

func (s *SubnetSuite) TestAddSubnetFailsWhenAllocatableIPHighSetButAllocatableIPLowNotSet(c *gc.C) {
	subnetInfo := state.SubnetInfo{CIDR: "192.168.0.1/24", AllocatableIPHigh: "192.168.0.1"}
	s.assertAddSubnetForInfoFailsWithSuffix(
		c, subnetInfo,
		"either both AllocatableIPLow and AllocatableIPHigh must be set or neither set",
	)
}

func (s *SubnetSuite) TestAddSubnetFailsWhenAllocatableIPLowSetButAllocatableIPHighNotSet(c *gc.C) {
	subnetInfo := state.SubnetInfo{CIDR: "192.168.0.1/24", AllocatableIPLow: "192.168.0.1"}
	s.assertAddSubnetForInfoFailsWithSuffix(
		c, subnetInfo,
		"either both AllocatableIPLow and AllocatableIPHigh must be set or neither set",
	)
}

func (s *SubnetSuite) TestAddSubnetFailsWithInvalidAllocatableIPHigh(c *gc.C) {
	subnetInfo := state.SubnetInfo{
		CIDR:              "192.168.0.1/24",
		AllocatableIPLow:  "192.168.0.1",
		AllocatableIPHigh: "foobar",
	}
	s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, `invalid AllocatableIPHigh "foobar"`)
}

func (s *SubnetSuite) TestAddSubnetFailsWithInvalidAllocatableIPLow(c *gc.C) {
	subnetInfo := state.SubnetInfo{
		CIDR:              "192.168.0.1/24",
		AllocatableIPLow:  "foobar",
		AllocatableIPHigh: "192.168.0.1",
	}
	s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, `invalid AllocatableIPLow "foobar"`)
}

func (s *SubnetSuite) TestAddSubnetFailsWithOutOfRangeAllocatableIPHigh(c *gc.C) {
	subnetInfo := state.SubnetInfo{
		CIDR:              "192.168.0.1/24",
		AllocatableIPLow:  "192.168.0.1",
		AllocatableIPHigh: "172.16.58.3",
	}
	s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, `invalid AllocatableIPHigh "172.16.58.3"`)
}

func (s *SubnetSuite) TestAddSubnetFailsWithOutOfRangeAllocatableIPLow(c *gc.C) {
	subnetInfo := state.SubnetInfo{
		CIDR:              "192.168.0.1/24",
		AllocatableIPLow:  "172.16.58.3",
		AllocatableIPHigh: "192.168.0.10",
	}
	s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, `invalid AllocatableIPLow "172.16.58.3"`)
}

func (s *SubnetSuite) TestAddSubnetFailsWithAlreadyExistsForDuplicateCIDR(c *gc.C) {
	subnetInfo := state.SubnetInfo{CIDR: "192.168.0.1/24"}
	subnet, err := s.State.AddSubnet(subnetInfo)
	c.Assert(err, jc.ErrorIsNil)
	s.assertSubnetMatchesInfo(c, subnet, subnetInfo)

	err = s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, `subnet "192.168.0.1/24" already exists`)
	c.Assert(err, jc.Satisfies, errors.IsAlreadyExists)
}

func (s *SubnetSuite) TestAddSubnetFailsWhenProviderIdNotUnique(c *gc.C) {
	subnetInfo1 := state.SubnetInfo{CIDR: "192.168.0.1/24", ProviderId: "foo"}
	subnetInfo2 := state.SubnetInfo{CIDR: "10.0.0.0/24", ProviderId: "foo"}

	s.addTwoSubnetsAndAssertSecondFailsWithSuffix(c, subnetInfo1, subnetInfo2, `ProviderId "foo" not unique`)
}

func (s *SubnetSuite) addTwoSubnetsAndAssertSecondFailsWithSuffix(c *gc.C, info1, info2 state.SubnetInfo, errorSuffix string) {
	_, err := s.State.AddSubnet(info1)
	c.Assert(err, jc.ErrorIsNil)

	s.assertAddSubnetForInfoFailsWithSuffix(c, info2, errorSuffix)
}

func (s *SubnetSuite) TestAddSubnetSucceedsForDifferentCIDRsAndEmptyProviderId(c *gc.C) {
	subnetInfo1 := state.SubnetInfo{CIDR: "192.168.0.1/24", ProviderId: ""}
	subnetInfo2 := state.SubnetInfo{CIDR: "10.0.0.0/24", ProviderId: ""}

	subnet1, subnet2 := s.addTwoSubnetsAssertSuccessAndReturnBoth(c, subnetInfo1, subnetInfo2)
	s.assertSubnetMatchesInfo(c, subnet1, subnetInfo1)
	s.assertSubnetMatchesInfo(c, subnet2, subnetInfo2)
}

func (s *SubnetSuite) addTwoSubnetsAssertSuccessAndReturnBoth(c *gc.C, info1, info2 state.SubnetInfo) (*state.Subnet, *state.Subnet) {
	subnet1, err := s.State.AddSubnet(info1)
	c.Assert(err, jc.ErrorIsNil)
	subnet2, err := s.State.AddSubnet(info2)
	c.Assert(err, jc.ErrorIsNil)

	return subnet1, subnet2
}

func (s *SubnetSuite) TestEnsureDeadSetsLifeToDeadWhenAlive(c *gc.C) {
	subnet := s.addAliveSubnet(c, "192.168.0.1/24")

	s.ensureDeadAndAssertLifeIsDead(c, subnet)
	s.refreshAndAssertSubnetLifeIs(c, subnet, state.Dead)
}

func (s *SubnetSuite) addAliveSubnet(c *gc.C, cidr string) *state.Subnet {
	subnetInfo := state.SubnetInfo{CIDR: cidr}
	subnet, err := s.State.AddSubnet(subnetInfo)
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(subnet.Life(), gc.Equals, state.Alive)

	return subnet
}

func (s *SubnetSuite) ensureDeadAndAssertLifeIsDead(c *gc.C, subnet *state.Subnet) {
	err := subnet.EnsureDead()
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(subnet.Life(), gc.Equals, state.Dead)
}
<commit_msg>state: Added cross-model tests for subnets, similar to those for spaces<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package state_test

import (
	"fmt"
	"sort"
	"sync"
	"sync/atomic"

	"github.com/juju/errors"
	jc "github.com/juju/testing/checkers"
	gc "gopkg.in/check.v1"

	"github.com/juju/juju/network"
	"github.com/juju/juju/state"
)

type SubnetSuite struct {
	ConnSuite
}

var _ = gc.Suite(&SubnetSuite{})

func (s *SubnetSuite) TestAddSubnetSucceedsWithFullyPopulatedInfo(c *gc.C) {
	subnetInfo := state.SubnetInfo{
		ProviderId:        "foo",
		CIDR:              "192.168.1.0/24",
		VLANTag:           79,
		AllocatableIPLow:  "192.168.1.0",
		AllocatableIPHigh: "192.168.1.1",
		AvailabilityZone:  "Timbuktu",
		SpaceName:         "foo",
	}

	subnet, err := s.State.AddSubnet(subnetInfo)
	c.Assert(err, jc.ErrorIsNil)
	s.assertSubnetMatchesInfo(c, subnet, subnetInfo)

	// check it's been stored in state by fetching it back again
	subnetFromDB, err := s.State.Subnet("192.168.1.0/24")
	c.Assert(err, jc.ErrorIsNil)
	s.assertSubnetMatchesInfo(c, subnetFromDB, subnetInfo)
}

func (s *SubnetSuite) assertSubnetMatchesInfo(c *gc.C, subnet *state.Subnet, info state.SubnetInfo) {
	c.Assert(subnet.ProviderId(), gc.Equals, info.ProviderId)
	c.Assert(subnet.CIDR(), gc.Equals, info.CIDR)
	c.Assert(subnet.VLANTag(), gc.Equals, info.VLANTag)
	c.Assert(subnet.AllocatableIPLow(), gc.Equals, info.AllocatableIPLow)
	c.Assert(subnet.AllocatableIPHigh(), gc.Equals, info.AllocatableIPHigh)
	c.Assert(subnet.AvailabilityZone(), gc.Equals, info.AvailabilityZone)
	c.Assert(subnet.String(), gc.Equals, info.CIDR)
	c.Assert(subnet.GoString(), gc.Equals, info.CIDR)
	c.Assert(subnet.SpaceName(), gc.Equals, info.SpaceName)
}

func (s *SubnetSuite) TestAddSubnetFailsWithEmptyCIDR(c *gc.C) {
	subnetInfo := state.SubnetInfo{}
	s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, "missing CIDR")
}

func (s *SubnetSuite) assertAddSubnetForInfoFailsWithSuffix(c *gc.C, subnetInfo state.SubnetInfo, errorSuffix string) error {
	subnet, err := s.State.AddSubnet(subnetInfo)
	errorMessage := fmt.Sprintf("adding subnet %q: %s", subnetInfo.CIDR, errorSuffix)
	c.Assert(err, gc.ErrorMatches, errorMessage)
	c.Assert(subnet, gc.IsNil)
	return err
}

func (s *SubnetSuite) TestAddSubnetFailsWithInvalidCIDR(c *gc.C) {
	subnetInfo := state.SubnetInfo{CIDR: "foobar"}
	s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, "invalid CIDR address: foobar")
}

func (s *SubnetSuite) TestAddSubnetFailsWithOutOfRangeVLANTag(c *gc.C) {
	subnetInfo := state.SubnetInfo{CIDR: "192.168.0.1/24", VLANTag: 4095}
	s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, "invalid VLAN tag 4095: must be between 0 and 4094")
}

func (s *SubnetSuite) TestAddSubnetFailsWhenAllocatableIPHighSetButAllocatableIPLowNotSet(c *gc.C) {
	subnetInfo := state.SubnetInfo{CIDR: "192.168.0.1/24", AllocatableIPHigh: "192.168.0.1"}
	s.assertAddSubnetForInfoFailsWithSuffix(
		c, subnetInfo,
		"either both AllocatableIPLow and AllocatableIPHigh must be set or neither set",
	)
}

func (s *SubnetSuite) TestAddSubnetFailsWhenAllocatableIPLowSetButAllocatableIPHighNotSet(c *gc.C) {
	subnetInfo := state.SubnetInfo{CIDR: "192.168.0.1/24", AllocatableIPLow: "192.168.0.1"}
	s.assertAddSubnetForInfoFailsWithSuffix(
		c, subnetInfo,
		"either both AllocatableIPLow and AllocatableIPHigh must be set or neither set",
	)
}

func (s *SubnetSuite) TestAddSubnetFailsWithInvalidAllocatableIPHigh(c *gc.C) {
	subnetInfo := state.SubnetInfo{
		CIDR:              "192.168.0.1/24",
		AllocatableIPLow:  "192.168.0.1",
		AllocatableIPHigh: "foobar",
	}
	s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, `invalid AllocatableIPHigh "foobar"`)
}

func (s *SubnetSuite) TestAddSubnetFailsWithInvalidAllocatableIPLow(c *gc.C) {
	subnetInfo := state.SubnetInfo{
		CIDR:              "192.168.0.1/24",
		AllocatableIPLow:  "foobar",
		AllocatableIPHigh: "192.168.0.1",
	}
	s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, `invalid AllocatableIPLow "foobar"`)
}

func (s *SubnetSuite) TestAddSubnetFailsWithOutOfRangeAllocatableIPHigh(c *gc.C) {
	subnetInfo := state.SubnetInfo{
		CIDR:              "192.168.0.1/24",
		AllocatableIPLow:  "192.168.0.1",
		AllocatableIPHigh: "172.16.58.3",
	}
	s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, `invalid AllocatableIPHigh "172.16.58.3"`)
}

func (s *SubnetSuite) TestAddSubnetFailsWithOutOfRangeAllocatableIPLow(c *gc.C) {
	subnetInfo := state.SubnetInfo{
		CIDR:              "192.168.0.1/24",
		AllocatableIPLow:  "172.16.58.3",
		AllocatableIPHigh: "192.168.0.10",
	}
	s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, `invalid AllocatableIPLow "172.16.58.3"`)
}

func (s *SubnetSuite) TestAddSubnetFailsWithAlreadyExistsForDuplicateCIDRInSameModel(c *gc.C) {
	subnetInfo := state.SubnetInfo{CIDR: "192.168.0.1/24"}
	subnet, err := s.State.AddSubnet(subnetInfo)
	c.Assert(err, jc.ErrorIsNil)
	s.assertSubnetMatchesInfo(c, subnet, subnetInfo)

	err = s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, `subnet "192.168.0.1/24" already exists`)
	c.Assert(err, jc.Satisfies, errors.IsAlreadyExists)
}

func (s *SubnetSuite) TestAddSubnetSucceedsForDuplicateCIDRInDifferentModels(c *gc.C) {
	subnetInfo1 := state.SubnetInfo{CIDR: "192.168.0.1/24"}
	subnetInfo2 := state.SubnetInfo{CIDR: "10.0.0.0/24"}
	subnet1State := s.NewStateForModelNamed(c, "other-model")

	subnet1, subnet2 := s.addTwoSubnetsInDifferentModelsAssertSuccessAndReturnBoth(c, subnetInfo1, subnetInfo2, subnet1State)
	s.assertSubnetMatchesInfo(c, subnet1, subnetInfo1)
	s.assertSubnetMatchesInfo(c, subnet2, subnetInfo2)
}

func (s *SubnetSuite) addTwoSubnetsInDifferentModelsAssertSuccessAndReturnBoth(c *gc.C, info1, info2 state.SubnetInfo, otherState *state.State) (*state.Subnet, *state.Subnet) {
	subnet1, err := otherState.AddSubnet(info1)
	c.Assert(err, jc.ErrorIsNil)
	subnet2, err := s.State.AddSubnet(info2)
	c.Assert(err, jc.ErrorIsNil)

	return subnet1, subnet2
}

func (s *SubnetSuite) TestAddSubnetFailsWhenProviderIdNotUniqueInSameModel(c *gc.C) {
	subnetInfo1 := state.SubnetInfo{CIDR: "192.168.0.1/24", ProviderId: "foo"}
	subnetInfo2 := state.SubnetInfo{CIDR: "10.0.0.0/24", ProviderId: "foo"}

	s.addTwoSubnetsAndAssertSecondFailsWithSuffix(c, subnetInfo1, subnetInfo2, `ProviderId "foo" not unique`)
}

func (s *SubnetSuite) addTwoSubnetsAndAssertSecondFailsWithSuffix(c *gc.C, info1, info2 state.SubnetInfo, errorSuffix string) {
	s.addTwoSubnetsInDifferentModelsAndAssertSecondFailsWithSuffix(c, info1, info2, s.State, errorSuffix)
}

func (s *SubnetSuite) addTwoSubnetsInDifferentModelsAndAssertSecondFailsWithSuffix(c *gc.C, info1, info2 state.SubnetInfo, otherState *state.State, errorSuffix string) {
	_, err := otherState.AddSubnet(info1)
	c.Assert(err, jc.ErrorIsNil)

	s.assertAddSubnetForInfoFailsWithSuffix(c, info2, errorSuffix)
}

func (s *SubnetSuite) TestAddSubnetSucceedsWhenProviderIdNotUniqueInDifferentModels(c *gc.C) {
	subnetInfo1 := state.SubnetInfo{CIDR: "192.168.0.1/24", ProviderId: "foo"}
	subnetInfo2 := state.SubnetInfo{CIDR: "10.0.0.0/24", ProviderId: "foo"}
	subnet1State := s.NewStateForModelNamed(c, "other-model")

	subnet1, subnet2 := s.addTwoSubnetsInDifferentModelsAssertSuccessAndReturnBoth(c, subnetInfo1, subnetInfo2, subnet1State)
	s.assertSubnetMatchesInfo(c, subnet1, subnetInfo1)
	s.assertSubnetMatchesInfo(c, subnet2, subnetInfo2)
}

func (s *SubnetSuite) TestAddSubnetSucceedsForDifferentCIDRsAndEmptyProviderIdInSameModel(c *gc.C) {
	subnetInfo1 := state.SubnetInfo{CIDR: "192.168.0.1/24", ProviderId: ""}
	subnetInfo2 := state.SubnetInfo{CIDR: "10.0.0.0/24", ProviderId: ""}

	subnet1, subnet2 := s.addTwoSubnetsAssertSuccessAndReturnBoth(c, subnetInfo1, subnetInfo2)
	s.assertSubnetMatchesInfo(c, subnet1, subnetInfo1)
	s.assertSubnetMatchesInfo(c, subnet2, subnetInfo2)
}

func (s *SubnetSuite) addTwoSubnetsAssertSuccessAndReturnBoth(c *gc.C, info1, info2 state.SubnetInfo) (*state.Subnet, *state.Subnet) {
	return s.addTwoSubnetsInDifferentModelsAssertSuccessAndReturnBoth(c, info1, info2, s.State)
}

func (s *SubnetSuite) TestAddSubnetSucceedsForDifferentCIDRsAndEmptyProviderIdInDifferentModels(c *gc.C) {
	subnetInfo1 := state.SubnetInfo{CIDR: "192.168.0.1/24", ProviderId: ""}
	subnetInfo2 := state.SubnetInfo{CIDR: "10.0.0.0/24", ProviderId: ""}
	subnet1State := s.NewStateForModelNamed(c, "other-model")

	subnet1, subnet2 := s.addTwoSubnetsInDifferentModelsAssertSuccessAndReturnBoth(c, subnetInfo1, subnetInfo2, subnet1State)
	s.assertSubnetMatchesInfo(c, subnet1, subnetInfo1)
	s.assertSubnetMatchesInfo(c, subnet2, subnetInfo2)
}

func (s *SubnetSuite) TestEnsureDeadSetsLifeToDeadWhenAlive(c *gc.C) {
	subnet := s.addAliveSubnet(c, "192.168.0.1/24")

	s.ensureDeadAndAssertLifeIsDead(c, subnet)
	s.refreshAndAssertSubnetLifeIs(c, subnet, state.Dead)
}

func (s *SubnetSuite) addAliveSubnet(c *gc.C, cidr string) *state.Subnet {
	subnetInfo := state.SubnetInfo{CIDR: cidr}
	subnet, err := s.State.AddSubnet(subnetInfo)
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(subnet.Life(), gc.Equals, state.Alive)

	return subnet
}

func (s *SubnetSuite) ensureDeadAndAssertLifeIsDead(c *gc.C, subnet *state.Subnet) {
	err := subnet.EnsureDead()
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(subnet.Life(), gc.Equals, state.Dead)
}

{'fraction_non_alphanumeric': 0.13357225293081457, 'fraction_numerical': 0.04197290894733645, 'mean_word_length': 5.709046454767726, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 62, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5793196', 'n_tokens_mistral': 7096, 'n_tokens_neox': 6635, 'n_words': 1072}