Skip to content

Commit

Permalink
add lease routes and update reservations
Browse files Browse the repository at this point in the history
  • Loading branch information
John Frey committed Oct 5, 2016
1 parent f495cba commit 13bb3a6
Show file tree
Hide file tree
Showing 10 changed files with 310 additions and 6 deletions.
3 changes: 3 additions & 0 deletions Makefile
Expand Up @@ -51,3 +51,6 @@ release: build

run: release
@docker-compose up

mongo:
@docker exec -it mongodb mongo ipam
32 changes: 32 additions & 0 deletions controllers/controllers_suite_test.go
Expand Up @@ -49,6 +49,9 @@ type MockIpam struct {
ReservationCreated models.Reservation
ReservationUpdated models.Reservation
ReservationDeleted string

Leases []models.Lease
LeaseUpdated models.Lease
}

func NewMockIpam() *MockIpam {
Expand Down Expand Up @@ -241,3 +244,32 @@ func (mock *MockIpam) GetPoolReservations(string) ([]models.Reservation, error)

return mock.Reservations, mock.Err
}

// GetReservations ...
func (mock *MockIpam) GetLeases(string) ([]models.Lease, error) {
if mock.Err != nil {
return []models.Lease{}, mock.Err
}

return mock.Leases, mock.Err
}

// GetReservation ...
func (mock *MockIpam) GetLease(string) (models.Lease, error) {
if mock.Err != nil {
return models.Lease{}, mock.Err
}

return mock.Leases[0], mock.Err
}

// UpdateReservation ...
func (mock *MockIpam) UpdateLease(lease models.Lease) error {
if mock.Err != nil {
return mock.Err
}

mock.LeaseUpdated = lease

return mock.Err
}
79 changes: 79 additions & 0 deletions controllers/leases.go
@@ -0,0 +1,79 @@
package controllers

import (
"fmt"
"net/http"

"gopkg.in/mgo.v2/bson"

"github.com/RackHD/ipam/controllers/helpers"
"github.com/RackHD/ipam/interfaces"
"github.com/RackHD/ipam/models"
"github.com/RackHD/ipam/resources"
"github.com/gorilla/mux"
)

// LeasesController provides methods for handling requests to the Leases API.
type LeasesController struct {
ipam interfaces.Ipam
}

// NewLeasesController returns a newly configured LeasesController.
func NewLeasesController(router *mux.Router, ipam interfaces.Ipam) (*LeasesController, error) {
c := LeasesController{
ipam: ipam,
}

router.Handle("/reservations/{id}/leases", helpers.ErrorHandler(c.Index)).Methods(http.MethodGet)
router.Handle("/leases/{id}", helpers.ErrorHandler(c.Show)).Methods(http.MethodGet)
router.Handle("/leases/{id}", helpers.ErrorHandler(c.Update)).Methods(http.MethodPut, http.MethodPatch)

return &c, nil
}

// Index returns a list of Leases.
func (c *LeasesController) Index(w http.ResponseWriter, r *http.Request) error {
vars := mux.Vars(r)

reservations, err := c.ipam.GetLeases(vars["id"])
if err != nil {
return err
}

return helpers.RenderResource(w, r, resources.LeasesResourceType, http.StatusOK, reservations)
}

// Show returns the requested Lease.
func (c *LeasesController) Show(w http.ResponseWriter, r *http.Request) error {
vars := mux.Vars(r)

reservation, err := c.ipam.GetLease(vars["id"])
if err != nil {
return err
}

return helpers.RenderResource(w, r, resources.LeaseResourceType, http.StatusOK, reservation)
}

