Subnets api: add, create, list #2980

Merged
merged 16 commits into from Aug 17, 2015
View
@@ -58,8 +58,8 @@ func (api *API) CreateSpace(name string, subnetIds []string, public bool) (param
}
// ListSpaces lists all available spaces and their associated subnets.
-func (api *API) ListSpaces() (params.ListSpacesResults, error) {
+func (api *API) ListSpaces() ([]params.Space, error) {
var response params.ListSpacesResults
err := api.facade.FacadeCall("ListSpaces", nil, &response)
- return response, err
+ return response.Results, err
}
@@ -135,7 +135,9 @@ func (s *SpacesSuite) TestListSpaces(c *gc.C) {
results, err := s.api.ListSpaces()
c.Assert(s.called, gc.Equals, 1)
c.Assert(err, gc.IsNil)
- c.Assert(results, gc.DeepEquals, expectResults)
+
+ var expectedResults []params.Space
+ c.Assert(results, gc.DeepEquals, expectedResults)
}
func (s *SpacesSuite) TestListSpacesFails(c *gc.C) {
@@ -149,5 +151,7 @@ func (s *SpacesSuite) TestListSpacesFails(c *gc.C) {
results, err := s.api.ListSpaces()
c.Assert(s.called, gc.Equals, 1)
c.Assert(err, gc.ErrorMatches, "bang")
- c.Assert(results, gc.DeepEquals, args.Results)
+
+ var expectedResults []params.Space
+ c.Assert(results, gc.DeepEquals, expectedResults)
}
View
@@ -4,16 +4,20 @@
package subnets
import (
+ "github.com/juju/errors"
"github.com/juju/loggo"
+ "github.com/juju/names"
"github.com/juju/juju/api/base"
+ "github.com/juju/juju/apiserver/params"
+ "github.com/juju/juju/network"
)
var logger = loggo.GetLogger("juju.api.subnets")
const subnetsFacade = "Subnets"
-// API provides access to the InstancePoller API facade.
+// API provides access to the Subnets API facade.
type API struct {
facade base.FacadeCaller
}
@@ -28,3 +32,51 @@ func NewAPI(caller base.APICaller) *API {
facade: facadeCaller,
}
}
+
+// AddSubnet adds an existing subnet to the environment.
+func (api *API) AddSubnet(subnet names.SubnetTag, providerId network.Id, space names.SpaceTag, zones []string) error {
+ var response params.ErrorResults
+ params := params.AddSubnetsParams{
+ Subnets: []params.AddSubnetParams{{
+ SubnetTag: subnet.String(),
+ SubnetProviderId: string(providerId),
+ SpaceTag: space.String(),
+ Zones: zones,
+ }},
+ }
+ err := api.facade.FacadeCall("AddSubnets", params, &response)
+ if err != nil {
+ return errors.Trace(err)
+ }
+ return response.OneError()
+}
+
+// CreateSubnet creates a new subnet with the provider.
+func (api *API) CreateSubnet(subnet names.SubnetTag, space names.SpaceTag, zones []string, isPublic bool) error {
+ var response params.ErrorResults
+ params := params.CreateSubnetsParams{
+ Subnets: []params.CreateSubnetParams{{
+ SubnetTag: subnet.String(),
+ SpaceTag: space.String(),
+ Zones: zones,
+ IsPublic: isPublic,
+ }},
+ }
+ err := api.facade.FacadeCall("CreateSubnets", params, &response)
+ if err != nil {
+ return errors.Trace(err)
+ }
+ return response.OneError()
+}
+
+// ListSubnets fetches all the subnets known by the environment.
+func (api *API) ListSubnets(spaceTag *names.SpaceTag, zone string) ([]params.Subnet, error) {
+ var response params.ListSubnetsResults
+ params := params.ListSubnetsParams{
+ Filters: []params.ListSubnetsFilterParams{
+ {SpaceTag: spaceTag.String(), Zone: zone},
+ },
+ }
+ err := api.facade.FacadeCall("ListSubnets", params, &response)
+ return response.Results, err
+}
@@ -4,12 +4,18 @@
package subnets_test
import (
+ "errors"
+
+ jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
"github.com/juju/juju/api/base"
apitesting "github.com/juju/juju/api/base/testing"
"github.com/juju/juju/api/subnets"
+ "github.com/juju/juju/apiserver/params"
+ "github.com/juju/juju/network"
coretesting "github.com/juju/juju/testing"
+ "github.com/juju/names"
)
// SubnetsSuite tests the client side subnets API
@@ -23,7 +29,7 @@ type SubnetsSuite struct {
var _ = gc.Suite(&SubnetsSuite{})
-func (s *SubnetsSuite) init(c *gc.C, args *apitesting.CheckArgs, err error) {
+func (s *SubnetsSuite) prepareAPICall(c *gc.C, args *apitesting.CheckArgs, err error) {
s.called = 0
s.apiCaller = apitesting.CheckingAPICaller(c, args, &s.called, err)
s.api = subnets.NewAPI(s.apiCaller)
@@ -45,3 +51,145 @@ func (s *SubnetsSuite) TestNewAPIWithNilCaller(c *gc.C) {
panicFunc := func() { subnets.NewAPI(nil) }
c.Assert(panicFunc, gc.PanicMatches, "caller is nil")
}
+
+func makeAddSubnetsArgs(cidr, providerId, space string, zones []string) apitesting.CheckArgs {
+ spaceTag := names.NewSpaceTag(space).String()
+ subnetTag := names.NewSubnetTag(cidr).String()
+
+ expectArgs := params.AddSubnetsParams{
+ Subnets: []params.AddSubnetParams{{
+ SpaceTag: spaceTag,
+ SubnetTag: subnetTag,
+ SubnetProviderId: providerId,
+ Zones: zones,
+ }}}
+
+ expectResults := params.ErrorResults{
+ Results: []params.ErrorResult{{}},
+ }
+
+ args := apitesting.CheckArgs{
+ Facade: "Subnets",
+ Method: "AddSubnets",
+ Args: expectArgs,
+ Results: expectResults,
+ }
+
+ return args
+}
+
+func makeCreateSubnetsArgs(cidr, space string, zones []string, isPublic bool) apitesting.CheckArgs {
+ spaceTag := names.NewSpaceTag(space).String()
+ subnetTag := names.NewSubnetTag(cidr).String()
+
+ expectArgs := params.CreateSubnetsParams{
+ Subnets: []params.CreateSubnetParams{{
+ SpaceTag: spaceTag,
+ SubnetTag: subnetTag,
+ Zones: zones,
+ IsPublic: isPublic,
+ }}}
+
+ expectResults := params.ErrorResults{
+ Results: []params.ErrorResult{{}},
+ }
+
+ args := apitesting.CheckArgs{
+ Facade: "Subnets",
+ Method: "CreateSubnets",
+ Args: expectArgs,
+ Results: expectResults,
+ }
+
+ return args
+}
+
+func makeListSubnetsArgs(space names.SpaceTag, zone string) apitesting.CheckArgs {
+ expectResults := params.ListSubnetsResults{}
+ expectArgs := params.ListSubnetsParams{
+ Filters: []params.ListSubnetsFilterParams{{
+ SpaceTag: space.String(),
+ Zone: zone,
+ }}}
+ args := apitesting.CheckArgs{
+ Facade: "Subnets",
+ Method: "ListSubnets",
+ Results: expectResults,
+ Args: expectArgs,
+ }
+ return args
+}
+
+func (s *SubnetsSuite) TestAddSubnet(c *gc.C) {
+ cidr := "1.1.1.0/24"
+ providerId := "foo"
+ space := "bar"
+ zones := []string{"foo", "bar"}
+ args := makeAddSubnetsArgs(cidr, providerId, space, zones)
+ s.prepareAPICall(c, &args, nil)
+ err := s.api.AddSubnet(names.NewSubnetTag(cidr), network.Id(providerId), names.NewSpaceTag(space), zones)
+ c.Assert(s.called, gc.Equals, 1)
+ c.Assert(err, jc.ErrorIsNil)
+}
+
+func (s *SubnetsSuite) TestAddSubnetFails(c *gc.C) {
+ cidr := "1.1.1.0/24"
+ providerId := "foo"
+ space := "bar"
+ zones := []string{"foo", "bar"}
+ args := makeAddSubnetsArgs(cidr, providerId, space, zones)
+ s.prepareAPICall(c, &args, errors.New("bang"))
+ err := s.api.AddSubnet(names.NewSubnetTag(cidr), network.Id(providerId), names.NewSpaceTag(space), zones)
+ c.Check(s.called, gc.Equals, 1)
+ c.Assert(err, gc.ErrorMatches, "bang")
+}
+
+func (s *SubnetsSuite) TestCreateSubnet(c *gc.C) {
+ cidr := "1.1.1.0/24"
+ space := "bar"
+ zones := []string{"foo", "bar"}
+ isPublic := true
+ args := makeCreateSubnetsArgs(cidr, space, zones, isPublic)
+ s.prepareAPICall(c, &args, nil)
+ err := s.api.CreateSubnet(names.NewSubnetTag(cidr), names.NewSpaceTag(space), zones, isPublic)
+ c.Assert(s.called, gc.Equals, 1)
+ c.Assert(err, jc.ErrorIsNil)
+}
+
+func (s *SubnetsSuite) TestCreateSubnetFails(c *gc.C) {
+ cidr := "1.1.1.0/24"
+ isPublic := true
+ space := "bar"
+ zones := []string{"foo", "bar"}
+ args := makeCreateSubnetsArgs(cidr, space, zones, isPublic)
+ s.prepareAPICall(c, &args, errors.New("bang"))
+ err := s.api.CreateSubnet(names.NewSubnetTag(cidr), names.NewSpaceTag(space), zones, isPublic)
+ c.Check(s.called, gc.Equals, 1)
+ c.Assert(err, gc.ErrorMatches, "bang")
+}
+
+func (s *SubnetsSuite) TestListSubnets(c *gc.C) {
+ space := names.NewSpaceTag("foo")
+ zone := "bar"
+ args := makeListSubnetsArgs(space, zone)
+ s.prepareAPICall(c, &args, nil)
+ results, err := s.api.ListSubnets(&space, zone)
+ c.Assert(s.called, gc.Equals, 1)
+ c.Assert(err, jc.ErrorIsNil)
+
+ var expectedResults []params.Subnet
+ c.Assert(results, jc.DeepEquals, expectedResults)
+}
+
+func (s *SubnetsSuite) TestListSubnetsFails(c *gc.C) {
+ space := names.NewSpaceTag("foo")
+ zone := "bar"
+ args := makeListSubnetsArgs(space, zone)
+ s.prepareAPICall(c, &args, errors.New("bang"))
+ results, err := s.api.ListSubnets(&space, zone)
+ c.Assert(s.called, gc.Equals, 1)
+ c.Assert(err, gc.ErrorMatches, "bang")
+
+ var expectedResults []params.Subnet
+ c.Assert(results, jc.DeepEquals, expectedResults)
+}
@@ -497,6 +497,23 @@ type SpaceResults struct {
Results []SpaceResult `json:"Results"`
}
+// ListSubnetsResults holds the result of a ListSubnets API call.
+type ListSubnetsResults struct {
+ Results []Subnet `json:"Results"`
+}
+
+// ListSubnetsParams holds the arguments of a ListSubnets API call.
+type ListSubnetsParams struct {
+ Filters []ListSubnetsFilterParams `json:"Filters"`
+}
+
+// ListSubnetsFilterParams holds an optional Space Tag and Availability Zone
+// for filtering the subnets returned by a ListSubnets call.
+type ListSubnetsFilterParams struct {
+ SpaceTag string `json:"SpaceTag,omitempty"`
+ Zone string `json:"Zone,omitempty"`
+}
+
// AddSubnetsParams holds the arguments of AddSubnets API call.
type AddSubnetsParams struct {
Subnets []AddSubnetParams `json:"Subnets"`
@@ -513,6 +530,21 @@ type AddSubnetParams struct {
Zones []string `json:"Zones,omitempty"`
}
+// CreateSubnetsParams holds the arguments of CreateSubnets API call.
+type CreateSubnetsParams struct {
+ Subnets []CreateSubnetParams `json:"Subnets"`
+}
+
+// CreateSubnetParams holds a subnet and space tags, vlan tag,
+// and a list of zones to associate the subnet to.
+type CreateSubnetParams struct {
+ SubnetTag string `json:"SubnetTag,omitempty"`
+ SpaceTag string `json:"SpaceTag"`
+ Zones []string `json:"Zones,omitempty"`
+ VLANTag int `json:"VLANTag,omitempty"`
+ IsPublic bool `json:"IsPublic"`
+}
+
// CreateSpacesParams olds the arguments of the AddSpaces API call.
type CreateSpacesParams struct {
Spaces []CreateSpaceParams `json:"Spaces"`
@@ -106,6 +106,8 @@ func (api *spacesAPI) ListSpaces() (results params.ListSpacesResults, err error)
result.Name = space.Name()
subnets, err := space.Subnets()
if err != nil {
+ // TODO(mfoord): the response type should have an error
+ // field so we can return partial results.
return results, err
}
for _, subnet := range subnets {
View
@@ -8,14 +8,15 @@ import (
"github.com/juju/cmd"
"github.com/juju/errors"
+ "github.com/juju/juju/network"
"github.com/juju/names"
)
// AddCommand calls the API to add an existing subnet to Juju.
type AddCommand struct {
SubnetCommandBase
- CIDR string
+ CIDR names.SubnetTag
RawCIDR string // before normalizing (e.g. 10.10.0.0/8 to 10.0.0.0/8)
ProviderId string
Space names.SpaceTag
@@ -93,28 +94,28 @@ func (c *AddCommand) Run(ctx *cmd.Context) error {
}
defer api.Close()
- if c.CIDR != "" && c.RawCIDR != c.CIDR {
+ if c.CIDR.Id() != "" && c.RawCIDR != c.CIDR.Id() {
ctx.Infof(
"WARNING: using CIDR %q instead of the incorrectly specified %q.",
- c.CIDR, c.RawCIDR,
+ c.CIDR.Id(), c.RawCIDR,
)
}
// Add the existing subnet.
- err = api.AddSubnet(c.CIDR, c.ProviderId, c.Space, c.Zones)
+ err = api.AddSubnet(c.CIDR, network.Id(c.ProviderId), c.Space, c.Zones)
// TODO(dimitern): Change this once the API returns a concrete error.
if err != nil && strings.Contains(err.Error(), "multiple subnets with") {
// Special case: multiple subnets with the same CIDR exist
ctx.Infof("ERROR: %v.", err)
return nil
} else if err != nil {
- return errors.Annotatef(err, "cannot add subnet %q", c.CIDR)
+ return errors.Annotatef(err, "cannot add subnet %q", c.CIDR.Id())
}
- if c.CIDR == "" {
+ if c.CIDR.Id() == "" {
ctx.Infof("added subnet with ProviderId %q in space %q", c.ProviderId, c.Space.Id())
} else {
- ctx.Infof("added subnet with CIDR %q in space %q", c.CIDR, c.Space.Id())
+ ctx.Infof("added subnet with CIDR %q in space %q", c.CIDR.Id(), c.Space.Id())
}
return nil
}
Oops, something went wrong.