Browse Source

Merge branch 'master' of https://github.com/coreos/etcd into mod-lock

Conflicts:
	mod/mod.go
Ben Johnson 12 years ago
parent
commit
74ad50efa6
100 changed files with 9094 additions and 8115 deletions
  1. 1 0
      README.md
  2. 13 55
      etcd.go
  3. 26 11
      mod/dashboard/app/scripts/common/services/etcd.js
  4. 4 4
      mod/dashboard/app/scripts/controllers/browser.js
  5. 0 0
      mod/dashboard/resources/scripts-browser-modules.js.go
  6. 0 0
      mod/dashboard/resources/scripts-browser-scripts.js.go
  7. 0 0
      mod/dashboard/resources/scripts-stats-modules.js.go
  8. 0 0
      mod/dashboard/resources/scripts-stats-scripts.js.go
  9. 0 0
      mod/dashboard/resources/styles-main.css.go
  10. 0 0
      mod/dashboard/resources/views-stats.html.go
  11. 8 0
      mod/mod.go
  12. 27 0
      profile.go
  13. 91 124
      server/config.go
  14. 166 66
      server/config_test.go
  15. 78 0
      server/cors_handler.go
  16. 1 1
      server/join_command.go
  17. 1 1
      server/peer_server.go
  18. 1 1
      server/peer_server_handlers.go
  19. 1 1
      server/raft_server_stats.go
  20. 1 1
      server/remove_command.go
  21. 17 31
      server/server.go
  22. 2 2
      server/tls_info.go
  23. 1 1
      server/transporter.go
  24. 57 0
      server/usage.go
  25. 1 1
      server/v1/set_key_handler.go
  26. 1 1
      server/v1/v1.go
  27. 1 1
      server/v2/get_handler.go
  28. 1 1
      server/v2/put_handler.go
  29. 1 1
      server/v2/v2.go
  30. 1 1
      store/command_factory.go
  31. 1 1
      store/v2/command_factory.go
  32. 1 1
      store/v2/compare_and_swap_command.go
  33. 1 1
      store/v2/create_command.go
  34. 1 1
      store/v2/delete_command.go
  35. 1 1
      store/v2/set_command.go
  36. 1 1
      store/v2/sync_command.go
  37. 1 1
      store/v2/update_command.go
  38. 2 2
      tests/functional/multi_node_kill_all_and_recovery_test.go
  39. 10 10
      tests/functional/remove_node_test.go
  40. 1 3
      tests/functional/single_node_recovery_test.go
  41. 3 10
      tests/functional/single_node_test.go
  42. 1 1
      tests/functional/util.go
  43. 31 20
      third_party/code.google.com/p/go.net/ipv4/mocktransponder_test.go
  44. 89 78
      third_party/code.google.com/p/go.net/ipv4/multicastlistener_test.go
  45. 1 1
      third_party/code.google.com/p/go.net/ipv4/packet.go
  46. 6 5
      third_party/code.google.com/p/go.net/ipv6/icmp.go
  47. 5 7
      third_party/code.google.com/p/go.net/ipv6/icmp_bsd.go
  48. 5 7
      third_party/code.google.com/p/go.net/ipv6/icmp_linux.go
  49. 4 4
      third_party/code.google.com/p/go.net/ipv6/icmp_plan9.go
  50. 4 4
      third_party/code.google.com/p/go.net/ipv6/icmp_windows.go
  51. 24 6
      third_party/code.google.com/p/go.net/ipv6/mockicmp_test.go
  52. 35 1
      third_party/code.google.com/p/go.net/ipv6/multicast_test.go
  53. 54 8
      third_party/code.google.com/p/go.net/ipv6/multicastlistener_test.go
  54. 3 2
      third_party/code.google.com/p/go.net/ipv6/sockopt_rfc3493_bsd.go
  55. 3 2
      third_party/code.google.com/p/go.net/ipv6/sockopt_rfc3493_linux.go
  56. 43 33
      third_party/code.google.com/p/go.net/ipv6/sockopt_rfc3493_unix.go
  57. 16 16
      third_party/code.google.com/p/go.net/ipv6/sockopt_rfc3493_windows.go
  58. 52 10
      third_party/code.google.com/p/go.net/ipv6/sockopt_rfc3542_unix.go
  59. 29 1
      third_party/code.google.com/p/go.net/ipv6/unicast_test.go
  60. 16 17
      third_party/code.google.com/p/go.net/publicsuffix/list_test.go
  61. 6564 6458
      third_party/code.google.com/p/go.net/publicsuffix/table.go
  62. 225 24
      third_party/code.google.com/p/go.net/publicsuffix/table_test.go
  63. 1 1
      third_party/code.google.com/p/go.net/websocket/websocket_test.go
  64. 1 1
      third_party/deps
  65. 1 0
      third_party/github.com/coreos/go-etcd/.gitignore
  66. 4 39
      third_party/github.com/coreos/go-etcd/README.md
  67. 11 0
      third_party/github.com/coreos/go-etcd/etcd/add_child.go
  68. 73 0
      third_party/github.com/coreos/go-etcd/etcd/add_child_test.go
  69. 198 116
      third_party/github.com/coreos/go-etcd/etcd/client.go
  70. 40 4
      third_party/github.com/coreos/go-etcd/etcd/client_test.go
  71. 18 0
      third_party/github.com/coreos/go-etcd/etcd/compare_and_swap.go
  72. 51 0
      third_party/github.com/coreos/go-etcd/etcd/compare_and_swap_test.go
  73. 2 0
      third_party/github.com/coreos/go-etcd/etcd/debug.go
  74. 12 35
      third_party/github.com/coreos/go-etcd/etcd/delete.go
  75. 47 5
      third_party/github.com/coreos/go-etcd/etcd/delete_test.go
  76. 18 77
      third_party/github.com/coreos/go-etcd/etcd/get.go
  77. 75 22
      third_party/github.com/coreos/go-etcd/etcd/get_test.go
  78. 0 23
      third_party/github.com/coreos/go-etcd/etcd/list_test.go
  79. 290 0
      third_party/github.com/coreos/go-etcd/etcd/requests.go
  80. 31 6
      third_party/github.com/coreos/go-etcd/etcd/response.go
  81. 0 89
      third_party/github.com/coreos/go-etcd/etcd/set.go
  82. 43 0
      third_party/github.com/coreos/go-etcd/etcd/set_curl_chan_test.go
  83. 0 42
      third_party/github.com/coreos/go-etcd/etcd/set_test.go
  84. 43 0
      third_party/github.com/coreos/go-etcd/etcd/set_update_create.go
  85. 183 0
      third_party/github.com/coreos/go-etcd/etcd/set_update_create_test.go
  86. 0 56
      third_party/github.com/coreos/go-etcd/etcd/testAndSet.go
  87. 0 39
      third_party/github.com/coreos/go-etcd/etcd/testAndSet_test.go
  88. 33 0
      third_party/github.com/coreos/go-etcd/etcd/utils.go
  89. 1 1
      third_party/github.com/coreos/go-etcd/etcd/version.go
  90. 48 76
      third_party/github.com/coreos/go-etcd/etcd/watch.go
  91. 61 19
      third_party/github.com/coreos/go-etcd/etcd/watch_test.go
  92. 0 71
      third_party/github.com/coreos/go-etcd/examples/mutex/mutex.go
  93. 0 31
      third_party/github.com/coreos/go-etcd/examples/speed/speed.go
  94. 0 3
      third_party/github.com/coreos/go-etcd/examples/sync-cluster/README.md
  95. 0 51
      third_party/github.com/coreos/go-etcd/examples/sync-cluster/sync-cluster.go
  96. 0 170
      third_party/github.com/coreos/go-raft/timer.go
  97. 0 86
      third_party/github.com/coreos/go-raft/timer_test.go
  98. 10 2
      third_party/github.com/coreos/go-systemd/activation/files.go
  99. 14 8
      third_party/github.com/coreos/go-systemd/dbus/dbus.go
  100. 50 0
      third_party/github.com/coreos/go-systemd/dbus/methods.go

+ 1 - 0
README.md

