mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
bump deps
This commit is contained in:
parent
68e7455374
commit
8a0496cfae
2
etcd.go
2
etcd.go
@ -23,7 +23,7 @@ import (
|
|||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/etcd/server"
|
"github.com/coreos/etcd/server"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
etcdErr "github.com/coreos/etcd/error"
|
etcdErr "github.com/coreos/etcd/error"
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -16,7 +16,7 @@ import (
|
|||||||
etcdErr "github.com/coreos/etcd/error"
|
etcdErr "github.com/coreos/etcd/error"
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
etcdErr "github.com/coreos/etcd/error"
|
etcdErr "github.com/coreos/etcd/error"
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ package server
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
type raftServerStats struct {
|
type raftServerStats struct {
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -17,7 +17,7 @@ import (
|
|||||||
"github.com/coreos/etcd/server/v2"
|
"github.com/coreos/etcd/server/v2"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
_ "github.com/coreos/etcd/store/v2"
|
_ "github.com/coreos/etcd/store/v2"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Timeout for setup internal raft http connection
|
// Timeout for setup internal raft http connection
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
etcdErr "github.com/coreos/etcd/error"
|
etcdErr "github.com/coreos/etcd/error"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ package v1
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
etcdErr "github.com/coreos/etcd/error"
|
etcdErr "github.com/coreos/etcd/error"
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
etcdErr "github.com/coreos/etcd/error"
|
etcdErr "github.com/coreos/etcd/error"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ package v2
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A lookup of factories by version.
|
// A lookup of factories by version.
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -3,7 +3,7 @@ package v2
|
|||||||
import (
|
import (
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -3,7 +3,7 @@ package v2
|
|||||||
import (
|
import (
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build darwin freebsd linux netbsd openbsd
|
|
||||||
|
|
||||||
package ipv4_test
|
package ipv4_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -75,6 +73,10 @@ func writeThenReadDatagram(t *testing.T, i int, c *ipv4.RawConn, wb []byte, src,
|
|||||||
return b
|
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
|
// LoopbackInterface returns a logical network interface for loopback
|
||||||
// tests.
|
// tests.
|
||||||
func loopbackInterface() *net.Interface {
|
func loopbackInterface() *net.Interface {
|
||||||
@ -83,9 +85,25 @@ func loopbackInterface() *net.Interface {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for _, ifi := range ift {
|
for _, ifi := range ift {
|
||||||
if ifi.Flags&net.FlagLoopback != 0 {
|
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
|
return &ifi
|
||||||
}
|
}
|
||||||
|
case *net.IPNet:
|
||||||
|
if isUnicast(ifa.IP) {
|
||||||
|
return &ifi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -94,31 +112,24 @@ func loopbackInterface() *net.Interface {
|
|||||||
// enabled network interface. It also returns a unicast IPv4 address
|
// enabled network interface. It also returns a unicast IPv4 address
|
||||||
// that can be used for listening on ifi.
|
// that can be used for listening on ifi.
|
||||||
func isMulticastAvailable(ifi *net.Interface) (net.IP, bool) {
|
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
|
return nil, false
|
||||||
}
|
}
|
||||||
ifat, err := ifi.Addrs()
|
ifat, err := ifi.Addrs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
if len(ifat) == 0 {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
var ip net.IP
|
|
||||||
for _, ifa := range ifat {
|
for _, ifa := range ifat {
|
||||||
switch v := ifa.(type) {
|
switch ifa := ifa.(type) {
|
||||||
case *net.IPAddr:
|
case *net.IPAddr:
|
||||||
ip = v.IP
|
if isUnicast(ifa.IP) {
|
||||||
|
return ifa.IP, true
|
||||||
|
}
|
||||||
case *net.IPNet:
|
case *net.IPNet:
|
||||||
ip = v.IP
|
if isUnicast(ifa.IP) {
|
||||||
default:
|
return ifa.IP, true
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
if ip.To4() == nil {
|
|
||||||
ip = nil
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
}
|
||||||
return ip, true
|
return nil, false
|
||||||
}
|
}
|
||||||
|
@ -2,41 +2,41 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build darwin freebsd linux netbsd openbsd
|
|
||||||
|
|
||||||
package ipv4_test
|
package ipv4_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.google.com/p/go.net/ipv4"
|
"code.google.com/p/go.net/ipv4"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var udpMultipleGroupListenerTests = []struct {
|
var udpMultipleGroupListenerTests = []net.Addr{
|
||||||
gaddr *net.UDPAddr
|
&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, 249)}}, // see RFC 4727
|
&net.UDPAddr{IP: net.IPv4(224, 0, 0, 254)},
|
||||||
{&net.UDPAddr{IP: net.IPv4(224, 0, 0, 250)}}, // see RFC 4727
|
|
||||||
{&net.UDPAddr{IP: net.IPv4(224, 0, 0, 254)}}, // see RFC 4727
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
if testing.Short() || !*testExternal {
|
||||||
t.Skip("to avoid external network")
|
t.Skip("to avoid external network")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range udpMultipleGroupListenerTests {
|
for _, gaddr := range udpMultipleGroupListenerTests {
|
||||||
// listen to a wildcard address with no reusable port
|
c, err := net.ListenPacket("udp4", "0.0.0.0:0") // wildcard address with no reusable port
|
||||||
c, err := net.ListenPacket("udp4", "0.0.0.0:0")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("net.ListenPacket failed: %v", err)
|
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
p := ipv4.NewPacketConn(c)
|
p := ipv4.NewPacketConn(c)
|
||||||
|
|
||||||
var mift []*net.Interface
|
var mift []*net.Interface
|
||||||
|
|
||||||
ift, err := net.Interfaces()
|
ift, err := net.Interfaces()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("net.Interfaces failed: %v", err)
|
t.Fatalf("net.Interfaces failed: %v", err)
|
||||||
@ -45,34 +45,36 @@ func TestUDPSingleConnWithMultipleGroupListeners(t *testing.T) {
|
|||||||
if _, ok := isMulticastAvailable(&ifi); !ok {
|
if _, ok := isMulticastAvailable(&ifi); !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := p.JoinGroup(&ifi, tt.gaddr); err != nil {
|
if err := p.JoinGroup(&ifi, gaddr); err != nil {
|
||||||
t.Fatalf("ipv4.PacketConn.JoinGroup %v on %v failed: %v", tt.gaddr, ifi, err)
|
t.Fatalf("ipv4.PacketConn.JoinGroup %v on %v failed: %v", gaddr, ifi, err)
|
||||||
}
|
}
|
||||||
mift = append(mift, &ift[i])
|
mift = append(mift, &ift[i])
|
||||||
}
|
}
|
||||||
for _, ifi := range mift {
|
for _, ifi := range mift {
|
||||||
if err := p.LeaveGroup(ifi, tt.gaddr); err != nil {
|
if err := p.LeaveGroup(ifi, gaddr); err != nil {
|
||||||
t.Fatalf("ipv4.PacketConn.LeaveGroup %v on %v failed: %v", tt.gaddr, ifi, err)
|
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 {
|
if testing.Short() || !*testExternal {
|
||||||
t.Skip("to avoid external network")
|
t.Skip("to avoid external network")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range udpMultipleGroupListenerTests {
|
for _, gaddr := range udpMultipleGroupListenerTests {
|
||||||
// listen to a group address, actually a wildcard address
|
c1, err := net.ListenPacket("udp4", "224.0.0.0:1024") // wildcard address with reusable port
|
||||||
// with reusable port
|
|
||||||
c1, err := net.ListenPacket("udp4", "224.0.0.0:1024") // see RFC 4727
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("net.ListenPacket failed: %v", err)
|
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||||
}
|
}
|
||||||
defer c1.Close()
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("net.ListenPacket failed: %v", err)
|
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||||
}
|
}
|
||||||
@ -81,8 +83,8 @@ func TestUDPMultipleConnWithMultipleGroupListeners(t *testing.T) {
|
|||||||
var ps [2]*ipv4.PacketConn
|
var ps [2]*ipv4.PacketConn
|
||||||
ps[0] = ipv4.NewPacketConn(c1)
|
ps[0] = ipv4.NewPacketConn(c1)
|
||||||
ps[1] = ipv4.NewPacketConn(c2)
|
ps[1] = ipv4.NewPacketConn(c2)
|
||||||
|
|
||||||
var mift []*net.Interface
|
var mift []*net.Interface
|
||||||
|
|
||||||
ift, err := net.Interfaces()
|
ift, err := net.Interfaces()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("net.Interfaces failed: %v", err)
|
t.Fatalf("net.Interfaces failed: %v", err)
|
||||||
@ -92,70 +94,32 @@ func TestUDPMultipleConnWithMultipleGroupListeners(t *testing.T) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, p := range ps {
|
for _, p := range ps {
|
||||||
if err := p.JoinGroup(&ifi, tt.gaddr); err != nil {
|
if err := p.JoinGroup(&ifi, gaddr); err != nil {
|
||||||
t.Fatalf("ipv4.PacketConn.JoinGroup %v on %v failed: %v", tt.gaddr, ifi, err)
|
t.Fatalf("ipv4.PacketConn.JoinGroup %v on %v failed: %v", gaddr, ifi, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mift = append(mift, &ift[i])
|
mift = append(mift, &ift[i])
|
||||||
}
|
}
|
||||||
for _, ifi := range mift {
|
for _, ifi := range mift {
|
||||||
for _, p := range ps {
|
for _, p := range ps {
|
||||||
if err := p.LeaveGroup(ifi, tt.gaddr); err != nil {
|
if err := p.LeaveGroup(ifi, gaddr); err != nil {
|
||||||
t.Fatalf("ipv4.PacketConn.LeaveGroup %v on %v failed: %v", tt.gaddr, ifi, err)
|
t.Fatalf("ipv4.PacketConn.LeaveGroup %v on %v failed: %v", gaddr, ifi, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIPSingleConnWithSingleGroupListener(t *testing.T) {
|
func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
|
||||||
if testing.Short() || !*testExternal {
|
switch runtime.GOOS {
|
||||||
t.Skip("to avoid external network")
|
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 {
|
if testing.Short() || !*testExternal {
|
||||||
t.Skip("to avoid external network")
|
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 {
|
type ml struct {
|
||||||
c *ipv4.PacketConn
|
c *ipv4.PacketConn
|
||||||
ifi *net.Interface
|
ifi *net.Interface
|
||||||
@ -171,26 +135,29 @@ func TestUDPPerInterfaceSingleConnWithSingleGroupListener(t *testing.T) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// listen to a unicast interface address
|
c, err := net.ListenPacket("udp4", ip.String()+":"+"1024") // unicast address with non-reusable port
|
||||||
c, err := net.ListenPacket("udp4", ip.String()+":"+"1024") // see RFC 4727
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("net.ListenPacket with %v failed: %v", ip, err)
|
t.Fatalf("net.ListenPacket with %v failed: %v", ip, err)
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
p := ipv4.NewPacketConn(c)
|
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)
|
t.Fatalf("ipv4.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
||||||
}
|
}
|
||||||
mlt = append(mlt, &ml{p, &ift[i]})
|
mlt = append(mlt, &ml{p, &ift[i]})
|
||||||
}
|
}
|
||||||
for _, m := range mlt {
|
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)
|
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 {
|
if testing.Short() || !*testExternal {
|
||||||
t.Skip("to avoid external network")
|
t.Skip("to avoid external network")
|
||||||
}
|
}
|
||||||
@ -198,7 +165,52 @@ func TestIPPerInterfaceSingleConnWithSingleGroupListener(t *testing.T) {
|
|||||||
t.Skip("must be root")
|
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 {
|
type ml struct {
|
||||||
c *ipv4.RawConn
|
c *ipv4.RawConn
|
||||||
ifi *net.Interface
|
ifi *net.Interface
|
||||||
@ -214,8 +226,7 @@ func TestIPPerInterfaceSingleConnWithSingleGroupListener(t *testing.T) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// listen to a unicast interface address
|
c, err := net.ListenPacket("ip4:253", ip.String()) // unicast address
|
||||||
c, err := net.ListenPacket("ip4:253", ip.String()) // see RFC 4727
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("net.ListenPacket with %v failed: %v", ip, err)
|
t.Fatalf("net.ListenPacket with %v failed: %v", ip, err)
|
||||||
}
|
}
|
||||||
@ -224,13 +235,13 @@ func TestIPPerInterfaceSingleConnWithSingleGroupListener(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("ipv4.NewRawConn failed: %v", err)
|
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)
|
t.Fatalf("ipv4.RawConn.JoinGroup on %v failed: %v", ifi, err)
|
||||||
}
|
}
|
||||||
mlt = append(mlt, &ml{r, &ift[i]})
|
mlt = append(mlt, &ml{r, &ift[i]})
|
||||||
}
|
}
|
||||||
for _, m := range mlt {
|
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)
|
t.Fatalf("ipv4.RawConn.LeaveGroup on %v failed: %v", m.ifi, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ func slicePacket(b []byte) (h, p []byte, err error) {
|
|||||||
// Checksum = platform sets an appropriate value if Checksum is zero
|
// Checksum = platform sets an appropriate value if Checksum is zero
|
||||||
// Src = platform sets an appropriate value if Src is nil
|
// Src = platform sets an appropriate value if Src is nil
|
||||||
// Dst = <must be specified>
|
// Dst = <must be specified>
|
||||||
// h.Options = optional
|
// Options = optional
|
||||||
func (c *packetHandler) WriteTo(h *Header, p []byte, cm *ControlMessage) error {
|
func (c *packetHandler) WriteTo(h *Header, p []byte, cm *ControlMessage) error {
|
||||||
if !c.ok() {
|
if !c.ok() {
|
||||||
return syscall.EINVAL
|
return syscall.EINVAL
|
||||||
|
@ -21,26 +21,27 @@ func (typ ICMPType) String() string {
|
|||||||
// packets.
|
// packets.
|
||||||
type ICMPFilter struct {
|
type ICMPFilter struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
rawICMPFilter
|
sysICMPFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set sets the ICMP type and filter action to the filter.
|
// Set sets the ICMP type and filter action to the filter.
|
||||||
func (f *ICMPFilter) Set(typ ICMPType, block bool) {
|
func (f *ICMPFilter) Set(typ ICMPType, block bool) {
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
|
||||||
f.set(typ, block)
|
f.set(typ, block)
|
||||||
|
f.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetAll sets the filter action to the filter.
|
// SetAll sets the filter action to the filter.
|
||||||
func (f *ICMPFilter) SetAll(block bool) {
|
func (f *ICMPFilter) SetAll(block bool) {
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
|
||||||
f.setAll(block)
|
f.setAll(block)
|
||||||
|
f.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// WillBlock reports whether the ICMP type will be blocked.
|
// WillBlock reports whether the ICMP type will be blocked.
|
||||||
func (f *ICMPFilter) WillBlock(typ ICMPType) bool {
|
func (f *ICMPFilter) WillBlock(typ ICMPType) bool {
|
||||||
f.mu.RLock()
|
f.mu.RLock()
|
||||||
defer f.mu.RUnlock()
|
ok := f.willBlock(typ)
|
||||||
return f.willBlock(typ)
|
f.mu.RUnlock()
|
||||||
|
return ok
|
||||||
}
|
}
|
||||||
|
@ -6,13 +6,11 @@
|
|||||||
|
|
||||||
package ipv6
|
package ipv6
|
||||||
|
|
||||||
import "syscall"
|
type sysICMPFilter struct {
|
||||||
|
Filt [8]uint32
|
||||||
type rawICMPFilter struct {
|
|
||||||
syscall.ICMPv6Filter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *rawICMPFilter) set(typ ICMPType, block bool) {
|
func (f *sysICMPFilter) set(typ ICMPType, block bool) {
|
||||||
if block {
|
if block {
|
||||||
f.Filt[typ>>5] &^= 1 << (uint32(typ) & 31)
|
f.Filt[typ>>5] &^= 1 << (uint32(typ) & 31)
|
||||||
} else {
|
} 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 {
|
for i := range f.Filt {
|
||||||
if block {
|
if block {
|
||||||
f.Filt[i] = 0
|
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
|
return f.Filt[typ>>5]&(1<<(uint32(typ)&31)) == 0
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,11 @@
|
|||||||
|
|
||||||
package ipv6
|
package ipv6
|
||||||
|
|
||||||
import "syscall"
|
type sysICMPFilter struct {
|
||||||
|
Data [8]uint32
|
||||||
type rawICMPFilter struct {
|
|
||||||
syscall.ICMPv6Filter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *rawICMPFilter) set(typ ICMPType, block bool) {
|
func (f *sysICMPFilter) set(typ ICMPType, block bool) {
|
||||||
if block {
|
if block {
|
||||||
f.Data[typ>>5] |= 1 << (uint32(typ) & 31)
|
f.Data[typ>>5] |= 1 << (uint32(typ) & 31)
|
||||||
} else {
|
} 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 {
|
for i := range f.Data {
|
||||||
if block {
|
if block {
|
||||||
f.Data[i] = 1<<32 - 1
|
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
|
return f.Data[typ>>5]&(1<<(uint32(typ)&31)) != 0
|
||||||
}
|
}
|
||||||
|
@ -4,19 +4,19 @@
|
|||||||
|
|
||||||
package ipv6
|
package ipv6
|
||||||
|
|
||||||
type rawICMPFilter struct {
|
type sysICMPFilter struct {
|
||||||
// TODO(mikio): Implement this
|
// TODO(mikio): Implement this
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *rawICMPFilter) set(typ ICMPType, block bool) {
|
func (f *sysICMPFilter) set(typ ICMPType, block bool) {
|
||||||
// TODO(mikio): Implement this
|
// TODO(mikio): Implement this
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *rawICMPFilter) setAll(block bool) {
|
func (f *sysICMPFilter) setAll(block bool) {
|
||||||
// TODO(mikio): Implement this
|
// TODO(mikio): Implement this
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *rawICMPFilter) willBlock(typ ICMPType) bool {
|
func (f *sysICMPFilter) willBlock(typ ICMPType) bool {
|
||||||
// TODO(mikio): Implement this
|
// TODO(mikio): Implement this
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -4,19 +4,19 @@
|
|||||||
|
|
||||||
package ipv6
|
package ipv6
|
||||||
|
|
||||||
type rawICMPFilter struct {
|
type sysICMPFilter struct {
|
||||||
// TODO(mikio): Implement this
|
// TODO(mikio): Implement this
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *rawICMPFilter) set(typ ICMPType, block bool) {
|
func (f *sysICMPFilter) set(typ ICMPType, block bool) {
|
||||||
// TODO(mikio): Implement this
|
// TODO(mikio): Implement this
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *rawICMPFilter) setAll(block bool) {
|
func (f *sysICMPFilter) setAll(block bool) {
|
||||||
// TODO(mikio): Implement this
|
// TODO(mikio): Implement this
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *rawICMPFilter) willBlock(typ ICMPType) bool {
|
func (f *sysICMPFilter) willBlock(typ ICMPType) bool {
|
||||||
// TODO(mikio): Implement this
|
// TODO(mikio): Implement this
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,22 @@ package ipv6_test
|
|||||||
import (
|
import (
|
||||||
"code.google.com/p/go.net/ipv6"
|
"code.google.com/p/go.net/ipv6"
|
||||||
"errors"
|
"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.
|
// icmpMessage represents an ICMP message.
|
||||||
type icmpMessage struct {
|
type icmpMessage struct {
|
||||||
Type ipv6.ICMPType // type
|
Type ipv6.ICMPType // type
|
||||||
@ -25,8 +39,11 @@ type icmpMessageBody interface {
|
|||||||
|
|
||||||
// Marshal returns the binary enconding of the ICMP echo request or
|
// Marshal returns the binary enconding of the ICMP echo request or
|
||||||
// reply message m.
|
// 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}
|
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 {
|
if m.Body != nil && m.Body.Len() != 0 {
|
||||||
mb, err := m.Body.Marshal()
|
mb, err := m.Body.Marshal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -34,10 +51,11 @@ func (m *icmpMessage) Marshal() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
b = append(b, mb...)
|
b = append(b, mb...)
|
||||||
}
|
}
|
||||||
switch m.Type {
|
if psh == nil {
|
||||||
case ipv6.ICMPTypeEchoRequest, ipv6.ICMPTypeEchoReply:
|
|
||||||
return b, 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
|
csumcv := len(b) - 1 // checksum coverage
|
||||||
s := uint32(0)
|
s := uint32(0)
|
||||||
for i := 0; i < csumcv; i += 2 {
|
for i := 0; i < csumcv; i += 2 {
|
||||||
@ -50,9 +68,9 @@ func (m *icmpMessage) Marshal() ([]byte, error) {
|
|||||||
s = s + s>>16
|
s = s + s>>16
|
||||||
// Place checksum back in header; using ^= avoids the
|
// Place checksum back in header; using ^= avoids the
|
||||||
// assumption the checksum bytes are zero.
|
// assumption the checksum bytes are zero.
|
||||||
b[2] ^= byte(^s)
|
b[len(psh)+2] ^= byte(^s)
|
||||||
b[3] ^= byte(^s >> 8)
|
b[len(psh)+3] ^= byte(^s >> 8)
|
||||||
return b, nil
|
return b[len(psh):], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseICMPMessage parses b as an ICMP message.
|
// parseICMPMessage parses b as an ICMP message.
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
|
func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
|
||||||
@ -44,15 +45,22 @@ func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
p := ipv6.NewPacketConn(c)
|
p := ipv6.NewPacketConn(c)
|
||||||
|
defer p.Close()
|
||||||
if err := p.JoinGroup(ifi, dst); err != nil {
|
if err := p.JoinGroup(ifi, dst); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
||||||
}
|
}
|
||||||
if err := p.SetMulticastInterface(ifi); err != nil {
|
if err := p.SetMulticastInterface(ifi); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.SetMulticastInterface failed: %v", err)
|
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 {
|
if err := p.SetMulticastLoopback(true); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.SetMulticastLoopback failed: %v", err)
|
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{
|
cm := ipv6.ControlMessage{
|
||||||
TrafficClass: DiffServAF11 | CongestionExperienced,
|
TrafficClass: DiffServAF11 | CongestionExperienced,
|
||||||
@ -64,6 +72,9 @@ func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
|
|||||||
if err := p.SetControlMessage(cf, toggle); err != nil {
|
if err := p.SetControlMessage(cf, toggle); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
|
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
|
cm.HopLimit = i + 1
|
||||||
if _, err := p.WriteTo([]byte("HELLO-R-U-THERE"), &cm, dst); err != nil {
|
if _, err := p.WriteTo([]byte("HELLO-R-U-THERE"), &cm, dst); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
|
||||||
@ -104,16 +115,24 @@ func TestPacketConnReadWriteMulticastICMP(t *testing.T) {
|
|||||||
t.Fatalf("net.ResolveIPAddr failed: %v", err)
|
t.Fatalf("net.ResolveIPAddr failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pshicmp := ipv6PseudoHeader(c.LocalAddr().(*net.IPAddr).IP, dst.IP, ianaProtocolIPv6ICMP)
|
||||||
p := ipv6.NewPacketConn(c)
|
p := ipv6.NewPacketConn(c)
|
||||||
|
defer p.Close()
|
||||||
if err := p.JoinGroup(ifi, dst); err != nil {
|
if err := p.JoinGroup(ifi, dst); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
||||||
}
|
}
|
||||||
if err := p.SetMulticastInterface(ifi); err != nil {
|
if err := p.SetMulticastInterface(ifi); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.SetMulticastInterface failed: %v", err)
|
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 {
|
if err := p.SetMulticastLoopback(true); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.SetMulticastLoopback failed: %v", err)
|
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{
|
cm := ipv6.ControlMessage{
|
||||||
TrafficClass: DiffServAF11 | CongestionExperienced,
|
TrafficClass: DiffServAF11 | CongestionExperienced,
|
||||||
@ -128,20 +147,35 @@ func TestPacketConnReadWriteMulticastICMP(t *testing.T) {
|
|||||||
t.Fatalf("ipv6.PacketConn.SetICMPFilter failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.SetICMPFilter failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var psh []byte
|
||||||
for i, toggle := range []bool{true, false, true} {
|
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{
|
wb, err := (&icmpMessage{
|
||||||
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
|
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
|
||||||
Body: &icmpEcho{
|
Body: &icmpEcho{
|
||||||
ID: os.Getpid() & 0xffff, Seq: i + 1,
|
ID: os.Getpid() & 0xffff, Seq: i + 1,
|
||||||
Data: []byte("HELLO-R-U-THERE"),
|
Data: []byte("HELLO-R-U-THERE"),
|
||||||
},
|
},
|
||||||
}).Marshal()
|
}).Marshal(psh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("icmpMessage.Marshal failed: %v", err)
|
t.Fatalf("icmpMessage.Marshal failed: %v", err)
|
||||||
}
|
}
|
||||||
if err := p.SetControlMessage(cf, toggle); err != nil {
|
if err := p.SetControlMessage(cf, toggle); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
|
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
|
cm.HopLimit = i + 1
|
||||||
if _, err := p.WriteTo(wb, &cm, dst); err != nil {
|
if _, err := p.WriteTo(wb, &cm, dst); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
|
||||||
|
@ -59,7 +59,7 @@ func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUDPMultipleConnWithMultipleGroupListeners(t *testing.T) {
|
func TestUDPMultiplePacketConnWithMultipleGroupListeners(t *testing.T) {
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "plan9", "windows":
|
case "plan9", "windows":
|
||||||
t.Skipf("not supported on %q", runtime.GOOS)
|
t.Skipf("not supported on %q", runtime.GOOS)
|
||||||
@ -120,7 +120,7 @@ func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
|
|||||||
t.Skip("ipv6 is not supported")
|
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 {
|
type ml struct {
|
||||||
c *ipv6.PacketConn
|
c *ipv6.PacketConn
|
||||||
ifi *net.Interface
|
ifi *net.Interface
|
||||||
@ -142,13 +142,13 @@ func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
p := ipv6.NewPacketConn(c)
|
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)
|
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
||||||
}
|
}
|
||||||
mlt = append(mlt, &ml{p, &ift[i]})
|
mlt = append(mlt, &ml{p, &ift[i]})
|
||||||
}
|
}
|
||||||
for _, m := range mlt {
|
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)
|
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")
|
t.Skip("must be root")
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := net.ListenPacket("ip6:ipv6-icmp", "::")
|
c, err := net.ListenPacket("ip6:ipv6-icmp", "::") // wildcard address
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("net.ListenPacket failed: %v", err)
|
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
p := ipv6.NewPacketConn(c)
|
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
|
var mift []*net.Interface
|
||||||
|
|
||||||
ift, err := net.Interfaces()
|
ift, err := net.Interfaces()
|
||||||
@ -184,14 +184,60 @@ func TestIPSinglePacketConnWithSingleGroupListener(t *testing.T) {
|
|||||||
if _, ok := isMulticastAvailable(&ifi); !ok {
|
if _, ok := isMulticastAvailable(&ifi); !ok {
|
||||||
continue
|
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)
|
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
||||||
}
|
}
|
||||||
mift = append(mift, &ift[i])
|
mift = append(mift, &ift[i])
|
||||||
}
|
}
|
||||||
for _, ifi := range mift {
|
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)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,12 +8,13 @@ package ipv6
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setIPv6Checksum(fd int, on bool, offset int) error {
|
func setIPv6Checksum(fd int, on bool, offset int) error {
|
||||||
if !on {
|
if !on {
|
||||||
offset = -1
|
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))
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,13 @@ package ipv6
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setIPv6Checksum(fd int, on bool, offset int) error {
|
func setIPv6Checksum(fd int, on bool, offset int) error {
|
||||||
if !on {
|
if !on {
|
||||||
offset = -1
|
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))
|
||||||
}
|
}
|
||||||
|
@ -9,66 +9,74 @@ package ipv6
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ipv6TrafficClass(fd int) (int, error) {
|
func ipv6TrafficClass(fd int) (int, error) {
|
||||||
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_TCLASS)
|
var v int32
|
||||||
if err != nil {
|
l := sysSockoptLen(4)
|
||||||
|
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptTrafficClass, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
return 0, os.NewSyscallError("getsockopt", err)
|
return 0, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
return v, nil
|
return int(v), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setIPv6TrafficClass(fd, v int) error {
|
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) {
|
func ipv6HopLimit(fd int) (int, error) {
|
||||||
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_UNICAST_HOPS)
|
var v int32
|
||||||
if err != nil {
|
l := sysSockoptLen(4)
|
||||||
|
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
return 0, os.NewSyscallError("getsockopt", err)
|
return 0, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
return v, nil
|
return int(v), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setIPv6HopLimit(fd, v int) error {
|
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) {
|
func ipv6Checksum(fd int) (bool, int, error) {
|
||||||
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_CHECKSUM)
|
var v int32
|
||||||
if err != nil {
|
l := sysSockoptLen(4)
|
||||||
|
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptChecksum, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
return false, 0, os.NewSyscallError("getsockopt", err)
|
return false, 0, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
on := true
|
on := true
|
||||||
if v == -1 {
|
if v == -1 {
|
||||||
on = false
|
on = false
|
||||||
}
|
}
|
||||||
return on, v, nil
|
return on, int(v), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipv6MulticastHopLimit(fd int) (int, error) {
|
func ipv6MulticastHopLimit(fd int) (int, error) {
|
||||||
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_HOPS)
|
var v int32
|
||||||
if err != nil {
|
l := sysSockoptLen(4)
|
||||||
|
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
return 0, os.NewSyscallError("getsockopt", err)
|
return 0, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
return v, nil
|
return int(v), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setIPv6MulticastHopLimit(fd, v int) error {
|
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) {
|
func ipv6MulticastInterface(fd int) (*net.Interface, error) {
|
||||||
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_IF)
|
var v int32
|
||||||
if err != nil {
|
l := sysSockoptLen(4)
|
||||||
|
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastInterface, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
return nil, os.NewSyscallError("getsockopt", err)
|
return nil, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
if v == 0 {
|
if v == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
ifi, err := net.InterfaceByIndex(v)
|
ifi, err := net.InterfaceByIndex(int(v))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -76,39 +84,41 @@ func ipv6MulticastInterface(fd int) (*net.Interface, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setIPv6MulticastInterface(fd int, ifi *net.Interface) error {
|
func setIPv6MulticastInterface(fd int, ifi *net.Interface) error {
|
||||||
var v int
|
var v int32
|
||||||
if ifi != nil {
|
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) {
|
func ipv6MulticastLoopback(fd int) (bool, error) {
|
||||||
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_LOOP)
|
var v int32
|
||||||
if err != nil {
|
l := sysSockoptLen(4)
|
||||||
|
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastLoopback, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
return false, os.NewSyscallError("getsockopt", err)
|
return false, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
return v == 1, nil
|
return v == 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setIPv6MulticastLoopback(fd int, v bool) error {
|
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 {
|
func joinIPv6Group(fd int, ifi *net.Interface, grp net.IP) error {
|
||||||
mreq := syscall.IPv6Mreq{}
|
mreq := sysMulticastReq{}
|
||||||
copy(mreq.Multiaddr[:], grp)
|
copy(mreq.IP[:], grp)
|
||||||
if ifi != nil {
|
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 {
|
func leaveIPv6Group(fd int, ifi *net.Interface, grp net.IP) error {
|
||||||
mreq := syscall.IPv6Mreq{}
|
mreq := sysMulticastReq{}
|
||||||
copy(mreq.Multiaddr[:], grp)
|
copy(mreq.IP[:], grp)
|
||||||
if ifi != nil {
|
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))
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ func setIPv6TrafficClass(fd syscall.Handle, v int) error {
|
|||||||
func ipv6HopLimit(fd syscall.Handle) (int, error) {
|
func ipv6HopLimit(fd syscall.Handle) (int, error) {
|
||||||
var v int32
|
var v int32
|
||||||
l := int32(4)
|
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 0, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
return int(v), nil
|
return int(v), nil
|
||||||
@ -32,7 +32,7 @@ func ipv6HopLimit(fd syscall.Handle) (int, error) {
|
|||||||
|
|
||||||
func setIPv6HopLimit(fd syscall.Handle, v int) error {
|
func setIPv6HopLimit(fd syscall.Handle, v int) error {
|
||||||
vv := int32(v)
|
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) {
|
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) {
|
func ipv6MulticastHopLimit(fd syscall.Handle) (int, error) {
|
||||||
var v int32
|
var v int32
|
||||||
l := int32(4)
|
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 0, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
return int(v), nil
|
return int(v), nil
|
||||||
@ -51,13 +51,13 @@ func ipv6MulticastHopLimit(fd syscall.Handle) (int, error) {
|
|||||||
|
|
||||||
func setIPv6MulticastHopLimit(fd syscall.Handle, v int) error {
|
func setIPv6MulticastHopLimit(fd syscall.Handle, v int) error {
|
||||||
vv := int32(v)
|
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) {
|
func ipv6MulticastInterface(fd syscall.Handle) (*net.Interface, error) {
|
||||||
var v int32
|
var v int32
|
||||||
l := int32(4)
|
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)
|
return nil, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
if v == 0 {
|
if v == 0 {
|
||||||
@ -75,13 +75,13 @@ func setIPv6MulticastInterface(fd syscall.Handle, ifi *net.Interface) error {
|
|||||||
if ifi != nil {
|
if ifi != nil {
|
||||||
v = int32(ifi.Index)
|
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) {
|
func ipv6MulticastLoopback(fd syscall.Handle) (bool, error) {
|
||||||
var v int32
|
var v int32
|
||||||
l := int32(4)
|
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 false, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
return v == 1, nil
|
return v == 1, nil
|
||||||
@ -89,25 +89,25 @@ func ipv6MulticastLoopback(fd syscall.Handle) (bool, error) {
|
|||||||
|
|
||||||
func setIPv6MulticastLoopback(fd syscall.Handle, v bool) error {
|
func setIPv6MulticastLoopback(fd syscall.Handle, v bool) error {
|
||||||
vv := int32(boolint(v))
|
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 {
|
func joinIPv6Group(fd syscall.Handle, ifi *net.Interface, grp net.IP) error {
|
||||||
mreq := syscall.IPv6Mreq{}
|
mreq := sysMulticastReq{}
|
||||||
copy(mreq.Multiaddr[:], grp)
|
copy(mreq.IP[:], grp)
|
||||||
if ifi != nil {
|
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 {
|
func leaveIPv6Group(fd syscall.Handle, ifi *net.Interface, grp net.IP) error {
|
||||||
mreq := syscall.IPv6Mreq{}
|
mreq := sysMulticastReq{}
|
||||||
copy(mreq.Multiaddr[:], grp)
|
copy(mreq.IP[:], grp)
|
||||||
if ifi != nil {
|
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 {
|
func setIPv6Checksum(fd syscall.Handle, on bool, offset int) error {
|
||||||
|
@ -8,41 +8,83 @@ package ipv6
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ipv6ReceiveTrafficClass(fd int) (bool, error) {
|
func ipv6ReceiveTrafficClass(fd int) (bool, error) {
|
||||||
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVTCLASS)
|
var v int32
|
||||||
if err != nil {
|
l := sysSockoptLen(4)
|
||||||
|
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveTrafficClass, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
return false, os.NewSyscallError("getsockopt", err)
|
return false, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
return v == 1, nil
|
return v == 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setIPv6ReceiveTrafficClass(fd int, v bool) error {
|
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) {
|
func ipv6ReceiveHopLimit(fd int) (bool, error) {
|
||||||
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVHOPLIMIT)
|
var v int32
|
||||||
if err != nil {
|
l := sysSockoptLen(4)
|
||||||
|
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveHopLimit, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
return false, os.NewSyscallError("getsockopt", err)
|
return false, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
return v == 1, nil
|
return v == 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setIPv6ReceiveHopLimit(fd int, v bool) error {
|
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) {
|
func ipv6ReceivePacketInfo(fd int) (bool, error) {
|
||||||
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVPKTINFO)
|
var v int32
|
||||||
if err != nil {
|
l := sysSockoptLen(4)
|
||||||
|
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceivePacketInfo, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
return false, os.NewSyscallError("getsockopt", err)
|
return false, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
return v == 1, nil
|
return v == 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setIPv6ReceivePacketInfo(fd int, v bool) error {
|
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))
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func benchmarkUDPListener() (net.PacketConn, net.Addr, error) {
|
func benchmarkUDPListener() (net.PacketConn, net.Addr, error) {
|
||||||
@ -106,6 +107,7 @@ func TestPacketConnReadWriteUnicastUDP(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
p := ipv6.NewPacketConn(c)
|
p := ipv6.NewPacketConn(c)
|
||||||
|
defer p.Close()
|
||||||
cm := ipv6.ControlMessage{
|
cm := ipv6.ControlMessage{
|
||||||
TrafficClass: DiffServAF11 | CongestionExperienced,
|
TrafficClass: DiffServAF11 | CongestionExperienced,
|
||||||
}
|
}
|
||||||
@ -120,10 +122,16 @@ func TestPacketConnReadWriteUnicastUDP(t *testing.T) {
|
|||||||
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
|
||||||
}
|
}
|
||||||
cm.HopLimit = i + 1
|
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 {
|
if _, err := p.WriteTo([]byte("HELLO-R-U-THERE"), &cm, dst); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
|
||||||
}
|
}
|
||||||
b := make([]byte, 128)
|
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 {
|
if _, cm, _, err := p.ReadFrom(b); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
|
||||||
} else {
|
} else {
|
||||||
@ -155,7 +163,9 @@ func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
|
|||||||
t.Fatalf("net.ResolveIPAddr failed: %v", err)
|
t.Fatalf("net.ResolveIPAddr failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pshicmp := ipv6PseudoHeader(c.LocalAddr().(*net.IPAddr).IP, dst.IP, ianaProtocolIPv6ICMP)
|
||||||
p := ipv6.NewPacketConn(c)
|
p := ipv6.NewPacketConn(c)
|
||||||
|
defer p.Close()
|
||||||
cm := ipv6.ControlMessage{TrafficClass: DiffServAF11 | CongestionExperienced}
|
cm := ipv6.ControlMessage{TrafficClass: DiffServAF11 | CongestionExperienced}
|
||||||
cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU
|
cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU
|
||||||
ifi := loopbackInterface()
|
ifi := loopbackInterface()
|
||||||
@ -170,14 +180,26 @@ func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
|
|||||||
t.Fatalf("ipv6.PacketConn.SetICMPFilter failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.SetICMPFilter failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var psh []byte
|
||||||
for i, toggle := range []bool{true, false, true} {
|
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{
|
wb, err := (&icmpMessage{
|
||||||
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
|
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
|
||||||
Body: &icmpEcho{
|
Body: &icmpEcho{
|
||||||
ID: os.Getpid() & 0xffff, Seq: i + 1,
|
ID: os.Getpid() & 0xffff, Seq: i + 1,
|
||||||
Data: []byte("HELLO-R-U-THERE"),
|
Data: []byte("HELLO-R-U-THERE"),
|
||||||
},
|
},
|
||||||
}).Marshal()
|
}).Marshal(psh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("icmpMessage.Marshal failed: %v", err)
|
t.Fatalf("icmpMessage.Marshal failed: %v", err)
|
||||||
}
|
}
|
||||||
@ -185,10 +207,16 @@ func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
|
|||||||
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
|
||||||
}
|
}
|
||||||
cm.HopLimit = i + 1
|
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 {
|
if _, err := p.WriteTo(wb, &cm, dst); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
|
||||||
}
|
}
|
||||||
b := make([]byte, 128)
|
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 {
|
if n, cm, _, err := p.ReadFrom(b); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
|
||||||
} else {
|
} else {
|
||||||
|
@ -99,19 +99,18 @@ var publicSuffixTestCases = []struct {
|
|||||||
{"www.xxx.yyy.zzz.pb.ao", "pb.ao"},
|
{"www.xxx.yyy.zzz.pb.ao", "pb.ao"},
|
||||||
|
|
||||||
// The .ar rules are:
|
// The .ar rules are:
|
||||||
// *.ar
|
// ar
|
||||||
// !congresodelalengua3.ar
|
// com.ar
|
||||||
// !educ.ar
|
// edu.ar
|
||||||
// !gobiernoelectronico.ar
|
// gob.ar
|
||||||
// !mecon.ar
|
// int.ar
|
||||||
// !nacion.ar
|
// mil.ar
|
||||||
// !nic.ar
|
// net.ar
|
||||||
// !promocion.ar
|
// org.ar
|
||||||
// !retina.ar
|
// tur.ar
|
||||||
// !uba.ar
|
|
||||||
// blogspot.com.ar
|
// blogspot.com.ar
|
||||||
{"ar", "ar"},
|
{"ar", "ar"},
|
||||||
{"www.ar", "www.ar"},
|
{"www.ar", "ar"},
|
||||||
{"nic.ar", "ar"},
|
{"nic.ar", "ar"},
|
||||||
{"www.nic.ar", "ar"},
|
{"www.nic.ar", "ar"},
|
||||||
{"com.ar", "com.ar"},
|
{"com.ar", "com.ar"},
|
||||||
@ -376,12 +375,12 @@ var eTLDPlusOneTestCases = []struct {
|
|||||||
{"city.kobe.jp", "city.kobe.jp"},
|
{"city.kobe.jp", "city.kobe.jp"},
|
||||||
{"www.city.kobe.jp", "city.kobe.jp"},
|
{"www.city.kobe.jp", "city.kobe.jp"},
|
||||||
// TLD with a wildcard rule and exceptions.
|
// TLD with a wildcard rule and exceptions.
|
||||||
{"om", ""},
|
{"ck", ""},
|
||||||
{"test.om", ""},
|
{"test.ck", ""},
|
||||||
{"b.test.om", "b.test.om"},
|
{"b.test.ck", "b.test.ck"},
|
||||||
{"a.b.test.om", "b.test.om"},
|
{"a.b.test.ck", "b.test.ck"},
|
||||||
{"songfest.om", "songfest.om"},
|
{"www.ck", "www.ck"},
|
||||||
{"www.songfest.om", "songfest.om"},
|
{"www.www.ck", "www.ck"},
|
||||||
// US K12.
|
// US K12.
|
||||||
{"us", ""},
|
{"us", ""},
|
||||||
{"test.us", "test.us"},
|
{"test.us", "test.us"},
|
||||||
|
13736
third_party/code.google.com/p/go.net/publicsuffix/table.go
vendored
13736
third_party/code.google.com/p/go.net/publicsuffix/table.go
vendored
File diff suppressed because it is too large
Load Diff
@ -148,16 +148,15 @@ var rules = [...]string{
|
|||||||
"pb.ao",
|
"pb.ao",
|
||||||
"it.ao",
|
"it.ao",
|
||||||
"aq",
|
"aq",
|
||||||
"*.ar",
|
"ar",
|
||||||
"!congresodelalengua3.ar",
|
"com.ar",
|
||||||
"!educ.ar",
|
"edu.ar",
|
||||||
"!gobiernoelectronico.ar",
|
"gob.ar",
|
||||||
"!mecon.ar",
|
"int.ar",
|
||||||
"!nacion.ar",
|
"mil.ar",
|
||||||
"!nic.ar",
|
"net.ar",
|
||||||
"!promocion.ar",
|
"org.ar",
|
||||||
"!retina.ar",
|
"tur.ar",
|
||||||
"!uba.ar",
|
|
||||||
"e164.arpa",
|
"e164.arpa",
|
||||||
"in-addr.arpa",
|
"in-addr.arpa",
|
||||||
"ip6.arpa",
|
"ip6.arpa",
|
||||||
@ -179,6 +178,7 @@ var rules = [...]string{
|
|||||||
"gov.au",
|
"gov.au",
|
||||||
"asn.au",
|
"asn.au",
|
||||||
"id.au",
|
"id.au",
|
||||||
|
"csiro.au",
|
||||||
"info.au",
|
"info.au",
|
||||||
"conf.au",
|
"conf.au",
|
||||||
"oz.au",
|
"oz.au",
|
||||||
@ -3770,12 +3770,16 @@ var rules = [...]string{
|
|||||||
"info.nf",
|
"info.nf",
|
||||||
"other.nf",
|
"other.nf",
|
||||||
"store.nf",
|
"store.nf",
|
||||||
"ac.ng",
|
"ng",
|
||||||
"com.ng",
|
"com.ng",
|
||||||
"edu.ng",
|
"edu.ng",
|
||||||
"gov.ng",
|
"name.ng",
|
||||||
"net.ng",
|
"net.ng",
|
||||||
"org.ng",
|
"org.ng",
|
||||||
|
"sch.ng",
|
||||||
|
"gov.ng",
|
||||||
|
"mil.ng",
|
||||||
|
"mobi.ng",
|
||||||
"*.ni",
|
"*.ni",
|
||||||
"nl",
|
"nl",
|
||||||
"bv.nl",
|
"bv.nl",
|
||||||
@ -4544,7 +4548,16 @@ var rules = [...]string{
|
|||||||
"com.nr",
|
"com.nr",
|
||||||
"nu",
|
"nu",
|
||||||
"*.nz",
|
"*.nz",
|
||||||
"*.om",
|
"om",
|
||||||
|
"co.om",
|
||||||
|
"com.om",
|
||||||
|
"edu.om",
|
||||||
|
"gov.om",
|
||||||
|
"med.om",
|
||||||
|
"museum.om",
|
||||||
|
"net.om",
|
||||||
|
"org.om",
|
||||||
|
"pro.om",
|
||||||
"!mediaphone.om",
|
"!mediaphone.om",
|
||||||
"!nawrastelecom.om",
|
"!nawrastelecom.om",
|
||||||
"!nawras.om",
|
"!nawras.om",
|
||||||
@ -5143,7 +5156,12 @@ var rules = [...]string{
|
|||||||
"saotome.st",
|
"saotome.st",
|
||||||
"store.st",
|
"store.st",
|
||||||
"su",
|
"su",
|
||||||
"*.sv",
|
"sv",
|
||||||
|
"com.sv",
|
||||||
|
"edu.sv",
|
||||||
|
"gob.sv",
|
||||||
|
"org.sv",
|
||||||
|
"red.sv",
|
||||||
"sx",
|
"sx",
|
||||||
"gov.sx",
|
"gov.sx",
|
||||||
"sy",
|
"sy",
|
||||||
@ -5709,7 +5727,79 @@ var rules = [...]string{
|
|||||||
"*.za",
|
"*.za",
|
||||||
"*.zm",
|
"*.zm",
|
||||||
"*.zw",
|
"*.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",
|
"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",
|
"elasticbeanstalk.com",
|
||||||
"elb.amazonaws.com",
|
"elb.amazonaws.com",
|
||||||
"s3.amazonaws.com",
|
"s3.amazonaws.com",
|
||||||
@ -5760,6 +5850,8 @@ var rules = [...]string{
|
|||||||
"uy.com",
|
"uy.com",
|
||||||
"za.com",
|
"za.com",
|
||||||
"c.la",
|
"c.la",
|
||||||
|
"cloudcontrolled.com",
|
||||||
|
"cloudcontrolapp.com",
|
||||||
"co.ca",
|
"co.ca",
|
||||||
"co.nl",
|
"co.nl",
|
||||||
"co.no",
|
"co.no",
|
||||||
@ -6043,7 +6135,13 @@ var rules = [...]string{
|
|||||||
"webhop.org",
|
"webhop.org",
|
||||||
"worse-than.tv",
|
"worse-than.tv",
|
||||||
"writesthisblog.com",
|
"writesthisblog.com",
|
||||||
|
"a.ssl.fastly.net",
|
||||||
|
"b.ssl.fastly.net",
|
||||||
|
"global.ssl.fastly.net",
|
||||||
|
"a.prod.fastly.net",
|
||||||
|
"global.prod.fastly.net",
|
||||||
"github.io",
|
"github.io",
|
||||||
|
"ro.com",
|
||||||
"appspot.com",
|
"appspot.com",
|
||||||
"blogspot.be",
|
"blogspot.be",
|
||||||
"blogspot.bj",
|
"blogspot.bj",
|
||||||
@ -6088,6 +6186,8 @@ var rules = [...]string{
|
|||||||
"codespot.com",
|
"codespot.com",
|
||||||
"googleapis.com",
|
"googleapis.com",
|
||||||
"googlecode.com",
|
"googlecode.com",
|
||||||
|
"herokuapp.com",
|
||||||
|
"herokussl.com",
|
||||||
"iki.fi",
|
"iki.fi",
|
||||||
"biz.at",
|
"biz.at",
|
||||||
"info.at",
|
"info.at",
|
||||||
@ -6130,6 +6230,7 @@ var nodeLabels = [...]string{
|
|||||||
"bg",
|
"bg",
|
||||||
"bh",
|
"bh",
|
||||||
"bi",
|
"bi",
|
||||||
|
"bike",
|
||||||
"biz",
|
"biz",
|
||||||
"bj",
|
"bj",
|
||||||
"bm",
|
"bm",
|
||||||
@ -6138,10 +6239,14 @@ var nodeLabels = [...]string{
|
|||||||
"br",
|
"br",
|
||||||
"bs",
|
"bs",
|
||||||
"bt",
|
"bt",
|
||||||
|
"buzz",
|
||||||
"bw",
|
"bw",
|
||||||
"by",
|
"by",
|
||||||
"bz",
|
"bz",
|
||||||
"ca",
|
"ca",
|
||||||
|
"camera",
|
||||||
|
"career",
|
||||||
|
"careers",
|
||||||
"cat",
|
"cat",
|
||||||
"cc",
|
"cc",
|
||||||
"cd",
|
"cd",
|
||||||
@ -6151,10 +6256,13 @@ var nodeLabels = [...]string{
|
|||||||
"ci",
|
"ci",
|
||||||
"ck",
|
"ck",
|
||||||
"cl",
|
"cl",
|
||||||
|
"clothing",
|
||||||
"cm",
|
"cm",
|
||||||
"cn",
|
"cn",
|
||||||
"co",
|
"co",
|
||||||
"com",
|
"com",
|
||||||
|
"construction",
|
||||||
|
"contractors",
|
||||||
"coop",
|
"coop",
|
||||||
"cr",
|
"cr",
|
||||||
"cu",
|
"cu",
|
||||||
@ -6164,6 +6272,8 @@ var nodeLabels = [...]string{
|
|||||||
"cy",
|
"cy",
|
||||||
"cz",
|
"cz",
|
||||||
"de",
|
"de",
|
||||||
|
"diamonds",
|
||||||
|
"directory",
|
||||||
"dj",
|
"dj",
|
||||||
"dk",
|
"dk",
|
||||||
"dm",
|
"dm",
|
||||||
@ -6173,8 +6283,11 @@ var nodeLabels = [...]string{
|
|||||||
"edu",
|
"edu",
|
||||||
"ee",
|
"ee",
|
||||||
"eg",
|
"eg",
|
||||||
|
"enterprises",
|
||||||
|
"equipment",
|
||||||
"er",
|
"er",
|
||||||
"es",
|
"es",
|
||||||
|
"estate",
|
||||||
"et",
|
"et",
|
||||||
"eu",
|
"eu",
|
||||||
"fi",
|
"fi",
|
||||||
@ -6183,7 +6296,9 @@ var nodeLabels = [...]string{
|
|||||||
"fm",
|
"fm",
|
||||||
"fo",
|
"fo",
|
||||||
"fr",
|
"fr",
|
||||||
|
"futbol",
|
||||||
"ga",
|
"ga",
|
||||||
|
"gallery",
|
||||||
"gd",
|
"gd",
|
||||||
"ge",
|
"ge",
|
||||||
"gf",
|
"gf",
|
||||||
@ -6197,14 +6312,18 @@ var nodeLabels = [...]string{
|
|||||||
"gp",
|
"gp",
|
||||||
"gq",
|
"gq",
|
||||||
"gr",
|
"gr",
|
||||||
|
"graphics",
|
||||||
"gs",
|
"gs",
|
||||||
"gt",
|
"gt",
|
||||||
"gu",
|
"gu",
|
||||||
|
"guide",
|
||||||
|
"guru",
|
||||||
"gw",
|
"gw",
|
||||||
"gy",
|
"gy",
|
||||||
"hk",
|
"hk",
|
||||||
"hm",
|
"hm",
|
||||||
"hn",
|
"hn",
|
||||||
|
"holdings",
|
||||||
"hr",
|
"hr",
|
||||||
"ht",
|
"ht",
|
||||||
"hu",
|
"hu",
|
||||||
@ -6229,6 +6348,9 @@ var nodeLabels = [...]string{
|
|||||||
"kg",
|
"kg",
|
||||||
"kh",
|
"kh",
|
||||||
"ki",
|
"ki",
|
||||||
|
"kim",
|
||||||
|
"kitchen",
|
||||||
|
"kiwi",
|
||||||
"km",
|
"km",
|
||||||
"kn",
|
"kn",
|
||||||
"kp",
|
"kp",
|
||||||
@ -6237,9 +6359,11 @@ var nodeLabels = [...]string{
|
|||||||
"ky",
|
"ky",
|
||||||
"kz",
|
"kz",
|
||||||
"la",
|
"la",
|
||||||
|
"land",
|
||||||
"lb",
|
"lb",
|
||||||
"lc",
|
"lc",
|
||||||
"li",
|
"li",
|
||||||
|
"lighting",
|
||||||
"lk",
|
"lk",
|
||||||
"lr",
|
"lr",
|
||||||
"ls",
|
"ls",
|
||||||
@ -6251,6 +6375,7 @@ var nodeLabels = [...]string{
|
|||||||
"mc",
|
"mc",
|
||||||
"md",
|
"md",
|
||||||
"me",
|
"me",
|
||||||
|
"menu",
|
||||||
"mg",
|
"mg",
|
||||||
"mh",
|
"mh",
|
||||||
"mil",
|
"mil",
|
||||||
@ -6260,6 +6385,7 @@ var nodeLabels = [...]string{
|
|||||||
"mn",
|
"mn",
|
||||||
"mo",
|
"mo",
|
||||||
"mobi",
|
"mobi",
|
||||||
|
"monash",
|
||||||
"mp",
|
"mp",
|
||||||
"mq",
|
"mq",
|
||||||
"mr",
|
"mr",
|
||||||
@ -6287,14 +6413,19 @@ var nodeLabels = [...]string{
|
|||||||
"nu",
|
"nu",
|
||||||
"nz",
|
"nz",
|
||||||
"om",
|
"om",
|
||||||
|
"onl",
|
||||||
"org",
|
"org",
|
||||||
|
"otsuka",
|
||||||
"pa",
|
"pa",
|
||||||
"pe",
|
"pe",
|
||||||
"pf",
|
"pf",
|
||||||
"pg",
|
"pg",
|
||||||
"ph",
|
"ph",
|
||||||
|
"photography",
|
||||||
|
"pink",
|
||||||
"pk",
|
"pk",
|
||||||
"pl",
|
"pl",
|
||||||
|
"plumbing",
|
||||||
"pm",
|
"pm",
|
||||||
"pn",
|
"pn",
|
||||||
"post",
|
"post",
|
||||||
@ -6306,18 +6437,23 @@ var nodeLabels = [...]string{
|
|||||||
"py",
|
"py",
|
||||||
"qa",
|
"qa",
|
||||||
"re",
|
"re",
|
||||||
|
"reviews",
|
||||||
"ro",
|
"ro",
|
||||||
"rs",
|
"rs",
|
||||||
"ru",
|
"ru",
|
||||||
|
"ruhr",
|
||||||
"rw",
|
"rw",
|
||||||
"sa",
|
"sa",
|
||||||
"sb",
|
"sb",
|
||||||
"sc",
|
"sc",
|
||||||
"sd",
|
"sd",
|
||||||
"se",
|
"se",
|
||||||
|
"sexy",
|
||||||
"sg",
|
"sg",
|
||||||
"sh",
|
"sh",
|
||||||
|
"shoes",
|
||||||
"si",
|
"si",
|
||||||
|
"singles",
|
||||||
"sk",
|
"sk",
|
||||||
"sl",
|
"sl",
|
||||||
"sm",
|
"sm",
|
||||||
@ -6330,18 +6466,22 @@ var nodeLabels = [...]string{
|
|||||||
"sx",
|
"sx",
|
||||||
"sy",
|
"sy",
|
||||||
"sz",
|
"sz",
|
||||||
|
"tattoo",
|
||||||
"tc",
|
"tc",
|
||||||
"td",
|
"td",
|
||||||
|
"technology",
|
||||||
"tel",
|
"tel",
|
||||||
"tf",
|
"tf",
|
||||||
"tg",
|
"tg",
|
||||||
"th",
|
"th",
|
||||||
|
"tips",
|
||||||
"tj",
|
"tj",
|
||||||
"tk",
|
"tk",
|
||||||
"tl",
|
"tl",
|
||||||
"tm",
|
"tm",
|
||||||
"tn",
|
"tn",
|
||||||
"to",
|
"to",
|
||||||
|
"today",
|
||||||
"tr",
|
"tr",
|
||||||
"travel",
|
"travel",
|
||||||
"tt",
|
"tt",
|
||||||
@ -6351,23 +6491,37 @@ var nodeLabels = [...]string{
|
|||||||
"ua",
|
"ua",
|
||||||
"ug",
|
"ug",
|
||||||
"uk",
|
"uk",
|
||||||
|
"uno",
|
||||||
"us",
|
"us",
|
||||||
"uy",
|
"uy",
|
||||||
"uz",
|
"uz",
|
||||||
"va",
|
"va",
|
||||||
"vc",
|
"vc",
|
||||||
"ve",
|
"ve",
|
||||||
|
"ventures",
|
||||||
"vg",
|
"vg",
|
||||||
"vi",
|
"vi",
|
||||||
"vn",
|
"vn",
|
||||||
|
"voyage",
|
||||||
"vu",
|
"vu",
|
||||||
|
"wed",
|
||||||
"wf",
|
"wf",
|
||||||
"ws",
|
"ws",
|
||||||
|
"xn--3bst00m",
|
||||||
|
"xn--3ds443g",
|
||||||
"xn--3e0b707e",
|
"xn--3e0b707e",
|
||||||
"xn--45brj9c",
|
"xn--45brj9c",
|
||||||
|
"xn--4gbrim",
|
||||||
"xn--54b7fta0cc",
|
"xn--54b7fta0cc",
|
||||||
|
"xn--6frz82g",
|
||||||
|
"xn--6qq986b3xl",
|
||||||
|
"xn--80asehdb",
|
||||||
|
"xn--80aswg",
|
||||||
"xn--90a3ac",
|
"xn--90a3ac",
|
||||||
|
"xn--cg4bki",
|
||||||
"xn--clchc0ea0b2g2a9gcd",
|
"xn--clchc0ea0b2g2a9gcd",
|
||||||
|
"xn--fiq228c5hs",
|
||||||
|
"xn--fiq64b",
|
||||||
"xn--fiqs8s",
|
"xn--fiqs8s",
|
||||||
"xn--fiqz9s",
|
"xn--fiqz9s",
|
||||||
"xn--fpcrj9c3d",
|
"xn--fpcrj9c3d",
|
||||||
@ -6392,13 +6546,18 @@ var nodeLabels = [...]string{
|
|||||||
"xn--mgbqly7c0a67fbc",
|
"xn--mgbqly7c0a67fbc",
|
||||||
"xn--mgbqly7cvafr",
|
"xn--mgbqly7cvafr",
|
||||||
"xn--mgbtf8fl",
|
"xn--mgbtf8fl",
|
||||||
|
"xn--ngbc5azd",
|
||||||
"xn--nnx388a",
|
"xn--nnx388a",
|
||||||
"xn--node",
|
"xn--node",
|
||||||
"xn--o3cw4h",
|
"xn--o3cw4h",
|
||||||
"xn--ogbpf8fl",
|
"xn--ogbpf8fl",
|
||||||
"xn--p1ai",
|
"xn--p1ai",
|
||||||
"xn--pgbs0dh",
|
"xn--pgbs0dh",
|
||||||
|
"xn--q9jyb4c",
|
||||||
|
"xn--rhqv96g",
|
||||||
"xn--s9brj9c",
|
"xn--s9brj9c",
|
||||||
|
"xn--unup4y",
|
||||||
|
"xn--vhquv",
|
||||||
"xn--wgbh1c",
|
"xn--wgbh1c",
|
||||||
"xn--wgbl6a",
|
"xn--wgbl6a",
|
||||||
"xn--xkc2al3hye2a",
|
"xn--xkc2al3hye2a",
|
||||||
@ -6545,15 +6704,13 @@ var nodeLabels = [...]string{
|
|||||||
"og",
|
"og",
|
||||||
"pb",
|
"pb",
|
||||||
"com",
|
"com",
|
||||||
"congresodelalengua3",
|
"edu",
|
||||||
"educ",
|
"gob",
|
||||||
"gobiernoelectronico",
|
"int",
|
||||||
"mecon",
|
"mil",
|
||||||
"nacion",
|
"net",
|
||||||
"nic",
|
"org",
|
||||||
"promocion",
|
"tur",
|
||||||
"retina",
|
|
||||||
"uba",
|
|
||||||
"blogspot",
|
"blogspot",
|
||||||
"e164",
|
"e164",
|
||||||
"in-addr",
|
"in-addr",
|
||||||
@ -6574,6 +6731,7 @@ var nodeLabels = [...]string{
|
|||||||
"asn",
|
"asn",
|
||||||
"com",
|
"com",
|
||||||
"conf",
|
"conf",
|
||||||
|
"csiro",
|
||||||
"edu",
|
"edu",
|
||||||
"gov",
|
"gov",
|
||||||
"id",
|
"id",
|
||||||
@ -6910,6 +7068,8 @@ var nodeLabels = [...]string{
|
|||||||
"blogspot",
|
"blogspot",
|
||||||
"br",
|
"br",
|
||||||
"cechire",
|
"cechire",
|
||||||
|
"cloudcontrolapp",
|
||||||
|
"cloudcontrolled",
|
||||||
"cn",
|
"cn",
|
||||||
"codespot",
|
"codespot",
|
||||||
"de",
|
"de",
|
||||||
@ -6994,6 +7154,8 @@ var nodeLabels = [...]string{
|
|||||||
"googlecode",
|
"googlecode",
|
||||||
"gotdns",
|
"gotdns",
|
||||||
"gr",
|
"gr",
|
||||||
|
"herokuapp",
|
||||||
|
"herokussl",
|
||||||
"hobby-site",
|
"hobby-site",
|
||||||
"homelinux",
|
"homelinux",
|
||||||
"homeunix",
|
"homeunix",
|
||||||
@ -7066,6 +7228,7 @@ var nodeLabels = [...]string{
|
|||||||
"operaunite",
|
"operaunite",
|
||||||
"qc",
|
"qc",
|
||||||
"rhcloud",
|
"rhcloud",
|
||||||
|
"ro",
|
||||||
"ru",
|
"ru",
|
||||||
"sa",
|
"sa",
|
||||||
"saves-the-whales",
|
"saves-the-whales",
|
||||||
@ -7082,6 +7245,8 @@ var nodeLabels = [...]string{
|
|||||||
"uy",
|
"uy",
|
||||||
"writesthisblog",
|
"writesthisblog",
|
||||||
"za",
|
"za",
|
||||||
|
"compute",
|
||||||
|
"compute-1",
|
||||||
"elb",
|
"elb",
|
||||||
"s3",
|
"s3",
|
||||||
"s3-ap-northeast-1",
|
"s3-ap-northeast-1",
|
||||||
@ -7102,6 +7267,17 @@ var nodeLabels = [...]string{
|
|||||||
"s3-website-us-gov-west-1",
|
"s3-website-us-gov-west-1",
|
||||||
"s3-website-us-west-1",
|
"s3-website-us-west-1",
|
||||||
"s3-website-us-west-2",
|
"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",
|
"ac",
|
||||||
"co",
|
"co",
|
||||||
"ed",
|
"ed",
|
||||||
@ -10279,6 +10455,7 @@ var nodeLabels = [...]string{
|
|||||||
"dynalias",
|
"dynalias",
|
||||||
"dynathome",
|
"dynathome",
|
||||||
"endofinternet",
|
"endofinternet",
|
||||||
|
"fastly",
|
||||||
"from-az",
|
"from-az",
|
||||||
"from-co",
|
"from-co",
|
||||||
"from-la",
|
"from-la",
|
||||||
@ -10309,6 +10486,13 @@ var nodeLabels = [...]string{
|
|||||||
"uk",
|
"uk",
|
||||||
"webhop",
|
"webhop",
|
||||||
"za",
|
"za",
|
||||||
|
"prod",
|
||||||
|
"ssl",
|
||||||
|
"a",
|
||||||
|
"global",
|
||||||
|
"a",
|
||||||
|
"b",
|
||||||
|
"global",
|
||||||
"arts",
|
"arts",
|
||||||
"com",
|
"com",
|
||||||
"firm",
|
"firm",
|
||||||
@ -10319,12 +10503,15 @@ var nodeLabels = [...]string{
|
|||||||
"rec",
|
"rec",
|
||||||
"store",
|
"store",
|
||||||
"web",
|
"web",
|
||||||
"ac",
|
|
||||||
"com",
|
"com",
|
||||||
"edu",
|
"edu",
|
||||||
"gov",
|
"gov",
|
||||||
|
"mil",
|
||||||
|
"mobi",
|
||||||
|
"name",
|
||||||
"net",
|
"net",
|
||||||
"org",
|
"org",
|
||||||
|
"sch",
|
||||||
"blogspot",
|
"blogspot",
|
||||||
"bv",
|
"bv",
|
||||||
"co",
|
"co",
|
||||||
@ -11106,12 +11293,21 @@ var nodeLabels = [...]string{
|
|||||||
"shacknet",
|
"shacknet",
|
||||||
"co",
|
"co",
|
||||||
"blogspot",
|
"blogspot",
|
||||||
|
"co",
|
||||||
|
"com",
|
||||||
|
"edu",
|
||||||
|
"gov",
|
||||||
|
"med",
|
||||||
"mediaphone",
|
"mediaphone",
|
||||||
|
"museum",
|
||||||
"nawras",
|
"nawras",
|
||||||
"nawrastelecom",
|
"nawrastelecom",
|
||||||
|
"net",
|
||||||
"omanmobile",
|
"omanmobile",
|
||||||
"omanpost",
|
"omanpost",
|
||||||
"omantel",
|
"omantel",
|
||||||
|
"org",
|
||||||
|
"pro",
|
||||||
"rakpetroleum",
|
"rakpetroleum",
|
||||||
"siemens",
|
"siemens",
|
||||||
"songfest",
|
"songfest",
|
||||||
@ -11727,6 +11923,11 @@ var nodeLabels = [...]string{
|
|||||||
"principe",
|
"principe",
|
||||||
"saotome",
|
"saotome",
|
||||||
"store",
|
"store",
|
||||||
|
"com",
|
||||||
|
"edu",
|
||||||
|
"gob",
|
||||||
|
"org",
|
||||||
|
"red",
|
||||||
"gov",
|
"gov",
|
||||||
"com",
|
"com",
|
||||||
"edu",
|
"edu",
|
||||||
|
@ -245,7 +245,7 @@ func TestWithTwoProtocol(t *testing.T) {
|
|||||||
func TestWithBadProtocol(t *testing.T) {
|
func TestWithBadProtocol(t *testing.T) {
|
||||||
_, err := testWithProtocol(t, []string{"test"})
|
_, err := testWithProtocol(t, []string{"test"})
|
||||||
if err != ErrBadStatus {
|
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
2
third_party/deps
vendored
@ -1,6 +1,6 @@
|
|||||||
packages="
|
packages="
|
||||||
github.com/BurntSushi/toml
|
github.com/BurntSushi/toml
|
||||||
github.com/coreos/go-raft
|
github.com/coreos/raft
|
||||||
github.com/coreos/go-etcd
|
github.com/coreos/go-etcd
|
||||||
github.com/coreos/go-log/log
|
github.com/coreos/go-log/log
|
||||||
github.com/coreos/go-systemd
|
github.com/coreos/go-systemd
|
||||||
|
1
third_party/github.com/coreos/go-etcd/.gitignore
vendored
Normal file
1
third_party/github.com/coreos/go-etcd/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
config.json
|
43
third_party/github.com/coreos/go-etcd/README.md
vendored
43
third_party/github.com/coreos/go-etcd/README.md
vendored
@ -1,12 +1,8 @@
|
|||||||
# go-etcd
|
# 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
|
For usage, please refer to: [go-etcd/etcd](http://godoc.org/github.com/coreos/go-etcd/etcd).
|
||||||
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)
|
|
||||||
|
|
||||||
## Install
|
## 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
|
go get github.com/coreos/go-etcd/etcd
|
||||||
```
|
```
|
||||||
|
|
||||||
## Examples
|
## License
|
||||||
|
|
||||||
Returning error values are not showed for the sake of simplicity, but you
|
See LICENSE file.
|
||||||
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)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
11
third_party/github.com/coreos/go-etcd/etcd/add_child.go
vendored
Normal file
11
third_party/github.com/coreos/go-etcd/etcd/add_child.go
vendored
Normal 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)
|
||||||
|
}
|
73
third_party/github.com/coreos/go-etcd/etcd/add_child_test.go
vendored
Normal file
73
third_party/github.com/coreos/go-etcd/etcd/add_child_test.go
vendored
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
304
third_party/github.com/coreos/go-etcd/etcd/client.go
vendored
304
third_party/github.com/coreos/go-etcd/etcd/client.go
vendored
@ -2,12 +2,16 @@ package etcd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -17,25 +21,43 @@ const (
|
|||||||
HTTPS
|
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 {
|
type Cluster struct {
|
||||||
Leader string
|
Leader string `json:"leader"`
|
||||||
Machines []string
|
Machines []string `json:"machines"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
CertFile string
|
CertFile string `json:"certFile"`
|
||||||
KeyFile string
|
KeyFile string `json:"keyFile"`
|
||||||
Scheme string
|
Scheme string `json:"scheme"`
|
||||||
Timeout time.Duration
|
Timeout time.Duration `json:"timeout"`
|
||||||
|
Consistency string `json: "consistency"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
cluster Cluster
|
cluster Cluster `json:"cluster"`
|
||||||
config Config
|
config Config `json:"config"`
|
||||||
httpClient *http.Client
|
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 {
|
func NewClient(machines []string) *Client {
|
||||||
// if an empty slice was sent in then just assume localhost
|
// if an empty slice was sent in then just assume localhost
|
||||||
if len(machines) == 0 {
|
if len(machines) == 0 {
|
||||||
@ -53,30 +75,168 @@ func NewClient(machines []string) *Client {
|
|||||||
Scheme: "http",
|
Scheme: "http",
|
||||||
// default timeout is one second
|
// default timeout is one second
|
||||||
Timeout: time.Second,
|
Timeout: time.Second,
|
||||||
|
// default consistency level is STRONG
|
||||||
|
Consistency: STRONG_CONSISTENCY,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client := &Client{
|
||||||
|
cluster: cluster,
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := setupHttpClient(client)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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{
|
tr := &http.Transport{
|
||||||
Dial: dialTimeout,
|
Dial: dialTimeout,
|
||||||
TLSClientConfig: &tls.Config{
|
TLSClientConfig: &tls.Config{
|
||||||
InsecureSkipVerify: true,
|
InsecureSkipVerify: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
client.httpClient = &http.Client{Transport: tr}
|
||||||
return &Client{
|
|
||||||
cluster: cluster,
|
|
||||||
config: config,
|
|
||||||
httpClient: &http.Client{Transport: tr},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) SetCertAndKey(cert string, key string) (bool, error) {
|
// 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 guranteed 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 guranteed that the GET requests
|
||||||
|
// will see changes made by previous requests (they might have not
|
||||||
|
// yet been commited 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 != "" {
|
if cert != "" && key != "" {
|
||||||
tlsCert, err := tls.LoadX509KeyPair(cert, key)
|
tlsCert, err := tls.LoadX509KeyPair(cert, key)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tr := &http.Transport{
|
tr := &http.Transport{
|
||||||
@ -88,24 +248,27 @@ func (c *Client) SetCertAndKey(cert string, key string) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.httpClient = &http.Client{Transport: tr}
|
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 {
|
if scheme == HTTP {
|
||||||
c.config.Scheme = "http"
|
c.config.Scheme = "http"
|
||||||
return true, nil
|
c.saveConfig()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
if scheme == HTTPS {
|
if scheme == HTTPS {
|
||||||
c.config.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 {
|
func (c *Client) SetCluster(machines []string) bool {
|
||||||
success := c.internalSyncCluster(machines)
|
success := c.internalSyncCluster(machines)
|
||||||
return success
|
return success
|
||||||
@ -115,13 +278,13 @@ func (c *Client) GetCluster() []string {
|
|||||||
return c.cluster.Machines
|
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 {
|
func (c *Client) SyncCluster() bool {
|
||||||
success := c.internalSyncCluster(c.cluster.Machines)
|
success := c.internalSyncCluster(c.cluster.Machines)
|
||||||
return success
|
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 {
|
func (c *Client) internalSyncCluster(machines []string) bool {
|
||||||
for _, machine := range machines {
|
for _, machine := range machines {
|
||||||
httpPath := c.createHttpPath(machine, version+"/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]
|
c.cluster.Leader = c.cluster.Machines[0]
|
||||||
|
|
||||||
logger.Debug("sync.machines ", c.cluster.Machines)
|
logger.Debug("sync.machines ", c.cluster.Machines)
|
||||||
|
c.saveConfig()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
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 {
|
func (c *Client) createHttpPath(serverName string, _path string) string {
|
||||||
u, _ := url.Parse(serverName)
|
u, _ := url.Parse(serverName)
|
||||||
u.Path = path.Join(u.Path, "/", _path)
|
u.Path = path.Join(u.Path, "/", _path)
|
||||||
|
|
||||||
if u.Scheme == "" {
|
if u.Scheme == "" {
|
||||||
u.Scheme = "http"
|
u.Scheme = "http"
|
||||||
}
|
}
|
||||||
@ -167,18 +333,6 @@ func dialTimeout(network, addr string) (net.Conn, error) {
|
|||||||
return net.DialTimeout(network, addr, time.Second)
|
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) {
|
func (c *Client) updateLeader(httpPath string) {
|
||||||
u, _ := url.Parse(httpPath)
|
u, _ := url.Parse(httpPath)
|
||||||
|
|
||||||
@ -191,77 +345,5 @@ func (c *Client) updateLeader(httpPath string) {
|
|||||||
|
|
||||||
logger.Debugf("update.leader[%s,%s]", c.cluster.Leader, leader)
|
logger.Debugf("update.leader[%s,%s]", c.cluster.Leader, leader)
|
||||||
c.cluster.Leader = leader
|
c.cluster.Leader = leader
|
||||||
}
|
c.saveConfig()
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package etcd
|
package etcd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
|
||||||
"net/url"
|
|
||||||
"net"
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
// To pass this test, we need to create a cluster of 3 machines
|
// 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")
|
t.Fatal("cannot sync machines")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, m := range(c.GetCluster()) {
|
for _, m := range c.GetCluster() {
|
||||||
u, err := url.Parse(m)
|
u, err := url.Parse(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
18
third_party/github.com/coreos/go-etcd/etcd/compare_and_swap.go
vendored
Normal file
18
third_party/github.com/coreos/go-etcd/etcd/compare_and_swap.go
vendored
Normal 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)
|
||||||
|
}
|
51
third_party/github.com/coreos/go-etcd/etcd/compare_and_swap_test.go
vendored
Normal file
51
third_party/github.com/coreos/go-etcd/etcd/compare_and_swap_test.go
vendored
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,8 @@ var logger *log.Logger
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
setLogger(log.PriErr)
|
setLogger(log.PriErr)
|
||||||
|
// Uncomment the following line if you want to see lots of logs
|
||||||
|
// OpenDebug()
|
||||||
}
|
}
|
||||||
|
|
||||||
func OpenDebug() {
|
func OpenDebug() {
|
||||||
|
@ -1,40 +1,17 @@
|
|||||||
package etcd
|
package etcd
|
||||||
|
|
||||||
import (
|
// DeleteAll deletes everything under the given key. If the key
|
||||||
"encoding/json"
|
// points to a file, the file will be deleted. If the key points
|
||||||
"io/ioutil"
|
// to a directory, then everything under the directory, include
|
||||||
"net/http"
|
// all child directories, will be deleted.
|
||||||
"path"
|
func (c *Client) DeleteAll(key string) (*Response, error) {
|
||||||
)
|
return c.delete(key, options{
|
||||||
|
"recursive": true,
|
||||||
func (c *Client) Delete(key string) (*Response, error) {
|
})
|
||||||
|
}
|
||||||
resp, err := c.sendRequest("DELETE", path.Join("keys", key), "")
|
|
||||||
|
// Delete deletes the given key. If the key points to a
|
||||||
if err != nil {
|
// directory, the method will fail.
|
||||||
return nil, err
|
func (c *Client) Delete(key string) (*Response, error) {
|
||||||
}
|
return c.delete(key, nil)
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,18 +5,60 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestDelete(t *testing.T) {
|
func TestDelete(t *testing.T) {
|
||||||
|
|
||||||
c := NewClient(nil)
|
c := NewClient(nil)
|
||||||
|
defer func() {
|
||||||
|
c.DeleteAll("foo")
|
||||||
|
}()
|
||||||
|
|
||||||
c.Set("foo", "bar", 100)
|
c.Set("foo", "bar", 5)
|
||||||
result, err := c.Delete("foo")
|
resp, err := c.Delete("foo")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.PrevValue != "bar" || result.Value != "" {
|
if !(resp.PrevValue == "bar" && resp.Value == "") {
|
||||||
t.Fatalf("Delete failed with %s %s", result.PrevValue,
|
t.Fatalf("Delete failed with %s %s", resp.PrevValue,
|
||||||
result.Value)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,82 +1,23 @@
|
|||||||
package etcd
|
package etcd
|
||||||
|
|
||||||
import (
|
// GetDir gets the all contents under the given key.
|
||||||
"encoding/json"
|
// If the key points to a file, the file is returned.
|
||||||
"io/ioutil"
|
// If the key points to a directory, everything under it is returnd,
|
||||||
"net/http"
|
// including all contents under all child directories.
|
||||||
"path"
|
func (c *Client) GetAll(key string, sort bool) (*Response, error) {
|
||||||
)
|
return c.get(key, options{
|
||||||
|
"recursive": true,
|
||||||
func (c *Client) Get(key string) ([]*Response, error) {
|
"sorted": sort,
|
||||||
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)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTo gets the value of the key from a given machine address.
|
// Get gets the file or directory associated with the given key.
|
||||||
// If the given machine is not available it returns an error.
|
// If the key points to a directory, files and directories under
|
||||||
// Mainly use for testing purpose
|
// it will be returned in sorted or unsorted order, depending on
|
||||||
func (c *Client) GetFrom(key string, addr string) ([]*Response, error) {
|
// the sort flag. Note that contents under child directories
|
||||||
httpPath := c.createHttpPath(addr, path.Join(version, "keys", key))
|
// will not be returned. To get those contents, use GetAll.
|
||||||
|
func (c *Client) Get(key string, sort bool) (*Response, error) {
|
||||||
resp, err := c.httpClient.Get(httpPath)
|
return c.get(key, options{
|
||||||
|
"sorted": sort,
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
@ -1,46 +1,99 @@
|
|||||||
package etcd
|
package etcd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
|
|
||||||
c := NewClient(nil)
|
c := NewClient(nil)
|
||||||
|
defer func() {
|
||||||
|
c.DeleteAll("foo")
|
||||||
|
}()
|
||||||
|
|
||||||
c.Set("foo", "bar", 100)
|
c.Set("foo", "bar", 5)
|
||||||
|
|
||||||
// wait for commit
|
result, err := c.Get("foo", false)
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
|
|
||||||
results, err := c.Get("foo")
|
|
||||||
|
|
||||||
if err != nil || results[0].Key != "/foo" || results[0].Value != "bar" {
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Fatalf("Get failed with %s %s %v", results[0].Key, results[0].Value, results[0].TTL)
|
|
||||||
|
if result.Key != "/foo" || result.Value != "bar" {
|
||||||
|
t.Fatalf("Get failed with %s %s %v", result.Key, result.Value, result.TTL)
|
||||||
}
|
}
|
||||||
|
|
||||||
results, err = c.Get("goo")
|
result, err = c.Get("goo", false)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("should not be able to get non-exist key")
|
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")
|
||||||
|
}()
|
||||||
|
|
||||||
|
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 || results[0].Key != "/foo" || results[0].Value != "bar" {
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Fatalf("Get failed with %s %s %v", results[0].Key, results[0].Value, results[0].TTL)
|
|
||||||
|
expected := kvPairs{
|
||||||
|
KeyValuePair{
|
||||||
|
Key: "/fooDir/k0",
|
||||||
|
Value: "v0",
|
||||||
|
},
|
||||||
|
KeyValuePair{
|
||||||
|
Key: "/fooDir/k1",
|
||||||
|
Value: "v1",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
results, err = c.GetFrom("foo", "0.0.0.0:4009")
|
if !reflect.DeepEqual(result.Kvs, expected) {
|
||||||
|
t.Fatalf("(actual) %v != (expected) %v", result.Kvs, expected)
|
||||||
|
}
|
||||||
|
|
||||||
if err == nil {
|
// Test the `recursive` option
|
||||||
t.Fatal("should not get from port 4009")
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
290
third_party/github.com/coreos/go-etcd/etcd/requests.go
vendored
Normal file
290
third_party/github.com/coreos/go-etcd/etcd/requests.go
vendored
Normal 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
|
||||||
|
}
|
@ -11,6 +11,7 @@ type Response struct {
|
|||||||
Dir bool `json:"dir,omitempty"`
|
Dir bool `json:"dir,omitempty"`
|
||||||
PrevValue string `json:"prevValue,omitempty"`
|
PrevValue string `json:"prevValue,omitempty"`
|
||||||
Value string `json:"value,omitempty"`
|
Value string `json:"value,omitempty"`
|
||||||
|
Kvs kvPairs `json:"kvs,omitempty"`
|
||||||
|
|
||||||
// If the key did not exist before the action,
|
// If the key did not exist before the action,
|
||||||
// this field should be set to true
|
// this field should be set to true
|
||||||
@ -22,5 +23,28 @@ type Response struct {
|
|||||||
TTL int64 `json:"ttl,omitempty"`
|
TTL int64 `json:"ttl,omitempty"`
|
||||||
|
|
||||||
// The command index of the raft machine when the command is executed
|
// 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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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]
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
}
|
|
43
third_party/github.com/coreos/go-etcd/etcd/set_curl_chan_test.go
vendored
Normal file
43
third_party/github.com/coreos/go-etcd/etcd/set_curl_chan_test.go
vendored
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
43
third_party/github.com/coreos/go-etcd/etcd/set_update_create.go
vendored
Normal file
43
third_party/github.com/coreos/go-etcd/etcd/set_update_create.go
vendored
Normal 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,
|
||||||
|
})
|
||||||
|
}
|
183
third_party/github.com/coreos/go-etcd/etcd/set_update_create_test.go
vendored
Normal file
183
third_party/github.com/coreos/go-etcd/etcd/set_update_create_test.go
vendored
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
|
||||||
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
33
third_party/github.com/coreos/go-etcd/etcd/utils.go
vendored
Normal file
33
third_party/github.com/coreos/go-etcd/etcd/utils.go
vendored
Normal 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
|
||||||
|
}
|
@ -1,3 +1,3 @@
|
|||||||
package etcd
|
package etcd
|
||||||
|
|
||||||
const version = "v1"
|
const version = "v2"
|
||||||
|
124
third_party/github.com/coreos/go-etcd/etcd/watch.go
vendored
124
third_party/github.com/coreos/go-etcd/etcd/watch.go
vendored
@ -1,42 +1,47 @@
|
|||||||
package etcd
|
package etcd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"path"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type respAndErr struct {
|
|
||||||
resp *http.Response
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Errors introduced by the Watch command.
|
// Errors introduced by the Watch command.
|
||||||
var (
|
var (
|
||||||
ErrWatchStoppedByUser = errors.New("Watch stopped by the user via stop channel")
|
ErrWatchStoppedByUser = errors.New("Watch stopped by the user via stop channel")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Watch any change under the given prefix.
|
// WatchAll returns the first change under the given prefix since the given index. To
|
||||||
// When a sinceIndex is given, watch will try to scan from that index to the last index
|
// watch for the latest change, set waitIndex = 0.
|
||||||
// and will return any changes under the given prefix during the history
|
//
|
||||||
|
// 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
|
// 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.
|
// 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
|
// 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)
|
logger.Debugf("watch %s [%s]", prefix, c.cluster.Leader)
|
||||||
if receiver == nil {
|
if receiver == nil {
|
||||||
return c.watchOnce(prefix, sinceIndex, stop)
|
return c.watchOnce(prefix, waitIndex, recursive, stop)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
for {
|
for {
|
||||||
resp, err := c.watchOnce(prefix, sinceIndex, stop)
|
resp, err := c.watchOnce(prefix, waitIndex, recursive, stop)
|
||||||
if resp != nil {
|
if resp != nil {
|
||||||
sinceIndex = resp.Index + 1
|
waitIndex = resp.ModifiedIndex + 1
|
||||||
receiver <- resp
|
receiver <- resp
|
||||||
} else {
|
} else {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -49,70 +54,37 @@ func (c *Client) Watch(prefix string, sinceIndex uint64, receiver chan *Response
|
|||||||
|
|
||||||
// helper func
|
// helper func
|
||||||
// return when there is change under the given prefix
|
// 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
|
respChan := make(chan *Response)
|
||||||
var err error
|
errChan := make(chan error)
|
||||||
|
|
||||||
if stop != nil {
|
|
||||||
ch := make(chan respAndErr)
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
resp, err = c.sendWatchRequest(key, sinceIndex)
|
options := options{
|
||||||
|
"wait": true,
|
||||||
|
}
|
||||||
|
if waitIndex > 0 {
|
||||||
|
options["waitIndex"] = waitIndex
|
||||||
|
}
|
||||||
|
if recursive {
|
||||||
|
options["recursive"] = true
|
||||||
|
}
|
||||||
|
|
||||||
ch <- respAndErr{resp, err}
|
resp, err := c.get(key, options)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
errChan <- err
|
||||||
|
}
|
||||||
|
|
||||||
|
respChan <- resp
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// select at stop or continue to receive
|
|
||||||
select {
|
select {
|
||||||
|
case resp := <-respChan:
|
||||||
case res := <-ch:
|
return resp, nil
|
||||||
resp, err = res.resp, res.err
|
case err := <-errChan:
|
||||||
|
return nil, err
|
||||||
case <-stop:
|
case <-stop:
|
||||||
resp, err = nil, ErrWatchStoppedByUser
|
return nil, ErrWatchStoppedByUser
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
resp, err = c.sendWatchRequest(key, sinceIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,31 +8,34 @@ import (
|
|||||||
|
|
||||||
func TestWatch(t *testing.T) {
|
func TestWatch(t *testing.T) {
|
||||||
c := NewClient(nil)
|
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)
|
resp, err := c.Watch("watch_foo", 0, nil, nil)
|
||||||
|
|
||||||
if err != nil || result.Key != "/watch_foo/foo" || result.Value != "bar" {
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Fatalf("Watch failed with %s %s %v %v", result.Key, result.Value, result.TTL, result.Index)
|
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" {
|
resp, err = c.Watch("watch_foo", resp.ModifiedIndex, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Fatalf("Watch with Index failed with %s %s %v %v", result.Key, result.Value, result.TTL, result.Index)
|
if !(resp.Key == "/watch_foo" && resp.Value == "bar") {
|
||||||
|
t.Fatalf("Watch 2 failed: %#v", resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
ch := make(chan *Response, 10)
|
ch := make(chan *Response, 10)
|
||||||
stop := make(chan bool, 1)
|
stop := make(chan bool, 1)
|
||||||
|
|
||||||
go setLoop("bar", c)
|
go setLoop("watch_foo", "bar", c)
|
||||||
|
|
||||||
go receiver(ch, stop)
|
go receiver(ch, stop)
|
||||||
|
|
||||||
@ -42,16 +45,55 @@ func TestWatch(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setHelper(value string, c *Client) {
|
func TestWatchAll(t *testing.T) {
|
||||||
time.Sleep(time.Second)
|
c := NewClient(nil)
|
||||||
c.Set("watch_foo/foo", value, 100)
|
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)
|
time.Sleep(time.Second)
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
newValue := fmt.Sprintf("%s_%v", value, 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)
|
time.Sleep(time.Second / 10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
@ -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))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
170
third_party/github.com/coreos/go-raft/timer.go
vendored
170
third_party/github.com/coreos/go-raft/timer.go
vendored
@ -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
|
|
||||||
}
|
|
@ -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()
|
|
||||||
}
|
|
@ -12,7 +12,15 @@ const (
|
|||||||
listenFdsStart = 3
|
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"))
|
pid, err := strconv.Atoi(os.Getenv("LISTEN_PID"))
|
||||||
if err != nil || pid != os.Getpid() {
|
if err != nil || pid != os.Getpid() {
|
||||||
return nil
|
return nil
|
||||||
@ -24,7 +32,7 @@ func Files() []*os.File {
|
|||||||
files := []*os.File(nil)
|
files := []*os.File(nil)
|
||||||
for fd := listenFdsStart; fd < listenFdsStart+nfds; fd++ {
|
for fd := listenFdsStart; fd < listenFdsStart+nfds; fd++ {
|
||||||
syscall.CloseOnExec(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
|
return files
|
||||||
}
|
}
|
||||||
|
@ -25,32 +25,36 @@ type Conn struct {
|
|||||||
dispatch map[string]func(dbus.Signal)
|
dispatch map[string]func(dbus.Signal)
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *Conn {
|
func New() (*Conn, error) {
|
||||||
c := new(Conn)
|
c := new(Conn)
|
||||||
c.initConnection()
|
|
||||||
|
if err := c.initConnection(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
c.initJobs()
|
c.initJobs()
|
||||||
c.initSubscription()
|
c.initSubscription()
|
||||||
c.initDispatch()
|
c.initDispatch()
|
||||||
return c
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) initConnection() {
|
func (c *Conn) initConnection() error {
|
||||||
var err error
|
var err error
|
||||||
c.sysconn, err = dbus.SystemBusPrivate()
|
c.sysconn, err = dbus.SystemBusPrivate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.sysconn.Auth(nil)
|
err = c.sysconn.Auth(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.sysconn.Close()
|
c.sysconn.Close()
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.sysconn.Hello()
|
err = c.sysconn.Hello()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.sysconn.Close()
|
c.sysconn.Close()
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.sysobj = c.sysconn.Object("org.freedesktop.systemd1", dbus.ObjectPath("/org/freedesktop/systemd1"))
|
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()
|
err = c.sysobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.sysconn.Close()
|
c.sysconn.Close()
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) initDispatch() {
|
func (c *Conn) initDispatch() {
|
||||||
|
@ -164,3 +164,53 @@ type UnitStatus struct {
|
|||||||
JobType string // The job type as string
|
JobType string // The job type as string
|
||||||
JobPath dbus.ObjectPath // The job object path
|
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
|
||||||
|
}
|
||||||
|
@ -20,9 +20,7 @@ import (
|
|||||||
// used for construction of the scope only and specifies the initial PIDs to
|
// used for construction of the scope only and specifies the initial PIDs to
|
||||||
// add to the scope object.
|
// add to the scope object.
|
||||||
|
|
||||||
type Property property
|
type Property struct {
|
||||||
|
|
||||||
type property struct {
|
|
||||||
Name string
|
Name string
|
||||||
Value dbus.Variant
|
Value dbus.Variant
|
||||||
}
|
}
|
||||||
@ -38,96 +36,95 @@ type execStart struct {
|
|||||||
// the executed command. See
|
// the executed command. See
|
||||||
// http://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart=
|
// http://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart=
|
||||||
func PropExecStart(command []string, uncleanIsFailure bool) Property {
|
func PropExecStart(command []string, uncleanIsFailure bool) Property {
|
||||||
return Property(
|
execStarts := []execStart{
|
||||||
property{
|
|
||||||
Name: "ExecStart",
|
|
||||||
Value: dbus.MakeVariant(
|
|
||||||
[]execStart{
|
|
||||||
execStart{
|
execStart{
|
||||||
Path: command[0],
|
Path: command[0],
|
||||||
Args: command,
|
Args: command,
|
||||||
UncleanIsFailure: uncleanIsFailure,
|
UncleanIsFailure: uncleanIsFailure,
|
||||||
}})})
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return Property{
|
||||||
|
Name: "ExecStart",
|
||||||
|
Value: dbus.MakeVariant(execStarts),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PropRemainAfterExit sets the RemainAfterExit service property. See
|
// PropRemainAfterExit sets the RemainAfterExit service property. See
|
||||||
// http://www.freedesktop.org/software/systemd/man/systemd.service.html#RemainAfterExit=
|
// http://www.freedesktop.org/software/systemd/man/systemd.service.html#RemainAfterExit=
|
||||||
func PropRemainAfterExit(b bool) Property {
|
func PropRemainAfterExit(b bool) Property {
|
||||||
return Property(
|
return Property{
|
||||||
property{
|
|
||||||
Name: "RemainAfterExit",
|
Name: "RemainAfterExit",
|
||||||
Value: dbus.MakeVariant(b),
|
Value: dbus.MakeVariant(b),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PropDescription sets the Description unit property. See
|
// PropDescription sets the Description unit property. See
|
||||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit#Description=
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit#Description=
|
||||||
func PropDescription(desc string) Property {
|
func PropDescription(desc string) Property {
|
||||||
return Property(
|
return Property{
|
||||||
property{
|
|
||||||
Name: "Description",
|
Name: "Description",
|
||||||
Value: dbus.MakeVariant(desc),
|
Value: dbus.MakeVariant(desc),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func propDependency(name string, units []string) Property {
|
func propDependency(name string, units []string) Property {
|
||||||
return Property(
|
return Property{
|
||||||
property{
|
|
||||||
Name: name,
|
Name: name,
|
||||||
Value: dbus.MakeVariant(units),
|
Value: dbus.MakeVariant(units),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PropRequires sets the Requires unit property. See
|
// 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 {
|
func PropRequires(units ...string) Property {
|
||||||
return propDependency("Requires", units)
|
return propDependency("Requires", units)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PropRequiresOverridable sets the RequiresOverridable unit property. See
|
// 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 {
|
func PropRequiresOverridable(units ...string) Property {
|
||||||
return propDependency("RequiresOverridable", units)
|
return propDependency("RequiresOverridable", units)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PropRequisite sets the Requisite unit property. See
|
// 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 {
|
func PropRequisite(units ...string) Property {
|
||||||
return propDependency("Requisite", units)
|
return propDependency("Requisite", units)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PropRequisiteOverridable sets the RequisiteOverridable unit property. See
|
// 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 {
|
func PropRequisiteOverridable(units ...string) Property {
|
||||||
return propDependency("RequisiteOverridable", units)
|
return propDependency("RequisiteOverridable", units)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PropWants sets the Wants unit property. See
|
// 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 {
|
func PropWants(units ...string) Property {
|
||||||
return propDependency("Wants", units)
|
return propDependency("Wants", units)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PropBindsTo sets the BindsTo unit property. See
|
// 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 {
|
func PropBindsTo(units ...string) Property {
|
||||||
return propDependency("BindsTo", units)
|
return propDependency("BindsTo", units)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PropRequiredBy sets the RequiredBy unit property. See
|
// 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 {
|
func PropRequiredBy(units ...string) Property {
|
||||||
return propDependency("RequiredBy", units)
|
return propDependency("RequiredBy", units)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PropRequiredByOverridable sets the RequiredByOverridable unit property. See
|
// 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 {
|
func PropRequiredByOverridable(units ...string) Property {
|
||||||
return propDependency("RequiredByOverridable", units)
|
return propDependency("RequiredByOverridable", units)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PropWantedBy sets the WantedBy unit property. See
|
// 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 {
|
func PropWantedBy(units ...string) Property {
|
||||||
return propDependency("WantedBy", units)
|
return propDependency("WantedBy", units)
|
||||||
}
|
}
|
||||||
@ -139,55 +136,55 @@ func PropBoundBy(units ...string) Property {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PropConflicts sets the Conflicts unit property. See
|
// 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 {
|
func PropConflicts(units ...string) Property {
|
||||||
return propDependency("Conflicts", units)
|
return propDependency("Conflicts", units)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PropConflictedBy sets the ConflictedBy unit property. See
|
// 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 {
|
func PropConflictedBy(units ...string) Property {
|
||||||
return propDependency("ConflictedBy", units)
|
return propDependency("ConflictedBy", units)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PropBefore sets the Before unit property. See
|
// 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 {
|
func PropBefore(units ...string) Property {
|
||||||
return propDependency("Before", units)
|
return propDependency("Before", units)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PropAfter sets the After unit property. See
|
// 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 {
|
func PropAfter(units ...string) Property {
|
||||||
return propDependency("After", units)
|
return propDependency("After", units)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PropOnFailure sets the OnFailure unit property. See
|
// 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 {
|
func PropOnFailure(units ...string) Property {
|
||||||
return propDependency("OnFailure", units)
|
return propDependency("OnFailure", units)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PropTriggers sets the Triggers unit property. See
|
// 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 {
|
func PropTriggers(units ...string) Property {
|
||||||
return propDependency("Triggers", units)
|
return propDependency("Triggers", units)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PropTriggeredBy sets the TriggeredBy unit property. See
|
// 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 {
|
func PropTriggeredBy(units ...string) Property {
|
||||||
return propDependency("TriggeredBy", units)
|
return propDependency("TriggeredBy", units)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PropPropagatesReloadTo sets the PropagatesReloadTo unit property. See
|
// 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 {
|
func PropPropagatesReloadTo(units ...string) Property {
|
||||||
return propDependency("PropagatesReloadTo", units)
|
return propDependency("PropagatesReloadTo", units)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PropRequiresMountsFor sets the RequiresMountsFor unit property. See
|
// 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 {
|
func PropRequiresMountsFor(units ...string) Property {
|
||||||
return propDependency("RequiresMountsFor", units)
|
return propDependency("RequiresMountsFor", units)
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package dbus
|
package dbus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/guelfey/go.dbus"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/guelfey/go.dbus"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -71,12 +73,6 @@ type SubStateUpdate struct {
|
|||||||
SubState string
|
SubState string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Error string
|
|
||||||
|
|
||||||
func (e Error) Error() string {
|
|
||||||
return string(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetSubStateSubscriber writes to updateCh when any unit's substate changes.
|
// SetSubStateSubscriber writes to updateCh when any unit's substate changes.
|
||||||
// Althrough this writes to updateCh on every state change, the reported state
|
// 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
|
// 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
info, err := c.getUnitInfo(path)
|
info, err := c.GetUnitInfo(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
select {
|
select {
|
||||||
case c.subscriber.errCh <- err:
|
case c.subscriber.errCh <- err:
|
||||||
@ -120,7 +116,7 @@ func (c *Conn) sendSubStateUpdate(path dbus.ObjectPath) {
|
|||||||
case c.subscriber.updateCh <- update:
|
case c.subscriber.updateCh <- update:
|
||||||
default:
|
default:
|
||||||
select {
|
select {
|
||||||
case c.subscriber.errCh <- Error("update channel full!"):
|
case c.subscriber.errCh <- errors.New("update channel full!"):
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,7 +124,7 @@ func (c *Conn) sendSubStateUpdate(path dbus.ObjectPath) {
|
|||||||
c.updateIgnore(path, info)
|
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 err error
|
||||||
var props map[string]dbus.Variant
|
var props map[string]dbus.Variant
|
||||||
obj := c.sysconn.Object("org.freedesktop.systemd1", path)
|
obj := c.sysconn.Object("org.freedesktop.systemd1", path)
|
||||||
|
@ -103,7 +103,7 @@ func appendVariable(w io.Writer, name, value string) {
|
|||||||
fmt.Fprintln(w, value)
|
fmt.Fprintln(w, value)
|
||||||
} else {
|
} else {
|
||||||
/* just write the variable and value all on one line */
|
/* 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,
|
/* 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)
|
* 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 {
|
for _, c := range name {
|
||||||
valid = valid && ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_'
|
valid = valid && ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_'
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package raft
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"code.google.com/p/goprotobuf/proto"
|
"code.google.com/p/goprotobuf/proto"
|
||||||
"github.com/coreos/go-raft/protobuf"
|
"github.com/coreos/raft/protobuf"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
)
|
)
|
@ -2,7 +2,7 @@ package raft
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"code.google.com/p/goprotobuf/proto"
|
"code.google.com/p/goprotobuf/proto"
|
||||||
"github.com/coreos/go-raft/protobuf"
|
"github.com/coreos/raft/protobuf"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
)
|
)
|
@ -5,7 +5,7 @@ import (
|
|||||||
"code.google.com/p/goprotobuf/proto"
|
"code.google.com/p/goprotobuf/proto"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/coreos/go-raft/protobuf"
|
"github.com/coreos/raft/protobuf"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
@ -5,7 +5,7 @@ import (
|
|||||||
"code.google.com/p/goprotobuf/proto"
|
"code.google.com/p/goprotobuf/proto"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/coreos/go-raft/protobuf"
|
"github.com/coreos/raft/protobuf"
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user