Beispiel #1
0
func TestAllocFree(t *testing.T) {
	const (
		container1 = "abcdef"
		container2 = "baddf00d"
		container3 = "b01df00d"
		universe   = "10.0.3.0/26"
		subnet1    = "10.0.3.0/28"
		subnet2    = "10.0.3.32/28"
		testAddr1  = "10.0.3.1"
		testAddr2  = "10.0.3.33"
		spaceSize  = 62 // 64 IP addresses in /26, minus .0 and .63
	)

	alloc, subnet := makeAllocatorWithMockGossip(t, "01:00:00:01:00:00", universe, 1)
	defer alloc.Stop()
	_, cidr1, _ := address.ParseCIDR(subnet1)
	_, cidr2, _ := address.ParseCIDR(subnet2)

	alloc.claimRingForTesting()
	addr1, err := alloc.Allocate(container1, cidr1.HostRange(), nil)
	require.NoError(t, err)
	require.Equal(t, testAddr1, addr1.String(), "address")

	addr2, err := alloc.Allocate(container1, cidr2.HostRange(), nil)
	require.NoError(t, err)
	require.Equal(t, testAddr2, addr2.String(), "address")

	// Ask for another address for a different container and check it's different
	addr1b, _ := alloc.Allocate(container2, cidr1.HostRange(), nil)
	if addr1b.String() == testAddr1 {
		t.Fatalf("Expected different address but got %s", addr1b.String())
	}

	// Ask for the first container again and we should get the same addresses again
	addr1a, _ := alloc.Allocate(container1, cidr1.HostRange(), nil)
	require.Equal(t, testAddr1, addr1a.String(), "address")
	addr2a, _ := alloc.Allocate(container1, cidr2.HostRange(), nil)
	require.Equal(t, testAddr2, addr2a.String(), "address")

	// Now delete the first container, and we should get its addresses back
	require.NoError(t, alloc.Delete(container1))
	addr3, _ := alloc.Allocate(container3, cidr1.HostRange(), nil)
	require.Equal(t, testAddr1, addr3.String(), "address")
	addr4, _ := alloc.Allocate(container3, cidr2.HostRange(), nil)
	require.Equal(t, testAddr2, addr4.String(), "address")

	alloc.ContainerDied(container2)
	alloc.ContainerDied(container3)
	require.Equal(t, address.Offset(spaceSize), alloc.NumFreeAddresses(subnet))
}
Beispiel #2
0
func impTestHTTPCancel(t *testing.T) {
	common.InitDefaultLogging(true)
	var (
		containerID = "deadbeef"
		testCIDR1   = "10.0.3.0/29"
	)

	alloc, _ := makeAllocatorWithMockGossip(t, "08:00:27:01:c3:9a", testCIDR1, 1)
	defer alloc.Stop()
	alloc.claimRingForTesting()
	_, cidr, _ := address.ParseCIDR(testCIDR1)
	port := listenHTTP(alloc, cidr)

	// Stop the alloc so nothing actually works
	unpause := alloc.pause()

	// Ask the http server for a new address
	done := make(chan *http.Response)
	req, _ := http.NewRequest("POST", allocURL(port, testCIDR1, containerID), nil)
	go func() {
		res, _ := http.DefaultClient.Do(req)
		done <- res
	}()

	time.Sleep(100 * time.Millisecond)
	fmt.Println("Cancelling allocate")
	http.DefaultTransport.(*http.Transport).CancelRequest(req)
	unpause()
	res := <-done
	if res != nil {
		require.FailNow(t, "Error: Allocate returned non-nil")
	}
}
Beispiel #3
0
func TestBadHttp(t *testing.T) {
	var (
		containerID = "deadbeef"
		testCIDR1   = "10.0.0.0/8"
	)

	alloc, _ := makeAllocatorWithMockGossip(t, "08:00:27:01:c3:9a", testCIDR1, 1)
	defer alloc.Stop()
	_, cidr, _ := address.ParseCIDR(testCIDR1)
	port := listenHTTP(alloc, cidr)

	alloc.claimRingForTesting()
	cidr1 := HTTPPost(t, allocURL(port, testCIDR1, containerID))
	parts := strings.Split(cidr1, "/")
	testAddr1 := parts[0]
	// Verb that's not handled
	resp, err := doHTTP("HEAD", fmt.Sprintf("http://localhost:%d/ip/%s/%s", port, containerID, testAddr1))
	require.NoError(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode, "http response")
	// Mis-spelled URL
	resp, err = doHTTP("POST", fmt.Sprintf("http://localhost:%d/xip/%s/", port, containerID))
	require.NoError(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode, "http response")
	// Malformed URL
	resp, err = doHTTP("POST", fmt.Sprintf("http://localhost:%d/ip/%s/foo/bar/baz", port, containerID))
	require.NoError(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode, "http response")
}
Beispiel #4
0
func parseAndCheckCIDR(cidrStr string) address.CIDR {
	_, cidr, err := address.ParseCIDR(cidrStr)
	if err != nil {
		Log.Fatal(err)
	}
	if cidr.Size() < ipam.MinSubnetSize {
		Log.Fatalf("Allocation range smaller than minimum size %d: %s", ipam.MinSubnetSize, cidrStr)
	}
	return cidr
}
Beispiel #5
0
func makeAllocator(name string, cidrStr string, quorum uint) (*Allocator, address.Range) {
	peername, err := router.PeerNameFromString(name)
	if err != nil {
		panic(err)
	}

	_, cidr, err := address.ParseCIDR(cidrStr)
	if err != nil {
		panic(err)
	}

	alloc := NewAllocator(peername, router.PeerUID(rand.Int63()),
		"nick-"+name, cidr.Range(), quorum)

	return alloc, cidr.HostRange()
}
Beispiel #6
0
func TestHttp(t *testing.T) {
	var (
		containerID = "deadbeef"
		container2  = "baddf00d"
		container3  = "b01df00d"
		universe    = "10.0.0.0/8"
		testCIDR1   = "10.0.3.8/29"
		testCIDR2   = "10.2.0.0/16"
		testAddr1   = "10.0.3.9/29"
		testAddr2   = "10.2.0.1/16"
	)

	alloc, _ := makeAllocatorWithMockGossip(t, "08:00:27:01:c3:9a", universe, 1)
	_, cidr, _ := address.ParseCIDR(universe)
	port := listenHTTP(alloc, cidr)
	alloc.claimRingForTesting()

	// Allocate an address in each subnet, and check we got what we expected
	cidr1a := HTTPPost(t, allocURL(port, testCIDR1, containerID))
	require.Equal(t, testAddr1, cidr1a, "address")
	cidr2a := HTTPPost(t, allocURL(port, testCIDR2, containerID))
	require.Equal(t, testAddr2, cidr2a, "address")
	// Now, make the same requests again to check the operation is idempotent
	check := HTTPGet(t, allocURL(port, testCIDR1, containerID))
	require.Equal(t, cidr1a, check, "address")
	check = HTTPGet(t, allocURL(port, testCIDR2, containerID))
	require.Equal(t, cidr2a, check, "address")

	// Ask the http server for a pair of addresses for another container and check they're different
	cidr1b := HTTPPost(t, allocURL(port, testCIDR1, container2))
	require.False(t, cidr1b == testAddr1, "address")
	cidr2b := HTTPPost(t, allocURL(port, testCIDR2, container2))
	require.False(t, cidr2b == testAddr2, "address")

	// Now free the first container, and we should get its addresses back when we ask
	doHTTP("DELETE", identURL(port, containerID))

	cidr1c := HTTPPost(t, allocURL(port, testCIDR1, container3))
	require.Equal(t, testAddr1, cidr1c, "address")
	cidr2c := HTTPPost(t, allocURL(port, testCIDR2, container3))
	require.Equal(t, testAddr2, cidr2c, "address")

	// Would like to shut down the http server at the end of this test
	// but it's complicated.
	// See https://groups.google.com/forum/#!topic/golang-nuts/vLHWa5sHnCE
}
Beispiel #7
0
// HandleHTTP wires up ipams HTTP endpoints to the provided mux.
func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CIDR, dockerCli *docker.Client) {
	router.Methods("PUT").Path("/ip/{id}/{ip}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		closedChan := w.(http.CloseNotifier).CloseNotify()
		vars := mux.Vars(r)
		ident := vars["id"]
		ipStr := vars["ip"]
		if ip, err := address.ParseIP(ipStr); err != nil {
			badRequest(w, err)
			return
		} else if err := alloc.Claim(ident, ip, closedChan); err != nil {
			badRequest(w, fmt.Errorf("Unable to claim: %s", err))
			return
		}

		w.WriteHeader(204)
	})

	router.Methods("GET").Path("/ip/{id}/{ip}/{prefixlen}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		cidr := vars["ip"] + "/" + vars["prefixlen"]
		_, subnet, err := address.ParseCIDR(cidr)
		if err != nil {
			badRequest(w, err)
			return
		}
		addr, err := alloc.Lookup(vars["id"], subnet.HostRange())
		if err != nil {
			http.NotFound(w, r)
			return
		}
		fmt.Fprintf(w, "%s/%d", addr, subnet.PrefixLen)
	})

	router.Methods("GET").Path("/ip/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		addr, err := alloc.Lookup(mux.Vars(r)["id"], defaultSubnet.HostRange())
		if err != nil {
			http.NotFound(w, r)
			return
		}
		fmt.Fprintf(w, "%s/%d", addr, defaultSubnet.PrefixLen)
	})

	router.Methods("POST").Path("/ip/{id}/{ip}/{prefixlen}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		closedChan := w.(http.CloseNotifier).CloseNotify()
		vars := mux.Vars(r)
		ident := vars["id"]
		cidrStr := vars["ip"] + "/" + vars["prefixlen"]
		subnetAddr, cidr, err := address.ParseCIDR(cidrStr)
		if err != nil {
			badRequest(w, err)
			return
		}
		if cidr.Start != subnetAddr {
			badRequest(w, fmt.Errorf("Invalid subnet %s - bits after network prefix are not all zero", cidrStr))
			return
		}
		addr, err := alloc.Allocate(ident, cidr.HostRange(), closedChan)
		if err != nil {
			badRequest(w, fmt.Errorf("Unable to allocate: %s", err))
			return
		}
		if dockerCli != nil && dockerCli.IsContainerNotRunning(ident) {
			common.Log.Infof("[allocator] '%s' is not running: freeing %s", ident, addr)
			alloc.Free(ident, addr)
			return
		}

		fmt.Fprintf(w, "%s/%d", addr, cidr.PrefixLen)
	})

	router.Methods("POST").Path("/ip/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		closedChan := w.(http.CloseNotifier).CloseNotify()
		ident := mux.Vars(r)["id"]
		newAddr, err := alloc.Allocate(ident, defaultSubnet.HostRange(), closedChan)
		if err != nil {
			badRequest(w, err)
			return
		}
		if dockerCli != nil && dockerCli.IsContainerNotRunning(ident) {
			common.Log.Infof("[allocator] '%s' is not running: freeing %s", ident, newAddr)
			alloc.Free(ident, newAddr)
			return
		}

		fmt.Fprintf(w, "%s/%d", newAddr, defaultSubnet.PrefixLen)
	})

	router.Methods("DELETE").Path("/ip/{id}/{ip}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		ident := vars["id"]
		ipStr := vars["ip"]
		if ip, err := address.ParseIP(ipStr); err != nil {
			badRequest(w, err)
			return
		} else if err := alloc.Free(ident, ip); err != nil {
			badRequest(w, fmt.Errorf("Unable to free: %s", err))
			return
		}

		w.WriteHeader(204)
	})

	router.Methods("DELETE").Path("/ip/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ident := mux.Vars(r)["id"]
		if err := alloc.Delete(ident); err != nil {
			badRequest(w, err)
			return
		}

		w.WriteHeader(204)
	})

	router.Methods("DELETE").Path("/peer").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		alloc.Shutdown()
		w.WriteHeader(204)
	})

	router.Methods("DELETE").Path("/peer/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ident := mux.Vars(r)["id"]
		if err := alloc.AdminTakeoverRanges(ident); err != nil {
			badRequest(w, err)
			return
		}

		w.WriteHeader(204)
	})
}