@@ -617,6 +617,7 @@ See [CONTRIBUTING](https://github.com/coreos/etcd/blob/master/CONTRIBUTING.md) f
 **Clojure libraries**
 
 - [aterreno/etcd-clojure](https://github.com/aterreno/etcd-clojure)
+- [rthomas/clj-etcd](https://github.com/rthomas/clj-etcd)
 
 **Erlang libraries**
 

+ 13 - 55
etcd.go

@@ -17,45 +17,39 @@ limitations under the License.
 package main
 
 import (
-	"flag"
 	"fmt"
-	"io/ioutil"
 	"os"
-	"os/signal"
-	"runtime/pprof"
 
 	"github.com/coreos/etcd/log"
 	"github.com/coreos/etcd/server"
 	"github.com/coreos/etcd/store"
-	"github.com/coreos/go-raft"
+	"github.com/coreos/raft"
 )
 
 func main() {
-	parseFlags()
-
 	// Load configuration.
 	var config = server.NewConfig()
 	if err := config.Load(os.Args[1:]); err != nil {
-		log.Fatal("Configuration error:", err)
+		fmt.Println(server.Usage() + "\n")
+		fmt.Println(err.Error() + "\n")
+		os.Exit(1)
+	} else if config.ShowVersion {
+		fmt.Println(server.ReleaseVersion)
+		os.Exit(0)
+	} else if config.ShowHelp {
+		fmt.Println(server.Usage() + "\n")
+		os.Exit(0)
 	}
 
-	// Turn on logging.
+	// Enable options.
 	if config.VeryVerbose {
 		log.Verbose = true
 		raft.SetLogLevel(raft.Debug)
 	} else if config.Verbose {
 		log.Verbose = true
 	}
-
-	// Setup a default directory based on the machine name
-	if config.DataDir == "" {
-		config.DataDir = config.Name + ".etcd"
-		log.Warnf("Using the directory %s as the etcd configuration directory because a directory was not specified. ", config.DataDir)
-	}
-
-	// Create data directory if it doesn't already exist.
-	if err := os.MkdirAll(config.DataDir, 0744); err != nil {
-		log.Fatalf("Unable to create path: %s", err)
+	if config.CPUProfileFile != "" {
+		profile(config.CPUProfileFile)
 	}
 
 	// Load info object.
@@ -116,39 +110,3 @@ func main() {
 	}()
 	log.Fatal(s.ListenAndServe())
 }
-
-// Parses non-configuration flags.
-func parseFlags() {
-	var versionFlag bool
-	var cpuprofile string
-
-	f := flag.NewFlagSet(os.Args[0], -1)
-	f.SetOutput(ioutil.Discard)
-	f.BoolVar(&versionFlag, "version", false, "print the version and exit")
-	f.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to file")
-	f.Parse(os.Args[1:])
-
-	// Print version if necessary.
-	if versionFlag {
-		fmt.Println(server.ReleaseVersion)
-		os.Exit(0)
-	}
-
-	// Begin CPU profiling if specified.
-	if cpuprofile != "" {
-		f, err := os.Create(cpuprofile)
-		if err != nil {
-			log.Fatal(err)
-		}
-		pprof.StartCPUProfile(f)
-
-		c := make(chan os.Signal, 1)
-		signal.Notify(c, os.Interrupt)
-		go func() {
-			sig := <-c
-			log.Infof("captured %v, stopping profiler and exiting..", sig)
-			pprof.StopCPUProfile()
-			os.Exit(1)
-		}()
-	}
-}

+ 26 - 11
mod/dashboard/app/scripts/common/services/etcd.js

@@ -2,10 +2,11 @@
 
 angular.module('etcd', [])
 
-.factory('EtcdV2', ['$http', function($http) {
+.factory('EtcdV2', ['$http', '$q', function($http, $q) {
   var keyPrefix = '/v2/keys/'
   var statsPrefix = '/v2/stats/'
   var baseURL = '/v2/'
+  var leaderURL = ''
 
   delete $http.defaults.headers.common['X-Requested-With'];
 
@@ -45,19 +46,23 @@ angular.module('etcd', [])
     };
 
     self.set = function(keyValue) {
-      return $http({
-        url: self.path(),
-        data: $.param({value: keyValue}),
-        method: 'PUT',
-        headers: {'Content-Type': 'application/x-www-form-urlencoded'}
+      return getLeader().then(function(leader) {
+        return $http({
+          url: leader + self.path(),
+          data: $.param({value: keyValue}),
+          method: 'PUT',
+          headers: {'Content-Type': 'application/x-www-form-urlencoded'}
+        });
       });
     };
 
     self.deleteKey = function(keyValue) {
-      return $http({
-        url: self.path(),
-        method: 'DELETE',
-        headers: {'Content-Type': 'application/x-www-form-urlencoded'}
+      return getLeader().then(function(leader) {
+        return $http({
+          url: leader + self.path(),
+          method: 'DELETE',
+          headers: {'Content-Type': 'application/x-www-form-urlencoded'}
+        });
       });
     };
 
@@ -79,8 +84,18 @@ angular.module('etcd', [])
     return self
   }
 
+  function getLeader() {
+    return newStat('leader').get().then(function(response) {
+      return newKey('/_etcd/machines/' + response.data.leader).get().then(function(response) {
+        // TODO: do something better here p.s. I hate javascript
+        var data = JSON.parse('{"' + decodeURI(response.data.value.replace(/&/g, "\",\"").replace(/=/g,"\":\"")) + '"}');
+        return data.etcd;
+      });
+    });
+  }
+
   return {
     getStat: newStat,
-    getKey: newKey
+    getKey: newKey,
   }
 }]);

+ 4 - 4
mod/dashboard/app/scripts/controllers/browser.js

@@ -94,23 +94,23 @@ angular.module('etcdBrowser', ['ngRoute', 'etcd', 'timeRelative'])
 
   $scope.saveData = function() {
     // TODO: fixup etcd to allow for empty values
-    $scope.key.set($scope.singleValue || ' ').success(function (data, status, headers, config) {
+    $scope.key.set($scope.singleValue || ' ').then(function(response) {
       $scope.save = 'etcd-save-hide';
       $scope.preview = 'etcd-preview-hide';
       $scope.back();
       $scope.writingNew = false;
-    }).error(function (data, status, headers, config) {
+    }, function (response) {
       $scope.showSaveError(data.message);
     });
   };
 
   $scope.deleteKey = function() {
-    $scope.key.deleteKey().success(function (data, status, headers, config) {
+    $scope.key.deleteKey().then(function(response) {
       //TODO: remove loader
       $scope.save = 'etcd-save-hide';
       $scope.preview = 'etcd-preview-hide';
       $scope.back();
-    }).error(function (data, status, headers, config) {
+    }, function (response) {
       //TODO: remove loader
       //show errors
       $scope.showBrowseError('Could not delete the key');

File diff suppressed because it is too large
+ 0 - 0
mod/dashboard/resources/scripts-browser-modules.js.go


File diff suppressed because it is too large
+ 0 - 0
mod/dashboard/resources/scripts-browser-scripts.js.go


File diff suppressed because it is too large
+ 0 - 0
mod/dashboard/resources/scripts-stats-modules.js.go


File diff suppressed because it is too large
+ 0 - 0
mod/dashboard/resources/scripts-stats-scripts.js.go


File diff suppressed because it is too large
+ 0 - 0
mod/dashboard/resources/styles-main.css.go


File diff suppressed because it is too large
+ 0 - 0
mod/dashboard/resources/views-stats.html.go


+ 8 - 0
mod/mod.go

@@ -3,6 +3,7 @@ package mod
 
 import (
 	"net/http"
+	"path"
 
 	"github.com/coreos/etcd/mod/dashboard"
 	"github.com/coreos/etcd/mod/lock"
@@ -11,11 +12,18 @@ import (
 
 var ServeMux *http.Handler
 
+func addSlash(w http.ResponseWriter, req *http.Request) {
+	http.Redirect(w, req, path.Join("mod", req.URL.Path) + "/", 302)
+	return
+}
+
 func HttpHandler() (handler http.Handler) {
 	r := mux.NewRouter()
+	r.HandleFunc("/dashboard", addSlash)
 	r.PathPrefix("/dashboard/").Handler(http.StripPrefix("/dashboard/", dashboard.HttpHandler()))
 
 	// TODO: Use correct addr.
+	r.HandleFunc("/lock", addSlash)
 	r.PathPrefix("/lock").Handler(http.StripPrefix("/lock", lock.NewHandler("127.0.0.1:4001")))
 	return r
 }

+ 27 - 0
profile.go

@@ -0,0 +1,27 @@
+package main
+
+import (
+	"os"
+	"os/signal"
+	"runtime/pprof"
+
+	"github.com/coreos/etcd/log"
+)
+
+// profile starts CPU profiling.
+func profile(path string) {
+	f, err := os.Create(path)
+	if err != nil {
+		log.Fatal(err)
+	}
+	pprof.StartCPUProfile(f)
+
+	c := make(chan os.Signal, 1)
+	signal.Notify(c, os.Interrupt)
+	go func() {
+		sig := <-c
+		log.Infof("captured %v, stopping profiler and exiting..", sig)
+		pprof.StopCPUProfile()
+		os.Exit(1)
+	}()
+}

+ 91 - 124
server/config.go

@@ -21,23 +21,23 @@ const DefaultSystemConfigPath = "/etc/etcd/etcd.conf"
 
 // A lookup of deprecated flags to their new flag name.
 var newFlagNameLookup = map[string]string{
-	"C": "peers",
-	"CF": "peers-file",
-	"n": "name",
-	"c": "addr",
-	"cl": "bind-addr",
-	"s": "peer-addr",
-	"sl": "peer-bind-addr",
-	"d": "data-dir",
-	"m": "max-result-buffer",
-	"r": "max-retry-attempts",
-	"maxsize": "max-cluster-size",
-	"clientCAFile": "ca-file",
-	"clientCert": "cert-file",
-	"clientKey": "key-file",
-	"serverCAFile": "peer-ca-file",
-	"serverCert": "peer-cert-file",
-	"serverKey": "peer-key-file",
+	"C":             "peers",
+	"CF":            "peers-file",
+	"n":             "name",
+	"c":             "addr",
+	"cl":            "bind-addr",
+	"s":             "peer-addr",
+	"sl":            "peer-bind-addr",
+	"d":             "data-dir",
+	"m":             "max-result-buffer",
+	"r":             "max-retry-attempts",
+	"maxsize":       "max-cluster-size",
+	"clientCAFile":  "ca-file",
+	"clientCert":    "cert-file",
+	"clientKey":     "key-file",
+	"serverCAFile":  "peer-ca-file",
+	"serverCert":    "peer-cert-file",
+	"serverKey":     "peer-key-file",
 	"snapshotCount": "snapshot-count",
 }
 
@@ -45,10 +45,11 @@ var newFlagNameLookup = map[string]string{
 type Config struct {
 	SystemPath string
 
-	Addr             string   `toml:"addr" env:"ETCD_ADDR"`
-	BindAddr         string   `toml:"bind_addr" env:"ETCD_BIND_ADDR"`
-	CAFile           string   `toml:"ca_file" env:"ETCD_CA_FILE"`
-	CertFile         string   `toml:"cert_file" env:"ETCD_CERT_FILE"`
+	Addr             string `toml:"addr" env:"ETCD_ADDR"`
+	BindAddr         string `toml:"bind_addr" env:"ETCD_BIND_ADDR"`
+	CAFile           string `toml:"ca_file" env:"ETCD_CA_FILE"`
+	CertFile         string `toml:"cert_file" env:"ETCD_CERT_FILE"`
+	CPUProfileFile   string
 	CorsOrigins      []string `toml:"cors" env:"ETCD_CORS"`
 	DataDir          string   `toml:"data_dir" env:"ETCD_DATA_DIR"`
 	Force            bool
@@ -61,8 +62,10 @@ type Config struct {
 	Name             string   `toml:"name" env:"ETCD_NAME"`
 	Snapshot         bool     `toml:"snapshot" env:"ETCD_SNAPSHOT"`
 	SnapshotCount    int      `toml:"snapshot_count" env:"ETCD_SNAPSHOTCOUNT"`
-	Verbose          bool     `toml:"verbose" env:"ETCD_VERBOSE"`
-	VeryVerbose      bool     `toml:"very_verbose" env:"ETCD_VERY_VERBOSE"`
+	ShowHelp         bool
+	ShowVersion      bool
+	Verbose          bool `toml:"verbose" env:"ETCD_VERBOSE"`
+	VeryVerbose      bool `toml:"very_verbose" env:"ETCD_VERY_VERBOSE"`
 
 	Peer struct {
 		Addr     string `toml:"addr" env:"ETCD_PEER_ADDR"`
@@ -117,15 +120,6 @@ func (c *Config) Load(arguments []string) error {
 		return err
 	}
 
-	// Load from command line flags (deprecated).
-	if err := c.LoadDeprecatedFlags(arguments); err != nil {
-		if err, ok := err.(*DeprecationError); ok {
-			fmt.Fprintln(os.Stderr, err.Error())
-		} else {
-			return err
-		}
-	}
-
 	// Loads peers if a peer file was specified.
 	if err := c.LoadPeersFile(); err != nil {
 		return err
@@ -133,7 +127,7 @@ func (c *Config) Load(arguments []string) error {
 
 	// Sanitize all the input fields.
 	if err := c.Sanitize(); err != nil {
-		return fmt.Errorf("sanitize:", err)
+		return fmt.Errorf("sanitize: %v", err)
 	}
 
 	return nil
@@ -195,100 +189,85 @@ func (c *Config) loadEnv(target interface{}) error {
 	return nil
 }
 
-// Loads deprecated configuration settings from the command line.
-func (c *Config) LoadDeprecatedFlags(arguments []string) error {
-	var peers string
-
-	f := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
-	f.SetOutput(ioutil.Discard)
-
-	f.StringVar(&peers, "C", "", "(deprecated)")
-	f.StringVar(&c.PeersFile, "CF", c.PeersFile, "(deprecated)")
-
-	f.StringVar(&c.Name, "n", c.Name, "(deprecated)")
-	f.StringVar(&c.Addr, "c", c.Addr, "(deprecated)")
-	f.StringVar(&c.BindAddr, "cl", c.BindAddr, "the listening hostname for etcd client communication (defaults to advertised ip)")
-	f.StringVar(&c.Peer.Addr, "s", c.Peer.Addr, "the advertised public hostname:port for raft server communication")
-	f.StringVar(&c.Peer.BindAddr, "sl", c.Peer.BindAddr, "the listening hostname for raft server communication (defaults to advertised ip)")
-
-	f.StringVar(&c.Peer.CAFile, "serverCAFile", c.Peer.CAFile, "the path of the CAFile")
-	f.StringVar(&c.Peer.CertFile, "serverCert", c.Peer.CertFile, "the cert file of the server")
-	f.StringVar(&c.Peer.KeyFile, "serverKey", c.Peer.KeyFile, "the key file of the server")
-
-	f.StringVar(&c.CAFile, "clientCAFile", c.CAFile, "the path of the client CAFile")
-	f.StringVar(&c.CertFile, "clientCert", c.CertFile, "the cert file of the client")
-	f.StringVar(&c.KeyFile, "clientKey", c.KeyFile, "the key file of the client")
-
-	f.StringVar(&c.DataDir, "d", c.DataDir, "the directory to store log and snapshot")
-	f.IntVar(&c.MaxResultBuffer, "m", c.MaxResultBuffer, "the max size of result buffer")
-	f.IntVar(&c.MaxRetryAttempts, "r", c.MaxRetryAttempts, "the max retry attempts when trying to join a cluster")
-	f.IntVar(&c.MaxClusterSize, "maxsize", c.MaxClusterSize, "the max size of the cluster")
-
-	f.IntVar(&c.SnapshotCount, "snapshotCount", c.SnapshotCount, "save the in-memory logs and states to a snapshot file a given number of transactions")
-
-	f.Parse(arguments)
-
-	// Convert some parameters to lists.
-	if peers != "" {
-		c.Peers = trimsplit(peers, ",")
-	}
-
-	// Generate deprecation warning.
-	warnings := make([]string, 0)
-	f.Visit(func(f *flag.Flag) {
-		warnings = append(warnings, fmt.Sprintf("[deprecated] use -%s, not -%s", newFlagNameLookup[f.Name], f.Name))
-	})
-	if len(warnings) > 0 {
-		return &DeprecationError{strings.Join(warnings, "\n")}
-	}
-
-	return nil
-}
-
 // Loads configuration from command line flags.
 func (c *Config) LoadFlags(arguments []string) error {
-	var peers, cors string
+	var peers, cors, path string
 
 	f := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
 	f.SetOutput(ioutil.Discard)
 
-	f.BoolVar(&c.Force, "f", false, "force new node configuration if existing is found (WARNING: data loss!)")
-	f.BoolVar(&c.Force, "force", false, "force new node configuration if existing is found (WARNING: data loss!)")
+	f.BoolVar(&c.ShowHelp, "h", false, "")
+	f.BoolVar(&c.ShowHelp, "help", false, "")
+	f.BoolVar(&c.ShowVersion, "version", false, "")
 
-	f.BoolVar(&c.Verbose, "v", c.Verbose, "verbose logging")
-	f.BoolVar(&c.VeryVerbose, "vv", c.Verbose, "very verbose logging")
+	f.BoolVar(&c.Force, "f", false, "")
+	f.BoolVar(&c.Force, "force", false, "")
 
-	f.StringVar(&peers, "peers", "", "the ip address and port of a existing peers in the cluster, sepearate by comma")
-	f.StringVar(&c.PeersFile, "peers-file", c.PeersFile, "the file contains a list of existing peers in the cluster, seperate by comma")
+	f.BoolVar(&c.Verbose, "v", c.Verbose, "")
+	f.BoolVar(&c.VeryVerbose, "vv", c.Verbose, "")
 
-	f.StringVar(&c.Name, "name", c.Name, "the node name (required)")
-	f.StringVar(&c.Addr, "addr", c.Addr, "the advertised public hostname:port for etcd client communication")
-	f.StringVar(&c.BindAddr, "bind-addr", c.BindAddr, "the listening hostname for etcd client communication (defaults to advertised ip)")
-	f.StringVar(&c.Peer.Addr, "peer-addr", c.Peer.Addr, "the advertised public hostname:port for raft server communication")
-	f.StringVar(&c.Peer.BindAddr, "peer-bind-addr", c.Peer.BindAddr, "the listening hostname for raft server communication (defaults to advertised ip)")
+	f.StringVar(&peers, "peers", "", "")
+	f.StringVar(&c.PeersFile, "peers-file", c.PeersFile, "")
 
-	f.StringVar(&c.Peer.CAFile, "peer-ca-file", c.Peer.CAFile, "the path of the CAFile")
-	f.StringVar(&c.Peer.CertFile, "peer-cert-file", c.Peer.CertFile, "the cert file of the server")
-	f.StringVar(&c.Peer.KeyFile, "peer-key-file", c.Peer.KeyFile, "the key file of the server")
+	f.StringVar(&c.Name, "name", c.Name, "")
+	f.StringVar(&c.Addr, "addr", c.Addr, "")
+	f.StringVar(&c.BindAddr, "bind-addr", c.BindAddr, "")
+	f.StringVar(&c.Peer.Addr, "peer-addr", c.Peer.Addr, "")
+	f.StringVar(&c.Peer.BindAddr, "peer-bind-addr", c.Peer.BindAddr, "")
 
-	f.StringVar(&c.CAFile, "ca-file", c.CAFile, "the path of the client CAFile")
-	f.StringVar(&c.CertFile, "cert-file", c.CertFile, "the cert file of the client")
-	f.StringVar(&c.KeyFile, "key-file", c.KeyFile, "the key file of the client")
+	f.StringVar(&c.CAFile, "ca-file", c.CAFile, "")
+	f.StringVar(&c.CertFile, "cert-file", c.CertFile, "")
+	f.StringVar(&c.KeyFile, "key-file", c.KeyFile, "")
 
-	f.StringVar(&c.DataDir, "data-dir", c.DataDir, "the directory to store log and snapshot")
-	f.IntVar(&c.MaxResultBuffer, "max-result-buffer", c.MaxResultBuffer, "the max size of result buffer")
-	f.IntVar(&c.MaxRetryAttempts, "max-retry-attempts", c.MaxRetryAttempts, "the max retry attempts when trying to join a cluster")
-	f.IntVar(&c.MaxClusterSize, "max-cluster-size", c.MaxClusterSize, "the max size of the cluster")
-	f.StringVar(&cors, "cors", "", "whitelist origins for cross-origin resource sharing (e.g. '*' or 'http://localhost:8001,etc')")
+	f.StringVar(&c.Peer.CAFile, "peer-ca-file", c.Peer.CAFile, "")
+	f.StringVar(&c.Peer.CertFile, "peer-cert-file", c.Peer.CertFile, "")
+	f.StringVar(&c.Peer.KeyFile, "peer-key-file", c.Peer.KeyFile, "")
 
-	f.BoolVar(&c.Snapshot, "snapshot", c.Snapshot, "open or close snapshot")
-	f.IntVar(&c.SnapshotCount, "snapshot-count", c.SnapshotCount, "save the in-memory logs and states to a snapshot file a given number of transactions")
+	f.StringVar(&c.DataDir, "data-dir", c.DataDir, "")
+	f.IntVar(&c.MaxResultBuffer, "max-result-buffer", c.MaxResultBuffer, "")
+	f.IntVar(&c.MaxRetryAttempts, "max-retry-attempts", c.MaxRetryAttempts, "")
+	f.IntVar(&c.MaxClusterSize, "max-cluster-size", c.MaxClusterSize, "")
+	f.StringVar(&cors, "cors", "", "")
 
-	// These flags are ignored since they were already parsed.
-	var path string
-	f.StringVar(&path, "config", "", "path to config file")
+	f.BoolVar(&c.Snapshot, "snapshot", c.Snapshot, "")
+	f.IntVar(&c.SnapshotCount, "snapshot-count", c.SnapshotCount, "")
+	f.StringVar(&c.CPUProfileFile, "cpuprofile", "", "")
 
-	f.Parse(arguments)
+	// BEGIN IGNORED FLAGS
+	f.StringVar(&path, "config", "", "")
+	// BEGIN IGNORED FLAGS
+
+	// BEGIN DEPRECATED FLAGS
+	f.StringVar(&peers, "C", "", "(deprecated)")
+	f.StringVar(&c.PeersFile, "CF", c.PeersFile, "(deprecated)")
+	f.StringVar(&c.Name, "n", c.Name, "(deprecated)")
+	f.StringVar(&c.Addr, "c", c.Addr, "(deprecated)")
+	f.StringVar(&c.BindAddr, "cl", c.BindAddr, "(deprecated)")
+	f.StringVar(&c.Peer.Addr, "s", c.Peer.Addr, "(deprecated)")
+	f.StringVar(&c.Peer.BindAddr, "sl", c.Peer.BindAddr, "(deprecated)")
+	f.StringVar(&c.Peer.CAFile, "serverCAFile", c.Peer.CAFile, "(deprecated)")
+	f.StringVar(&c.Peer.CertFile, "serverCert", c.Peer.CertFile, "(deprecated)")
+	f.StringVar(&c.Peer.KeyFile, "serverKey", c.Peer.KeyFile, "(deprecated)")
+	f.StringVar(&c.CAFile, "clientCAFile", c.CAFile, "(deprecated)")
+	f.StringVar(&c.CertFile, "clientCert", c.CertFile, "(deprecated)")
+	f.StringVar(&c.KeyFile, "clientKey", c.KeyFile, "(deprecated)")
+	f.StringVar(&c.DataDir, "d", c.DataDir, "(deprecated)")
+	f.IntVar(&c.MaxResultBuffer, "m", c.MaxResultBuffer, "(deprecated)")
+	f.IntVar(&c.MaxRetryAttempts, "r", c.MaxRetryAttempts, "(deprecated)")
+	f.IntVar(&c.MaxClusterSize, "maxsize", c.MaxClusterSize, "(deprecated)")
+	f.IntVar(&c.SnapshotCount, "snapshotCount", c.SnapshotCount, "(deprecated)")
+	// END DEPRECATED FLAGS
+
+	if err := f.Parse(arguments); err != nil {
+		return err
+	}
+
+	// Print deprecation warnings on STDERR.
+	f.Visit(func(f *flag.Flag) {
+		if len(newFlagNameLookup[f.Name]) > 0 {
+			fmt.Fprintf(os.Stderr, "[deprecated] use -%s, not -%s", newFlagNameLookup[f.Name], f.Name)
+		}
+	})
 
 	// Convert some parameters to lists.
 	if peers != "" {
@@ -479,15 +458,3 @@ func sanitizeBindAddr(bindAddr string, addr string) (string, error) {
 
 	return net.JoinHostPort(bindAddr, aport), nil
 }
-
-
-// DeprecationError is a warning for CLI users that one or more arguments will
-// not be supported in future released.
-type DeprecationError struct {
-	s string
-}
-
-func (e *DeprecationError) Error() string {
-	return e.s
-}
-

+ 166 - 66
server/config_test.go

@@ -93,7 +93,7 @@ func TestConfigEnv(t *testing.T) {
 	c.LoadEnv()
 	assert.Equal(t, c.CAFile, "/tmp/file.ca", "")
 	assert.Equal(t, c.CertFile, "/tmp/file.cert", "")
-	assert.Equal(t, c.CorsOrigins, []string{"localhost:4001", "localhost:4002"}, "") 
+	assert.Equal(t, c.CorsOrigins, []string{"localhost:4001", "localhost:4002"}, "")
 	assert.Equal(t, c.DataDir, "/tmp/data", "")
 	assert.Equal(t, c.KeyFile, "/tmp/file.key", "")
 	assert.Equal(t, c.BindAddr, "127.0.0.1:4003", "")
@@ -113,6 +113,27 @@ func TestConfigEnv(t *testing.T) {
 	assert.Equal(t, c.Peer.BindAddr, "127.0.0.1:7003", "")
 }
 
+// Ensures that the "help" flag can be parsed.
+func TestConfigHelpFlag(t *testing.T) {
+	c := NewConfig()
+	assert.Nil(t, c.LoadFlags([]string{"-help"}), "")
+	assert.True(t, c.ShowHelp)
+}
+
+// Ensures that the abbreviated "help" flag can be parsed.
+func TestConfigAbbreviatedHelpFlag(t *testing.T) {
+	c := NewConfig()
+	assert.Nil(t, c.LoadFlags([]string{"-h"}), "")
+	assert.True(t, c.ShowHelp)
+}
+
+// Ensures that the "version" flag can be parsed.
+func TestConfigVersionFlag(t *testing.T) {
+	c := NewConfig()
+	assert.Nil(t, c.LoadFlags([]string{"-version"}), "")
+	assert.True(t, c.ShowVersion)
+}
+
 // Ensures that the "force config" flag can be parsed.
 func TestConfigForceFlag(t *testing.T) {
 	c := NewConfig()
@@ -405,6 +426,14 @@ func TestConfigPeerBindAddrEnv(t *testing.T) {
 	})
 }
 
+// Ensures that a bad flag returns an error.
+func TestConfigBadFlag(t *testing.T) {
+	c := NewConfig()
+	err := c.LoadFlags([]string{"-no-such-flag"})
+	assert.Error(t, err)
+	assert.Equal(t, err.Error(), `flag provided but not defined: -no-such-flag`)
+}
+
 // Ensures that a the Peer Listen Host file flag can be parsed.
 func TestConfigPeerBindAddrFlag(t *testing.T) {
 	c := NewConfig()
@@ -456,118 +485,165 @@ func TestConfigCLIArgsOverrideEnvVar(t *testing.T) {
 //--------------------------------------
 
 func TestConfigDeprecatedAddrFlag(t *testing.T) {
-	c := NewConfig()
-	err := c.LoadDeprecatedFlags([]string{"-c", "127.0.0.1:4002"})
-	assert.Equal(t, err.Error(), "[deprecated] use -addr, not -c", "")
-	assert.Equal(t, c.Addr, "127.0.0.1:4002", "")
+	_, stderr := capture(func() {
+		c := NewConfig()
+		err := c.LoadFlags([]string{"-c", "127.0.0.1:4002"})
+		assert.NoError(t, err)
+		assert.Equal(t, c.Addr, "127.0.0.1:4002")
+	})
+	assert.Equal(t, stderr, "[deprecated] use -addr, not -c")
 }
 
 func TestConfigDeprecatedBindAddrFlag(t *testing.T) {
-	c := NewConfig()
-	err := c.LoadDeprecatedFlags([]string{"-cl", "127.0.0.1:4003"})
-	assert.Equal(t, err.Error(), "[deprecated] use -bind-addr, not -cl", "")
-	assert.Equal(t, c.BindAddr, "127.0.0.1:4003", "")
+	_, stderr := capture(func() {
+		c := NewConfig()
+		err := c.LoadFlags([]string{"-cl", "127.0.0.1:4003"})
+		assert.NoError(t, err)
+		assert.Equal(t, c.BindAddr, "127.0.0.1:4003", "")
+	})
+	assert.Equal(t, stderr, "[deprecated] use -bind-addr, not -cl", "")
 }
 
 func TestConfigDeprecatedCAFileFlag(t *testing.T) {
-	c := NewConfig()
-	err := c.LoadDeprecatedFlags([]string{"-clientCAFile", "/tmp/file.ca"})
-	assert.Equal(t, err.Error(), "[deprecated] use -ca-file, not -clientCAFile", "")
-	assert.Equal(t, c.CAFile, "/tmp/file.ca", "")
+	_, stderr := capture(func() {
+		c := NewConfig()
+		err := c.LoadFlags([]string{"-clientCAFile", "/tmp/file.ca"})
+		assert.NoError(t, err)
+		assert.Equal(t, c.CAFile, "/tmp/file.ca", "")
+	})
+	assert.Equal(t, stderr, "[deprecated] use -ca-file, not -clientCAFile", "")
 }
 
 func TestConfigDeprecatedCertFileFlag(t *testing.T) {
-	c := NewConfig()
-	err := c.LoadDeprecatedFlags([]string{"-clientCert", "/tmp/file.cert"})
-	assert.Equal(t, err.Error(), "[deprecated] use -cert-file, not -clientCert", "")
-	assert.Equal(t, c.CertFile, "/tmp/file.cert", "")
+	_, stderr := capture(func() {
+		c := NewConfig()
+		err := c.LoadFlags([]string{"-clientCert", "/tmp/file.cert"})
+		assert.NoError(t, err)
+		assert.Equal(t, c.CertFile, "/tmp/file.cert", "")
+	})
+	assert.Equal(t, stderr, "[deprecated] use -cert-file, not -clientCert", "")
 }
 
 func TestConfigDeprecatedKeyFileFlag(t *testing.T) {
-	c := NewConfig()
-	err := c.LoadDeprecatedFlags([]string{"-clientKey", "/tmp/file.key"})
-	assert.Equal(t, err.Error(), "[deprecated] use -key-file, not -clientKey", "")
-	assert.Equal(t, c.KeyFile, "/tmp/file.key", "")
+	_, stderr := capture(func() {
+		c := NewConfig()
+		err := c.LoadFlags([]string{"-clientKey", "/tmp/file.key"})
+		assert.NoError(t, err)
+		assert.Equal(t, c.KeyFile, "/tmp/file.key", "")
+	})
+	assert.Equal(t, stderr, "[deprecated] use -key-file, not -clientKey", "")
 }
 
 func TestConfigDeprecatedPeersFlag(t *testing.T) {
-	c := NewConfig()
-	err := c.LoadDeprecatedFlags([]string{"-C", "coreos.com:4001,coreos.com:4002"})
-	assert.Equal(t, err.Error(), "[deprecated] use -peers, not -C", "")
-	assert.Equal(t, c.Peers, []string{"coreos.com:4001", "coreos.com:4002"}, "")
+	_, stderr := capture(func() {
+		c := NewConfig()
+		err := c.LoadFlags([]string{"-C", "coreos.com:4001,coreos.com:4002"})
+		assert.NoError(t, err)
+		assert.Equal(t, c.Peers, []string{"coreos.com:4001", "coreos.com:4002"}, "")
+	})
+	assert.Equal(t, stderr, "[deprecated] use -peers, not -C", "")
 }
 
 func TestConfigDeprecatedPeersFileFlag(t *testing.T) {
-	c := NewConfig()
-	err := c.LoadDeprecatedFlags([]string{"-CF", "/tmp/machines"})
-	assert.Equal(t, err.Error(), "[deprecated] use -peers-file, not -CF", "")
-	assert.Equal(t, c.PeersFile, "/tmp/machines", "")
+	_, stderr := capture(func() {
+		c := NewConfig()
+		err := c.LoadFlags([]string{"-CF", "/tmp/machines"})
+		assert.NoError(t, err)
+		assert.Equal(t, c.PeersFile, "/tmp/machines", "")
+	})
+	assert.Equal(t, stderr, "[deprecated] use -peers-file, not -CF", "")
 }
 
 func TestConfigDeprecatedMaxClusterSizeFlag(t *testing.T) {
-	c := NewConfig()
-	err := c.LoadDeprecatedFlags([]string{"-maxsize", "5"})
-	assert.Equal(t, err.Error(), "[deprecated] use -max-cluster-size, not -maxsize", "")
-	assert.Equal(t, c.MaxClusterSize, 5, "")
+	_, stderr := capture(func() {
+		c := NewConfig()
+		err := c.LoadFlags([]string{"-maxsize", "5"})
+		assert.NoError(t, err)
+		assert.Equal(t, c.MaxClusterSize, 5, "")
+	})
+	assert.Equal(t, stderr, "[deprecated] use -max-cluster-size, not -maxsize", "")
 }
 
 func TestConfigDeprecatedMaxResultBufferFlag(t *testing.T) {
-	c := NewConfig()
-	err := c.LoadDeprecatedFlags([]string{"-m", "512"})
-	assert.Equal(t, err.Error(), "[deprecated] use -max-result-buffer, not -m", "")
-	assert.Equal(t, c.MaxResultBuffer, 512, "")
+	_, stderr := capture(func() {
+		c := NewConfig()
+		err := c.LoadFlags([]string{"-m", "512"})
+		assert.NoError(t, err)
+		assert.Equal(t, c.MaxResultBuffer, 512, "")
+	})
+	assert.Equal(t, stderr, "[deprecated] use -max-result-buffer, not -m", "")
 }
 
 func TestConfigDeprecatedMaxRetryAttemptsFlag(t *testing.T) {
-	c := NewConfig()
-	err := c.LoadDeprecatedFlags([]string{"-r", "10"})
-	assert.Equal(t, err.Error(), "[deprecated] use -max-retry-attempts, not -r", "")
-	assert.Equal(t, c.MaxRetryAttempts, 10, "")
+	_, stderr := capture(func() {
+		c := NewConfig()
+		err := c.LoadFlags([]string{"-r", "10"})
+		assert.NoError(t, err)
+		assert.Equal(t, c.MaxRetryAttempts, 10, "")
+	})
+	assert.Equal(t, stderr, "[deprecated] use -max-retry-attempts, not -r", "")
 }
 
 func TestConfigDeprecatedNameFlag(t *testing.T) {
-	c := NewConfig()
-	err := c.LoadDeprecatedFlags([]string{"-n", "test-name"})
-	assert.Equal(t, err.Error(), "[deprecated] use -name, not -n", "")
-	assert.Equal(t, c.Name, "test-name", "")
+	_, stderr := capture(func() {
+		c := NewConfig()
+		err := c.LoadFlags([]string{"-n", "test-name"})
+		assert.NoError(t, err)
+		assert.Equal(t, c.Name, "test-name", "")
+	})
+	assert.Equal(t, stderr, "[deprecated] use -name, not -n", "")
 }
 
 func TestConfigDeprecatedPeerAddrFlag(t *testing.T) {
-	c := NewConfig()
-	err := c.LoadDeprecatedFlags([]string{"-s", "localhost:7002"})
-	assert.Equal(t, err.Error(), "[deprecated] use -peer-addr, not -s", "")
-	assert.Equal(t, c.Peer.Addr, "localhost:7002", "")
+	_, stderr := capture(func() {
+		c := NewConfig()
+		err := c.LoadFlags([]string{"-s", "localhost:7002"})
+		assert.NoError(t, err)
+		assert.Equal(t, c.Peer.Addr, "localhost:7002", "")
+	})
+	assert.Equal(t, stderr, "[deprecated] use -peer-addr, not -s", "")
 }
 
 func TestConfigDeprecatedPeerBindAddrFlag(t *testing.T) {
-	c := NewConfig()
-	err := c.LoadDeprecatedFlags([]string{"-sl", "127.0.0.1:4003"})
-	assert.Equal(t, err.Error(), "[deprecated] use -peer-bind-addr, not -sl", "")
-	assert.Equal(t, c.Peer.BindAddr, "127.0.0.1:4003", "")
+	_, stderr := capture(func() {
+		c := NewConfig()
+		err := c.LoadFlags([]string{"-sl", "127.0.0.1:4003"})
+		assert.NoError(t, err)
+		assert.Equal(t, c.Peer.BindAddr, "127.0.0.1:4003", "")
+	})
+	assert.Equal(t, stderr, "[deprecated] use -peer-bind-addr, not -sl", "")
 }
 
 func TestConfigDeprecatedPeerCAFileFlag(t *testing.T) {
-	c := NewConfig()
-	err := c.LoadDeprecatedFlags([]string{"-serverCAFile", "/tmp/peer/file.ca"})
-	assert.Equal(t, err.Error(), "[deprecated] use -peer-ca-file, not -serverCAFile", "")
-	assert.Equal(t, c.Peer.CAFile, "/tmp/peer/file.ca", "")
+	_, stderr := capture(func() {
+		c := NewConfig()
+		err := c.LoadFlags([]string{"-serverCAFile", "/tmp/peer/file.ca"})
+		assert.NoError(t, err)
+		assert.Equal(t, c.Peer.CAFile, "/tmp/peer/file.ca", "")
+	})
+	assert.Equal(t, stderr, "[deprecated] use -peer-ca-file, not -serverCAFile", "")
 }
 
 func TestConfigDeprecatedPeerCertFileFlag(t *testing.T) {
-	c := NewConfig()
-	err := c.LoadDeprecatedFlags([]string{"-serverCert", "/tmp/peer/file.cert"})
-	assert.Equal(t, err.Error(), "[deprecated] use -peer-cert-file, not -serverCert", "")
-	assert.Equal(t, c.Peer.CertFile, "/tmp/peer/file.cert", "")
+	_, stderr := capture(func() {
+		c := NewConfig()
+		err := c.LoadFlags([]string{"-serverCert", "/tmp/peer/file.cert"})
+		assert.NoError(t, err)
+		assert.Equal(t, c.Peer.CertFile, "/tmp/peer/file.cert", "")
+	})
+	assert.Equal(t, stderr, "[deprecated] use -peer-cert-file, not -serverCert", "")
 }
 
 func TestConfigDeprecatedPeerKeyFileFlag(t *testing.T) {
-	c := NewConfig()
-	err := c.LoadDeprecatedFlags([]string{"-serverKey", "/tmp/peer/file.key"})
-	assert.Equal(t, err.Error(), "[deprecated] use -peer-key-file, not -serverKey", "")
-	assert.Equal(t, c.Peer.KeyFile, "/tmp/peer/file.key", "")
+	_, stderr := capture(func() {
+		c := NewConfig()
+		err := c.LoadFlags([]string{"-serverKey", "/tmp/peer/file.key"})
+		assert.NoError(t, err)
+		assert.Equal(t, c.Peer.KeyFile, "/tmp/peer/file.key", "")
+	})
+	assert.Equal(t, stderr, "[deprecated] use -peer-key-file, not -serverKey", "")
 }
 
-
 //--------------------------------------
 // Helpers
 //--------------------------------------
@@ -588,3 +664,27 @@ func withTempFile(content string, fn func(string)) {
 	defer os.Remove(f.Name())
 	fn(f.Name())
 }
+
+// Captures STDOUT & STDERR and returns the output as strings.
+func capture(fn func()) (string, string) {
+	// Create temp files.
+	tmpout, _ := ioutil.TempFile("", "")
+	defer os.Remove(tmpout.Name())
+	tmperr, _ := ioutil.TempFile("", "")
+	defer os.Remove(tmperr.Name())
+
+	stdout, stderr := os.Stdout, os.Stderr
+	os.Stdout, os.Stderr = tmpout, tmperr
+
+	// Execute function argument and then reassign stdout/stderr.
+	fn()
+	os.Stdout, os.Stderr = stdout, stderr
+
+	// Close temp files and read them.
+	tmpout.Close()
+	bout, _ := ioutil.ReadFile(tmpout.Name())
+	tmperr.Close()
+	berr, _ := ioutil.ReadFile(tmperr.Name())
+
+	return string(bout), string(berr)
+}

+ 78 - 0
server/cors_handler.go

@@ -0,0 +1,78 @@
+/*
+Copyright 2013 CoreOS Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package server
+
+import (
+	"fmt"
+	"net/http"
+	"net/url"
+
+	"github.com/gorilla/mux"
+)
+
+type corsHandler struct {
+	router *mux.Router
+	corsOrigins map[string]bool
+}
+
+// AllowOrigins sets a comma-delimited list of origins that are allowed.
+func (s *corsHandler) AllowOrigins(origins []string) error {
+	// Construct a lookup of all origins.
+	m := make(map[string]bool)
+	for _, v := range origins {
+		if v != "*" {
+			if _, err := url.Parse(v); err != nil {
+				return fmt.Errorf("Invalid CORS origin: %s", err)
+			}
+		}
+		m[v] = true
+	}
+	s.corsOrigins = m
+
+	return nil
+}
+
+// OriginAllowed determines whether the server will allow a given CORS origin.
+func (c *corsHandler) OriginAllowed(origin string) bool {
+	return c.corsOrigins["*"] || c.corsOrigins[origin]
+}
+
+// addHeader adds the correct cors headers given an origin
+func (h *corsHandler) addHeader(w http.ResponseWriter, origin string) {
+	w.Header().Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
+	w.Header().Add("Access-Control-Allow-Origin", origin)
+}
+
+// ServeHTTP adds the correct CORS headers based on the origin and returns immediatly
+// with a 200 OK if the method is OPTIONS.
+func (h *corsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+	// Write CORS header.
+	if h.OriginAllowed("*") {
+		h.addHeader(w, "*")
+	} else if origin := req.Header.Get("Origin"); h.OriginAllowed(origin) {
+		h.addHeader(w, origin)
+	}
+
+	if req.Method == "OPTIONS" {
+		w.WriteHeader(http.StatusOK)
+		return
+	}
+
+	h.router.ServeHTTP(w, req)
+}
+
+

+ 1 - 1
server/join_command.go

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

+ 1 - 1
server/peer_server.go

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

+ 1 - 1
server/peer_server_handlers.go

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

+ 1 - 1
server/raft_server_stats.go

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

+ 1 - 1
server/remove_command.go

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

+ 17 - 31
server/server.go

@@ -6,7 +6,6 @@ import (
 	"fmt"
 	"net"
 	"net/http"
-	"net/url"
 	"strings"
 	"time"
 
@@ -17,7 +16,7 @@ import (
 	"github.com/coreos/etcd/server/v2"
 	"github.com/coreos/etcd/store"
 	_ "github.com/coreos/etcd/store/v2"
-	"github.com/coreos/go-raft"
+	"github.com/coreos/raft"
 	"github.com/gorilla/mux"
 )
 
@@ -32,14 +31,18 @@ type Server struct {
 	url         string
 	tlsConf     *TLSConfig
 	tlsInfo     *TLSInfo
-	corsOrigins map[string]bool
+	router      *mux.Router
+	corsHandler *corsHandler
 }
 
 // Creates a new Server.
 func New(name string, urlStr string, bindAddr string, tlsConf *TLSConfig, tlsInfo *TLSInfo, peerServer *PeerServer, registry *Registry, store store.Store) *Server {
+	r := mux.NewRouter()
+	cors := &corsHandler{router: r}
+
 	s := &Server{
 		Server: http.Server{
-			Handler:   mux.NewRouter(),
+			Handler:   cors,
 			TLSConfig: &tlsConf.Server,
 			Addr:      bindAddr,
 		},
@@ -50,6 +53,8 @@ func New(name string, urlStr string, bindAddr string, tlsConf *TLSConfig, tlsInf
 		tlsConf:    tlsConf,
 		tlsInfo:    tlsInfo,
 		peerServer: peerServer,
+		router:     r,
+		corsHandler: cors,
 	}
 
 	// Install the routes.
@@ -124,7 +129,7 @@ func (s *Server) installV2() {
 }
 
 func (s *Server) installMod() {
-	r := s.Handler.(*mux.Router)
+	r := s.router
 	r.PathPrefix("/mod").Handler(http.StripPrefix("/mod", mod.HttpHandler()))
 }
 
@@ -144,20 +149,13 @@ func (s *Server) handleFuncV2(path string, f func(http.ResponseWriter, *http.Req
 
 // Adds a server handler to the router.
 func (s *Server) handleFunc(path string, f func(http.ResponseWriter, *http.Request) error) *mux.Route {
-	r := s.Handler.(*mux.Router)
+	r := s.router
 
 	// Wrap the standard HandleFunc interface to pass in the server reference.
 	return r.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) {
 		// Log request.
 		log.Debugf("[recv] %s %s %s [%s]", req.Method, s.url, req.URL.Path, req.RemoteAddr)
 
-		// Write CORS header.
-		if s.OriginAllowed("*") {
-			w.Header().Add("Access-Control-Allow-Origin", "*")
-		} else if origin := req.Header.Get("Origin"); s.OriginAllowed(origin) {
-			w.Header().Add("Access-Control-Allow-Origin", origin)
-		}
-
 		// Execute handler function and return error if necessary.
 		if err := f(w, req); err != nil {
 			if etcdErr, ok := err.(*etcdErr.Error); ok {
@@ -302,26 +300,14 @@ func (s *Server) Dispatch(c raft.Command, w http.ResponseWriter, req *http.Reque
 	}
 }
 
-// Sets a comma-delimited list of origins that are allowed.
-func (s *Server) AllowOrigins(origins []string) error {
-	// Construct a lookup of all origins.
-	m := make(map[string]bool)
-	for _, v := range origins {
-		if v != "*" {
-			if _, err := url.Parse(v); err != nil {
-				return fmt.Errorf("Invalid CORS origin: %s", err)
-			}
-		}
-		m[v] = true
-	}
-	s.corsOrigins = m
-
-	return nil
+// OriginAllowed determines whether the server will allow a given CORS origin.
+func (s *Server) OriginAllowed(origin string) bool {
+	return s.corsHandler.OriginAllowed(origin)
 }
 
-// Determines whether the server will allow a given CORS origin.
-func (s *Server) OriginAllowed(origin string) bool {
-	return s.corsOrigins["*"] || s.corsOrigins[origin]
+// AllowOrigins sets a comma-delimited list of origins that are allowed.
+func (s *Server) AllowOrigins(origins []string) error {
+	return s.corsHandler.AllowOrigins(origins)
 }
 
 // Handler to return the current version of etcd.

+ 2 - 2
server/tls_info.go

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

+ 1 - 1
server/transporter.go

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

+ 57 - 0
server/usage.go

@@ -0,0 +1,57 @@
+package server
+
+import (
+	"strings"
+)
+
+// usage defines the message shown when a help flag is passed to etcd.
+var usage = `
+etcd
+
+Usage:
+  etcd -name <name>
+  etcd -name <name> [-data-dir=<path>]
+  etcd -h | -help
+  etcd -version
+
+Options:
+  -h -help          Show this screen.
+  --version         Show version.
+  -f -force         Force a new configuration to be used.
+  -config=<path>    Path to configuration file.
+  -name=<name>      Name of this node in the etcd cluster.
+  -data-dir=<path>  Path to the data directory.
+  -cors=<origins>   Comma-separated list of CORS origins.
+  -v                Enabled verbose logging.
+  -vv               Enabled very verbose logging.
+
+Cluster Configuration Options:
+  -peers=<peers>      Comma-separated list of peers (ip + port) in the cluster.
+  -peers-file=<path>  Path to a file containing the peer list.
+
+Client Communication Options:
+  -addr=<host:port>   The public host:port used for client communication.
+  -bind-addr=<host>   The listening hostname used for client communication.
+  -ca-file=<path>     Path to the client CA file.
+  -cert-file=<path>   Path to the client cert file.
+  -key-file=<path>    Path to the client key file.
+
+Peer Communication Options:
+  -peer-addr=<host:port>  The public host:port used for peer communication.
+  -peer-bind-addr=<host>  The listening hostname used for peer communication.
+  -peer-ca-file=<path>    Path to the peer CA file.
+  -peer-cert-file=<path>  Path to the peer cert file.
+  -peer-key-file=<path>   Path to the peer key file.
+
+Other Options:
+  -max-result-buffer   Max size of the result buffer.
+  -max-retry-attempts  Number of times a node will try to join a cluster.
+  -max-cluster-size    Maximum number of nodes in the cluster.
+  -snapshot            Open or close the snapshot.
+  -snapshot-count      Number of transactions before issuing a snapshot.
+`
+
+// Usage returns the usage message for etcd.
+func Usage() string {
+	return strings.TrimSpace(usage)
+}

+ 1 - 1
server/v1/set_key_handler.go

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

+ 1 - 1
server/v1/v1.go

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

+ 1 - 1
server/v2/get_handler.go

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

+ 1 - 1
server/v2/put_handler.go

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

+ 1 - 1
server/v2/v2.go

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

+ 1 - 1
store/command_factory.go

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

+ 1 - 1
store/v2/command_factory.go

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

+ 1 - 1
store/v2/compare_and_swap_command.go

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

+ 1 - 1
store/v2/create_command.go

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

+ 1 - 1
store/v2/delete_command.go

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

+ 1 - 1
store/v2/set_command.go

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

+ 1 - 1
store/v2/sync_command.go

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

+ 1 - 1
store/v2/update_command.go

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

+ 2 - 2
tests/functional/multi_node_kill_all_and_recovery_test.go

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

+ 10 - 10
tests/functional/remove_node_test.go

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

+ 1 - 3
tests/functional/single_node_recovery_test.go

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

+ 3 - 10
tests/functional/single_node_test.go

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

+ 1 - 1
tests/functional/util.go

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

+ 31 - 20
third_party/code.google.com/p/go.net/ipv4/mocktransponder_test.go

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

+ 89 - 78
third_party/code.google.com/p/go.net/ipv4/multicastlistener_test.go

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

+ 1 - 1
third_party/code.google.com/p/go.net/ipv4/packet.go

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

+ 6 - 5
third_party/code.google.com/p/go.net/ipv6/icmp.go

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

+ 5 - 7
third_party/code.google.com/p/go.net/ipv6/icmp_bsd.go

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

+ 5 - 7
third_party/code.google.com/p/go.net/ipv6/icmp_linux.go

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

+ 4 - 4
third_party/code.google.com/p/go.net/ipv6/icmp_plan9.go

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

+ 4 - 4
third_party/code.google.com/p/go.net/ipv6/icmp_windows.go

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

+ 24 - 6
third_party/code.google.com/p/go.net/ipv6/mockicmp_test.go

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

+ 35 - 1
third_party/code.google.com/p/go.net/ipv6/multicast_test.go

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

+ 54 - 8
third_party/code.google.com/p/go.net/ipv6/multicastlistener_test.go

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

+ 3 - 2
third_party/code.google.com/p/go.net/ipv6/sockopt_rfc3493_bsd.go

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

+ 3 - 2
third_party/code.google.com/p/go.net/ipv6/sockopt_rfc3493_linux.go

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

+ 43 - 33
third_party/code.google.com/p/go.net/ipv6/sockopt_rfc3493_unix.go

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

+ 16 - 16
third_party/code.google.com/p/go.net/ipv6/sockopt_rfc3493_windows.go

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

+ 52 - 10
third_party/code.google.com/p/go.net/ipv6/sockopt_rfc3542_unix.go

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

+ 29 - 1
third_party/code.google.com/p/go.net/ipv6/unicast_test.go

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

+ 16 - 17
third_party/code.google.com/p/go.net/publicsuffix/list_test.go

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

File diff suppressed because it is too large
+ 6564 - 6458
third_party/code.google.com/p/go.net/publicsuffix/table.go


+ 225 - 24
third_party/code.google.com/p/go.net/publicsuffix/table_test.go

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

+ 1 - 1
third_party/code.google.com/p/go.net/websocket/websocket_test.go

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

+ 1 - 1
third_party/deps

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

+ 1 - 0
third_party/github.com/coreos/go-etcd/.gitignore

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

+ 4 - 39
third_party/github.com/coreos/go-etcd/README.md

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

+ 11 - 0
third_party/github.com/coreos/go-etcd/etcd/add_child.go

@@ -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 - 0
third_party/github.com/coreos/go-etcd/etcd/add_child_test.go

@@ -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)
+	}
+}

+ 198 - 116
third_party/github.com/coreos/go-etcd/etcd/client.go

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

+ 40 - 4
third_party/github.com/coreos/go-etcd/etcd/client_test.go

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

+ 18 - 0
third_party/github.com/coreos/go-etcd/etcd/compare_and_swap.go

@@ -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 - 0
third_party/github.com/coreos/go-etcd/etcd/compare_and_swap_test.go

@@ -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)
+	}
+}

+ 2 - 0
third_party/github.com/coreos/go-etcd/etcd/debug.go

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

+ 12 - 35
third_party/github.com/coreos/go-etcd/etcd/delete.go

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

+ 47 - 5
third_party/github.com/coreos/go-etcd/etcd/delete_test.go

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

+ 18 - 77
third_party/github.com/coreos/go-etcd/etcd/get.go

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

+ 75 - 22
third_party/github.com/coreos/go-etcd/etcd/get_test.go

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

+ 0 - 23
third_party/github.com/coreos/go-etcd/etcd/list_test.go

@@ -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 - 0
third_party/github.com/coreos/go-etcd/etcd/requests.go

@@ -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
+}

+ 31 - 6
third_party/github.com/coreos/go-etcd/etcd/response.go

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

+ 0 - 89
third_party/github.com/coreos/go-etcd/etcd/set.go

@@ -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 - 0
third_party/github.com/coreos/go-etcd/etcd/set_curl_chan_test.go

@@ -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)
+	}
+}

+ 0 - 42
third_party/github.com/coreos/go-etcd/etcd/set_test.go

@@ -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 - 0
third_party/github.com/coreos/go-etcd/etcd/set_update_create.go

@@ -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 - 0
third_party/github.com/coreos/go-etcd/etcd/set_update_create_test.go

@@ -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)
+	}
+}

+ 0 - 56
third_party/github.com/coreos/go-etcd/etcd/testAndSet.go

@@ -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
-
-}

+ 0 - 39
third_party/github.com/coreos/go-etcd/etcd/testAndSet_test.go

@@ -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 - 0
third_party/github.com/coreos/go-etcd/etcd/utils.go

@@ -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 - 1
third_party/github.com/coreos/go-etcd/etcd/version.go

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

+ 48 - 76
third_party/github.com/coreos/go-etcd/etcd/watch.go

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

+ 61 - 19
third_party/github.com/coreos/go-etcd/etcd/watch_test.go

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

+ 0 - 71
third_party/github.com/coreos/go-etcd/examples/mutex/mutex.go

@@ -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)
-	}
-}

+ 0 - 31
third_party/github.com/coreos/go-etcd/examples/speed/speed.go

@@ -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
-}

+ 0 - 3
third_party/github.com/coreos/go-etcd/examples/sync-cluster/README.md

@@ -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

+ 0 - 51
third_party/github.com/coreos/go-etcd/examples/sync-cluster/sync-cluster.go

@@ -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))
-			}
-
-		}
-	}
-}

+ 0 - 170
third_party/github.com/coreos/go-raft/timer.go

@@ -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
-}

+ 0 - 86
third_party/github.com/coreos/go-raft/timer_test.go

@@ -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()
-}

+ 10 - 2
third_party/github.com/coreos/go-systemd/activation/files.go

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

+ 14 - 8
third_party/github.com/coreos/go-systemd/dbus/dbus.go

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

+ 50 - 0
third_party/github.com/coreos/go-systemd/dbus/methods.go

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

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