// Update updates the requested Lease.
func (c *LeasesController) Update(w http.ResponseWriter, r *http.Request) error {
vars := mux.Vars(r)

resource, err := helpers.AcceptResource(r, resources.LeaseResourceType)
if err != nil {
return err
}

if reservation, ok := resource.(models.Lease); ok {
reservation.ID = bson.ObjectIdHex(vars["id"])

err = c.ipam.UpdateLease(reservation)
if err != nil {
return err
}

return helpers.RenderLocation(w, http.StatusNoContent, fmt.Sprintf("/reservations/%s", reservation.ID.Hex()))
}

return fmt.Errorf("Invalid Resource Type")
}
1 change: 1 addition & 0 deletions interfaces/ipam.go
Expand Up @@ -7,4 +7,5 @@ type Ipam interface {
Pools
Subnets
Reservations
Leases
}
10 changes: 10 additions & 0 deletions interfaces/leases.go
@@ -0,0 +1,10 @@
package interfaces

import "github.com/RackHD/ipam/models"

// Leases interface defines the methods for implementing Lease related business logic.
type Leases interface {
GetLeases(string) ([]models.Lease, error)
GetLease(string) (models.Lease, error)
UpdateLease(models.Lease) error
}
35 changes: 35 additions & 0 deletions ipam/leases.go
@@ -1,4 +1,39 @@
package ipam

import (
"github.com/RackHD/ipam/models"
"gopkg.in/mgo.v2/bson"
)

// IpamCollectionLeases is the name of the Mongo collection which stores Leases.
const IpamCollectionLeases string = "leases"

// GetLeases returns a list of Leases.
func (ipam *Ipam) GetLeases(id string) ([]models.Lease, error) {
session := ipam.session.Copy()
defer session.Close()

var reservations []models.Lease

session.DB(IpamDatabase).C(IpamCollectionLeases).Find(bson.M{"reservation": bson.ObjectIdHex(id)}).All(&reservations)

return reservations, nil
}

// GetLease returns the requested Lease.
func (ipam *Ipam) GetLease(id string) (models.Lease, error) {
session := ipam.session.Copy()
defer session.Close()

var reservation models.Lease

return reservation, session.DB(IpamDatabase).C(IpamCollectionLeases).Find(bson.M{"_id": bson.ObjectIdHex(id)}).One(&reservation)
}

