diff --git a/Makefile b/Makefile index 5ce0b27..071cb86 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ deps: deps-local: @if ! [ -f glide.yaml ]; then glide init --non-interactive; fi - @glide install --strip-vcs --strip-vendor + @glide install build: @${DOCKER_CMD} make build-local @@ -36,8 +36,18 @@ lint-local: test: @${DOCKER_CMD} make test-local +test-client: + @docker-compose build + @docker-compose create + @docker-compose start + @-make test-client-local + @docker-compose kill + test-local: lint-local - @ginkgo -race -trace -randomizeAllSpecs -r -cover + @ginkgo -race -trace -randomizeAllSpecs -r -cover --skipPackage=client + +test-client-local: + @ginkgo -race -trace -randomizeAllSpecs -cover client coveralls: @go get github.com/mattn/goveralls diff --git a/client/README.md b/client/README.md new file mode 100644 index 0000000..89d6567 --- /dev/null +++ b/client/README.md @@ -0,0 +1 @@ +# Golang REST client for RackHD ipam diff --git a/client/client.go b/client/client.go new file mode 100644 index 0000000..c520f52 --- /dev/null +++ b/client/client.go @@ -0,0 +1,143 @@ +package ipamapi + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "log" + "mime" + "net/http" + "time" + + "github.com/RackHD/ipam/controllers/helpers" + "github.com/RackHD/ipam/interfaces" + "github.com/RackHD/ipam/resources/factory" + "github.com/hashicorp/go-cleanhttp" +) + +// Client struct is used to configure the creation of a client +type Client struct { + Address string + Scheme string +} + +// NewClient returns a new client +func NewClient(address string) *Client { + // bootstrap the config + c := &Client{ + Address: address, + Scheme: "http", + } + + // Make sure IPAM connection is alive, with retries + for i := 0; i < 5; i++ { + _, err := c.IndexPools() + if err == nil { + return c + } + log.Println("Could not connect to IPAM, retrying in 5 Seconds...") + time.Sleep(5 * time.Second) + } + + return nil +} + +// SendResource is used to send a generic resource type +func (c *Client) SendResource(method, path string, in interfaces.Resource) (string, error) { + + body, err := encodeBody(in) + if err != nil { + return "", err + } + req, err := http.NewRequest(method, c.Scheme+"://"+c.Address+path, body) + if err != nil { + return "", err + } + req.Header.Set( + "Content-Type", + mime.FormatMediaType( + fmt.Sprintf("%s+%s", in.Type(), "json"), + map[string]string{"version": in.Version()}, + ), + ) + + client := cleanhttp.DefaultClient() + resp, err := client.Do(req) + if err != nil { + return "", err + } + if resp.StatusCode < 200 || resp.StatusCode > 300 { + return "", errors.New(resp.Status) + } + + return resp.Header.Get("Location"), nil +} + +// ReceiveResource is used to receive the passed reasource type +func (c *Client) ReceiveResource(method, path, resourceType, resourceVersion string) (interfaces.Resource, error) { + + req, err := http.NewRequest(method, c.Scheme+"://"+c.Address+path, nil) + + req.Header.Set( + "Content-Type", + mime.FormatMediaType( + fmt.Sprintf("%s+%s", resourceType, "json"), + map[string]string{"version": resourceVersion}, + ), + ) + + client := cleanhttp.DefaultClient() + resp, err := client.Do(req) + if err != nil { + return nil, err + } + + mediaType, err := helpers.NewMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return nil, err + } + resource, err := factory.Require(mediaType.Type, mediaType.Version) + if err != nil { + return nil, err + } + + err = decodeBody(resp, &resource) + if err != nil { + return nil, err + } + + return resource, nil +} + +// SendReceiveResource is used to send a resource type and then +// upon success, fetch and recieve that resource type +func (c *Client) SendReceiveResource(methodSend, methodReceive, path string, in interfaces.Resource) (interfaces.Resource, error) { + + location, err := c.SendResource(methodSend, path, in) + if err != nil { + return nil, err + } + out, err := c.ReceiveResource(methodReceive, location, "", "") + return out, err +} + +// decodeBody is used to JSON decode a body +func decodeBody(resp *http.Response, out interface{}) error { + dec := json.NewDecoder(resp.Body) + return dec.Decode(out) +} + +// encodeBody is used to encode a request body +func encodeBody(obj interface{}) (io.Reader, error) { + if obj == nil { + return nil, nil + } + buf := bytes.NewBuffer(nil) + enc := json.NewEncoder(buf) + if err := enc.Encode(obj); err != nil { + return nil, err + } + return buf, nil +} diff --git a/client/client_suite_test.go b/client/client_suite_test.go new file mode 100644 index 0000000..4423aee --- /dev/null +++ b/client/client_suite_test.go @@ -0,0 +1,13 @@ +package ipamapi_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "testing" +) + +func TestClient(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Client Suite") +} diff --git a/client/client_test.go b/client/client_test.go new file mode 100644 index 0000000..fd642a7 --- /dev/null +++ b/client/client_test.go @@ -0,0 +1,226 @@ +package ipamapi_test + +import ( + . "github.com/RackHD/ipam/client" + "github.com/RackHD/ipam/resources" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Client tests", func() { + var ipamAddress, start, end string + var err error + + BeforeEach(func() { + ipamAddress = "127.0.0.1:8000" + start = "192.168.1.10" + end = "192.168.1.11" + }) + var _ = Describe("Pools tests", func() { + var ipamClient *Client + var pool resources.PoolV1 + + BeforeEach(func() { + ipamClient = NewClient(ipamAddress) + Expect(ipamClient).ToNot(BeNil()) + }) + + AfterEach(func() { + poolLocation, err := ipamClient.DeletePool(pool.ID, pool) + Expect(err).To(BeNil()) + Expect(poolLocation).To(Equal("/pools")) + }) + It("Should create a pool and return that pool object", func() { + + pool = resources.PoolV1{ + Name: "Pool1", + Metadata: "yodawg I heard you like interfaces", + } + + pool, err = ipamClient.CreateShowPool(pool) + Expect(err).To(BeNil()) + Expect(pool.ID).ToNot(Equal("")) + Expect(pool.Name).To(Equal("Pool1")) + + }) + }) + + var _ = Describe("Subnets tests", func() { + var ipamClient *Client + var pool resources.PoolV1 + var subnet resources.SubnetV1 + + BeforeEach(func() { + ipamClient = NewClient(ipamAddress) + Expect(ipamClient).ToNot(BeNil()) + + pool = resources.PoolV1{ + Name: "SubnetTestPool1", + } + + pool, err = ipamClient.CreateShowPool(pool) + Expect(err).To(BeNil()) + Expect(pool.ID).ToNot(Equal("")) + Expect(pool.Name).To(Equal("SubnetTestPool1")) + + }) + + AfterEach(func() { + poolLocation, err := ipamClient.DeletePool(pool.ID, pool) + Expect(err).To(BeNil()) + Expect(poolLocation).To(Equal("/pools")) + }) + + It("Should create a subnet and return that subnet object", func() { + + subnet = resources.SubnetV1{ + Name: "Subnet1", + Pool: pool.ID, + Start: start, + End: end, + } + + subnet, err = ipamClient.CreateShowSubnet(pool.ID, subnet) + Expect(err).To(BeNil()) + Expect(subnet.ID).ToNot(Equal("")) + Expect(subnet.Name).To(Equal("Subnet1")) + Expect(subnet.Pool).To(Equal(pool.ID)) + + }) + + }) + var _ = Describe("Reservations tests", func() { + + var ipamClient *Client + var pool resources.PoolV1 + var subnet resources.SubnetV1 + var reservation resources.ReservationV1 + + BeforeEach(func() { + ipamClient = NewClient(ipamAddress) + Expect(ipamClient).ToNot(BeNil()) + + pool = resources.PoolV1{ + Name: "ReservationTestPool1", + } + + pool, err = ipamClient.CreateShowPool(pool) + Expect(err).To(BeNil()) + Expect(pool.ID).ToNot(Equal("")) + Expect(pool.Name).To(Equal("ReservationTestPool1")) + + subnet = resources.SubnetV1{ + Name: "ReservationTestSubnet1", + Pool: pool.ID, + Start: start, + End: end, + } + + subnet, err = ipamClient.CreateShowSubnet(pool.ID, subnet) + Expect(err).To(BeNil()) + Expect(subnet.ID).ToNot(Equal("")) + Expect(subnet.Name).To(Equal("ReservationTestSubnet1")) + Expect(subnet.Pool).To(Equal(pool.ID)) + + }) + + AfterEach(func() { + poolLocation, err := ipamClient.DeletePool(pool.ID, pool) + Expect(err).To(BeNil()) + Expect(poolLocation).To(Equal("/pools")) + }) + + It("Should create a reservation and return that reservation object", func() { + + reservation = resources.ReservationV1{ + Name: "Reservation1", + Subnet: subnet.ID, + } + + reservation, err = ipamClient.CreateShowReservation(subnet.ID, reservation) + Expect(err).To(BeNil()) + Expect(reservation.ID).ToNot(Equal("")) + Expect(reservation.Name).To(Equal("Reservation1")) + Expect(reservation.Subnet).To(Equal(subnet.ID)) + + }) + + }) + var _ = Describe("Leases tests", func() { + var ipamClient *Client + var pool resources.PoolV1 + var subnet resources.SubnetV1 + var reservation, reservation2 resources.ReservationV1 + var leases, leases2 resources.LeasesV1 + + BeforeEach(func() { + ipamClient = NewClient(ipamAddress) + Expect(ipamClient).ToNot(BeNil()) + + pool = resources.PoolV1{ + Name: "LeaseTestPool1", + } + + pool, err = ipamClient.CreateShowPool(pool) + Expect(err).To(BeNil()) + Expect(pool.ID).ToNot(Equal("")) + Expect(pool.Name).To(Equal("LeaseTestPool1")) + + subnet = resources.SubnetV1{ + Name: "LeaseTestSubnet1", + Pool: pool.ID, + Start: start, + End: end, + } + + subnet, err = ipamClient.CreateShowSubnet(pool.ID, subnet) + Expect(err).To(BeNil()) + Expect(subnet.ID).ToNot(Equal("")) + Expect(subnet.Name).To(Equal("LeaseTestSubnet1")) + Expect(subnet.Pool).To(Equal(pool.ID)) + + reservation = resources.ReservationV1{ + Name: "LeaseTestReservation1", + Subnet: subnet.ID, + } + + reservation, err = ipamClient.CreateShowReservation(subnet.ID, reservation) + Expect(err).To(BeNil()) + Expect(reservation.ID).ToNot(Equal("")) + Expect(reservation.Name).To(Equal("LeaseTestReservation1")) + Expect(reservation.Subnet).To(Equal(subnet.ID)) + + reservation2 = resources.ReservationV1{ + Name: "LeaseTestReservation2", + Subnet: subnet.ID, + } + + reservation2, err = ipamClient.CreateShowReservation(subnet.ID, reservation2) + Expect(err).To(BeNil()) + Expect(reservation2.ID).ToNot(Equal("")) + Expect(reservation2.Name).To(Equal("LeaseTestReservation2")) + Expect(reservation2.Subnet).To(Equal(subnet.ID)) + + }) + + AfterEach(func() { + poolLocation, err := ipamClient.DeletePool(pool.ID, pool) + Expect(err).To(BeNil()) + Expect(poolLocation).To(Equal("/pools")) + }) + + It("Should show all leases", func() { + leases, err = ipamClient.IndexLeases(reservation.ID) + Expect(err).To(BeNil()) + Expect(leases.Leases[0].ID).ToNot(Equal("")) + Expect(leases.Leases[0].Reservation).To(Equal(reservation.ID)) + + leases2, err = ipamClient.IndexLeases(reservation2.ID) + Expect(err).To(BeNil()) + Expect(leases2.Leases[0].ID).ToNot(Equal("")) + Expect(leases2.Leases[0].Reservation).To(Equal(reservation2.ID)) + + }) + }) +}) diff --git a/client/leases.go b/client/leases.go new file mode 100644 index 0000000..e11018f --- /dev/null +++ b/client/leases.go @@ -0,0 +1,52 @@ +package ipamapi + +import ( + "errors" + + "github.com/RackHD/ipam/resources" +) + +// IndexLeases returns a list of Leases. +func (c *Client) IndexLeases(reservationID string) (resources.LeasesV1, error) { + returnedLeases, err := c.ReceiveResource("GET", "/reservations/"+reservationID+"/leases", "", "") + if err != nil { + return resources.LeasesV1{}, err + } + if leases, ok := returnedLeases.(*resources.LeasesV1); ok { + return *leases, nil + } + return resources.LeasesV1{}, errors.New("Lease Index call error.") +} + +// ShowLease returns the requested Lease. +func (c *Client) ShowLease(leaseID string, leaseToShow resources.LeaseV1) (resources.LeaseV1, error) { + returnedLease, err := c.ReceiveResource("GET", "/leases/"+leaseID, leaseToShow.Type(), leaseToShow.Version()) + if err != nil { + return resources.LeaseV1{}, err + } + if lease, ok := returnedLease.(*resources.LeaseV1); ok { + return *lease, nil + } + return resources.LeaseV1{}, errors.New("Lease Show call error.") +} + +// UpdateLease updates the requested Lease and returns its location. +func (c *Client) UpdateLease(leaseID string, leaseToUpdate resources.LeaseV1) (string, error) { + leaseLocation, err := c.SendResource("PATCH", "/leases/"+leaseID, &leaseToUpdate) + if err != nil { + return "", err + } + return leaseLocation, nil +} + +// UpdateShowLease updates a Lease and then returns that Lease. +func (c *Client) UpdateShowLease(leaseID string, leaseToUpdate resources.LeaseV1) (resources.LeaseV1, error) { + returnedLease, err := c.SendReceiveResource("PATCH", "GET", "/leases/"+leaseID, &leaseToUpdate) + if err != nil { + return resources.LeaseV1{}, err + } + if lease, ok := returnedLease.(*resources.LeaseV1); ok { + return *lease, nil + } + return resources.LeaseV1{}, errors.New("UpdateShowLease call error.") +} diff --git a/client/pools.go b/client/pools.go new file mode 100644 index 0000000..5473c56 --- /dev/null +++ b/client/pools.go @@ -0,0 +1,84 @@ +package ipamapi + +import ( + "errors" + + "github.com/RackHD/ipam/resources" +) + +// IndexPools returns a list of Pools. +func (c *Client) IndexPools() (resources.PoolsV1, error) { + pools, err := c.ReceiveResource("GET", "/pools", "", "") + if err != nil { + return resources.PoolsV1{}, err + } + + if newPools, ok := pools.(*resources.PoolsV1); ok { + return *newPools, nil + } + return resources.PoolsV1{}, errors.New("Pool Index call error.") +} + +// CreatePool a pool and returns the location. +func (c *Client) CreatePool(poolToCreate resources.PoolV1) (string, error) { + + poolLocation, err := c.SendResource("POST", "/pools", &poolToCreate) + if err != nil { + return "", err + } + return poolLocation, nil +} + +// CreateShowPool creates a pool and then returns that pool. +func (c *Client) CreateShowPool(poolToCreate resources.PoolV1) (resources.PoolV1, error) { + receivedPool, err := c.SendReceiveResource("POST", "GET", "/pools", &poolToCreate) + if err != nil { + return resources.PoolV1{}, err + } + if pool, ok := receivedPool.(*resources.PoolV1); ok { + return *pool, nil + } + return resources.PoolV1{}, errors.New("CreateShowPool call error.") +} + +// ShowPool returns the requested Pool. +func (c *Client) ShowPool(poolID string, poolToShow resources.PoolV1) (resources.PoolV1, error) { + receivedPool, err := c.ReceiveResource("GET", "/pools/"+poolID, poolToShow.Type(), poolToShow.Version()) + if err != nil { + return resources.PoolV1{}, err + } + if pool, ok := receivedPool.(*resources.PoolV1); ok { + return *pool, nil + } + return resources.PoolV1{}, errors.New("Pools Show call error.") +} + +// UpdatePool updates the requested Pool and returns its location. +func (c *Client) UpdatePool(poolID string, poolToUpdate resources.PoolV1) (string, error) { + location, err := c.SendResource("PATCH", "/pools/"+poolID, &poolToUpdate) + if err != nil { + return "", err + } + return location, nil +} + +// UpdateShowPool updates a pool and then returns that pool. +func (c *Client) UpdateShowPool(poolID string, poolToUpdate resources.PoolV1) (resources.PoolV1, error) { + receivedPool, err := c.SendReceiveResource("PATCH", "GET", "/pools/"+poolID, &poolToUpdate) + if err != nil { + return resources.PoolV1{}, err + } + if pools, ok := receivedPool.(*resources.PoolV1); ok { + return *pools, nil + } + return resources.PoolV1{}, errors.New("UpdateShowPool call error.") +} + +// DeletePool removes the requested Pool and returns the location. +func (c *Client) DeletePool(poolID string, poolToDelete resources.PoolV1) (string, error) { + location, err := c.SendResource("DELETE", "/pools/"+poolID, &poolToDelete) + if err != nil { + return "", err + } + return location, nil +} diff --git a/client/reservations.go b/client/reservations.go new file mode 100644 index 0000000..10830a8 --- /dev/null +++ b/client/reservations.go @@ -0,0 +1,82 @@ +package ipamapi + +import ( + "errors" + + "github.com/RackHD/ipam/resources" +) + +// IndexReservations returns a list of Reservations. +func (c *Client) IndexReservations(subnetID string) (resources.ReservationsV1, error) { + receivedReservations, err := c.ReceiveResource("GET", "/subnets/"+subnetID+"/reservations", "", "") + if err != nil { + return resources.ReservationsV1{}, err + } + if reservations, ok := receivedReservations.(*resources.ReservationsV1); ok { + return *reservations, nil + } + return resources.ReservationsV1{}, errors.New("Reservation Index call error.") +} + +// CreateReservation a Reservation and return the location. +func (c *Client) CreateReservation(subnetID string, reservationToCreate resources.ReservationV1) (string, error) { + reservationLocation, err := c.SendResource("POST", "/subnets/"+subnetID+"/reservations", &reservationToCreate) + if err != nil { + return "", err + } + return reservationLocation, nil +} + +// CreateShowReservation creates a Reservation and then returns that Reservation. +func (c *Client) CreateShowReservation(subnetID string, reservationToCreate resources.ReservationV1) (resources.ReservationV1, error) { + receivedReservation, err := c.SendReceiveResource("POST", "GET", "/subnets/"+subnetID+"/reservations", &reservationToCreate) + if err != nil { + return resources.ReservationV1{}, err + } + if reservation, ok := receivedReservation.(*resources.ReservationV1); ok { + return *reservation, nil + } + return resources.ReservationV1{}, errors.New("CreateShowReservation call error.") +} + +// ShowReservation returns the requested Reservation. +func (c *Client) ShowReservation(reservationID string, reservationToShow resources.ReservationV1) (resources.ReservationV1, error) { + receivedReservation, err := c.ReceiveResource("GET", "/reservations/"+reservationID, reservationToShow.Type(), reservationToShow.Version()) + if err != nil { + return resources.ReservationV1{}, err + } + if reservation, ok := receivedReservation.(*resources.ReservationV1); ok { + return *reservation, nil + } + return resources.ReservationV1{}, errors.New("Reservation Show call error.") +} + +// UpdateReservation updates the requested Reservation and returns its location. +func (c *Client) UpdateReservation(reservationID string, reservationToUpdate resources.ReservationV1) (string, error) { + reservationLocation, err := c.SendResource("PATCH", "/reservations/"+reservationID, &reservationToUpdate) + if err != nil { + return "", err + } + return reservationLocation, nil +} + +// UpdateShowReservation updates a Reservation and then returns that Reservation. +func (c *Client) UpdateShowReservation(reservationID string, reservationToUpdate resources.ReservationV1) (resources.ReservationV1, error) { + receivedReservation, err := c.SendReceiveResource("PATCH", "GET", "/reservations/"+reservationID, &reservationToUpdate) + if err != nil { + return resources.ReservationV1{}, err + } + if reservation, ok := receivedReservation.(*resources.ReservationV1); ok { + return *reservation, nil + } + return resources.ReservationV1{}, errors.New("UpdateShowReservation call error.") +} + +// DeleteReservation removed the requested Reservation and returns the location. +func (c *Client) DeleteReservation(reservationID string, reservationToDelete resources.ReservationV1) (string, error) { + reservationLocation, err := c.SendResource("DELETE", "/reservations/"+reservationID, &reservationToDelete) + if err != nil { + return "", err + } + return reservationLocation, nil +} diff --git a/client/subnets.go b/client/subnets.go new file mode 100644 index 0000000..ce2d71b --- /dev/null +++ b/client/subnets.go @@ -0,0 +1,82 @@ +package ipamapi + +import ( + "errors" + + "github.com/RackHD/ipam/resources" +) + +// IndexSubnets returns a list of Subnets. +func (c *Client) IndexSubnets(poolID string) (resources.SubnetsV1, error) { + receivedSubnets, err := c.ReceiveResource("GET", "/pools/"+poolID+"/subnets", "", "") + if err != nil { + return resources.SubnetsV1{}, err + } + if subnets, ok := receivedSubnets.(*resources.SubnetsV1); ok { + return *subnets, nil + } + return resources.SubnetsV1{}, errors.New("Subnet Index call error.") +} + +// CreateSubnet a subnet and return the location. +func (c *Client) CreateSubnet(poolID string, subnetToCreate resources.SubnetV1) (string, error) { + subnetLocation, err := c.SendResource("POST", "/pools/"+poolID+"/subnets", &subnetToCreate) + if err != nil { + return "", err + } + return subnetLocation, nil +} + +// CreateShowSubnet creates a subnet and then returns that subnet. +func (c *Client) CreateShowSubnet(poolID string, subnetToCreate resources.SubnetV1) (resources.SubnetV1, error) { + receivedSubnet, err := c.SendReceiveResource("POST", "GET", "/pools/"+poolID+"/subnets", &subnetToCreate) + if err != nil { + return resources.SubnetV1{}, err + } + if subnet, ok := receivedSubnet.(*resources.SubnetV1); ok { + return *subnet, nil + } + return resources.SubnetV1{}, errors.New("CreateShowSubnet call error.") +} + +// ShowSubnet returns the requested subnet. +func (c *Client) ShowSubnet(subnetID string, subnetToGet resources.SubnetV1) (resources.SubnetV1, error) { + receivedSubnet, err := c.ReceiveResource("GET", "/subnets/"+subnetID, subnetToGet.Type(), subnetToGet.Version()) + if err != nil { + return resources.SubnetV1{}, err + } + if subnet, ok := receivedSubnet.(*resources.SubnetV1); ok { + return *subnet, nil + } + return resources.SubnetV1{}, errors.New("Subnet Show call error.") +} + +// UpdateSubnet updates the requested subnet and returns its location. +func (c *Client) UpdateSubnet(subnetID string, subnetToUpdate resources.SubnetV1) (string, error) { + subnetLocation, err := c.SendResource("PATCH", "/subnets/"+subnetID, &subnetToUpdate) + if err != nil { + return "", err + } + return subnetLocation, nil +} + +// UpdateShowSubnet updates a Subnet and then returns that Subnet. +func (c *Client) UpdateShowSubnet(subnetID string, subnetToUpdate resources.SubnetV1) (resources.SubnetV1, error) { + receivedSubnet, err := c.SendReceiveResource("PATCH", "GET", "/subnets/"+subnetID, &subnetToUpdate) + if err != nil { + return resources.SubnetV1{}, err + } + if subnet, ok := receivedSubnet.(*resources.SubnetV1); ok { + return *subnet, nil + } + return resources.SubnetV1{}, errors.New("UpdateShowSubnet call error.") +} + +// DeleteSubnet removed the requested subnet and returns the location. +func (c *Client) DeleteSubnet(subnetID string, subnetToDelete resources.SubnetV1) (string, error) { + subnetLocation, err := c.SendResource("DELETE", "/subnets/"+subnetID, &subnetToDelete) + if err != nil { + return "", err + } + return subnetLocation, nil +} diff --git a/glide.lock b/glide.lock index f5db7d6..25a5a29 100644 --- a/glide.lock +++ b/glide.lock @@ -1,17 +1,18 @@ -hash: e2dabaf1e451282bcc8632bacf0c02624d1a56deb9a0bd9c8122854174548738 -updated: 2016-08-18T17:15:25.722854231Z +hash: bd5079f55eccc49a9cb23d2c571ac41b7e196415050baff6a7fb53631e558ae6 +updated: 2016-11-22T13:38:19.705256414-05:00 imports: - name: github.com/gorilla/context version: 08b5f424b9271eedf6f9f0ce86cb9396ed337a42 - name: github.com/gorilla/handlers - version: a5775781a543af3c6b9f5baf10995e4d14168950 + version: ee54c7b44cab12289237fb8631314790076e728b - name: github.com/gorilla/mux - version: cf79e51a62d8219d52060dfc1b4e810414ba2d15 + version: 0eeaf8392f5b04950925b8a69fe70f110fa7cbfc +- name: github.com/hashicorp/go-cleanhttp + version: ad28ea4487f05916463e2423a55166280e8254b5 - name: gopkg.in/mgo.v2 version: 3f83fa5005286a7fe593b055f0d7771a7dce4655 subpackages: - bson + - internal/json - internal/sasl - internal/scram - - internal/json -testImports: [] diff --git a/glide.yaml b/glide.yaml index 0022327..97ce678 100644 --- a/glide.yaml +++ b/glide.yaml @@ -1,7 +1,10 @@ package: github.com/RackHD/ipam import: - package: github.com/gorilla/handlers + version: ^1.1.0 - package: github.com/gorilla/mux + version: ^1.1.0 +- package: github.com/hashicorp/go-cleanhttp - package: gopkg.in/mgo.v2 subpackages: - bson