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(), returnFalse)
	require.NoError(t, err)
	require.Equal(t, testAddr1, addr1.String(), "address")

	addr2, err := alloc.Allocate(container1, cidr2.HostRange(), returnFalse)
	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(), returnFalse)
	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(), returnFalse)
	require.Equal(t, testAddr1, addr1a.String(), "address")
	addr2a, _ := alloc.Allocate(container1, cidr2.HostRange(), returnFalse)
	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(), returnFalse)
	require.Equal(t, testAddr1, addr3.String(), "address")
	addr4, _ := alloc.Allocate(container3, cidr2.HostRange(), returnFalse)
	require.Equal(t, testAddr2, addr4.String(), "address")

	alloc.ContainerDied(container2)

	// Resurrect
	addr1c, err := alloc.Allocate(container2, cidr1.HostRange(), returnFalse)
	require.NoError(t, err)
	require.Equal(t, addr1b, addr1c, "address")

	alloc.ContainerDied(container3)
	alloc.Encode() // sync up
	// Move the clock forward and clear out the dead container
	alloc.actionChan <- func() { alloc.now = func() time.Time { return time.Now().Add(containerDiedTimeout * 2) } }
	alloc.actionChan <- func() { alloc.removeDeadContainers() }
	require.Equal(t, address.Offset(spaceSize-1), alloc.NumFreeAddresses(subnet))
}
Beispiel #2
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 #3
0
func impTestHTTPCancel(t *testing.T) {
	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 #4
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 #5
0
func TestHTTPCancel(t *testing.T) {
	var (
		containerID = "deadbeef"
		testCIDR1   = "10.0.3.0/29"
	)

	// Say quorum=2, so the allocate won't go ahead
	alloc, _ := makeAllocatorWithMockGossip(t, "08:00:27:01:c3:9a", testCIDR1, 2)
	defer alloc.Stop()
	ExpectBroadcastMessage(alloc, nil) // trying to form consensus
	cidr, _ := address.ParseCIDR(testCIDR1)
	port := listenHTTP(alloc, cidr)

	// Ask the http server for a new address
	req, _ := http.NewRequest("POST", allocURL(port, testCIDR1, containerID), nil)
	// On another goroutine, wait for a bit then cancel the request
	go func() {
		time.Sleep(100 * time.Millisecond)
		common.Log.Debug("Cancelling allocate")
		http.DefaultTransport.(*http.Transport).CancelRequest(req)
	}()

	res, _ := http.DefaultClient.Do(req)
	if res != nil {
		body, _ := ioutil.ReadAll(res.Body)
		require.FailNow(t, "Error: Allocate returned non-nil", string(body))
	}
}
Beispiel #6
0
func parseAndCheckCIDR(cidrStr string) address.CIDR {
	_, cidr, err := address.ParseCIDR(cidrStr)
	checkFatal(err)

	if cidr.Size() < ipam.MinSubnetSize {
		Log.Fatalf("Allocation range smaller than minimum size %d: %s", ipam.MinSubnetSize, cidrStr)
	}
	return cidr
}
Beispiel #7
0
func TestAllocatorClaim(t *testing.T) {
	const (
		container1 = "abcdef"
		container3 = "b01df00d"
		universe   = "10.0.3.0/24"
		testAddr1  = "10.0.3.2/24"
		testAddr2  = "10.0.4.2/24"
	)

	allocs, router, subnet := makeNetworkOfAllocators(2, universe)
	defer stopNetworkOfAllocators(allocs, router)
	alloc := allocs[1]
	addr1, _ := address.ParseCIDR(testAddr1)

	// First claim should trigger "dunno, I'm going to wait"
	err := alloc.SimplyClaim(container3, addr1)
	require.NoError(t, err)

	alloc.Prime()
	// Do an allocate on the other peer, which we will try to claim later
	addrx, err := allocs[0].Allocate(container1, subnet, true, returnFalse)
	router.Flush()

	// Now try the claim again
	err = alloc.SimplyClaim(container3, addr1)
	require.NoError(t, err)
	// Check we get this address back if we try an allocate
	addr3, _ := alloc.SimplyAllocate(container3, subnet)
	require.Equal(t, testAddr1, address.MakeCIDR(subnet, addr3).String(), "address")
	// one more claim should still work
	err = alloc.SimplyClaim(container3, addr1)
	require.NoError(t, err)
	// claim for a different container should fail
	err = alloc.SimplyClaim(container1, addr1)
	require.Error(t, err)
	// claiming the address allocated on the other peer should fail
	err = alloc.SimplyClaim(container1, address.MakeCIDR(subnet, addrx))
	require.Error(t, err, "claiming address allocated on other peer should fail")
	// Check an address outside of our universe
	addr2, _ := address.ParseCIDR(testAddr2)
	err = alloc.SimplyClaim(container1, addr2)
	require.NoError(t, err)
}
Beispiel #8
0
func parseCIDR(w http.ResponseWriter, cidrStr string) (address.CIDR, bool) {
	subnetAddr, cidr, err := address.ParseCIDR(cidrStr)
	if err != nil {
		badRequest(w, err)
		return address.CIDR{}, false
	}
	if cidr.Start != subnetAddr {
		badRequest(w, fmt.Errorf("Invalid subnet %s - bits after network prefix are not all zero", cidrStr))
		return address.CIDR{}, false
	}
	return cidr, true
}
Beispiel #9
0
func ParseCIDRSubnet(cidrStr string) (cidr address.CIDR, err error) {
	cidr, err = address.ParseCIDR(cidrStr)
	if err != nil {
		return
	}
	if !cidr.IsSubnet() {
		err = fmt.Errorf("invalid subnet - bits after network prefix are not all zero: %s", cidrStr)
	}
	if cidr.Size() < MinSubnetSize {
		err = fmt.Errorf("invalid subnet - smaller than minimum size %d: %s", MinSubnetSize, cidrStr)
	}
	return
}
Beispiel #10
0
func parseCIDR(w http.ResponseWriter, cidrStr string, net bool) (address.CIDR, bool) {
	var cidr address.CIDR
	var err error
	if net {
		cidr, err = ParseCIDRSubnet(cidrStr)
	} else {
		cidr, err = address.ParseCIDR(cidrStr)
	}
	if err != nil {
		badRequest(w, err)
		return address.CIDR{}, false
	}
	return cidr, true
}
Beispiel #11
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, func(router.PeerName) bool { return true })

	return alloc, cidr.HostRange()
}
Beispiel #12
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 #13
0
func cidrRanges(s string) []address.Range {
	c, _ := address.ParseCIDR(s)
	return []address.Range{c.Range()}
}
Beispiel #14
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 r.FormValue("check-alive") == "true" && 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 r.FormValue("check-alive") == "true" && 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)
	})
}