// UpdateLease updates a Lease.
func (ipam *Ipam) UpdateLease(reservation models.Lease) error {
session := ipam.session.Copy()
defer session.Close()

return session.DB(IpamDatabase).C(IpamCollectionLeases).UpdateId(reservation.ID, reservation)
}
6 changes: 4 additions & 2 deletions ipam/subnets.go
Expand Up @@ -50,10 +50,12 @@ func (ipam *Ipam) CreateSubnet(subnet models.Subnet) error {
// Iterate through the range of IP's and insert a record for each.
for ; start < end; start++ {
// IP's are stored as 16 byte arrays and we're only doing IPv4 so prepend
// 12 empty bytes.
prefix := make([]byte, 12)
// the net.IP prefix that denotes an IPv4 address.
prefix := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}
address := make([]byte, 4)

binary.BigEndian.PutUint32(address, start)

// Create the lease record, tie it to the subnet.
lease := models.Lease{
ID: bson.NewObjectId(),
Expand Down
13 changes: 9 additions & 4 deletions main.go
Expand Up @@ -26,10 +26,10 @@ func main() {
var mgoDebug = true

if mgoDebug {
mgo.SetDebug(true)
var aLogger *log.Logger
aLogger = log.New(os.Stderr, "", log.LstdFlags)
mgo.SetLogger(aLogger)
mgo.SetDebug(true)
var aLogger *log.Logger
aLogger = log.New(os.Stderr, "", log.LstdFlags)
mgo.SetLogger(aLogger)
}

// Start off with a new mux router.
Expand Down Expand Up @@ -63,6 +63,11 @@ func main() {
log.Fatalf("%s", err)
}

_, err = controllers.NewLeasesController(router, ipam)
if err != nil {
log.Fatalf("%s", err)
}

// Show off request logging middleware.
logged := handlers.LoggingHandler(os.Stdout, router)

Expand Down
78 changes: 78 additions & 0 deletions resources/lease.go
@@ -0,0 +1,78 @@
package resources

import (
"fmt"
"net"

"github.com/RackHD/ipam/interfaces"
"github.com/RackHD/ipam/models"
"github.com/RackHD/ipam/resources/factory"
"gopkg.in/mgo.v2/bson"
)

// LeaseResourceType is the media type assigned to a Lease resource.
const LeaseResourceType string = "application/vnd.ipam.lease"

// LeaseResourceVersionV1 is the semantic version identifier for the Subnet resource.
const LeaseResourceVersionV1 string = "1.0.0"

func init() {
factory.Register(LeaseResourceType, LeaseCreator)
}

// LeaseCreator is a factory function for turning a version string into a Lease resource.
func LeaseCreator(version string) (interfaces.Resource, error) {
return &LeaseV1{}, nil
}

// LeaseV1 represents the v1.0.0 version of the Lease resource.
type LeaseV1 struct {
ID string `json:"id"`
Name string `json:"name"`
Tags []string `json:"tags"`
Metadata interface{} `json:"metadata"`
Subnet string `json:"subnet"`
Reservation string `json:"reservation"`
Address string `json:"address"`
}

// Type returns the resource type for use in rendering HTTP response headers.
func (s *LeaseV1) Type() string {
return LeaseResourceType
}

// Version returns the resource version for use in rendering HTTP response headers.
func (s *LeaseV1) Version() string {
return LeaseResourceVersionV1
}

// Marshal converts a models.Lease object into this version of the resource.
func (s *LeaseV1) Marshal(object interface{}) error {
if target, ok := object.(models.Lease); ok {
s.ID = target.ID.Hex()
s.Name = target.Name
s.Tags = target.Tags
s.Metadata = target.Metadata
s.Subnet = target.Subnet.Hex()
s.Reservation = target.Reservation.Hex()
s.Address = net.IP(target.Address.Data).String()

return nil
}

return fmt.Errorf("Invalid Object Type: %+v", object)
}

// Unmarshal converts the resource into a models.Lease object.
func (s *LeaseV1) Unmarshal() (interface{}, error) {
if s.ID == "" {
s.ID = bson.NewObjectId().Hex()
}

return models.Lease{
ID: bson.ObjectIdHex(s.ID),
Name: s.Name,
Tags: s.Tags,
Metadata: s.Metadata,
}, nil
}
59 changes: 59 additions & 0 deletions resources/leases.go
@@ -0,0 +1,59 @@
package resources

import (
"fmt"

"github.com/RackHD/ipam/interfaces"
"github.com/RackHD/ipam/models"
"github.com/RackHD/ipam/resources/factory"
)

// LeasesResourceType is the media type assigned to a collection of Lease resources.
const LeasesResourceType string = "application/vnd.ipam.leases"

// LeasesResourceVersionV1 is the semantic version identifier for the Pool resource.
const LeasesResourceVersionV1 string = "1.0.0"

func init() {
factory.Register(LeasesResourceType, LeasesCreator)
}

// LeasesCreator is a factory function for turning a version string into a Leases resource.
func LeasesCreator(version string) (interfaces.Resource, error) {
return &LeasesV1{}, nil
}

// LeasesV1 represents the v1.0.0 version of the Leases resource.
type LeasesV1 struct {
Leases []LeaseV1 `json:"leases"`
}

// Type returns the resource type for use in rendering HTTP response headers.
func (p *LeasesV1) Type() string {
return LeasesResourceType
}

// Version returns the resource version for use in rendering HTTP response headers.
func (p *LeasesV1) Version() string {
return LeasesResourceVersionV1
}

// Marshal converts an array of models.Lease objects into this version of the resource.
func (p *LeasesV1) Marshal(object interface{}) error {
if subnets, ok := object.([]models.Lease); ok {
p.Leases = make([]LeaseV1, len(subnets))

for i := range p.Leases {
p.Leases[i].Marshal(subnets[i])
}

return nil
}

return fmt.Errorf("Invalid Object Type.")
}

// Unmarshal converts the resource into an array of models.Lease objects.
func (p *LeasesV1) Unmarshal() (interface{}, error) {
return nil, fmt.Errorf("Invalid Action for Resource.")
}

0 comments on commit 13bb3a6

Please sign in to comment.