Merge pull request #322 from xiangli-cmu/master

bump deps
This commit is contained in:
Xiang Li 2013-11-22 14:18:17 -08:00
commit b4f3d02c1c
145 changed files with 9286 additions and 8274 deletions

View File

@ -23,7 +23,7 @@ import (
"github.com/coreos/etcd/log"
"github.com/coreos/etcd/server"
"github.com/coreos/etcd/store"
"github.com/coreos/go-raft"
"github.com/coreos/raft"
)
func main() {

View File

@ -5,7 +5,7 @@ import (
etcdErr "github.com/coreos/etcd/error"
"github.com/coreos/etcd/log"
"github.com/coreos/go-raft"
"github.com/coreos/raft"
)
func init() {

View File

@ -16,7 +16,7 @@ import (
etcdErr "github.com/coreos/etcd/error"
"github.com/coreos/etcd/log"
"github.com/coreos/etcd/store"
"github.com/coreos/go-raft"
"github.com/coreos/raft"
"github.com/gorilla/mux"
)

View File

@ -8,7 +8,7 @@ import (
etcdErr "github.com/coreos/etcd/error"
"github.com/coreos/etcd/log"
"github.com/coreos/etcd/store"
"github.com/coreos/go-raft"
"github.com/coreos/raft"
"github.com/gorilla/mux"
)

View File

@ -3,7 +3,7 @@ package server
import (
"time"
"github.com/coreos/go-raft"
"github.com/coreos/raft"
)
type raftServerStats struct {

View File

@ -5,7 +5,7 @@ import (
"os"
"github.com/coreos/etcd/log"
"github.com/coreos/go-raft"
"github.com/coreos/raft"
)
func init() {

View File

@ -16,7 +16,7 @@ import (
"github.com/coreos/etcd/server/v2"
"github.com/coreos/etcd/store"
_ "github.com/coreos/etcd/store/v2"
"github.com/coreos/go-raft"
"github.com/coreos/raft"
"github.com/gorilla/mux"
)

View File

@ -4,7 +4,7 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
)
@ -27,7 +27,7 @@ func (info TLSInfo) Config() (TLSConfig, error) {
// Both the key and cert must be present.
if info.KeyFile == "" || info.CertFile == "" {
return t, errors.New("KeyFile and CertFile must both be present")
return t, fmt.Errorf("KeyFile and CertFile must both be present[key: %v, cert: %v]", info.KeyFile, info.CertFile)
}
tlsCert, err := tls.LoadX509KeyPair(info.CertFile, info.KeyFile)

View File

@ -10,7 +10,7 @@ import (
"time"
"github.com/coreos/etcd/log"
"github.com/coreos/go-raft"
"github.com/coreos/raft"
)
// Timeout for setup internal raft http connection

View File

@ -5,7 +5,7 @@ import (
etcdErr "github.com/coreos/etcd/error"
"github.com/coreos/etcd/store"
"github.com/coreos/go-raft"
"github.com/coreos/raft"
"github.com/gorilla/mux"
)

View File

@ -2,7 +2,7 @@ package v1
import (
"github.com/coreos/etcd/store"
"github.com/coreos/go-raft"
"github.com/coreos/raft"
"net/http"
)

View File

@ -9,7 +9,7 @@ import (
etcdErr "github.com/coreos/etcd/error"
"github.com/coreos/etcd/log"
"github.com/coreos/etcd/store"
"github.com/coreos/go-raft"
"github.com/coreos/raft"
"github.com/gorilla/mux"
)

View File

@ -7,7 +7,7 @@ import (
etcdErr "github.com/coreos/etcd/error"
"github.com/coreos/etcd/store"
"github.com/coreos/go-raft"
"github.com/coreos/raft"
"github.com/gorilla/mux"
)

View File

@ -2,7 +2,7 @@ package v2
import (
"github.com/coreos/etcd/store"
"github.com/coreos/go-raft"
"github.com/coreos/raft"
"net/http"
)

View File

@ -4,7 +4,7 @@ import (
"fmt"
"time"
"github.com/coreos/go-raft"
"github.com/coreos/raft"
)
// A lookup of factories by version.

View File

@ -4,7 +4,7 @@ import (
"time"
"github.com/coreos/etcd/store"
"github.com/coreos/go-raft"
"github.com/coreos/raft"
)
func init() {

View File

@ -5,7 +5,7 @@ import (
"github.com/coreos/etcd/log"
"github.com/coreos/etcd/store"
"github.com/coreos/go-raft"
"github.com/coreos/raft"
)
func init() {

View File

@ -5,7 +5,7 @@ import (
"github.com/coreos/etcd/log"
"github.com/coreos/etcd/store"
"github.com/coreos/go-raft"
"github.com/coreos/raft"
)
func init() {

View File

@ -3,7 +3,7 @@ package v2
import (
"github.com/coreos/etcd/log"
"github.com/coreos/etcd/store"
"github.com/coreos/go-raft"
"github.com/coreos/raft"
)
func init() {

View File

@ -5,7 +5,7 @@ import (
"github.com/coreos/etcd/log"
"github.com/coreos/etcd/store"
"github.com/coreos/go-raft"
"github.com/coreos/raft"
)
func init() {

View File

@ -4,7 +4,7 @@ import (
"time"
"github.com/coreos/etcd/store"
"github.com/coreos/go-raft"
"github.com/coreos/raft"
)
func init() {

View File

@ -3,7 +3,7 @@ package v2
import (
"github.com/coreos/etcd/log"
"github.com/coreos/etcd/store"
"github.com/coreos/go-raft"
"github.com/coreos/raft"
"time"
)

View File

@ -65,7 +65,7 @@ func TestMultiNodeKillAllAndRecovery(t *testing.T) {
t.Fatalf("Recovery error: %s", err)
}
if result.Index != 16 {
t.Fatalf("recovery failed! [%d/16]", result.Index)
if result.ModifiedIndex != 16 {
t.Fatalf("recovery failed! [%d/16]", result.ModifiedIndex)
}
}

View File

@ -35,13 +35,13 @@ func TestRemoveNode(t *testing.T) {
fmt.Println("send remove to node3 and wait for its exiting")
etcds[2].Wait()
resp, err := c.Get("_etcd/machines")
resp, err := c.Get("_etcd/machines", false)
if err != nil {
panic(err)
}
if len(resp) != 2 {
if len(resp.Kvs) != 2 {
t.Fatal("cannot remove peer")
}
@ -59,14 +59,14 @@ func TestRemoveNode(t *testing.T) {
time.Sleep(time.Second)
resp, err = c.Get("_etcd/machines")
resp, err = c.Get("_etcd/machines", false)
if err != nil {
panic(err)
}
if len(resp) != 3 {
t.Fatalf("add peer fails #1 (%d != 3)", len(resp))
if len(resp.Kvs) != 3 {
t.Fatalf("add peer fails #1 (%d != 3)", len(resp.Kvs))
}
}
@ -78,13 +78,13 @@ func TestRemoveNode(t *testing.T) {
client.Do(rmReq)
resp, err := c.Get("_etcd/machines")
resp, err := c.Get("_etcd/machines", false)
if err != nil {
panic(err)
}
if len(resp) != 2 {
if len(resp.Kvs) != 2 {
t.Fatal("cannot remove peer")
}
@ -102,14 +102,14 @@ func TestRemoveNode(t *testing.T) {
time.Sleep(time.Second)
resp, err = c.Get("_etcd/machines")
resp, err = c.Get("_etcd/machines", false)
if err != nil {
panic(err)
}
if len(resp) != 3 {
t.Fatalf("add peer fails #2 (%d != 3)", len(resp))
if len(resp.Kvs) != 3 {
t.Fatalf("add peer fails #2 (%d != 3)", len(resp.Kvs))
}
}
}

View File

@ -50,14 +50,12 @@ func TestSingleNodeRecovery(t *testing.T) {
time.Sleep(time.Second)
results, err := c.Get("foo")
result, err = c.Get("foo", false)
if err != nil {
t.Fatal("get fail: " + err.Error())
return
}
result = results[0]
if err != nil || result.Key != "/foo" || result.Value != "bar" || result.TTL > 99 {
if err != nil {
t.Fatal(err)

View File

@ -51,9 +51,9 @@ func TestSingleNode(t *testing.T) {
// Add a test-and-set test
// First, we'll test we can change the value if we get it write
result, match, err := c.TestAndSet("foo", "bar", "foobar", 100)
result, err = c.CompareAndSwap("foo", "foobar", 100, "bar", 0)
if err != nil || result.Key != "/foo" || result.Value != "foobar" || result.PrevValue != "bar" || result.TTL != 100 || !match {
if err != nil || result.Key != "/foo" || result.Value != "foobar" || result.PrevValue != "bar" || result.TTL != 100 {
if err != nil {
t.Fatal(err)
}
@ -61,16 +61,9 @@ func TestSingleNode(t *testing.T) {
}
// Next, we'll make sure we can't set it without the correct prior value
_, _, err = c.TestAndSet("foo", "bar", "foofoo", 100)
_, err = c.CompareAndSwap("foo", "foofoo", 100, "bar", 0)
if err == nil {
t.Fatalf("Set 4 expecting error when setting key with incorrect previous value")
}
// Finally, we'll make sure a blank previous value still counts as a test-and-set and still has to match
_, _, err = c.TestAndSet("foo", "", "barbar", 100)
if err == nil {
t.Fatalf("Set 5 expecting error when setting key with blank (incorrect) previous value")
}
}

View File

@ -80,7 +80,7 @@ func CreateCluster(size int, procAttr *os.ProcAttr, ssl bool) ([][]string, []*os
sslServer2 := []string{"-peer-ca-file=../../fixtures/ca/ca.crt",
"-peer-cert-file=../../fixtures/ca/server2.crt",
"-peer-cert-file=../../fixtures/ca/server2.key.insecure",
"-peer-key-file=../../fixtures/ca/server2.key.insecure",
}
for i := 0; i < size; i++ {

View File

@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin freebsd linux netbsd openbsd
package ipv4_test
import (
@ -75,6 +73,10 @@ func writeThenReadDatagram(t *testing.T, i int, c *ipv4.RawConn, wb []byte, src,
return b
}
func isUnicast(ip net.IP) bool {
return ip.To4() != nil && (ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsGlobalUnicast())
}
// LoopbackInterface returns a logical network interface for loopback
// tests.
func loopbackInterface() *net.Interface {
@ -83,8 +85,24 @@ func loopbackInterface() *net.Interface {
return nil
}
for _, ifi := range ift {
if ifi.Flags&net.FlagLoopback != 0 {
return &ifi
if ifi.Flags&net.FlagLoopback == 0 || ifi.Flags&net.FlagUp == 0 {
continue
}
ifat, err := ifi.Addrs()
if err != nil {
continue
}
for _, ifa := range ifat {
switch ifa := ifa.(type) {
case *net.IPAddr:
if isUnicast(ifa.IP) {
return &ifi
}
case *net.IPNet:
if isUnicast(ifa.IP) {
return &ifi
}
}
}
}
return nil
@ -94,31 +112,24 @@ func loopbackInterface() *net.Interface {
// enabled network interface. It also returns a unicast IPv4 address
// that can be used for listening on ifi.
func isMulticastAvailable(ifi *net.Interface) (net.IP, bool) {
if ifi.Flags&net.FlagUp == 0 || ifi.Flags&net.FlagMulticast == 0 {
if ifi == nil || ifi.Flags&net.FlagUp == 0 || ifi.Flags&net.FlagMulticast == 0 {
return nil, false
}
ifat, err := ifi.Addrs()
if err != nil {
return nil, false
}
if len(ifat) == 0 {
return nil, false
}
var ip net.IP
for _, ifa := range ifat {
switch v := ifa.(type) {
switch ifa := ifa.(type) {
case *net.IPAddr:
ip = v.IP
if isUnicast(ifa.IP) {
return ifa.IP, true
}
case *net.IPNet:
ip = v.IP
default:
continue
if isUnicast(ifa.IP) {
return ifa.IP, true
}
}
if ip.To4() == nil {
ip = nil
continue
}
break
}
return ip, true
return nil, false
}

View File

@ -2,41 +2,41 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin freebsd linux netbsd openbsd
package ipv4_test
import (
"code.google.com/p/go.net/ipv4"
"net"
"os"
"runtime"
"testing"
)
var udpMultipleGroupListenerTests = []struct {
gaddr *net.UDPAddr
}{
{&net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}}, // see RFC 4727
{&net.UDPAddr{IP: net.IPv4(224, 0, 0, 250)}}, // see RFC 4727
{&net.UDPAddr{IP: net.IPv4(224, 0, 0, 254)}}, // see RFC 4727
var udpMultipleGroupListenerTests = []net.Addr{
&net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}, // see RFC 4727
&net.UDPAddr{IP: net.IPv4(224, 0, 0, 250)},
&net.UDPAddr{IP: net.IPv4(224, 0, 0, 254)},
}
func TestUDPSingleConnWithMultipleGroupListeners(t *testing.T) {
func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if testing.Short() || !*testExternal {
t.Skip("to avoid external network")
}
for _, tt := range udpMultipleGroupListenerTests {
// listen to a wildcard address with no reusable port
c, err := net.ListenPacket("udp4", "0.0.0.0:0")
for _, gaddr := range udpMultipleGroupListenerTests {
c, err := net.ListenPacket("udp4", "0.0.0.0:0") // wildcard address with no reusable port
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c.Close()
p := ipv4.NewPacketConn(c)
var mift []*net.Interface
ift, err := net.Interfaces()
if err != nil {
t.Fatalf("net.Interfaces failed: %v", err)
@ -45,34 +45,36 @@ func TestUDPSingleConnWithMultipleGroupListeners(t *testing.T) {
if _, ok := isMulticastAvailable(&ifi); !ok {
continue
}
if err := p.JoinGroup(&ifi, tt.gaddr); err != nil {
t.Fatalf("ipv4.PacketConn.JoinGroup %v on %v failed: %v", tt.gaddr, ifi, err)
if err := p.JoinGroup(&ifi, gaddr); err != nil {
t.Fatalf("ipv4.PacketConn.JoinGroup %v on %v failed: %v", gaddr, ifi, err)
}
mift = append(mift, &ift[i])
}
for _, ifi := range mift {
if err := p.LeaveGroup(ifi, tt.gaddr); err != nil {
t.Fatalf("ipv4.PacketConn.LeaveGroup %v on %v failed: %v", tt.gaddr, ifi, err)
if err := p.LeaveGroup(ifi, gaddr); err != nil {
t.Fatalf("ipv4.PacketConn.LeaveGroup %v on %v failed: %v", gaddr, ifi, err)
}
}
}
}
func TestUDPMultipleConnWithMultipleGroupListeners(t *testing.T) {
func TestUDPMultiplePacketConnWithMultipleGroupListeners(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if testing.Short() || !*testExternal {
t.Skip("to avoid external network")
}
for _, tt := range udpMultipleGroupListenerTests {
// listen to a group address, actually a wildcard address
// with reusable port
c1, err := net.ListenPacket("udp4", "224.0.0.0:1024") // see RFC 4727
for _, gaddr := range udpMultipleGroupListenerTests {
c1, err := net.ListenPacket("udp4", "224.0.0.0:1024") // wildcard address with reusable port
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c1.Close()
c2, err := net.ListenPacket("udp4", "224.0.0.0:1024") // see RFC 4727
c2, err := net.ListenPacket("udp4", "224.0.0.0:1024") // wildcard address with reusable port
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
@ -81,8 +83,8 @@ func TestUDPMultipleConnWithMultipleGroupListeners(t *testing.T) {
var ps [2]*ipv4.PacketConn
ps[0] = ipv4.NewPacketConn(c1)
ps[1] = ipv4.NewPacketConn(c2)
var mift []*net.Interface
ift, err := net.Interfaces()
if err != nil {
t.Fatalf("net.Interfaces failed: %v", err)
@ -92,70 +94,32 @@ func TestUDPMultipleConnWithMultipleGroupListeners(t *testing.T) {
continue
}
for _, p := range ps {
if err := p.JoinGroup(&ifi, tt.gaddr); err != nil {
t.Fatalf("ipv4.PacketConn.JoinGroup %v on %v failed: %v", tt.gaddr, ifi, err)
if err := p.JoinGroup(&ifi, gaddr); err != nil {
t.Fatalf("ipv4.PacketConn.JoinGroup %v on %v failed: %v", gaddr, ifi, err)
}
}
mift = append(mift, &ift[i])
}
for _, ifi := range mift {
for _, p := range ps {
if err := p.LeaveGroup(ifi, tt.gaddr); err != nil {
t.Fatalf("ipv4.PacketConn.LeaveGroup %v on %v failed: %v", tt.gaddr, ifi, err)
if err := p.LeaveGroup(ifi, gaddr); err != nil {
t.Fatalf("ipv4.PacketConn.LeaveGroup %v on %v failed: %v", gaddr, ifi, err)
}
}
}
}
}
func TestIPSingleConnWithSingleGroupListener(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("to avoid external network")
func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if os.Getuid() != 0 {
t.Skip("must be root")
}
// listen to a wildcard address
c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c.Close()
r, err := ipv4.NewRawConn(c)
if err != nil {
t.Fatalf("ipv4.RawConn failed: %v", err)
}
gaddr := &net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
var mift []*net.Interface
ift, err := net.Interfaces()
if err != nil {
t.Fatalf("net.Interfaces failed: %v", err)
}
for i, ifi := range ift {
if _, ok := isMulticastAvailable(&ifi); !ok {
continue
}
if err := r.JoinGroup(&ifi, gaddr); err != nil {
t.Fatalf("ipv4.RawConn.JoinGroup on %v failed: %v", ifi, err)
}
mift = append(mift, &ift[i])
}
for _, ifi := range mift {
if err := r.LeaveGroup(ifi, gaddr); err != nil {
t.Fatalf("ipv4.RawConn.LeaveGroup on %v failed: %v", ifi, err)
}
}
}
func TestUDPPerInterfaceSingleConnWithSingleGroupListener(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("to avoid external network")
}
gaddr := &net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
gaddr := net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
type ml struct {
c *ipv4.PacketConn
ifi *net.Interface
@ -171,26 +135,29 @@ func TestUDPPerInterfaceSingleConnWithSingleGroupListener(t *testing.T) {
if !ok {
continue
}
// listen to a unicast interface address
c, err := net.ListenPacket("udp4", ip.String()+":"+"1024") // see RFC 4727
c, err := net.ListenPacket("udp4", ip.String()+":"+"1024") // unicast address with non-reusable port
if err != nil {
t.Fatalf("net.ListenPacket with %v failed: %v", ip, err)
}
defer c.Close()
p := ipv4.NewPacketConn(c)
if err := p.JoinGroup(&ifi, gaddr); err != nil {
if err := p.JoinGroup(&ifi, &gaddr); err != nil {
t.Fatalf("ipv4.PacketConn.JoinGroup on %v failed: %v", ifi, err)
}
mlt = append(mlt, &ml{p, &ift[i]})
}
for _, m := range mlt {
if err := m.c.LeaveGroup(m.ifi, gaddr); err != nil {
if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil {
t.Fatalf("ipv4.PacketConn.LeaveGroup on %v failed: %v", m.ifi, err)
}
}
}
func TestIPPerInterfaceSingleConnWithSingleGroupListener(t *testing.T) {
func TestIPSingleRawConnWithSingleGroupListener(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if testing.Short() || !*testExternal {
t.Skip("to avoid external network")
}
@ -198,7 +165,52 @@ func TestIPPerInterfaceSingleConnWithSingleGroupListener(t *testing.T) {
t.Skip("must be root")
}
gaddr := &net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") // wildcard address
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c.Close()
r, err := ipv4.NewRawConn(c)
if err != nil {
t.Fatalf("ipv4.RawConn failed: %v", err)
}
gaddr := net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
var mift []*net.Interface
ift, err := net.Interfaces()
if err != nil {
t.Fatalf("net.Interfaces failed: %v", err)
}
for i, ifi := range ift {
if _, ok := isMulticastAvailable(&ifi); !ok {
continue
}
if err := r.JoinGroup(&ifi, &gaddr); err != nil {
t.Fatalf("ipv4.RawConn.JoinGroup on %v failed: %v", ifi, err)
}
mift = append(mift, &ift[i])
}
for _, ifi := range mift {
if err := r.LeaveGroup(ifi, &gaddr); err != nil {
t.Fatalf("ipv4.RawConn.LeaveGroup on %v failed: %v", ifi, err)
}
}
}
func TestIPPerInterfaceSingleRawConnWithSingleGroupListener(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if testing.Short() || !*testExternal {
t.Skip("to avoid external network")
}
if os.Getuid() != 0 {
t.Skip("must be root")
}
gaddr := net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
type ml struct {
c *ipv4.RawConn
ifi *net.Interface
@ -214,8 +226,7 @@ func TestIPPerInterfaceSingleConnWithSingleGroupListener(t *testing.T) {
if !ok {
continue
}
// listen to a unicast interface address
c, err := net.ListenPacket("ip4:253", ip.String()) // see RFC 4727
c, err := net.ListenPacket("ip4:253", ip.String()) // unicast address
if err != nil {
t.Fatalf("net.ListenPacket with %v failed: %v", ip, err)
}
@ -224,13 +235,13 @@ func TestIPPerInterfaceSingleConnWithSingleGroupListener(t *testing.T) {
if err != nil {
t.Fatalf("ipv4.NewRawConn failed: %v", err)
}
if err := r.JoinGroup(&ifi, gaddr); err != nil {
if err := r.JoinGroup(&ifi, &gaddr); err != nil {
t.Fatalf("ipv4.RawConn.JoinGroup on %v failed: %v", ifi, err)
}
mlt = append(mlt, &ml{r, &ift[i]})
}
for _, m := range mlt {
if err := m.c.LeaveGroup(m.ifi, gaddr); err != nil {
if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil {
t.Fatalf("ipv4.RawConn.LeaveGroup on %v failed: %v", m.ifi, err)
}
}

View File

@ -72,7 +72,7 @@ func slicePacket(b []byte) (h, p []byte, err error) {
// Checksum = platform sets an appropriate value if Checksum is zero
// Src = platform sets an appropriate value if Src is nil
// Dst = <must be specified>
// h.Options = optional
// Options = optional
func (c *packetHandler) WriteTo(h *Header, p []byte, cm *ControlMessage) error {
if !c.ok() {
return syscall.EINVAL

View File

@ -21,26 +21,27 @@ func (typ ICMPType) String() string {
// packets.
type ICMPFilter struct {
mu sync.RWMutex
rawICMPFilter
sysICMPFilter
}
// Set sets the ICMP type and filter action to the filter.
func (f *ICMPFilter) Set(typ ICMPType, block bool) {
f.mu.Lock()
defer f.mu.Unlock()
f.set(typ, block)
f.mu.Unlock()
}
// SetAll sets the filter action to the filter.
func (f *ICMPFilter) SetAll(block bool) {
f.mu.Lock()
defer f.mu.Unlock()
f.setAll(block)
f.mu.Unlock()
}
// WillBlock reports whether the ICMP type will be blocked.
func (f *ICMPFilter) WillBlock(typ ICMPType) bool {
f.mu.RLock()
defer f.mu.RUnlock()
return f.willBlock(typ)
ok := f.willBlock(typ)
f.mu.RUnlock()
return ok
}

View File

@ -6,13 +6,11 @@
package ipv6
import "syscall"
type rawICMPFilter struct {
syscall.ICMPv6Filter
type sysICMPFilter struct {
Filt [8]uint32
}
func (f *rawICMPFilter) set(typ ICMPType, block bool) {
func (f *sysICMPFilter) set(typ ICMPType, block bool) {
if block {
f.Filt[typ>>5] &^= 1 << (uint32(typ) & 31)
} else {
@ -20,7 +18,7 @@ func (f *rawICMPFilter) set(typ ICMPType, block bool) {
}
}
func (f *rawICMPFilter) setAll(block bool) {
func (f *sysICMPFilter) setAll(block bool) {
for i := range f.Filt {
if block {
f.Filt[i] = 0
@ -30,6 +28,6 @@ func (f *rawICMPFilter) setAll(block bool) {
}
}
func (f *rawICMPFilter) willBlock(typ ICMPType) bool {
func (f *sysICMPFilter) willBlock(typ ICMPType) bool {
return f.Filt[typ>>5]&(1<<(uint32(typ)&31)) == 0
}

View File

@ -4,13 +4,11 @@
package ipv6
import "syscall"
type rawICMPFilter struct {
syscall.ICMPv6Filter
type sysICMPFilter struct {
Data [8]uint32
}
func (f *rawICMPFilter) set(typ ICMPType, block bool) {
func (f *sysICMPFilter) set(typ ICMPType, block bool) {
if block {
f.Data[typ>>5] |= 1 << (uint32(typ) & 31)
} else {
@ -18,7 +16,7 @@ func (f *rawICMPFilter) set(typ ICMPType, block bool) {
}
}
func (f *rawICMPFilter) setAll(block bool) {
func (f *sysICMPFilter) setAll(block bool) {
for i := range f.Data {
if block {
f.Data[i] = 1<<32 - 1
@ -28,6 +26,6 @@ func (f *rawICMPFilter) setAll(block bool) {
}
}
func (f *rawICMPFilter) willBlock(typ ICMPType) bool {
func (f *sysICMPFilter) willBlock(typ ICMPType) bool {
return f.Data[typ>>5]&(1<<(uint32(typ)&31)) != 0
}

View File

@ -4,19 +4,19 @@
package ipv6
type rawICMPFilter struct {
type sysICMPFilter struct {
// TODO(mikio): Implement this
}
func (f *rawICMPFilter) set(typ ICMPType, block bool) {
func (f *sysICMPFilter) set(typ ICMPType, block bool) {
// TODO(mikio): Implement this
}
func (f *rawICMPFilter) setAll(block bool) {
func (f *sysICMPFilter) setAll(block bool) {
// TODO(mikio): Implement this
}
func (f *rawICMPFilter) willBlock(typ ICMPType) bool {
func (f *sysICMPFilter) willBlock(typ ICMPType) bool {
// TODO(mikio): Implement this
return false
}

View File

@ -4,19 +4,19 @@
package ipv6
type rawICMPFilter struct {
type sysICMPFilter struct {
// TODO(mikio): Implement this
}
func (f *rawICMPFilter) set(typ ICMPType, block bool) {
func (f *sysICMPFilter) set(typ ICMPType, block bool) {
// TODO(mikio): Implement this
}
func (f *rawICMPFilter) setAll(block bool) {
func (f *sysICMPFilter) setAll(block bool) {
// TODO(mikio): Implement this
}
func (f *rawICMPFilter) willBlock(typ ICMPType) bool {
func (f *sysICMPFilter) willBlock(typ ICMPType) bool {
// TODO(mikio): Implement this
return false
}

View File

@ -7,8 +7,22 @@ package ipv6_test
import (
"code.google.com/p/go.net/ipv6"
"errors"
"net"
)
const (
ipv6PseudoHeaderLen = 2*net.IPv6len + 8
ianaProtocolIPv6ICMP = 58
)
func ipv6PseudoHeader(src, dst net.IP, nextHeader int) []byte {
b := make([]byte, ipv6PseudoHeaderLen)
copy(b[:net.IPv6len], src)
copy(b[net.IPv6len:], dst)
b[len(b)-1] = byte(nextHeader)
return b
}
// icmpMessage represents an ICMP message.
type icmpMessage struct {
Type ipv6.ICMPType // type
@ -25,8 +39,11 @@ type icmpMessageBody interface {
// Marshal returns the binary enconding of the ICMP echo request or
// reply message m.
func (m *icmpMessage) Marshal() ([]byte, error) {
func (m *icmpMessage) Marshal(psh []byte) ([]byte, error) {
b := []byte{byte(m.Type), byte(m.Code), 0, 0}
if psh != nil {
b = append(psh, b...)
}
if m.Body != nil && m.Body.Len() != 0 {
mb, err := m.Body.Marshal()
if err != nil {
@ -34,10 +51,11 @@ func (m *icmpMessage) Marshal() ([]byte, error) {
}
b = append(b, mb...)
}
switch m.Type {
case ipv6.ICMPTypeEchoRequest, ipv6.ICMPTypeEchoReply:
if psh == nil {
return b, nil
}
off, l := 2*net.IPv6len, len(b)-len(psh)
b[off], b[off+1], b[off+2], b[off+3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
csumcv := len(b) - 1 // checksum coverage
s := uint32(0)
for i := 0; i < csumcv; i += 2 {
@ -50,9 +68,9 @@ func (m *icmpMessage) Marshal() ([]byte, error) {
s = s + s>>16
// Place checksum back in header; using ^= avoids the
// assumption the checksum bytes are zero.
b[2] ^= byte(^s)
b[3] ^= byte(^s >> 8)
return b, nil
b[len(psh)+2] ^= byte(^s)
b[len(psh)+3] ^= byte(^s >> 8)
return b[len(psh):], nil
}
// parseICMPMessage parses b as an ICMP message.

View File

@ -10,6 +10,7 @@ import (
"os"
"runtime"
"testing"
"time"
)
func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
@ -44,15 +45,22 @@ func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
}
p := ipv6.NewPacketConn(c)
defer p.Close()
if err := p.JoinGroup(ifi, dst); err != nil {
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
}
if err := p.SetMulticastInterface(ifi); err != nil {
t.Fatalf("ipv6.PacketConn.SetMulticastInterface failed: %v", err)
}
if _, err := p.MulticastInterface(); err != nil {
t.Fatalf("ipv6.PacketConn.MulticastInterface failed: %v", err)
}
if err := p.SetMulticastLoopback(true); err != nil {
t.Fatalf("ipv6.PacketConn.SetMulticastLoopback failed: %v", err)
}
if _, err := p.MulticastLoopback(); err != nil {
t.Fatalf("ipv6.PacketConn.MulticastLoopback failed: %v", err)
}
cm := ipv6.ControlMessage{
TrafficClass: DiffServAF11 | CongestionExperienced,
@ -64,6 +72,9 @@ func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
if err := p.SetControlMessage(cf, toggle); err != nil {
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
}
if err := p.SetDeadline(time.Now().Add(time.Millisecond * 200)); err != nil {
t.Fatalf("ipv6.PacketConn.SetDeadline failed: %v", err)
}
cm.HopLimit = i + 1
if _, err := p.WriteTo([]byte("HELLO-R-U-THERE"), &cm, dst); err != nil {
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
@ -104,16 +115,24 @@ func TestPacketConnReadWriteMulticastICMP(t *testing.T) {
t.Fatalf("net.ResolveIPAddr failed: %v", err)
}
pshicmp := ipv6PseudoHeader(c.LocalAddr().(*net.IPAddr).IP, dst.IP, ianaProtocolIPv6ICMP)
p := ipv6.NewPacketConn(c)
defer p.Close()
if err := p.JoinGroup(ifi, dst); err != nil {
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
}
if err := p.SetMulticastInterface(ifi); err != nil {
t.Fatalf("ipv6.PacketConn.SetMulticastInterface failed: %v", err)
}
if _, err := p.MulticastInterface(); err != nil {
t.Fatalf("ipv6.PacketConn.MulticastInterface failed: %v", err)
}
if err := p.SetMulticastLoopback(true); err != nil {
t.Fatalf("ipv6.PacketConn.SetMulticastLoopback failed: %v", err)
}
if _, err := p.MulticastLoopback(); err != nil {
t.Fatalf("ipv6.PacketConn.MulticastLoopback failed: %v", err)
}
cm := ipv6.ControlMessage{
TrafficClass: DiffServAF11 | CongestionExperienced,
@ -128,20 +147,35 @@ func TestPacketConnReadWriteMulticastICMP(t *testing.T) {
t.Fatalf("ipv6.PacketConn.SetICMPFilter failed: %v", err)
}
var psh []byte
for i, toggle := range []bool{true, false, true} {
if toggle {
psh = nil
if err := p.SetChecksum(true, 2); err != nil {
t.Fatalf("ipv6.PacketConn.SetChecksum failed: %v", err)
}
} else {
psh = pshicmp
// Some platforms never allow to disable the
// kernel checksum processing.
p.SetChecksum(false, -1)
}
wb, err := (&icmpMessage{
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
Body: &icmpEcho{
ID: os.Getpid() & 0xffff, Seq: i + 1,
Data: []byte("HELLO-R-U-THERE"),
},
}).Marshal()
}).Marshal(psh)
if err != nil {
t.Fatalf("icmpMessage.Marshal failed: %v", err)
}
if err := p.SetControlMessage(cf, toggle); err != nil {
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
}
if err := p.SetDeadline(time.Now().Add(time.Millisecond * 200)); err != nil {
t.Fatalf("ipv6.PacketConn.SetDeadline failed: %v", err)
}
cm.HopLimit = i + 1
if _, err := p.WriteTo(wb, &cm, dst); err != nil {
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)

View File

@ -59,7 +59,7 @@ func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) {
}
}
func TestUDPMultipleConnWithMultipleGroupListeners(t *testing.T) {
func TestUDPMultiplePacketConnWithMultipleGroupListeners(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
@ -120,7 +120,7 @@ func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
t.Skip("ipv6 is not supported")
}
gaddr := &net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
gaddr := net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
type ml struct {
c *ipv6.PacketConn
ifi *net.Interface
@ -142,13 +142,13 @@ func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
}
defer c.Close()
p := ipv6.NewPacketConn(c)
if err := p.JoinGroup(&ifi, gaddr); err != nil {
if err := p.JoinGroup(&ifi, &gaddr); err != nil {
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
}
mlt = append(mlt, &ml{p, &ift[i]})
}
for _, m := range mlt {
if err := m.c.LeaveGroup(m.ifi, gaddr); err != nil {
if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil {
t.Fatalf("ipv6.PacketConn.LeaveGroup on %v failed: %v", m.ifi, err)
}
}
@ -166,14 +166,14 @@ func TestIPSinglePacketConnWithSingleGroupListener(t *testing.T) {
t.Skip("must be root")
}
c, err := net.ListenPacket("ip6:ipv6-icmp", "::")
c, err := net.ListenPacket("ip6:ipv6-icmp", "::") // wildcard address
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c.Close()
p := ipv6.NewPacketConn(c)
gaddr := &net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
gaddr := net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
var mift []*net.Interface
ift, err := net.Interfaces()
@ -184,14 +184,60 @@ func TestIPSinglePacketConnWithSingleGroupListener(t *testing.T) {
if _, ok := isMulticastAvailable(&ifi); !ok {
continue
}
if err := p.JoinGroup(&ifi, gaddr); err != nil {
if err := p.JoinGroup(&ifi, &gaddr); err != nil {
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
}
mift = append(mift, &ift[i])
}
for _, ifi := range mift {
if err := p.LeaveGroup(ifi, gaddr); err != nil {
if err := p.LeaveGroup(ifi, &gaddr); err != nil {
t.Fatalf("ipv6.PacketConn.LeaveGroup on %v failed: %v", ifi, err)
}
}
}
func TestIPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
switch runtime.GOOS {
case "darwin", "plan9", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if !supportsIPv6 {
t.Skip("ipv6 is not supported")
}
if os.Getuid() != 0 {
t.Skip("must be root")
}
gaddr := net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
type ml struct {
c *ipv6.PacketConn
ifi *net.Interface
}
var mlt []*ml
ift, err := net.Interfaces()
if err != nil {
t.Fatalf("net.Interfaces failed: %v", err)
}
for i, ifi := range ift {
ip, ok := isMulticastAvailable(&ifi)
if !ok {
continue
}
c, err := net.ListenPacket("ip6:ipv6-icmp", fmt.Sprintf("%s%%%s", ip.String(), ifi.Name)) // unicast address
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c.Close()
p := ipv6.NewPacketConn(c)
if err := p.JoinGroup(&ifi, &gaddr); err != nil {
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
}
mlt = append(mlt, &ml{p, &ift[i]})
}
for _, m := range mlt {
if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil {
t.Fatalf("ipv6.PacketConn.LeaveGroup on %v failed: %v", m.ifi, err)
}
}
}

View File

@ -8,12 +8,13 @@ package ipv6
import (
"os"
"syscall"
"unsafe"
)
func setIPv6Checksum(fd int, on bool, offset int) error {
if !on {
offset = -1
}
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_CHECKSUM, offset))
v := int32(offset)
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptChecksum, uintptr(unsafe.Pointer(&v)), 4))
}

View File

@ -6,12 +6,13 @@ package ipv6
import (
"os"
"syscall"
"unsafe"
)
func setIPv6Checksum(fd int, on bool, offset int) error {
if !on {
offset = -1
}
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolReserved, syscall.IPV6_CHECKSUM, offset))
v := int32(offset)
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolReserved, sysSockoptChecksum, uintptr(unsafe.Pointer(&v)), 4))
}

View File

@ -9,66 +9,74 @@ package ipv6
import (
"net"
"os"
"syscall"
"unsafe"
)
func ipv6TrafficClass(fd int) (int, error) {
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_TCLASS)
if err != nil {
var v int32
l := sysSockoptLen(4)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptTrafficClass, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
return v, nil
return int(v), nil
}
func setIPv6TrafficClass(fd, v int) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_TCLASS, v))
vv := int32(v)
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptTrafficClass, uintptr(unsafe.Pointer(&vv)), 4))
}
func ipv6HopLimit(fd int) (int, error) {
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_UNICAST_HOPS)
if err != nil {
var v int32
l := sysSockoptLen(4)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
return v, nil
return int(v), nil
}
func setIPv6HopLimit(fd, v int) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_UNICAST_HOPS, v))
vv := int32(v)
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, uintptr(unsafe.Pointer(&vv)), 4))
}
func ipv6Checksum(fd int) (bool, int, error) {
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_CHECKSUM)
if err != nil {
var v int32
l := sysSockoptLen(4)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptChecksum, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return false, 0, os.NewSyscallError("getsockopt", err)
}
on := true
if v == -1 {
on = false
}
return on, v, nil
return on, int(v), nil
}
func ipv6MulticastHopLimit(fd int) (int, error) {
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_HOPS)
if err != nil {
var v int32
l := sysSockoptLen(4)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
return v, nil
return int(v), nil
}
func setIPv6MulticastHopLimit(fd, v int) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_HOPS, v))
vv := int32(v)
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, uintptr(unsafe.Pointer(&vv)), 4))
}
func ipv6MulticastInterface(fd int) (*net.Interface, error) {
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_IF)
if err != nil {
var v int32
l := sysSockoptLen(4)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastInterface, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return nil, os.NewSyscallError("getsockopt", err)
}
if v == 0 {
return nil, nil
}
ifi, err := net.InterfaceByIndex(v)
ifi, err := net.InterfaceByIndex(int(v))
if err != nil {
return nil, err
}
@ -76,39 +84,41 @@ func ipv6MulticastInterface(fd int) (*net.Interface, error) {
}
func setIPv6MulticastInterface(fd int, ifi *net.Interface) error {
var v int
var v int32
if ifi != nil {
v = ifi.Index
v = int32(ifi.Index)
}
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_IF, v))
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastInterface, uintptr(unsafe.Pointer(&v)), 4))
}
func ipv6MulticastLoopback(fd int) (bool, error) {
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_LOOP)
if err != nil {
var v int32
l := sysSockoptLen(4)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastLoopback, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv6MulticastLoopback(fd int, v bool) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_LOOP, boolint(v)))
vv := int32(boolint(v))
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastLoopback, uintptr(unsafe.Pointer(&vv)), 4))
}
func joinIPv6Group(fd int, ifi *net.Interface, grp net.IP) error {
mreq := syscall.IPv6Mreq{}
copy(mreq.Multiaddr[:], grp)
mreq := sysMulticastReq{}
copy(mreq.IP[:], grp)
if ifi != nil {
mreq.Interface = uint32(ifi.Index)
mreq.IfIndex = uint32(ifi.Index)
}
return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd, ianaProtocolIPv6, syscall.IPV6_JOIN_GROUP, &mreq))
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptJoinGroup, uintptr(unsafe.Pointer(&mreq)), sysSizeofMulticastReq))
}
func leaveIPv6Group(fd int, ifi *net.Interface, grp net.IP) error {
mreq := syscall.IPv6Mreq{}
copy(mreq.Multiaddr[:], grp)
mreq := sysMulticastReq{}
copy(mreq.IP[:], grp)
if ifi != nil {
mreq.Interface = uint32(ifi.Index)
mreq.IfIndex = uint32(ifi.Index)
}
return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd, ianaProtocolIPv6, syscall.IPV6_LEAVE_GROUP, &mreq))
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptLeaveGroup, uintptr(unsafe.Pointer(&mreq)), sysSizeofMulticastReq))
}

View File

@ -24,7 +24,7 @@ func setIPv6TrafficClass(fd syscall.Handle, v int) error {
func ipv6HopLimit(fd syscall.Handle) (int, error) {
var v int32
l := int32(4)
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, syscall.IPV6_UNICAST_HOPS, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
return int(v), nil
@ -32,7 +32,7 @@ func ipv6HopLimit(fd syscall.Handle) (int, error) {
func setIPv6HopLimit(fd syscall.Handle, v int) error {
vv := int32(v)
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_UNICAST_HOPS, (*byte)(unsafe.Pointer(&vv)), 4))
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, (*byte)(unsafe.Pointer(&vv)), 4))
}
func ipv6Checksum(fd syscall.Handle) (bool, int, error) {
@ -43,7 +43,7 @@ func ipv6Checksum(fd syscall.Handle) (bool, int, error) {
func ipv6MulticastHopLimit(fd syscall.Handle) (int, error) {
var v int32
l := int32(4)
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_HOPS, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
return int(v), nil
@ -51,13 +51,13 @@ func ipv6MulticastHopLimit(fd syscall.Handle) (int, error) {
func setIPv6MulticastHopLimit(fd syscall.Handle, v int) error {
vv := int32(v)
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_HOPS, (*byte)(unsafe.Pointer(&vv)), 4))
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, (*byte)(unsafe.Pointer(&vv)), 4))
}
func ipv6MulticastInterface(fd syscall.Handle) (*net.Interface, error) {
var v int32
l := int32(4)
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_IF, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastInterface, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
return nil, os.NewSyscallError("getsockopt", err)
}
if v == 0 {
@ -75,13 +75,13 @@ func setIPv6MulticastInterface(fd syscall.Handle, ifi *net.Interface) error {
if ifi != nil {
v = int32(ifi.Index)
}
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_IF, (*byte)(unsafe.Pointer(&v)), 4))
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastInterface, (*byte)(unsafe.Pointer(&v)), 4))
}
func ipv6MulticastLoopback(fd syscall.Handle) (bool, error) {
var v int32
l := int32(4)
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_LOOP, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastLoopback, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
@ -89,25 +89,25 @@ func ipv6MulticastLoopback(fd syscall.Handle) (bool, error) {
func setIPv6MulticastLoopback(fd syscall.Handle, v bool) error {
vv := int32(boolint(v))
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_LOOP, (*byte)(unsafe.Pointer(&vv)), 4))
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastLoopback, (*byte)(unsafe.Pointer(&vv)), 4))
}
func joinIPv6Group(fd syscall.Handle, ifi *net.Interface, grp net.IP) error {
mreq := syscall.IPv6Mreq{}
copy(mreq.Multiaddr[:], grp)
mreq := sysMulticastReq{}
copy(mreq.IP[:], grp)
if ifi != nil {
mreq.Interface = uint32(ifi.Index)
mreq.IfIndex = uint32(ifi.Index)
}
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_JOIN_GROUP, (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq))))
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptJoinGroup, (*byte)(unsafe.Pointer(&mreq)), int32(sysSizeofMulticastReq)))
}
func leaveIPv6Group(fd syscall.Handle, ifi *net.Interface, grp net.IP) error {
mreq := syscall.IPv6Mreq{}
copy(mreq.Multiaddr[:], grp)
mreq := sysMulticastReq{}
copy(mreq.IP[:], grp)
if ifi != nil {
mreq.Interface = uint32(ifi.Index)
mreq.IfIndex = uint32(ifi.Index)
}
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_LEAVE_GROUP, (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq))))
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptLeaveGroup, (*byte)(unsafe.Pointer(&mreq)), int32(sysSizeofMulticastReq)))
}
func setIPv6Checksum(fd syscall.Handle, on bool, offset int) error {

View File

@ -8,41 +8,83 @@ package ipv6
import (
"os"
"syscall"
"unsafe"
)
func ipv6ReceiveTrafficClass(fd int) (bool, error) {
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVTCLASS)
if err != nil {
var v int32
l := sysSockoptLen(4)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveTrafficClass, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv6ReceiveTrafficClass(fd int, v bool) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVTCLASS, boolint(v)))
vv := int32(boolint(v))
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveTrafficClass, uintptr(unsafe.Pointer(&vv)), 4))
}
func ipv6ReceiveHopLimit(fd int) (bool, error) {
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVHOPLIMIT)
if err != nil {
var v int32
l := sysSockoptLen(4)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveHopLimit, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv6ReceiveHopLimit(fd int, v bool) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVHOPLIMIT, boolint(v)))
vv := int32(boolint(v))
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveHopLimit, uintptr(unsafe.Pointer(&vv)), 4))
}
func ipv6ReceivePacketInfo(fd int) (bool, error) {
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVPKTINFO)
if err != nil {
var v int32
l := sysSockoptLen(4)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceivePacketInfo, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv6ReceivePacketInfo(fd int, v bool) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVPKTINFO, boolint(v)))
vv := int32(boolint(v))
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptReceivePacketInfo, uintptr(unsafe.Pointer(&vv)), 4))
}
func ipv6PathMTU(fd int) (int, error) {
var v sysMTUInfo
l := sysSockoptLen(sysSizeofMTUInfo)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptPathMTU, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
return int(v.MTU), nil
}
func ipv6ReceivePathMTU(fd int) (bool, error) {
var v int32
l := sysSockoptLen(4)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceivePathMTU, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv6ReceivePathMTU(fd int, v bool) error {
vv := int32(boolint(v))
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptReceivePathMTU, uintptr(unsafe.Pointer(&vv)), 4))
}
func ipv6ICMPFilter(fd int) (*ICMPFilter, error) {
var v ICMPFilter
l := sysSockoptLen(sysSizeofICMPFilter)
if err := getsockopt(fd, ianaProtocolIPv6ICMP, sysSockoptICMPFilter, uintptr(unsafe.Pointer(&v.sysICMPFilter)), &l); err != nil {
return nil, os.NewSyscallError("getsockopt", err)
}
return &v, nil
}
func setIPv6ICMPFilter(fd int, f *ICMPFilter) error {
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6ICMP, sysSockoptICMPFilter, uintptr(unsafe.Pointer(&f.sysICMPFilter)), sysSizeofICMPFilter))
}

View File

@ -10,6 +10,7 @@ import (
"os"
"runtime"
"testing"
"time"
)
func benchmarkUDPListener() (net.PacketConn, net.Addr, error) {
@ -106,6 +107,7 @@ func TestPacketConnReadWriteUnicastUDP(t *testing.T) {
}
p := ipv6.NewPacketConn(c)
defer p.Close()
cm := ipv6.ControlMessage{
TrafficClass: DiffServAF11 | CongestionExperienced,
}
@ -120,10 +122,16 @@ func TestPacketConnReadWriteUnicastUDP(t *testing.T) {
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
}
cm.HopLimit = i + 1
if err := p.SetWriteDeadline(time.Now().Add(time.Millisecond * 100)); err != nil {
t.Fatalf("ipv6.PacketConn.SetWriteDeadline failed: %v", err)
}
if _, err := p.WriteTo([]byte("HELLO-R-U-THERE"), &cm, dst); err != nil {
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
}
b := make([]byte, 128)
if err := p.SetReadDeadline(time.Now().Add(time.Millisecond * 100)); err != nil {
t.Fatalf("ipv6.PacketConn.SetReadDeadline failed: %v", err)
}
if _, cm, _, err := p.ReadFrom(b); err != nil {
t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
} else {
@ -155,7 +163,9 @@ func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
t.Fatalf("net.ResolveIPAddr failed: %v", err)
}
pshicmp := ipv6PseudoHeader(c.LocalAddr().(*net.IPAddr).IP, dst.IP, ianaProtocolIPv6ICMP)
p := ipv6.NewPacketConn(c)
defer p.Close()
cm := ipv6.ControlMessage{TrafficClass: DiffServAF11 | CongestionExperienced}
cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU
ifi := loopbackInterface()
@ -170,14 +180,26 @@ func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
t.Fatalf("ipv6.PacketConn.SetICMPFilter failed: %v", err)
}
var psh []byte
for i, toggle := range []bool{true, false, true} {
if toggle {
psh = nil
if err := p.SetChecksum(true, 2); err != nil {
t.Fatalf("ipv6.PacketConn.SetChecksum failed: %v", err)
}
} else {
psh = pshicmp
// Some platforms never allow to disable the
// kernel checksum processing.
p.SetChecksum(false, -1)
}
wb, err := (&icmpMessage{
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
Body: &icmpEcho{
ID: os.Getpid() & 0xffff, Seq: i + 1,
Data: []byte("HELLO-R-U-THERE"),
},
}).Marshal()
}).Marshal(psh)
if err != nil {
t.Fatalf("icmpMessage.Marshal failed: %v", err)
}
@ -185,10 +207,16 @@ func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
}
cm.HopLimit = i + 1
if err := p.SetWriteDeadline(time.Now().Add(time.Millisecond * 100)); err != nil {
t.Fatalf("ipv6.PacketConn.SetWriteDeadline failed: %v", err)
}
if _, err := p.WriteTo(wb, &cm, dst); err != nil {
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
}
b := make([]byte, 128)
if err := p.SetReadDeadline(time.Now().Add(time.Millisecond * 100)); err != nil {
t.Fatalf("ipv6.PacketConn.SetReadDeadline failed: %v", err)
}
if n, cm, _, err := p.ReadFrom(b); err != nil {
t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
} else {

View File

@ -99,19 +99,18 @@ var publicSuffixTestCases = []struct {
{"www.xxx.yyy.zzz.pb.ao", "pb.ao"},
// The .ar rules are:
// *.ar
// !congresodelalengua3.ar
// !educ.ar
// !gobiernoelectronico.ar
// !mecon.ar
// !nacion.ar
// !nic.ar
// !promocion.ar
// !retina.ar
// !uba.ar
// ar
// com.ar
// edu.ar
// gob.ar
// int.ar
// mil.ar
// net.ar
// org.ar
// tur.ar
// blogspot.com.ar
{"ar", "ar"},
{"www.ar", "www.ar"},
{"www.ar", "ar"},
{"nic.ar", "ar"},
{"www.nic.ar", "ar"},
{"com.ar", "com.ar"},
@ -376,12 +375,12 @@ var eTLDPlusOneTestCases = []struct {
{"city.kobe.jp", "city.kobe.jp"},
{"www.city.kobe.jp", "city.kobe.jp"},
// TLD with a wildcard rule and exceptions.
{"om", ""},
{"test.om", ""},
{"b.test.om", "b.test.om"},
{"a.b.test.om", "b.test.om"},
{"songfest.om", "songfest.om"},
{"www.songfest.om", "songfest.om"},
{"ck", ""},
{"test.ck", ""},
{"b.test.ck", "b.test.ck"},
{"a.b.test.ck", "b.test.ck"},
{"www.ck", "www.ck"},
{"www.www.ck", "www.ck"},
// US K12.
{"us", ""},
{"test.us", "test.us"},

File diff suppressed because it is too large Load Diff

View File

@ -148,16 +148,15 @@ var rules = [...]string{
"pb.ao",
"it.ao",
"aq",
"*.ar",
"!congresodelalengua3.ar",
"!educ.ar",
"!gobiernoelectronico.ar",
"!mecon.ar",
"!nacion.ar",
"!nic.ar",
"!promocion.ar",
"!retina.ar",
"!uba.ar",
"ar",
"com.ar",
"edu.ar",
"gob.ar",
"int.ar",
"mil.ar",
"net.ar",
"org.ar",
"tur.ar",
"e164.arpa",
"in-addr.arpa",
"ip6.arpa",
@ -179,6 +178,7 @@ var rules = [...]string{
"gov.au",
"asn.au",
"id.au",
"csiro.au",
"info.au",
"conf.au",
"oz.au",
@ -3770,12 +3770,16 @@ var rules = [...]string{
"info.nf",
"other.nf",
"store.nf",
"ac.ng",
"ng",
"com.ng",
"edu.ng",
"gov.ng",
"name.ng",
"net.ng",
"org.ng",
"sch.ng",
"gov.ng",
"mil.ng",
"mobi.ng",
"*.ni",
"nl",
"bv.nl",
@ -4544,7 +4548,16 @@ var rules = [...]string{
"com.nr",
"nu",
"*.nz",
"*.om",
"om",
"co.om",
"com.om",
"edu.om",
"gov.om",
"med.om",
"museum.om",
"net.om",
"org.om",
"pro.om",
"!mediaphone.om",
"!nawrastelecom.om",
"!nawras.om",
@ -5143,7 +5156,12 @@ var rules = [...]string{
"saotome.st",
"store.st",
"su",
"*.sv",
"sv",
"com.sv",
"edu.sv",
"gob.sv",
"org.sv",
"red.sv",
"sx",
"gov.sx",
"sy",
@ -5709,7 +5727,79 @@ var rules = [...]string{
"*.za",
"*.zm",
"*.zw",
"xn--80asehdb",
"xn--80aswg",
"xn--ngbc5azd",
"xn--unup4y",
"xn--vhquv",
"camera",
"clothing",
"lighting",
"singles",
"ventures",
"voyage",
"guru",
"holdings",
"equipment",
"bike",
"estate",
"tattoo",
"xn--3ds443g",
"xn--fiq228c5hs",
"land",
"plumbing",
"contractors",
"sexy",
"menu",
"xn--rhqv96g",
"uno",
"gallery",
"technology",
"xn--3bst00m",
"reviews",
"guide",
"xn--6qq986b3xl",
"graphics",
"construction",
"onl",
"xn--q9jyb4c",
"diamonds",
"kiwi",
"enterprises",
"today",
"futbol",
"photography",
"tips",
"directory",
"kitchen",
"xn--6frz82g",
"kim",
"xn--cg4bki",
"monash",
"wed",
"pink",
"ruhr",
"buzz",
"careers",
"shoes",
"xn--4gbrim",
"career",
"otsuka",
"xn--fiq64b",
"cloudfront.net",
"compute.amazonaws.com",
"us-east-1.amazonaws.com",
"compute-1.amazonaws.com",
"z-1.compute-1.amazonaws.com",
"z-2.compute-1.amazonaws.com",
"ap-northeast-1.compute.amazonaws.com",
"ap-southeast-1.compute.amazonaws.com",
"ap-southeast-2.compute.amazonaws.com",
"eu-west-1.compute.amazonaws.com",
"sa-east-1.compute.amazonaws.com",
"us-gov-west-1.compute.amazonaws.com",
"us-west-1.compute.amazonaws.com",
"us-west-2.compute.amazonaws.com",
"elasticbeanstalk.com",
"elb.amazonaws.com",
"s3.amazonaws.com",
@ -5760,6 +5850,8 @@ var rules = [...]string{
"uy.com",
"za.com",
"c.la",
"cloudcontrolled.com",
"cloudcontrolapp.com",
"co.ca",
"co.nl",
"co.no",
@ -6043,7 +6135,13 @@ var rules = [...]string{
"webhop.org",
"worse-than.tv",
"writesthisblog.com",
"a.ssl.fastly.net",
"b.ssl.fastly.net",
"global.ssl.fastly.net",
"a.prod.fastly.net",
"global.prod.fastly.net",
"github.io",
"ro.com",
"appspot.com",
"blogspot.be",
"blogspot.bj",
@ -6088,6 +6186,8 @@ var rules = [...]string{
"codespot.com",
"googleapis.com",
"googlecode.com",
"herokuapp.com",
"herokussl.com",
"iki.fi",
"biz.at",
"info.at",
@ -6130,6 +6230,7 @@ var nodeLabels = [...]string{
"bg",
"bh",
"bi",
"bike",
"biz",
"bj",
"bm",
@ -6138,10 +6239,14 @@ var nodeLabels = [...]string{
"br",
"bs",
"bt",
"buzz",
"bw",
"by",
"bz",
"ca",
"camera",
"career",
"careers",
"cat",
"cc",
"cd",
@ -6151,10 +6256,13 @@ var nodeLabels = [...]string{
"ci",
"ck",
"cl",
"clothing",
"cm",
"cn",
"co",
"com",
"construction",
"contractors",
"coop",
"cr",
"cu",
@ -6164,6 +6272,8 @@ var nodeLabels = [...]string{
"cy",
"cz",
"de",
"diamonds",
"directory",
"dj",
"dk",
"dm",
@ -6173,8 +6283,11 @@ var nodeLabels = [...]string{
"edu",
"ee",
"eg",
"enterprises",
"equipment",
"er",
"es",
"estate",
"et",
"eu",
"fi",
@ -6183,7 +6296,9 @@ var nodeLabels = [...]string{
"fm",
"fo",
"fr",
"futbol",
"ga",
"gallery",
"gd",
"ge",
"gf",
@ -6197,14 +6312,18 @@ var nodeLabels = [...]string{
"gp",
"gq",
"gr",
"graphics",
"gs",
"gt",
"gu",
"guide",
"guru",
"gw",
"gy",
"hk",
"hm",
"hn",
"holdings",
"hr",
"ht",
"hu",
@ -6229,6 +6348,9 @@ var nodeLabels = [...]string{
"kg",
"kh",
"ki",
"kim",
"kitchen",
"kiwi",
"km",
"kn",
"kp",
@ -6237,9 +6359,11 @@ var nodeLabels = [...]string{
"ky",
"kz",
"la",
"land",
"lb",
"lc",
"li",
"lighting",
"lk",
"lr",
"ls",
@ -6251,6 +6375,7 @@ var nodeLabels = [...]string{
"mc",
"md",
"me",
"menu",
"mg",
"mh",
"mil",
@ -6260,6 +6385,7 @@ var nodeLabels = [...]string{
"mn",
"mo",
"mobi",
"monash",
"mp",
"mq",
"mr",
@ -6287,14 +6413,19 @@ var nodeLabels = [...]string{
"nu",
"nz",
"om",
"onl",
"org",
"otsuka",
"pa",
"pe",
"pf",
"pg",
"ph",
"photography",
"pink",
"pk",
"pl",
"plumbing",
"pm",
"pn",
"post",
@ -6306,18 +6437,23 @@ var nodeLabels = [...]string{
"py",
"qa",
"re",
"reviews",
"ro",
"rs",
"ru",
"ruhr",
"rw",
"sa",
"sb",
"sc",
"sd",
"se",
"sexy",
"sg",
"sh",
"shoes",
"si",
"singles",
"sk",
"sl",
"sm",
@ -6330,18 +6466,22 @@ var nodeLabels = [...]string{
"sx",
"sy",
"sz",
"tattoo",
"tc",
"td",
"technology",
"tel",
"tf",
"tg",
"th",
"tips",
"tj",
"tk",
"tl",
"tm",
"tn",
"to",
"today",
"tr",
"travel",
"tt",
@ -6351,23 +6491,37 @@ var nodeLabels = [...]string{
"ua",
"ug",
"uk",
"uno",
"us",
"uy",
"uz",
"va",
"vc",
"ve",
"ventures",
"vg",
"vi",
"vn",
"voyage",
"vu",
"wed",
"wf",
"ws",
"xn--3bst00m",
"xn--3ds443g",
"xn--3e0b707e",
"xn--45brj9c",
"xn--4gbrim",
"xn--54b7fta0cc",
"xn--6frz82g",
"xn--6qq986b3xl",
"xn--80asehdb",
"xn--80aswg",
"xn--90a3ac",
"xn--cg4bki",
"xn--clchc0ea0b2g2a9gcd",
"xn--fiq228c5hs",
"xn--fiq64b",
"xn--fiqs8s",
"xn--fiqz9s",
"xn--fpcrj9c3d",
@ -6392,13 +6546,18 @@ var nodeLabels = [...]string{
"xn--mgbqly7c0a67fbc",
"xn--mgbqly7cvafr",
"xn--mgbtf8fl",
"xn--ngbc5azd",
"xn--nnx388a",
"xn--node",
"xn--o3cw4h",
"xn--ogbpf8fl",
"xn--p1ai",
"xn--pgbs0dh",
"xn--q9jyb4c",
"xn--rhqv96g",
"xn--s9brj9c",
"xn--unup4y",
"xn--vhquv",
"xn--wgbh1c",
"xn--wgbl6a",
"xn--xkc2al3hye2a",
@ -6545,15 +6704,13 @@ var nodeLabels = [...]string{
"og",
"pb",
"com",
"congresodelalengua3",
"educ",
"gobiernoelectronico",
"mecon",
"nacion",
"nic",
"promocion",
"retina",
"uba",
"edu",
"gob",
"int",
"mil",
"net",
"org",
"tur",
"blogspot",
"e164",
"in-addr",
@ -6574,6 +6731,7 @@ var nodeLabels = [...]string{
"asn",
"com",
"conf",
"csiro",
"edu",
"gov",
"id",
@ -6910,6 +7068,8 @@ var nodeLabels = [...]string{
"blogspot",
"br",
"cechire",
"cloudcontrolapp",
"cloudcontrolled",
"cn",
"codespot",
"de",
@ -6994,6 +7154,8 @@ var nodeLabels = [...]string{
"googlecode",
"gotdns",
"gr",
"herokuapp",
"herokussl",
"hobby-site",
"homelinux",
"homeunix",
@ -7066,6 +7228,7 @@ var nodeLabels = [...]string{
"operaunite",
"qc",
"rhcloud",
"ro",
"ru",
"sa",
"saves-the-whales",
@ -7082,6 +7245,8 @@ var nodeLabels = [...]string{
"uy",
"writesthisblog",
"za",
"compute",
"compute-1",
"elb",
"s3",
"s3-ap-northeast-1",
@ -7102,6 +7267,17 @@ var nodeLabels = [...]string{
"s3-website-us-gov-west-1",
"s3-website-us-west-1",
"s3-website-us-west-2",
"us-east-1",
"ap-northeast-1",
"ap-southeast-1",
"ap-southeast-2",
"eu-west-1",
"sa-east-1",
"us-gov-west-1",
"us-west-1",
"us-west-2",
"z-1",
"z-2",
"ac",
"co",
"ed",
@ -10279,6 +10455,7 @@ var nodeLabels = [...]string{
"dynalias",
"dynathome",
"endofinternet",
"fastly",
"from-az",
"from-co",
"from-la",
@ -10309,6 +10486,13 @@ var nodeLabels = [...]string{
"uk",
"webhop",
"za",
"prod",
"ssl",
"a",
"global",
"a",
"b",
"global",
"arts",
"com",
"firm",
@ -10319,12 +10503,15 @@ var nodeLabels = [...]string{
"rec",
"store",
"web",
"ac",
"com",
"edu",
"gov",
"mil",
"mobi",
"name",
"net",
"org",
"sch",
"blogspot",
"bv",
"co",
@ -11106,12 +11293,21 @@ var nodeLabels = [...]string{
"shacknet",
"co",
"blogspot",
"co",
"com",
"edu",
"gov",
"med",
"mediaphone",
"museum",
"nawras",
"nawrastelecom",
"net",
"omanmobile",
"omanpost",
"omantel",
"org",
"pro",
"rakpetroleum",
"siemens",
"songfest",
@ -11727,6 +11923,11 @@ var nodeLabels = [...]string{
"principe",
"saotome",
"store",
"com",
"edu",
"gob",
"org",
"red",
"gov",
"com",
"edu",

View File

@ -245,7 +245,7 @@ func TestWithTwoProtocol(t *testing.T) {
func TestWithBadProtocol(t *testing.T) {
_, err := testWithProtocol(t, []string{"test"})
if err != ErrBadStatus {
t.Errorf("SubProto: expected %q, got %q", ErrBadStatus)
t.Errorf("SubProto: expected %v, got %v", ErrBadStatus, err)
}
}

2
third_party/deps vendored
View File

@ -1,6 +1,6 @@
packages="
github.com/BurntSushi/toml
github.com/coreos/go-raft
github.com/coreos/raft
github.com/coreos/go-etcd
github.com/coreos/go-log/log
github.com/coreos/go-systemd

View File

@ -0,0 +1 @@
config.json

View File

@ -1,12 +1,8 @@
# go-etcd
golang client library for etcd
The official etcd v0.2 client library for Go.
This etcd client library is under heavy development. Check back soon for more
docs. In the meantime, check out [etcd](https://github.com/coreos/etcd) for
details on the client protocol.
For usage see example below or look at godoc: [go-etcd/etcd](http://godoc.org/github.com/coreos/go-etcd/etcd)
For usage, please refer to: [go-etcd/etcd](http://godoc.org/github.com/coreos/go-etcd/etcd).
## Install
@ -14,37 +10,6 @@ For usage see example below or look at godoc: [go-etcd/etcd](http://godoc.org/gi
go get github.com/coreos/go-etcd/etcd
```
## Examples
## License
Returning error values are not showed for the sake of simplicity, but you
should check them.
```go
package main
import (
"fmt"
"github.com/coreos/go-etcd/etcd"
)
func main() {
c := etcd.NewClient() // default binds to http://0.0.0.0:4001
// SET the value "bar" to the key "foo" with zero TTL
// returns a: *Response
res, _ := c.Set("foo", "bar", 0)
fmt.Printf("set response: %+v\n", res)
// GET the value that is stored for the key "foo"
// return a slice: []*Response
values, _ := c.Get("foo")
for i, res := range values { // .. and print them out
fmt.Printf("[%d] get response: %+v\n", i, res)
}
// DELETE the key "foo"
// returns a: *Response
res, _ = c.Delete("foo")
fmt.Printf("delete response: %+v\n", res)
}
```
See LICENSE file.

View File

@ -0,0 +1,11 @@
package etcd
// Add a new directory with a random etcd-generated key under the given path.
func (c *Client) AddChildDir(key string, ttl uint64) (*Response, error) {
return c.post(key, "", ttl)
}
// Add a new file with a random etcd-generated key under the given path.
func (c *Client) AddChild(key string, value string, ttl uint64) (*Response, error) {
return c.post(key, value, ttl)
}

View File

@ -0,0 +1,73 @@
package etcd
import "testing"
func TestAddChild(t *testing.T) {
c := NewClient(nil)
defer func() {
c.DeleteAll("fooDir")
c.DeleteAll("nonexistentDir")
}()
c.SetDir("fooDir", 5)
_, err := c.AddChild("fooDir", "v0", 5)
if err != nil {
t.Fatal(err)
}
_, err = c.AddChild("fooDir", "v1", 5)
if err != nil {
t.Fatal(err)
}
resp, err := c.Get("fooDir", true)
// The child with v0 should proceed the child with v1 because it's added
// earlier, so it should have a lower key.
if !(len(resp.Kvs) == 2 && (resp.Kvs[0].Value == "v0" && resp.Kvs[1].Value == "v1")) {
t.Fatalf("AddChild 1 failed. There should be two chlidren whose values are v0 and v1, respectively."+
" The response was: %#v", resp)
}
// Creating a child under a nonexistent directory should succeed.
// The directory should be created.
resp, err = c.AddChild("nonexistentDir", "foo", 5)
if err != nil {
t.Fatal(err)
}
}
func TestAddChildDir(t *testing.T) {
c := NewClient(nil)
defer func() {
c.DeleteAll("fooDir")
c.DeleteAll("nonexistentDir")
}()
c.SetDir("fooDir", 5)
_, err := c.AddChildDir("fooDir", 5)
if err != nil {
t.Fatal(err)
}
_, err = c.AddChildDir("fooDir", 5)
if err != nil {
t.Fatal(err)
}
resp, err := c.Get("fooDir", true)
// The child with v0 should proceed the child with v1 because it's added
// earlier, so it should have a lower key.
if !(len(resp.Kvs) == 2 && (len(resp.Kvs[0].KVPairs) == 0 && len(resp.Kvs[1].KVPairs) == 0)) {
t.Fatalf("AddChildDir 1 failed. There should be two chlidren whose values are v0 and v1, respectively."+
" The response was: %#v", resp)
}
// Creating a child under a nonexistent directory should succeed.
// The directory should be created.
resp, err = c.AddChildDir("nonexistentDir", 5)
if err != nil {
t.Fatal(err)
}
}

View File

@ -2,12 +2,16 @@ package etcd
import (
"crypto/tls"
"encoding/json"
"errors"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"path"
"reflect"
"strings"
"time"
)
@ -17,25 +21,43 @@ const (
HTTPS
)
// See SetConsistency for how to use these constants.
const (
// Using strings rather than iota because the consistency level
// could be persisted to disk, so it'd be better to use
// human-readable values.
STRONG_CONSISTENCY = "STRONG"
WEAK_CONSISTENCY = "WEAK"
)
type Cluster struct {
Leader string
Machines []string
Leader string `json:"leader"`
Machines []string `json:"machines"`
}
type Config struct {
CertFile string
KeyFile string
Scheme string
Timeout time.Duration
CertFile string `json:"certFile"`
KeyFile string `json:"keyFile"`
Scheme string `json:"scheme"`
Timeout time.Duration `json:"timeout"`
Consistency string `json: "consistency"`
}
type Client struct {
cluster Cluster
config Config
httpClient *http.Client
cluster Cluster `json:"cluster"`
config Config `json:"config"`
httpClient *http.Client
persistence io.Writer
}
// Setup a basic conf and cluster
type options map[string]interface{}
// An internally-used data structure that represents a mapping
// between valid options and their kinds
type validOptions map[string]reflect.Kind
// NewClient create a basic client that is configured to be used
// with the given machine list.
func NewClient(machines []string) *Client {
// if an empty slice was sent in then just assume localhost
if len(machines) == 0 {
@ -53,30 +75,168 @@ func NewClient(machines []string) *Client {
Scheme: "http",
// default timeout is one second
Timeout: time.Second,
// default consistency level is STRONG
Consistency: STRONG_CONSISTENCY,
}
tr := &http.Transport{
Dial: dialTimeout,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
client := &Client{
cluster: cluster,
config: config,
}
return &Client{
cluster: cluster,
config: config,
httpClient: &http.Client{Transport: tr},
err := setupHttpClient(client)
if err != nil {
panic(err)
}
return client
}
func (c *Client) SetCertAndKey(cert string, key string) (bool, error) {
// NewClientFile creates a client from a given file path.
// The given file is expected to use the JSON format.
func NewClientFile(fpath string) (*Client, error) {
fi, err := os.Open(fpath)
if err != nil {
return nil, err
}
defer func() {
if err := fi.Close(); err != nil {
panic(err)
}
}()
return NewClientReader(fi)
}
// NewClientReader creates a Client configured from a given reader.
// The config is expected to use the JSON format.
func NewClientReader(reader io.Reader) (*Client, error) {
var client Client
b, err := ioutil.ReadAll(reader)
if err != nil {
return nil, err
}
err = json.Unmarshal(b, &client)
if err != nil {
return nil, err
}
err = setupHttpClient(&client)
if err != nil {
return nil, err
}
return &client, nil
}
func setupHttpClient(client *Client) error {
if client.config.CertFile != "" && client.config.KeyFile != "" {
err := client.SetCertAndKey(client.config.CertFile, client.config.KeyFile)
if err != nil {
return err
}
} else {
client.config.CertFile = ""
client.config.KeyFile = ""
tr := &http.Transport{
Dial: dialTimeout,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
client.httpClient = &http.Client{Transport: tr}
}
return nil
}
// SetPersistence sets a writer to which the config will be
// written every time it's changed.
func (c *Client) SetPersistence(writer io.Writer) {
c.persistence = writer
}
// SetConsistency changes the consistency level of the client.
//
// When consistency is set to STRONG_CONSISTENCY, all requests,
// including GET, are sent to the leader. This means that, assuming
// the absence of leader failures, GET requests are guaranteed to see
// the changes made by previous requests.
//
// When consistency is set to WEAK_CONSISTENCY, other requests
// are still sent to the leader, but GET requests are sent to a
// random server from the server pool. This reduces the read
// load on the leader, but it's not guaranteed that the GET requests
// will see changes made by previous requests (they might have not
// yet been committed on non-leader servers).
func (c *Client) SetConsistency(consistency string) error {
if !(consistency == STRONG_CONSISTENCY || consistency == WEAK_CONSISTENCY) {
return errors.New("The argument must be either STRONG_CONSISTENCY or WEAK_CONSISTENCY.")
}
c.config.Consistency = consistency
return nil
}
// MarshalJSON implements the Marshaller interface
// as defined by the standard JSON package.
func (c *Client) MarshalJSON() ([]byte, error) {
b, err := json.Marshal(struct {
Config Config `json:"config"`
Cluster Cluster `json:"cluster"`
}{
Config: c.config,
Cluster: c.cluster,
})
if err != nil {
return nil, err
}
return b, nil
}
// UnmarshalJSON implements the Unmarshaller interface
// as defined by the standard JSON package.
func (c *Client) UnmarshalJSON(b []byte) error {
temp := struct {
Config Config `json: "config"`
Cluster Cluster `json: "cluster"`
}{}
err := json.Unmarshal(b, &temp)
if err != nil {
return err
}
c.cluster = temp.Cluster
c.config = temp.Config
return nil
}
// saveConfig saves the current config using c.persistence.
func (c *Client) saveConfig() error {
if c.persistence != nil {
b, err := json.Marshal(c)
if err != nil {
return err
}
_, err = c.persistence.Write(b)
if err != nil {
return err
}
}
return nil
}
func (c *Client) SetCertAndKey(cert string, key string) error {
if cert != "" && key != "" {
tlsCert, err := tls.LoadX509KeyPair(cert, key)
if err != nil {
return false, err
return err
}
tr := &http.Transport{
@ -88,24 +248,27 @@ func (c *Client) SetCertAndKey(cert string, key string) (bool, error) {
}
c.httpClient = &http.Client{Transport: tr}
return true, nil
c.saveConfig()
return nil
}
return false, errors.New("Require both cert and key path")
return errors.New("Require both cert and key path")
}
func (c *Client) SetScheme(scheme int) (bool, error) {
func (c *Client) SetScheme(scheme int) error {
if scheme == HTTP {
c.config.Scheme = "http"
return true, nil
c.saveConfig()
return nil
}
if scheme == HTTPS {
c.config.Scheme = "https"
return true, nil
c.saveConfig()
return nil
}
return false, errors.New("Unknown Scheme")
return errors.New("Unknown Scheme")
}
// Try to sync from the given machine
// SetCluster updates config using the given machine list.
func (c *Client) SetCluster(machines []string) bool {
success := c.internalSyncCluster(machines)
return success
@ -115,13 +278,13 @@ func (c *Client) GetCluster() []string {
return c.cluster.Machines
}
// sycn cluster information using the existing machine list
// SyncCluster updates config using the internal machine list.
func (c *Client) SyncCluster() bool {
success := c.internalSyncCluster(c.cluster.Machines)
return success
}
// sync cluster information by providing machine list
// internalSyncCluster syncs cluster information using the given machine list.
func (c *Client) internalSyncCluster(machines []string) bool {
for _, machine := range machines {
httpPath := c.createHttpPath(machine, version+"/machines")
@ -146,16 +309,19 @@ func (c *Client) internalSyncCluster(machines []string) bool {
c.cluster.Leader = c.cluster.Machines[0]
logger.Debug("sync.machines ", c.cluster.Machines)
c.saveConfig()
return true
}
}
return false
}
// serverName should contain both hostName and port
// createHttpPath creates a complete HTTP URL.
// serverName should contain both the host name and a port number, if any.
func (c *Client) createHttpPath(serverName string, _path string) string {
u, _ := url.Parse(serverName)
u.Path = path.Join(u.Path, "/", _path)
if u.Scheme == "" {
u.Scheme = "http"
}
@ -167,18 +333,6 @@ func dialTimeout(network, addr string) (net.Conn, error) {
return net.DialTimeout(network, addr, time.Second)
}
func (c *Client) getHttpPath(s ...string) string {
u, _ := url.Parse(c.cluster.Leader)
u.Path = path.Join(u.Path, "/", version)
for _, seg := range s {
u.Path = path.Join(u.Path, seg)
}
return u.String()
}
func (c *Client) updateLeader(httpPath string) {
u, _ := url.Parse(httpPath)
@ -191,77 +345,5 @@ func (c *Client) updateLeader(httpPath string) {
logger.Debugf("update.leader[%s,%s]", c.cluster.Leader, leader)
c.cluster.Leader = leader
}
// Wrap GET, POST and internal error handling
func (c *Client) sendRequest(method string, _path string, body string) (*http.Response, error) {
var resp *http.Response
var err error
var req *http.Request
retry := 0
// if we connect to a follower, we will retry until we found a leader
for {
httpPath := c.getHttpPath(_path)
logger.Debug("send.request.to ", httpPath)
if body == "" {
req, _ = http.NewRequest(method, httpPath, nil)
} else {
req, _ = http.NewRequest(method, httpPath, strings.NewReader(body))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
}
resp, err = c.httpClient.Do(req)
logger.Debug("recv.response.from ", httpPath)
// network error, change a machine!
if err != nil {
retry++
if retry > 2*len(c.cluster.Machines) {
return nil, errors.New("Cannot reach servers")
}
num := retry % len(c.cluster.Machines)
logger.Debug("update.leader[", c.cluster.Leader, ",", c.cluster.Machines[num], "]")
c.cluster.Leader = c.cluster.Machines[num]
time.Sleep(time.Millisecond * 200)
continue
}
if resp != nil {
if resp.StatusCode == http.StatusTemporaryRedirect {
httpPath := resp.Header.Get("Location")
resp.Body.Close()
if httpPath == "" {
return nil, errors.New("Cannot get redirection location")
}
c.updateLeader(httpPath)
logger.Debug("send.redirect")
// try to connect the leader
continue
} else if resp.StatusCode == http.StatusInternalServerError {
resp.Body.Close()
retry++
if retry > 2*len(c.cluster.Machines) {
return nil, errors.New("Cannot reach servers")
}
continue
} else {
logger.Debug("send.return.response ", httpPath)
break
}
}
logger.Debug("error.from ", httpPath, " ", err.Error())
return nil, err
}
return resp, nil
c.saveConfig()
}

View File

@ -1,10 +1,12 @@
package etcd
import (
"encoding/json"
"fmt"
"testing"
"net/url"
"net"
"net/url"
"os"
"testing"
)
// To pass this test, we need to create a cluster of 3 machines
@ -19,7 +21,7 @@ func TestSync(t *testing.T) {
t.Fatal("cannot sync machines")
}
for _, m := range(c.GetCluster()) {
for _, m := range c.GetCluster() {
u, err := url.Parse(m)
if err != nil {
t.Fatal(err)
@ -27,7 +29,7 @@ func TestSync(t *testing.T) {
if u.Scheme != "http" {
t.Fatal("scheme must be http")
}
host, _, err := net.SplitHostPort(u.Host)
if err != nil {
t.Fatal(err)
@ -56,3 +58,37 @@ func TestSync(t *testing.T) {
}
}
func TestPersistence(t *testing.T) {
c := NewClient(nil)
c.SyncCluster()
fo, err := os.Create("config.json")
if err != nil {
t.Fatal(err)
}
defer func() {
if err := fo.Close(); err != nil {
panic(err)
}
}()
c.SetPersistence(fo)
err = c.saveConfig()
if err != nil {
t.Fatal(err)
}
c2, err := NewClientFile("config.json")
if err != nil {
t.Fatal(err)
}
// Verify that the two clients have the same config
b1, _ := json.Marshal(c)
b2, _ := json.Marshal(c2)
if string(b1) != string(b2) {
t.Fatalf("The two configs should be equal!")
}
}

View File

@ -0,0 +1,18 @@
package etcd
import "fmt"
func (c *Client) CompareAndSwap(key string, value string, ttl uint64, prevValue string, prevIndex uint64) (*Response, error) {
if prevValue == "" && prevIndex == 0 {
return nil, fmt.Errorf("You must give either prevValue or prevIndex.")
}
options := options{}
if prevValue != "" {
options["prevValue"] = prevValue
}
if prevIndex != 0 {
options["prevIndex"] = prevIndex
}
return c.put(key, value, ttl, options)
}

View File

@ -0,0 +1,51 @@
package etcd
import (
"testing"
)
func TestCompareAndSwap(t *testing.T) {
c := NewClient(nil)
defer func() {
c.DeleteAll("foo")
}()
c.Set("foo", "bar", 5)
// This should succeed
resp, err := c.CompareAndSwap("foo", "bar2", 5, "bar", 0)
if err != nil {
t.Fatal(err)
}
if !(resp.Value == "bar2" && resp.PrevValue == "bar" &&
resp.Key == "/foo" && resp.TTL == 5) {
t.Fatalf("CompareAndSwap 1 failed: %#v", resp)
}
// This should fail because it gives an incorrect prevValue
resp, err = c.CompareAndSwap("foo", "bar3", 5, "xxx", 0)
if err == nil {
t.Fatalf("CompareAndSwap 2 should have failed. The response is: %#v", resp)
}
resp, err = c.Set("foo", "bar", 5)
if err != nil {
t.Fatal(err)
}
// This should succeed
resp, err = c.CompareAndSwap("foo", "bar2", 5, "", resp.ModifiedIndex)
if err != nil {
t.Fatal(err)
}
if !(resp.Value == "bar2" && resp.PrevValue == "bar" &&
resp.Key == "/foo" && resp.TTL == 5) {
t.Fatalf("CompareAndSwap 1 failed: %#v", resp)
}
// This should fail because it gives an incorrect prevIndex
resp, err = c.CompareAndSwap("foo", "bar3", 5, "", 29817514)
if err == nil {
t.Fatalf("CompareAndSwap 2 should have failed. The response is: %#v", resp)
}
}

View File

@ -9,6 +9,8 @@ var logger *log.Logger
func init() {
setLogger(log.PriErr)
// Uncomment the following line if you want to see lots of logs
// OpenDebug()
}
func OpenDebug() {

View File

@ -1,40 +1,17 @@
package etcd
import (
"encoding/json"
"io/ioutil"
"net/http"
"path"
)
func (c *Client) Delete(key string) (*Response, error) {
resp, err := c.sendRequest("DELETE", path.Join("keys", key), "")
if err != nil {
return nil, err
}
b, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, handleError(b)
}
var result Response
err = json.Unmarshal(b, &result)
if err != nil {
return nil, err
}
return &result, nil
// DeleteAll deletes everything under the given key. If the key
// points to a file, the file will be deleted. If the key points
// to a directory, then everything under the directory, include
// all child directories, will be deleted.
func (c *Client) DeleteAll(key string) (*Response, error) {
return c.delete(key, options{
"recursive": true,
})
}
// Delete deletes the given key. If the key points to a
// directory, the method will fail.
func (c *Client) Delete(key string) (*Response, error) {
return c.delete(key, nil)
}

View File

@ -5,18 +5,60 @@ import (
)
func TestDelete(t *testing.T) {
c := NewClient(nil)
defer func() {
c.DeleteAll("foo")
}()
c.Set("foo", "bar", 100)
result, err := c.Delete("foo")
c.Set("foo", "bar", 5)
resp, err := c.Delete("foo")
if err != nil {
t.Fatal(err)
}
if result.PrevValue != "bar" || result.Value != "" {
t.Fatalf("Delete failed with %s %s", result.PrevValue,
result.Value)
if !(resp.PrevValue == "bar" && resp.Value == "") {
t.Fatalf("Delete failed with %s %s", resp.PrevValue,
resp.Value)
}
resp, err = c.Delete("foo")
if err == nil {
t.Fatalf("Delete should have failed because the key foo did not exist. "+
"The response was: %v", resp)
}
}
func TestDeleteAll(t *testing.T) {
c := NewClient(nil)
defer func() {
c.DeleteAll("foo")
c.DeleteAll("fooDir")
}()
c.Set("foo", "bar", 5)
resp, err := c.DeleteAll("foo")
if err != nil {
t.Fatal(err)
}
if !(resp.PrevValue == "bar" && resp.Value == "") {
t.Fatalf("DeleteAll 1 failed: %#v", resp)
}
c.SetDir("fooDir", 5)
c.Set("fooDir/foo", "bar", 5)
resp, err = c.DeleteAll("fooDir")
if err != nil {
t.Fatal(err)
}
if !(resp.PrevValue == "" && resp.Value == "") {
t.Fatalf("DeleteAll 2 failed: %#v", resp)
}
resp, err = c.DeleteAll("foo")
if err == nil {
t.Fatalf("DeleteAll should have failed because the key foo did not exist. "+
"The response was: %v", resp)
}
}

View File

@ -1,82 +1,23 @@
package etcd
import (
"encoding/json"
"io/ioutil"
"net/http"
"path"
)
func (c *Client) Get(key string) ([]*Response, error) {
logger.Debugf("get %s [%s]", key, c.cluster.Leader)
resp, err := c.sendRequest("GET", path.Join("keys", key), "")
if err != nil {
return nil, err
}
b, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, handleError(b)
}
return convertGetResponse(b)
// GetDir gets the all contents under the given key.
// If the key points to a file, the file is returned.
// If the key points to a directory, everything under it is returnd,
// including all contents under all child directories.
func (c *Client) GetAll(key string, sort bool) (*Response, error) {
return c.get(key, options{
"recursive": true,
"sorted": sort,
})
}
// GetTo gets the value of the key from a given machine address.
// If the given machine is not available it returns an error.
// Mainly use for testing purpose
func (c *Client) GetFrom(key string, addr string) ([]*Response, error) {
httpPath := c.createHttpPath(addr, path.Join(version, "keys", key))
resp, err := c.httpClient.Get(httpPath)
if err != nil {
return nil, err
}
b, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, handleError(b)
}
return convertGetResponse(b)
}
// Convert byte stream to response.
func convertGetResponse(b []byte) ([]*Response, error) {
var results []*Response
var result *Response
err := json.Unmarshal(b, &result)
if err != nil {
err = json.Unmarshal(b, &results)
if err != nil {
return nil, err
}
} else {
results = make([]*Response, 1)
results[0] = result
}
return results, nil
// Get gets the file or directory associated with the given key.
// If the key points to a directory, files and directories under
// it will be returned in sorted or unsorted order, depending on
// the sort flag. Note that contents under child directories
// will not be returned. To get those contents, use GetAll.
func (c *Client) Get(key string, sort bool) (*Response, error) {
return c.get(key, options{
"sorted": sort,
})
}

View File

@ -1,46 +1,99 @@
package etcd
import (
"reflect"
"testing"
"time"
)
func TestGet(t *testing.T) {
c := NewClient(nil)
defer func() {
c.DeleteAll("foo")
}()
c.Set("foo", "bar", 100)
c.Set("foo", "bar", 5)
// wait for commit
time.Sleep(100 * time.Millisecond)
result, err := c.Get("foo", false)
results, err := c.Get("foo")
if err != nil || results[0].Key != "/foo" || results[0].Value != "bar" {
if err != nil {
t.Fatal(err)
}
t.Fatalf("Get failed with %s %s %v", results[0].Key, results[0].Value, results[0].TTL)
if err != nil {
t.Fatal(err)
}
results, err = c.Get("goo")
if result.Key != "/foo" || result.Value != "bar" {
t.Fatalf("Get failed with %s %s %v", result.Key, result.Value, result.TTL)
}
result, err = c.Get("goo", false)
if err == nil {
t.Fatalf("should not be able to get non-exist key")
}
}
results, err = c.GetFrom("foo", "0.0.0.0:4001")
func TestGetAll(t *testing.T) {
c := NewClient(nil)
defer func() {
c.DeleteAll("fooDir")
}()
if err != nil || results[0].Key != "/foo" || results[0].Value != "bar" {
if err != nil {
t.Fatal(err)
}
t.Fatalf("Get failed with %s %s %v", results[0].Key, results[0].Value, results[0].TTL)
c.SetDir("fooDir", 5)
c.Set("fooDir/k0", "v0", 5)
c.Set("fooDir/k1", "v1", 5)
// Return kv-pairs in sorted order
result, err := c.Get("fooDir", true)
if err != nil {
t.Fatal(err)
}
results, err = c.GetFrom("foo", "0.0.0.0:4009")
expected := kvPairs{
KeyValuePair{
Key: "/fooDir/k0",
Value: "v0",
},
KeyValuePair{
Key: "/fooDir/k1",
Value: "v1",
},
}
if err == nil {
t.Fatal("should not get from port 4009")
if !reflect.DeepEqual(result.Kvs, expected) {
t.Fatalf("(actual) %v != (expected) %v", result.Kvs, expected)
}
// Test the `recursive` option
c.SetDir("fooDir/childDir", 5)
c.Set("fooDir/childDir/k2", "v2", 5)
// Return kv-pairs in sorted order
result, err = c.GetAll("fooDir", true)
if err != nil {
t.Fatal(err)
}
expected = kvPairs{
KeyValuePair{
Key: "/fooDir/childDir",
Dir: true,
KVPairs: kvPairs{
KeyValuePair{
Key: "/fooDir/childDir/k2",
Value: "v2",
},
},
},
KeyValuePair{
Key: "/fooDir/k0",
Value: "v0",
},
KeyValuePair{
Key: "/fooDir/k1",
Value: "v1",
},
}
if !reflect.DeepEqual(result.Kvs, expected) {
t.Fatalf("(actual) %v != (expected) %v", result.Kvs)
}
}

View File

@ -1,23 +0,0 @@
package etcd
import (
"testing"
"time"
)
func TestList(t *testing.T) {
c := NewClient(nil)
c.Set("foo_list/foo", "bar", 100)
c.Set("foo_list/fooo", "barbar", 100)
c.Set("foo_list/foooo/foo", "barbarbar", 100)
// wait for commit
time.Sleep(time.Second)
_, err := c.Get("foo_list")
if err != nil {
t.Fatal(err)
}
}

View File

@ -0,0 +1,290 @@
package etcd
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"math/rand"
"net/http"
"net/url"
"path"
"reflect"
"strings"
"time"
)
// Valid options for GET, PUT, POST, DELETE
// Using CAPITALIZED_UNDERSCORE to emphasize that these
// values are meant to be used as constants.
var (
VALID_GET_OPTIONS = validOptions{
"recursive": reflect.Bool,
"consistent": reflect.Bool,
"sorted": reflect.Bool,
"wait": reflect.Bool,
"waitIndex": reflect.Uint64,
}
VALID_PUT_OPTIONS = validOptions{
"prevValue": reflect.String,
"prevIndex": reflect.Uint64,
"prevExist": reflect.Bool,
}
VALID_POST_OPTIONS = validOptions{}
VALID_DELETE_OPTIONS = validOptions{
"recursive": reflect.Bool,
}
curlChan chan string
)
// SetCurlChan sets a channel to which cURL commands which can be used to
// re-produce requests are sent. This is useful for debugging.
func SetCurlChan(c chan string) {
curlChan = c
}
// get issues a GET request
func (c *Client) get(key string, options options) (*Response, error) {
logger.Debugf("get %s [%s]", key, c.cluster.Leader)
p := path.Join("keys", key)
// If consistency level is set to STRONG, append
// the `consistent` query string.
if c.config.Consistency == STRONG_CONSISTENCY {
options["consistent"] = true
}
if options != nil {
str, err := optionsToString(options, VALID_GET_OPTIONS)
if err != nil {
return nil, err
}
p += str
}
resp, err := c.sendRequest("GET", p, url.Values{})
if err != nil {
return nil, err
}
return resp, nil
}
// put issues a PUT request
func (c *Client) put(key string, value string, ttl uint64, options options) (*Response, error) {
logger.Debugf("put %s, %s, ttl: %d, [%s]", key, value, ttl, c.cluster.Leader)
v := url.Values{}
if value != "" {
v.Set("value", value)
}
if ttl > 0 {
v.Set("ttl", fmt.Sprintf("%v", ttl))
}
p := path.Join("keys", key)
if options != nil {
str, err := optionsToString(options, VALID_PUT_OPTIONS)
if err != nil {
return nil, err
}
p += str
}
resp, err := c.sendRequest("PUT", p, v)
if err != nil {
return nil, err
}
return resp, nil
}
// post issues a POST request
func (c *Client) post(key string, value string, ttl uint64) (*Response, error) {
logger.Debugf("post %s, %s, ttl: %d, [%s]", key, value, ttl, c.cluster.Leader)
v := url.Values{}
if value != "" {
v.Set("value", value)
}
if ttl > 0 {
v.Set("ttl", fmt.Sprintf("%v", ttl))
}
resp, err := c.sendRequest("POST", path.Join("keys", key), v)
if err != nil {
return nil, err
}
return resp, nil
}
// delete issues a DELETE request
func (c *Client) delete(key string, options options) (*Response, error) {
logger.Debugf("delete %s [%s]", key, c.cluster.Leader)
v := url.Values{}
p := path.Join("keys", key)
if options != nil {
str, err := optionsToString(options, VALID_DELETE_OPTIONS)
if err != nil {
return nil, err
}
p += str
}
resp, err := c.sendRequest("DELETE", p, v)
if err != nil {
return nil, err
}
return resp, nil
}
// sendRequest sends a HTTP request and returns a Response as defined by etcd
func (c *Client) sendRequest(method string, _path string, values url.Values) (*Response, error) {
var body string = values.Encode()
var resp *http.Response
var req *http.Request
retry := 0
// if we connect to a follower, we will retry until we found a leader
for {
var httpPath string
// If _path has schema already, then it's assumed to be
// a complete URL and therefore needs no further processing.
u, err := url.Parse(_path)
if err != nil {
return nil, err
}
if u.Scheme != "" {
httpPath = _path
} else {
if method == "GET" && c.config.Consistency == WEAK_CONSISTENCY {
// If it's a GET and consistency level is set to WEAK,
// then use a random machine.
httpPath = c.getHttpPath(true, _path)
} else {
// Else use the leader.
httpPath = c.getHttpPath(false, _path)
}
}
// Return a cURL command if curlChan is set
if curlChan != nil {
command := fmt.Sprintf("curl -X %s %s", method, httpPath)
for key, value := range values {
command += fmt.Sprintf(" -d %s=%s", key, value[0])
}
curlChan <- command
}
logger.Debug("send.request.to ", httpPath, " | method ", method)
if body == "" {
req, _ = http.NewRequest(method, httpPath, nil)
} else {
req, _ = http.NewRequest(method, httpPath, strings.NewReader(body))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
}
resp, err = c.httpClient.Do(req)
logger.Debug("recv.response.from ", httpPath)
// network error, change a machine!
if err != nil {
retry++
if retry > 2*len(c.cluster.Machines) {
return nil, errors.New("Cannot reach servers")
}
num := retry % len(c.cluster.Machines)
logger.Debug("update.leader[", c.cluster.Leader, ",", c.cluster.Machines[num], "]")
c.cluster.Leader = c.cluster.Machines[num]
time.Sleep(time.Millisecond * 200)
continue
}
if resp != nil {
if resp.StatusCode == http.StatusTemporaryRedirect {
httpPath := resp.Header.Get("Location")
resp.Body.Close()
if httpPath == "" {
return nil, errors.New("Cannot get redirection location")
}
c.updateLeader(httpPath)
logger.Debug("send.redirect")
// try to connect the leader
continue
} else if resp.StatusCode == http.StatusInternalServerError {
resp.Body.Close()
retry++
if retry > 2*len(c.cluster.Machines) {
return nil, errors.New("Cannot reach servers")
}
continue
} else {
logger.Debug("send.return.response ", httpPath)
break
}
}
logger.Debug("error.from ", httpPath, " ", err.Error())
return nil, err
}
// Convert HTTP response to etcd response
b, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, err
}
if !(resp.StatusCode == http.StatusOK ||
resp.StatusCode == http.StatusCreated) {
return nil, handleError(b)
}
var result Response
err = json.Unmarshal(b, &result)
if err != nil {
return nil, err
}
return &result, nil
}
func (c *Client) getHttpPath(random bool, s ...string) string {
var machine string
if random {
machine = c.cluster.Machines[rand.Intn(len(c.cluster.Machines))]
} else {
machine = c.cluster.Leader
}
fullPath := machine + "/" + version
for _, seg := range s {
fullPath = fullPath + "/" + seg
}
return fullPath
}

View File

@ -6,11 +6,12 @@ import (
// The response object from the server.
type Response struct {
Action string `json:"action"`
Key string `json:"key"`
Dir bool `json:"dir,omitempty"`
PrevValue string `json:"prevValue,omitempty"`
Value string `json:"value,omitempty"`
Action string `json:"action"`
Key string `json:"key"`
Dir bool `json:"dir,omitempty"`
PrevValue string `json:"prevValue,omitempty"`
Value string `json:"value,omitempty"`
Kvs kvPairs `json:"kvs,omitempty"`
// If the key did not exist before the action,
// this field should be set to true
@ -22,5 +23,29 @@ type Response struct {
TTL int64 `json:"ttl,omitempty"`
// The command index of the raft machine when the command is executed
Index uint64 `json:"index"`
ModifiedIndex uint64 `json:"modifiedIndex"`
}
// When user list a directory, we add all the node into key-value pair slice
type KeyValuePair struct {
Key string `json:"key, omitempty"`
Value string `json:"value,omitempty"`
Dir bool `json:"dir,omitempty"`
KVPairs kvPairs `json:"kvs,omitempty"`
TTL int64 `json:"ttl,omitempty"`
}
type kvPairs []KeyValuePair
// interfaces for sorting
func (kvs kvPairs) Len() int {
return len(kvs)
}
func (kvs kvPairs) Less(i, j int) bool {
return kvs[i].Key < kvs[j].Key
}
func (kvs kvPairs) Swap(i, j int) {
kvs[i], kvs[j] = kvs[j], kvs[i]
}

View File

@ -1,89 +0,0 @@
package etcd
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"path"
)
func (c *Client) Set(key string, value string, ttl uint64) (*Response, error) {
logger.Debugf("set %s, %s, ttl: %d, [%s]", key, value, ttl, c.cluster.Leader)
v := url.Values{}
v.Set("value", value)
if ttl > 0 {
v.Set("ttl", fmt.Sprintf("%v", ttl))
}
resp, err := c.sendRequest("POST", path.Join("keys", key), v.Encode())
if err != nil {
return nil, err
}
b, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, handleError(b)
}
return convertSetResponse(b)
}
// SetTo sets the value of the key to a given machine address.
// If the given machine is not available or is not leader it returns an error
// Mainly use for testing purpose.
func (c *Client) SetTo(key string, value string, ttl uint64, addr string) (*Response, error) {
v := url.Values{}
v.Set("value", value)
if ttl > 0 {
v.Set("ttl", fmt.Sprintf("%v", ttl))
}
httpPath := c.createHttpPath(addr, path.Join(version, "keys", key))
resp, err := c.httpClient.PostForm(httpPath, v)
if err != nil {
return nil, err
}
b, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, handleError(b)
}
return convertSetResponse(b)
}
// Convert byte stream to response.
func convertSetResponse(b []byte) (*Response, error) {
var result Response
err := json.Unmarshal(b, &result)
if err != nil {
return nil, err
}
return &result, nil
}

View File

@ -0,0 +1,43 @@
package etcd
import (
"fmt"
"testing"
)
func TestSetCurlChan(t *testing.T) {
c := NewClient(nil)
defer func() {
c.DeleteAll("foo")
}()
curlChan := make(chan string, 1)
SetCurlChan(curlChan)
_, err := c.Set("foo", "bar", 5)
if err != nil {
t.Fatal(err)
}
expected := fmt.Sprintf("curl -X PUT %s/v2/keys/foo -d value=bar -d ttl=5",
c.cluster.Leader)
actual := <-curlChan
if expected != actual {
t.Fatalf(`Command "%s" is not equal to expected value "%s"`,
actual, expected)
}
c.SetConsistency(STRONG_CONSISTENCY)
_, err = c.Get("foo", false)
if err != nil {
t.Fatal(err)
}
expected = fmt.Sprintf("curl -X GET %s/v2/keys/foo?consistent=true&sorted=false",
c.cluster.Leader)
actual = <-curlChan
if expected != actual {
t.Fatalf(`Command "%s" is not equal to expected value "%s"`,
actual, expected)
}
}

View File

@ -1,42 +0,0 @@
package etcd
import (
"testing"
"time"
)
func TestSet(t *testing.T) {
c := NewClient(nil)
result, err := c.Set("foo", "bar", 100)
if err != nil || result.Key != "/foo" || result.Value != "bar" || result.TTL != 99 {
if err != nil {
t.Fatal(err)
}
t.Fatalf("Set 1 failed with %s %s %v", result.Key, result.Value, result.TTL)
}
time.Sleep(time.Second)
result, err = c.Set("foo", "bar", 100)
if err != nil || result.Key != "/foo" || result.Value != "bar" || result.PrevValue != "bar" || result.TTL != 99 {
if err != nil {
t.Fatal(err)
}
t.Fatalf("Set 2 failed with %s %s %v", result.Key, result.Value, result.TTL)
}
result, err = c.SetTo("toFoo", "bar", 100, "0.0.0.0:4001")
if err != nil || result.Key != "/toFoo" || result.Value != "bar" || result.TTL != 99 {
if err != nil {
t.Fatal(err)
}
t.Fatalf("SetTo failed with %s %s %v", result.Key, result.Value, result.TTL)
}
}

View File

@ -0,0 +1,43 @@
package etcd
// SetDir sets the given key to a directory.
func (c *Client) SetDir(key string, ttl uint64) (*Response, error) {
return c.put(key, "", ttl, nil)
}
// UpdateDir updates the given key to a directory. It succeeds only if the
// given key already exists.
func (c *Client) UpdateDir(key string, ttl uint64) (*Response, error) {
return c.put(key, "", ttl, options{
"prevExist": true,
})
}
// UpdateDir creates a directory under the given key. It succeeds only if
// the given key does not yet exist.
func (c *Client) CreateDir(key string, ttl uint64) (*Response, error) {
return c.put(key, "", ttl, options{
"prevExist": false,
})
}
// Set sets the given key to the given value.
func (c *Client) Set(key string, value string, ttl uint64) (*Response, error) {
return c.put(key, value, ttl, nil)
}
// Update updates the given key to the given value. It succeeds only if the
// given key already exists.
func (c *Client) Update(key string, value string, ttl uint64) (*Response, error) {
return c.put(key, value, ttl, options{
"prevExist": true,
})
}
// Create creates a file with the given value under the given key. It succeeds
// only if the given key does not yet exist.
func (c *Client) Create(key string, value string, ttl uint64) (*Response, error) {
return c.put(key, value, ttl, options{
"prevExist": false,
})
}

View File

@ -0,0 +1,183 @@
package etcd
import (
"testing"
)
func TestSet(t *testing.T) {
c := NewClient(nil)
defer func() {
c.DeleteAll("foo")
}()
resp, err := c.Set("foo", "bar", 5)
if err != nil {
t.Fatal(err)
}
if resp.Key != "/foo" || resp.Value != "bar" || resp.TTL != 5 {
t.Fatalf("Set 1 failed: %#v", resp)
}
resp, err = c.Set("foo", "bar2", 5)
if err != nil {
t.Fatal(err)
}
if !(resp.Key == "/foo" && resp.Value == "bar2" &&
resp.PrevValue == "bar" && resp.TTL == 5) {
t.Fatalf("Set 2 failed: %#v", resp)
}
}
func TestUpdate(t *testing.T) {
c := NewClient(nil)
defer func() {
c.DeleteAll("foo")
c.DeleteAll("nonexistent")
}()
resp, err := c.Set("foo", "bar", 5)
t.Logf("%#v", resp)
if err != nil {
t.Fatal(err)
}
// This should succeed.
resp, err = c.Update("foo", "wakawaka", 5)
if err != nil {
t.Fatal(err)
}
if !(resp.Action == "update" && resp.Key == "/foo" &&
resp.PrevValue == "bar" && resp.TTL == 5) {
t.Fatalf("Update 1 failed: %#v", resp)
}
// This should fail because the key does not exist.
resp, err = c.Update("nonexistent", "whatever", 5)
if err == nil {
t.Fatalf("The key %v did not exist, so the update should have failed."+
"The response was: %#v", resp.Key, resp)
}
}
func TestCreate(t *testing.T) {
c := NewClient(nil)
defer func() {
c.DeleteAll("newKey")
}()
newKey := "/newKey"
newValue := "/newValue"
// This should succeed
resp, err := c.Create(newKey, newValue, 5)
if err != nil {
t.Fatal(err)
}
if !(resp.Action == "create" && resp.Key == newKey &&
resp.Value == newValue && resp.PrevValue == "" && resp.TTL == 5) {
t.Fatalf("Create 1 failed: %#v", resp)
}
// This should fail, because the key is already there
resp, err = c.Create(newKey, newValue, 5)
if err == nil {
t.Fatalf("The key %v did exist, so the creation should have failed."+
"The response was: %#v", resp.Key, resp)
}
}
func TestSetDir(t *testing.T) {
c := NewClient(nil)
defer func() {
c.DeleteAll("foo")
c.DeleteAll("fooDir")
}()
resp, err := c.SetDir("fooDir", 5)
if err != nil {
t.Fatal(err)
}
if !(resp.Key == "/fooDir" && resp.Value == "" && resp.TTL == 5) {
t.Fatalf("SetDir 1 failed: %#v", resp)
}
// This should fail because /fooDir already points to a directory
resp, err = c.SetDir("/fooDir", 5)
if err == nil {
t.Fatalf("fooDir already points to a directory, so SetDir should have failed."+
"The response was: %#v", resp)
}
_, err = c.Set("foo", "bar", 5)
if err != nil {
t.Fatal(err)
}
// This should succeed
resp, err = c.SetDir("foo", 5)
if err != nil {
t.Fatal(err)
}
if !(resp.Key == "/foo" && resp.Value == "" &&
resp.PrevValue == "bar" && resp.TTL == 5) {
t.Fatalf("SetDir 2 failed: %#v", resp)
}
}
func TestUpdateDir(t *testing.T) {
c := NewClient(nil)
defer func() {
c.DeleteAll("fooDir")
}()
resp, err := c.SetDir("fooDir", 5)
t.Logf("%#v", resp)
if err != nil {
t.Fatal(err)
}
// This should succeed.
resp, err = c.UpdateDir("fooDir", 5)
if err != nil {
t.Fatal(err)
}
if !(resp.Action == "update" && resp.Key == "/fooDir" &&
resp.Value == "" && resp.PrevValue == "" && resp.TTL == 5) {
t.Fatalf("UpdateDir 1 failed: %#v", resp)
}
// This should fail because the key does not exist.
resp, err = c.UpdateDir("nonexistentDir", 5)
if err == nil {
t.Fatalf("The key %v did not exist, so the update should have failed."+
"The response was: %#v", resp.Key, resp)
}
}
func TestCreateDir(t *testing.T) {
c := NewClient(nil)
defer func() {
c.DeleteAll("fooDir")
}()
// This should succeed
resp, err := c.CreateDir("fooDir", 5)
if err != nil {
t.Fatal(err)
}
if !(resp.Action == "create" && resp.Key == "/fooDir" &&
resp.Value == "" && resp.PrevValue == "" && resp.TTL == 5) {
t.Fatalf("CreateDir 1 failed: %#v", resp)
}
// This should fail, because the key is already there
resp, err = c.CreateDir("fooDir", 5)
if err == nil {
t.Fatalf("The key %v did exist, so the creation should have failed."+
"The response was: %#v", resp.Key, resp)
}
}

View File

@ -1,56 +0,0 @@
package etcd
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"path"
)
func (c *Client) TestAndSet(key string, prevValue string, value string, ttl uint64) (*Response, bool, error) {
logger.Debugf("set %s, %s[%s], ttl: %d, [%s]", key, value, prevValue, ttl, c.cluster.Leader)
v := url.Values{}
v.Set("value", value)
v.Set("prevValue", prevValue)
if ttl > 0 {
v.Set("ttl", fmt.Sprintf("%v", ttl))
}
resp, err := c.sendRequest("POST", path.Join("keys", key), v.Encode())
if err != nil {
return nil, false, err
}
b, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, false, err
}
if resp.StatusCode != http.StatusOK {
return nil, false, handleError(b)
}
var result Response
err = json.Unmarshal(b, &result)
if err != nil {
return nil, false, err
}
if result.PrevValue == prevValue && result.Value == value {
return &result, true, nil
}
return &result, false, nil
}

View File

@ -1,39 +0,0 @@
package etcd
import (
"testing"
"time"
)
func TestTestAndSet(t *testing.T) {
c := NewClient(nil)
c.Set("foo_testAndSet", "bar", 100)
time.Sleep(time.Second)
results := make(chan bool, 3)
for i := 0; i < 3; i++ {
testAndSet("foo_testAndSet", "bar", "barbar", results, c)
}
count := 0
for i := 0; i < 3; i++ {
result := <-results
if result {
count++
}
}
if count != 1 {
t.Fatalf("test and set fails %v", count)
}
}
func testAndSet(key string, prevValue string, value string, ch chan bool, c *Client) {
_, success, _ := c.TestAndSet(key, prevValue, value, 0)
ch <- success
}

View File

@ -0,0 +1,33 @@
// Utility functions
package etcd
import (
"fmt"
"net/url"
"reflect"
)
// Convert options to a string of HTML parameters
func optionsToString(options options, vops validOptions) (string, error) {
p := "?"
v := url.Values{}
for opKey, opVal := range options {
// Check if the given option is valid (that it exists)
kind := vops[opKey]
if kind == reflect.Invalid {
return "", fmt.Errorf("Invalid option: %v", opKey)
}
// Check if the given option is of the valid type
t := reflect.TypeOf(opVal)
if kind != t.Kind() {
return "", fmt.Errorf("Option %s should be of %v kind, not of %v kind.",
opKey, kind, t.Kind())
}
v.Set(opKey, fmt.Sprintf("%v", opVal))
}
p += v.Encode()
return p, nil
}

View File

@ -1,3 +1,3 @@
package etcd
const version = "v1"
const version = "v2"

View File

@ -1,42 +1,47 @@
package etcd
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"path"
)
type respAndErr struct {
resp *http.Response
err error
}
// Errors introduced by the Watch command.
var (
ErrWatchStoppedByUser = errors.New("Watch stopped by the user via stop channel")
)
// Watch any change under the given prefix.
// When a sinceIndex is given, watch will try to scan from that index to the last index
// and will return any changes under the given prefix during the history
// WatchAll returns the first change under the given prefix since the given index. To
// watch for the latest change, set waitIndex = 0.
//
// If the prefix points to a directory, any change under it, including all child directories,
// will be returned.
//
// If a receiver channel is given, it will be a long-term watch. Watch will block at the
// channel. And after someone receive the channel, it will go on to watch that prefix.
// If a stop channel is given, client can close long-term watch using the stop channel
func (c *Client) WatchAll(prefix string, waitIndex uint64, receiver chan *Response, stop chan bool) (*Response, error) {
return c.watch(prefix, waitIndex, true, receiver, stop)
}
func (c *Client) Watch(prefix string, sinceIndex uint64, receiver chan *Response, stop chan bool) (*Response, error) {
// Watch returns the first change to the given key since the given index. To
// watch for the latest change, set waitIndex = 0.
//
// If a receiver channel is given, it will be a long-term watch. Watch will block at the
// channel. And after someone receive the channel, it will go on to watch that
// prefix. If a stop channel is given, client can close long-term watch using
// the stop channel
func (c *Client) Watch(key string, waitIndex uint64, receiver chan *Response, stop chan bool) (*Response, error) {
return c.watch(key, waitIndex, false, receiver, stop)
}
func (c *Client) watch(prefix string, waitIndex uint64, recursive bool, receiver chan *Response, stop chan bool) (*Response, error) {
logger.Debugf("watch %s [%s]", prefix, c.cluster.Leader)
if receiver == nil {
return c.watchOnce(prefix, sinceIndex, stop)
return c.watchOnce(prefix, waitIndex, recursive, stop)
} else {
for {
resp, err := c.watchOnce(prefix, sinceIndex, stop)
resp, err := c.watchOnce(prefix, waitIndex, recursive, stop)
if resp != nil {
sinceIndex = resp.Index + 1
waitIndex = resp.ModifiedIndex + 1
receiver <- resp
} else {
return nil, err
@ -49,70 +54,37 @@ func (c *Client) Watch(prefix string, sinceIndex uint64, receiver chan *Response
// helper func
// return when there is change under the given prefix
func (c *Client) watchOnce(key string, sinceIndex uint64, stop chan bool) (*Response, error) {
func (c *Client) watchOnce(key string, waitIndex uint64, recursive bool, stop chan bool) (*Response, error) {
var resp *http.Response
var err error
respChan := make(chan *Response)
errChan := make(chan error)
if stop != nil {
ch := make(chan respAndErr)
go func() {
resp, err = c.sendWatchRequest(key, sinceIndex)
ch <- respAndErr{resp, err}
}()
// select at stop or continue to receive
select {
case res := <-ch:
resp, err = res.resp, res.err
case <-stop:
resp, err = nil, ErrWatchStoppedByUser
go func() {
options := options{
"wait": true,
}
if waitIndex > 0 {
options["waitIndex"] = waitIndex
}
if recursive {
options["recursive"] = true
}
} else {
resp, err = c.sendWatchRequest(key, sinceIndex)
}
if err != nil {
resp, err := c.get(key, options)
if err != nil {
errChan <- err
}
respChan <- resp
}()
select {
case resp := <-respChan:
return resp, nil
case err := <-errChan:
return nil, err
case <-stop:
return nil, ErrWatchStoppedByUser
}
b, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, handleError(b)
}
var result Response
err = json.Unmarshal(b, &result)
if err != nil {
return nil, err
}
return &result, nil
}
func (c *Client) sendWatchRequest(key string, sinceIndex uint64) (*http.Response, error) {
if sinceIndex == 0 {
resp, err := c.sendRequest("GET", path.Join("watch", key), "")
return resp, err
} else {
v := url.Values{}
v.Set("index", fmt.Sprintf("%v", sinceIndex))
resp, err := c.sendRequest("POST", path.Join("watch", key), v.Encode())
return resp, err
}
}

View File

@ -8,31 +8,34 @@ import (
func TestWatch(t *testing.T) {
c := NewClient(nil)
defer func() {
c.DeleteAll("watch_foo")
}()
go setHelper("bar", c)
go setHelper("watch_foo", "bar", c)
result, err := c.Watch("watch_foo", 0, nil, nil)
if err != nil || result.Key != "/watch_foo/foo" || result.Value != "bar" {
if err != nil {
t.Fatal(err)
}
t.Fatalf("Watch failed with %s %s %v %v", result.Key, result.Value, result.TTL, result.Index)
resp, err := c.Watch("watch_foo", 0, nil, nil)
if err != nil {
t.Fatal(err)
}
if !(resp.Key == "/watch_foo" && resp.Value == "bar") {
t.Fatalf("Watch 1 failed: %#v", resp)
}
result, err = c.Watch("watch_foo", result.Index, nil, nil)
go setHelper("watch_foo", "bar", c)
if err != nil || result.Key != "/watch_foo/foo" || result.Value != "bar" {
if err != nil {
t.Fatal(err)
}
t.Fatalf("Watch with Index failed with %s %s %v %v", result.Key, result.Value, result.TTL, result.Index)
resp, err = c.Watch("watch_foo", resp.ModifiedIndex, nil, nil)
if err != nil {
t.Fatal(err)
}
if !(resp.Key == "/watch_foo" && resp.Value == "bar") {
t.Fatalf("Watch 2 failed: %#v", resp)
}
ch := make(chan *Response, 10)
stop := make(chan bool, 1)
go setLoop("bar", c)
go setLoop("watch_foo", "bar", c)
go receiver(ch, stop)
@ -42,16 +45,55 @@ func TestWatch(t *testing.T) {
}
}
func setHelper(value string, c *Client) {
time.Sleep(time.Second)
c.Set("watch_foo/foo", value, 100)
func TestWatchAll(t *testing.T) {
c := NewClient(nil)
defer func() {
c.DeleteAll("watch_foo")
}()
go setHelper("watch_foo/foo", "bar", c)
resp, err := c.WatchAll("watch_foo", 0, nil, nil)
if err != nil {
t.Fatal(err)
}
if !(resp.Key == "/watch_foo/foo" && resp.Value == "bar") {
t.Fatalf("WatchAll 1 failed: %#v", resp)
}
go setHelper("watch_foo/foo", "bar", c)
resp, err = c.WatchAll("watch_foo", resp.ModifiedIndex, nil, nil)
if err != nil {
t.Fatal(err)
}
if !(resp.Key == "/watch_foo/foo" && resp.Value == "bar") {
t.Fatalf("WatchAll 2 failed: %#v", resp)
}
ch := make(chan *Response, 10)
stop := make(chan bool, 1)
go setLoop("watch_foo/foo", "bar", c)
go receiver(ch, stop)
_, err = c.WatchAll("watch_foo", 0, ch, stop)
if err != ErrWatchStoppedByUser {
t.Fatalf("Watch returned a non-user stop error")
}
}
func setLoop(value string, c *Client) {
func setHelper(key, value string, c *Client) {
time.Sleep(time.Second)
c.Set(key, value, 100)
}
func setLoop(key, value string, c *Client) {
time.Sleep(time.Second)
for i := 0; i < 10; i++ {
newValue := fmt.Sprintf("%s_%v", value, i)
c.Set("watch_foo/foo", newValue, 100)
c.Set(key, newValue, 100)
time.Sleep(time.Second / 10)
}
}

View File

@ -1,71 +0,0 @@
package main
import (
"fmt"
"github.com/coreos/go-etcd/etcd"
)
var count = 0
func main() {
good := 0
bad := 0
ch := make(chan bool, 10)
// set up a lock
c := etcd.NewClient()
c.Set("lock", "unlock", 0)
for i := 0; i < 10; i++ {
go t(i, ch, etcd.NewClient())
}
for i := 0; i < 10; i++ {
if <-ch {
good++
} else {
bad++
}
}
fmt.Println("good: ", good, "bad: ", bad)
}
func t(num int, ch chan bool, c *etcd.Client) {
for i := 0; i < 100; i++ {
if lock(c) {
// a stupid spin lock
count++
fmt.Println(num, " got the lock and update count to", count)
unlock(c)
fmt.Println(num, " released the lock")
} else {
ch <- false
return
}
}
ch <- true
}
// A stupid spin lock
func lock(c *etcd.Client) bool {
for {
_, success, _ := c.TestAndSet("lock", "unlock", "lock", 0)
if success != true {
fmt.Println("tried lock failed!")
} else {
return true
}
}
}
func unlock(c *etcd.Client) {
for {
_, err := c.Set("lock", "unlock", 0)
if err == nil {
return
}
fmt.Println(err)
}
}

View File

@ -1,31 +0,0 @@
package main
import (
"fmt"
"github.com/coreos/go-etcd/etcd"
"time"
)
var count = 0
func main() {
ch := make(chan bool, 10)
// set up a lock
for i := 0; i < 100; i++ {
go t(i, ch, etcd.NewClient())
}
start := time.Now()
for i := 0; i < 100; i++ {
<-ch
}
fmt.Println(time.Now().Sub(start), ": ", 100*50, "commands")
}
func t(num int, ch chan bool, c *etcd.Client) {
c.SyncCluster()
for i := 0; i < 50; i++ {
str := fmt.Sprintf("foo_%d", num*i)
c.Set(str, "10", 0)
}
ch <- true
}

View File

@ -1,3 +0,0 @@
Example script from the sync-cluster bug https://github.com/coreos/go-etcd/issues/27
TODO: turn this into a test case

View File

@ -1,51 +0,0 @@
package main
import (
"fmt"
"github.com/coreos/go-etcd/etcd"
"strconv"
"time"
)
func main() {
fmt.Println("etcd-client started")
c := etcd.NewClient(nil)
c.SetCluster([]string{
"http://127.0.0.1:4001",
"http://127.0.0.1:4002",
"http://127.0.0.1:4003",
})
ticker := time.NewTicker(time.Second * 3)
for {
select {
case d := <-ticker.C:
n := d.Second()
if n <= 0 {
n = 60
}
for ok := c.SyncCluster(); ok == false; {
fmt.Println("SyncCluster failed, trying again")
time.Sleep(100 * time.Millisecond)
}
result, err := c.Set("foo", "exp_"+strconv.Itoa(n), 0)
if err != nil {
fmt.Println("set error", err)
} else {
fmt.Printf("set %+v\n", result)
}
ss, err := c.Get("foo")
if err != nil {
fmt.Println("get error", err)
} else {
fmt.Println(len(ss))
}
}
}
}

View File

@ -1,170 +0,0 @@
package raft
import (
"math/rand"
"sync"
"time"
)
//------------------------------------------------------------------------------
//
// Typedefs
//
//------------------------------------------------------------------------------
type timer struct {
fireChan chan time.Time
stopChan chan bool
state int
rand *rand.Rand
minDuration time.Duration
maxDuration time.Duration
internalTimer *time.Timer
mutex sync.Mutex
}
const (
STOPPED = iota
READY
RUNNING
)
//------------------------------------------------------------------------------
//
// Constructors
//
//------------------------------------------------------------------------------
// Creates a new timer. Panics if a non-positive duration is used.
func newTimer(minDuration time.Duration, maxDuration time.Duration) *timer {
if minDuration <= 0 {
panic("raft: Non-positive minimum duration not allowed")
} else if maxDuration <= 0 {
panic("raft: Non-positive maximum duration not allowed")
} else if minDuration > maxDuration {
panic("raft: Minimum duration cannot be greater than maximum duration")
}
return &timer{
minDuration: minDuration,
maxDuration: maxDuration,
state: READY,
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
stopChan: make(chan bool, 1),
fireChan: make(chan time.Time),
}
}
//------------------------------------------------------------------------------
//
// Accessors
//
//------------------------------------------------------------------------------
// Sets the minimum and maximum duration of the timer.
func (t *timer) setDuration(duration time.Duration) {
t.minDuration = duration
t.maxDuration = duration
}
//------------------------------------------------------------------------------
//
// Methods
//
//------------------------------------------------------------------------------
// Checks if the timer is currently running.
func (t *timer) running() bool {
return t.state == RUNNING
}
// Stops the timer and closes the channel.
func (t *timer) stop() {
t.mutex.Lock()
defer t.mutex.Unlock()
if t.internalTimer != nil {
t.internalTimer.Stop()
}
if t.state != STOPPED {
t.state = STOPPED
// non-blocking buffer
t.stopChan <- true
}
}
// Change the state of timer to ready
func (t *timer) ready() {
t.mutex.Lock()
defer t.mutex.Unlock()
if t.state == RUNNING {
panic("Timer is already running")
}
t.state = READY
t.stopChan = make(chan bool, 1)
t.fireChan = make(chan time.Time)
}
// Fire at the timer
func (t *timer) fire() {
select {
case t.fireChan <- time.Now():
return
default:
return
}
}
// Start the timer, this func will be blocked until the timer:
// (1) times out
// (2) stopped
// (3) fired
// Return false if stopped.
// Make sure the start func will not restart the stopped timer.
func (t *timer) start() bool {
t.mutex.Lock()
if t.state != READY {
t.mutex.Unlock()
return false
}
t.state = RUNNING
d := t.minDuration
if t.maxDuration > t.minDuration {
d += time.Duration(t.rand.Int63n(int64(t.maxDuration - t.minDuration)))
}
t.internalTimer = time.NewTimer(d)
internalTimer := t.internalTimer
t.mutex.Unlock()
// Wait for the timer channel, stop channel or fire channel.
stopped := false
select {
case <-internalTimer.C:
case <-t.fireChan:
case <-t.stopChan:
stopped = true
}
// Clean up timer and state.
t.mutex.Lock()
t.internalTimer.Stop()
t.internalTimer = nil
if stopped {
t.state = STOPPED
} else if t.state == RUNNING {
t.state = READY
}
t.mutex.Unlock()
return !stopped
}

View File

@ -1,86 +0,0 @@
package raft
import (
"testing"
"time"
)
//------------------------------------------------------------------------------
//
// Tests
//
//------------------------------------------------------------------------------
// Ensure that we can start an election timer and it will go off in the specified duration.
func TestTimer(t *testing.T) {
timer := newTimer(5*time.Millisecond, 10*time.Millisecond)
// test timer start
for i := 0; i < 10; i++ {
start := time.Now()
timer.start()
duration := time.Now().Sub(start)
if duration > 12*time.Millisecond || duration < 5*time.Millisecond {
t.Fatal("Duration Error! ", duration)
}
}
// test timer stop
for i := 0; i < 100; i++ {
start := time.Now()
go stop(timer)
timer.start()
duration := time.Now().Sub(start)
if duration > 3*time.Millisecond {
t.Fatal("Duration Error! ", duration)
}
// ready the timer after stop it
timer.ready()
}
// test timer fire
for i := 0; i < 100; i++ {
start := time.Now()
go fire(timer)
timer.start()
duration := time.Now().Sub(start)
if duration > 3*time.Millisecond {
t.Fatal("Fire Duration Error! ", duration)
}
}
resp := make(chan bool)
// play with start and stop
// make sure we can stop timer
// in all the possible seq of start and stop
for i := 0; i < 100; i++ {
go stop(timer)
go start(timer, resp)
ret := <-resp
if ret != false {
t.Fatal("cannot stop timer!")
}
timer.ready()
}
}
func stop(t *timer) {
time.Sleep(time.Millisecond)
t.stop()
}
func start(t *timer, resp chan bool) {
time.Sleep(time.Millisecond)
resp <- t.start()
}
func fire(t *timer) {
time.Sleep(time.Millisecond)
t.fire()
}

View File

@ -12,7 +12,15 @@ const (
listenFdsStart = 3
)
func Files() []*os.File {
func Files(unsetEnv bool) []*os.File {
if unsetEnv {
// there is no way to unset env in golang os package for now
// https://code.google.com/p/go/issues/detail?id=6423
defer os.Setenv("LISTEN_PID", "")
defer os.Setenv("LISTEN_FDS", "")
}
pid, err := strconv.Atoi(os.Getenv("LISTEN_PID"))
if err != nil || pid != os.Getpid() {
return nil
@ -24,7 +32,7 @@ func Files() []*os.File {
files := []*os.File(nil)
for fd := listenFdsStart; fd < listenFdsStart+nfds; fd++ {
syscall.CloseOnExec(fd)
files = append(files, os.NewFile(uintptr(fd), "LISTEN_FD_" + strconv.Itoa(fd)))
files = append(files, os.NewFile(uintptr(fd), "LISTEN_FD_"+strconv.Itoa(fd)))
}
return files
}

View File

@ -25,32 +25,36 @@ type Conn struct {
dispatch map[string]func(dbus.Signal)
}
func New() *Conn {
func New() (*Conn, error) {
c := new(Conn)
c.initConnection()
if err := c.initConnection(); err != nil {
return nil, err
}
c.initJobs()
c.initSubscription()
c.initDispatch()
return c
return c, nil
}
func (c *Conn) initConnection() {
func (c *Conn) initConnection() error {
var err error
c.sysconn, err = dbus.SystemBusPrivate()
if err != nil {
return
return err
}
err = c.sysconn.Auth(nil)
if err != nil {
c.sysconn.Close()
return
return err
}
err = c.sysconn.Hello()
if err != nil {
c.sysconn.Close()
return
return err
}
c.sysobj = c.sysconn.Object("org.freedesktop.systemd1", dbus.ObjectPath("/org/freedesktop/systemd1"))
@ -65,8 +69,10 @@ func (c *Conn) initConnection() {
err = c.sysobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store()
if err != nil {
c.sysconn.Close()
return
return err
}
return nil
}
func (c *Conn) initDispatch() {

View File

@ -164,3 +164,53 @@ type UnitStatus struct {
JobType string // The job type as string
JobPath dbus.ObjectPath // The job object path
}
// EnableUnitFiles() may be used to enable one or more units in the system (by
// creating symlinks to them in /etc or /run).
//
// It takes a list of unit files to enable (either just file names or full
// absolute paths if the unit files are residing outside the usual unit
// search paths), and two booleans: the first controls whether the unit shall
// be enabled for runtime only (true, /run), or persistently (false, /etc).
// The second one controls whether symlinks pointing to other units shall
// be replaced if necessary.
//
// This call returns one boolean and an array with the changes made. The
// boolean signals whether the unit files contained any enablement
// information (i.e. an [Install]) section. The changes list consists of
// structures with three strings: the type of the change (one of symlink
// or unlink), the file name of the symlink and the destination of the
// symlink.
func (c *Conn) EnableUnitFiles(files []string, runtime bool, force bool) (bool, []EnableUnitFileChange, error) {
var carries_install_info bool
result := make([][]interface{}, 0)
err := c.sysobj.Call("EnableUnitFiles", 0, files, runtime, force).Store(&carries_install_info, &result)
if err != nil {
return false, nil, err
}
resultInterface := make([]interface{}, len(result))
for i := range result {
resultInterface[i] = result[i]
}
changes := make([]EnableUnitFileChange, len(result))
changesInterface := make([]interface{}, len(changes))
for i := range changes {
changesInterface[i] = &changes[i]
}
err = dbus.Store(resultInterface, changesInterface...)
if err != nil {
return false, nil, err
}
return carries_install_info, changes, nil
}
type EnableUnitFileChange struct {
Type string // Type of the change (one of symlink or unlink)
Filename string // File name of the symlink
Destination string // Destination of the symlink
}

View File

@ -20,9 +20,7 @@ import (
// used for construction of the scope only and specifies the initial PIDs to
// add to the scope object.
type Property property
type property struct {
type Property struct {
Name string
Value dbus.Variant
}
@ -38,96 +36,95 @@ type execStart struct {
// the executed command. See
// http://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart=
func PropExecStart(command []string, uncleanIsFailure bool) Property {
return Property(
property{
Name: "ExecStart",
Value: dbus.MakeVariant(
[]execStart{
execStart{
Path: command[0],
Args: command,
UncleanIsFailure: uncleanIsFailure,
}})})
execStarts := []execStart{
execStart{
Path: command[0],
Args: command,
UncleanIsFailure: uncleanIsFailure,
},
}
return Property{
Name: "ExecStart",
Value: dbus.MakeVariant(execStarts),
}
}
// PropRemainAfterExit sets the RemainAfterExit service property. See
// http://www.freedesktop.org/software/systemd/man/systemd.service.html#RemainAfterExit=
func PropRemainAfterExit(b bool) Property {
return Property(
property{
Name: "RemainAfterExit",
Value: dbus.MakeVariant(b),
})
return Property{
Name: "RemainAfterExit",
Value: dbus.MakeVariant(b),
}
}
// PropDescription sets the Description unit property. See
// http://www.freedesktop.org/software/systemd/man/systemd.unit#Description=
func PropDescription(desc string) Property {
return Property(
property{
Name: "Description",
Value: dbus.MakeVariant(desc),
})
return Property{
Name: "Description",
Value: dbus.MakeVariant(desc),
}
}
func propDependency(name string, units []string) Property {
return Property(
property{
Name: name,
Value: dbus.MakeVariant(units),
})
return Property{
Name: name,
Value: dbus.MakeVariant(units),
}
}
// PropRequires sets the Requires unit property. See
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#Requires=
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requires=
func PropRequires(units ...string) Property {
return propDependency("Requires", units)
}
// PropRequiresOverridable sets the RequiresOverridable unit property. See
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#RequiresOverridable=
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresOverridable=
func PropRequiresOverridable(units ...string) Property {
return propDependency("RequiresOverridable", units)
}
// PropRequisite sets the Requisite unit property. See
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#Requisite=
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requisite=
func PropRequisite(units ...string) Property {
return propDependency("Requisite", units)
}
// PropRequisiteOverridable sets the RequisiteOverridable unit property. See
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#RequisiteOverridable=
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequisiteOverridable=
func PropRequisiteOverridable(units ...string) Property {
return propDependency("RequisiteOverridable", units)
}
// PropWants sets the Wants unit property. See
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#Wants=
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Wants=
func PropWants(units ...string) Property {
return propDependency("Wants", units)
}
// PropBindsTo sets the BindsTo unit property. See
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#BindsTo=
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#BindsTo=
func PropBindsTo(units ...string) Property {
return propDependency("BindsTo", units)
}
// PropRequiredBy sets the RequiredBy unit property. See
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#RequiredBy=
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredBy=
func PropRequiredBy(units ...string) Property {
return propDependency("RequiredBy", units)
}
// PropRequiredByOverridable sets the RequiredByOverridable unit property. See
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#RequiredByOverridable=
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredByOverridable=
func PropRequiredByOverridable(units ...string) Property {
return propDependency("RequiredByOverridable", units)
}
// PropWantedBy sets the WantedBy unit property. See
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#WantedBy=
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#WantedBy=
func PropWantedBy(units ...string) Property {
return propDependency("WantedBy", units)
}
@ -139,55 +136,55 @@ func PropBoundBy(units ...string) Property {
}
// PropConflicts sets the Conflicts unit property. See
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#Conflicts=
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Conflicts=
func PropConflicts(units ...string) Property {
return propDependency("Conflicts", units)
}
// PropConflictedBy sets the ConflictedBy unit property. See
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#ConflictedBy=
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#ConflictedBy=
func PropConflictedBy(units ...string) Property {
return propDependency("ConflictedBy", units)
}
// PropBefore sets the Before unit property. See
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#Before=
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Before=
func PropBefore(units ...string) Property {
return propDependency("Before", units)
}
// PropAfter sets the After unit property. See
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#After=
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#After=
func PropAfter(units ...string) Property {
return propDependency("After", units)
}
// PropOnFailure sets the OnFailure unit property. See
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#OnFailure=
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#OnFailure=
func PropOnFailure(units ...string) Property {
return propDependency("OnFailure", units)
}
// PropTriggers sets the Triggers unit property. See
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#Triggers=
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Triggers=
func PropTriggers(units ...string) Property {
return propDependency("Triggers", units)
}
// PropTriggeredBy sets the TriggeredBy unit property. See
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#TriggeredBy=
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#TriggeredBy=
func PropTriggeredBy(units ...string) Property {
return propDependency("TriggeredBy", units)
}
// PropPropagatesReloadTo sets the PropagatesReloadTo unit property. See
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#PropagatesReloadTo=
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#PropagatesReloadTo=
func PropPropagatesReloadTo(units ...string) Property {
return propDependency("PropagatesReloadTo", units)
}
// PropRequiresMountsFor sets the RequiresMountsFor unit property. See
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#RequiresMountsFor=
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresMountsFor=
func PropRequiresMountsFor(units ...string) Property {
return propDependency("RequiresMountsFor", units)
}

View File

@ -1,8 +1,10 @@
package dbus
import (
"github.com/guelfey/go.dbus"
"errors"
"time"
"github.com/guelfey/go.dbus"
)
const (
@ -71,12 +73,6 @@ type SubStateUpdate struct {
SubState string
}
type Error string
func (e Error) Error() string {
return string(e)
}
// SetSubStateSubscriber writes to updateCh when any unit's substate changes.
// Althrough this writes to updateCh on every state change, the reported state
// may be more recent than the change that generated it (due to an unavoidable
@ -104,7 +100,7 @@ func (c *Conn) sendSubStateUpdate(path dbus.ObjectPath) {
return
}
info, err := c.getUnitInfo(path)
info, err := c.GetUnitInfo(path)
if err != nil {
select {
case c.subscriber.errCh <- err:
@ -120,7 +116,7 @@ func (c *Conn) sendSubStateUpdate(path dbus.ObjectPath) {
case c.subscriber.updateCh <- update:
default:
select {
case c.subscriber.errCh <- Error("update channel full!"):
case c.subscriber.errCh <- errors.New("update channel full!"):
default:
}
}
@ -128,7 +124,7 @@ func (c *Conn) sendSubStateUpdate(path dbus.ObjectPath) {
c.updateIgnore(path, info)
}
func (c *Conn) getUnitInfo(path dbus.ObjectPath) (map[string]dbus.Variant, error) {
func (c *Conn) GetUnitInfo(path dbus.ObjectPath) (map[string]dbus.Variant, error) {
var err error
var props map[string]dbus.Variant
obj := c.sysconn.Object("org.freedesktop.systemd1", path)

View File

@ -103,7 +103,7 @@ func appendVariable(w io.Writer, name, value string) {
fmt.Fprintln(w, value)
} else {
/* just write the variable and value all on one line */
fmt.Fprintf(w, "%s=%s\n", name, value)
fmt.Fprintln(w, "%s=%s", name, value)
}
}
@ -111,8 +111,8 @@ func validVarName(name string) bool {
/* The variable name must be in uppercase and consist only of characters,
* numbers and underscores, and may not begin with an underscore. (from the docs)
*/
valid := true
valid = valid && name[0] != '_'
valid := name[0] != '_'
for _, c := range name {
valid = valid && ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_'
}

View File

@ -2,7 +2,7 @@ package raft
import (
"code.google.com/p/goprotobuf/proto"
"github.com/coreos/go-raft/protobuf"
"github.com/coreos/raft/protobuf"
"io"
"io/ioutil"
)

View File

@ -2,7 +2,7 @@ package raft
import (
"code.google.com/p/goprotobuf/proto"
"github.com/coreos/go-raft/protobuf"
"github.com/coreos/raft/protobuf"
"io"
"io/ioutil"
)

Some files were not shown because too many files have changed in this diff Show More