mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Merge branch 'master' of https://github.com/coreos/etcd into mod-lock
Conflicts: mod/mod.go
This commit is contained in:
commit
74ad50efa6
@ -617,6 +617,7 @@ See [CONTRIBUTING](https://github.com/coreos/etcd/blob/master/CONTRIBUTING.md) f
|
|||||||
**Clojure libraries**
|
**Clojure libraries**
|
||||||
|
|
||||||
- [aterreno/etcd-clojure](https://github.com/aterreno/etcd-clojure)
|
- [aterreno/etcd-clojure](https://github.com/aterreno/etcd-clojure)
|
||||||
|
- [rthomas/clj-etcd](https://github.com/rthomas/clj-etcd)
|
||||||
|
|
||||||
**Erlang libraries**
|
**Erlang libraries**
|
||||||
|
|
||||||
|
68
etcd.go
68
etcd.go
@ -17,45 +17,39 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
|
||||||
"runtime/pprof"
|
|
||||||
|
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/etcd/server"
|
"github.com/coreos/etcd/server"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
parseFlags()
|
|
||||||
|
|
||||||
// Load configuration.
|
// Load configuration.
|
||||||
var config = server.NewConfig()
|
var config = server.NewConfig()
|
||||||
if err := config.Load(os.Args[1:]); err != nil {
|
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 {
|
if config.VeryVerbose {
|
||||||
log.Verbose = true
|
log.Verbose = true
|
||||||
raft.SetLogLevel(raft.Debug)
|
raft.SetLogLevel(raft.Debug)
|
||||||
} else if config.Verbose {
|
} else if config.Verbose {
|
||||||
log.Verbose = true
|
log.Verbose = true
|
||||||
}
|
}
|
||||||
|
if config.CPUProfileFile != "" {
|
||||||
// Setup a default directory based on the machine name
|
profile(config.CPUProfileFile)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load info object.
|
// Load info object.
|
||||||
@ -116,39 +110,3 @@ func main() {
|
|||||||
}()
|
}()
|
||||||
log.Fatal(s.ListenAndServe())
|
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)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -2,10 +2,11 @@
|
|||||||
|
|
||||||
angular.module('etcd', [])
|
angular.module('etcd', [])
|
||||||
|
|
||||||
.factory('EtcdV2', ['$http', function($http) {
|
.factory('EtcdV2', ['$http', '$q', function($http, $q) {
|
||||||
var keyPrefix = '/v2/keys/'
|
var keyPrefix = '/v2/keys/'
|
||||||
var statsPrefix = '/v2/stats/'
|
var statsPrefix = '/v2/stats/'
|
||||||
var baseURL = '/v2/'
|
var baseURL = '/v2/'
|
||||||
|
var leaderURL = ''
|
||||||
|
|
||||||
delete $http.defaults.headers.common['X-Requested-With'];
|
delete $http.defaults.headers.common['X-Requested-With'];
|
||||||
|
|
||||||
@ -45,20 +46,24 @@ angular.module('etcd', [])
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.set = function(keyValue) {
|
self.set = function(keyValue) {
|
||||||
|
return getLeader().then(function(leader) {
|
||||||
return $http({
|
return $http({
|
||||||
url: self.path(),
|
url: leader + self.path(),
|
||||||
data: $.param({value: keyValue}),
|
data: $.param({value: keyValue}),
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
|
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
self.deleteKey = function(keyValue) {
|
self.deleteKey = function(keyValue) {
|
||||||
|
return getLeader().then(function(leader) {
|
||||||
return $http({
|
return $http({
|
||||||
url: self.path(),
|
url: leader + self.path(),
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
|
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@ -79,8 +84,18 @@ angular.module('etcd', [])
|
|||||||
return self
|
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 {
|
return {
|
||||||
getStat: newStat,
|
getStat: newStat,
|
||||||
getKey: newKey
|
getKey: newKey,
|
||||||
}
|
}
|
||||||
}]);
|
}]);
|
||||||
|
@ -94,23 +94,23 @@ angular.module('etcdBrowser', ['ngRoute', 'etcd', 'timeRelative'])
|
|||||||
|
|
||||||
$scope.saveData = function() {
|
$scope.saveData = function() {
|
||||||
// TODO: fixup etcd to allow for empty values
|
// 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.save = 'etcd-save-hide';
|
||||||
$scope.preview = 'etcd-preview-hide';
|
$scope.preview = 'etcd-preview-hide';
|
||||||
$scope.back();
|
$scope.back();
|
||||||
$scope.writingNew = false;
|
$scope.writingNew = false;
|
||||||
}).error(function (data, status, headers, config) {
|
}, function (response) {
|
||||||
$scope.showSaveError(data.message);
|
$scope.showSaveError(data.message);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.deleteKey = function() {
|
$scope.deleteKey = function() {
|
||||||
$scope.key.deleteKey().success(function (data, status, headers, config) {
|
$scope.key.deleteKey().then(function(response) {
|
||||||
//TODO: remove loader
|
//TODO: remove loader
|
||||||
$scope.save = 'etcd-save-hide';
|
$scope.save = 'etcd-save-hide';
|
||||||
$scope.preview = 'etcd-preview-hide';
|
$scope.preview = 'etcd-preview-hide';
|
||||||
$scope.back();
|
$scope.back();
|
||||||
}).error(function (data, status, headers, config) {
|
}, function (response) {
|
||||||
//TODO: remove loader
|
//TODO: remove loader
|
||||||
//show errors
|
//show errors
|
||||||
$scope.showBrowseError('Could not delete the key');
|
$scope.showBrowseError('Could not delete the key');
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -8,7 +8,7 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _views_stats_html = "\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xd4\x56\x4b\x8f\xdb\x36\x10\xbe\xe7\x57\x10\xec\xc1\x09\x20\xc9\x7a\xf8\xb1\x32\x2c\x03\x4d\x0b\xf4\xd2\xed\xa1\x29\x0a\x14\x45\x0f\x34\x45\x5b\xc4\xd2\x94\x4a\xd2\xd6\x3a\xae\xff\x7b\x87\xa2\xec\xae\x64\xc5\xdd\xa0\x7b\xe9\x3e\x40\x71\x38\xf3\xcd\xa7\xe1\xc7\xa1\x96\x39\x3f\x20\x2a\x88\xd6\x19\x66\x86\xe6\x3e\x2d\xa5\x21\x5c\x32\x85\x9a\xa9\x36\xc4\x68\x74\x3a\xd1\x52\xec\x77\x52\x9f\xcf\xf0\x6c\xc8\x5a\xb0\x5f\xb9\xe6\x6b\x2e\xb8\x39\x9e\xcf\x78\xf5\x0e\xc1\xcf\x0d\xd6\xba\xcc\x8f\xed\xda\xe0\xfa\xa6\x54\x3b\x62\x7c\xcd\x04\xa3\xa6\x54\x2f\x5c\x07\xdd\x2f\x7e\x3e\x37\x6c\x87\xba\xa6\xad\x22\x55\x81\x91\xdc\xfa\x54\x70\xfa\x94\x61\x5d\x94\xf5\x0f\xd6\xf8\xfe\x43\x0f\xb7\xc1\xd6\x87\x2d\x3a\x30\xa5\x79\x29\x33\x1c\x05\x11\x46\xcf\x3b\x21\x21\x53\x61\x4c\xb5\x18\x8f\xeb\xba\x0e\xea\x24\x28\xd5\x76\x1c\x87\x61\x38\x06\xff\xd6\x65\xf1\x2c\xb8\x7c\x1a\x72\x8c\xd2\x34\x1d\x37\xab\xe0\x9a\xe1\xb0\x7a\xc6\xe8\xe8\xc6\x1b\x02\x95\x62\x9a\xa9\x03\xfb\x56\x57\xf0\x02\x3f\x13\xc3\xcb\x0c\x3f\x3f\x72\xf9\x1b\xfc\x63\x74\xe0\xac\xfe\x58\x5a\x10\x14\xa2\x99\xfd\x0b\xc2\x70\x8e\x11\x93\xb6\xf6\xfe\x9a\xd0\xa7\xad\x2a\xf7\x32\xcf\xb0\x64\x35\xea\x79\x01\xcf\x85\xae\x08\x65\x19\xbe\xe4\x19\xaa\x41\x45\x4c\x81\x36\x5c\x88\x0c\x7f\x13\x7d\x0f\xbf\x1f\x31\x02\xc0\xc7\x24\xf4\x01\x28\x8d\xa8\x1f\xcd\x82\x59\x1c\x79\xa1\x9f\x58\xc3\xc4\x8b\x92\x60\x32\x9f\x5c\x66\x6e\xa0\xa1\xd7\xba\xb9\x55\xaf\xb3\xda\x0e\x37\xc9\xb5\xb3\xfb\x9d\x98\x16\xf8\xbb\xd9\x35\x5b\xf2\x90\x78\x93\x06\xdd\x51\xf2\x2e\xdc\x3e\x23\xa0\xe9\x4d\xe6\x41\x9c\xa4\xd4\x4f\x83\x69\x94\x02\xcd\xc8\xce\xa7\xfe\x3c\x98\x47\xb3\xcb\xc4\x0d\x37\x04\x3e\xc5\x61\x30\x79\x00\xd2\x71\x30\x9f\x3d\x00\x6e\xfb\x44\x5b\x2c\xcf\xc5\x79\x0d\xd6\x65\xe2\x86\x4f\x89\xf3\x71\xd9\xbd\x2b\x8f\xcf\x78\x3c\x50\x65\x2b\x9d\xd5\xbb\xae\xb2\xc7\x20\xed\xff\x20\xf6\xe6\x00\xf6\xc5\xfe\x8b\x35\xfe\xff\xc5\x3e\x4d\x83\x49\x3a\xb3\xc3\x34\x4c\xee\x0a\xbe\xe7\xf9\x4a\xd1\x2b\x60\xd0\x10\x76\x74\x71\xff\x00\xd4\x3c\x37\x45\x86\x1d\x38\x46\x05\xe3\xdb\xc2\x40\xd5\x92\x20\x8a\xa2\xc1\x0d\xee\x22\xc6\xe0\x68\x03\xdf\x18\x16\x0e\x41\x92\xc6\x6f\x00\xeb\xe4\x78\x47\x8d\xfd\x69\x5f\x98\xae\xd3\xf6\x20\x8a\x78\xf5\x48\x68\x01\xf7\x06\xfa\x91\x18\x26\xe9\x71\x39\x06\xdb\x7d\x89\x37\x48\xff\x5c\x38\x18\x71\xd8\x5d\xe1\xc2\xfb\x09\xbe\x8e\xa3\xe0\xda\xdc\xa3\x08\xcb\x03\xfc\x9a\x63\x85\x28\x13\xa2\x22\x79\xce\xe5\xb6\x29\xbe\x9d\x5b\x5d\xb5\xf3\x7e\x4c\xc1\x48\x3e\x50\x66\x93\x77\x08\x49\xb2\x63\xbe\x75\x85\xd7\xbc\xd2\xf8\x09\x8c\xcb\xb1\x79\x45\xf8\xb5\x28\xd7\xe2\xf6\xa3\xc0\x72\xcb\x64\x69\xec\x0d\x3c\x04\xaf\x6c\xf3\x50\xac\x62\x04\xd4\xb2\x6b\xf9\x70\x89\xda\x47\x3d\x70\x72\x2e\xbc\x20\x50\xd7\xdc\xd0\x02\xd9\x6e\x72\x32\x6a\xcf\x16\x23\xd1\xbc\xda\xc8\x43\x1b\x22\x34\x5b\xa0\xd1\xa6\x14\xa2\xac\xc1\x74\xfe\xdd\xad\xd9\x97\x45\x59\x76\xc9\x10\xd8\x8a\xfc\xf1\x85\x34\x4d\x2a\xbb\xa7\xd7\x5c\x7e\x5d\x30\xc8\x26\xda\x0a\x9e\x4e\x2f\x61\xce\xe7\x25\x6c\x90\xec\x54\xac\x5d\xf7\xcd\xb1\x82\x36\xf0\xde\x05\x7e\x00\xf5\x83\xe3\x6a\xa0\xff\xde\x49\x9d\xb3\x0d\xd9\x0b\x73\x9b\xf4\xcb\x28\xc3\xbb\xda\x56\xf0\x5f\x12\x77\x2e\x81\x3f\xf7\x44\x31\xd7\xe8\xc1\xba\x40\xa7\xd1\x0b\x3b\x1c\x20\xc6\xe4\x68\x71\xad\x69\x2b\x93\x80\xee\x95\x62\xd2\xa0\x25\x8a\xa7\x1e\xea\x84\x94\x8a\xc8\x2d\xbb\x1b\x33\x0b\x7b\x31\x8a\xe5\x77\x02\x56\x19\x44\xc0\x27\xe0\x6b\x8a\x3a\xa0\x69\xff\x40\xc4\x9e\xbd\xdc\xd2\x7e\x82\xbf\x90\xdc\xef\xd6\x4c\x2d\x22\x04\x9f\x9f\x3b\xfd\xd5\x75\x07\xab\xba\x39\x2d\xb7\x27\x03\x8c\xb6\x01\x0c\x76\x99\xf6\xb1\x1d\xfe\x0e\x00\x00\xff\xff\xe8\x36\xb1\x2a\x35\x0b\x00\x00"
|
var _views_stats_html = "\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xd4\x56\x4b\x8f\xdb\x36\x10\xbe\xe7\x57\x10\xec\xc1\x09\x20\xc9\x7a\xf8\xb1\x32\x2c\x03\x4d\x0b\xf4\xd2\x14\x45\x53\x14\x28\x8a\x1e\x68\x8a\xb6\x88\xa5\x29\x95\xa4\xad\x75\x5c\xff\xf7\x0e\x45\xd9\x8d\x64\x79\xbb\x41\xf7\x92\x7d\x80\xe2\x70\xe6\x9b\x8f\xc3\x6f\x28\x2d\x73\x7e\x40\x54\x10\xad\x33\xcc\x0c\xcd\x7d\x5a\x4a\x43\xb8\x64\x0a\x35\x53\x6d\x88\xd1\xe8\x74\xa2\xa5\xd8\xef\xa4\x3e\x9f\xe1\xd9\x90\xb5\x60\xbf\x71\xcd\xd7\x5c\x70\x73\x3c\x9f\xf1\xea\x0d\x82\x9f\x1b\xac\x75\x99\x1f\xdb\xb5\xc1\xf5\x4d\xa9\x76\xc4\xf8\x9a\x09\x46\x4d\xa9\x3e\x73\x1d\x74\xbf\xf8\xf9\xdc\xb0\x1d\xea\x9a\xb6\x8a\x54\x05\x46\x72\xeb\x53\xc1\xe9\x63\x86\x75\x51\xd6\x3f\x58\xe3\xdb\x77\x3d\xdc\x06\x5b\x1f\xb6\xe8\xc0\x94\xe6\xa5\xcc\x70\x14\x44\x18\x3d\xed\x84\x84\x4c\x85\x31\xd5\x62\x3c\xae\xeb\x3a\xa8\x93\xa0\x54\xdb\x71\x1c\x86\xe1\x18\xfc\x5b\x97\xc5\x93\xe0\xf2\x71\xc8\x31\x4a\xd3\x74\xdc\xac\x82\x6b\x86\xc3\xea\x09\xa3\xa3\x1b\x6f\x08\x54\x8a\x69\xa6\x0e\xec\x5b\x5d\xc1\x06\x7e\x21\x86\x97\x19\x7e\xfa\xc0\xe5\xef\xf0\x8f\xd1\x81\xb3\xfa\x7d\x69\x41\x50\x88\x66\xf6\x2f\x08\xc3\x39\x46\x4c\xda\xda\xfb\x6b\x42\x1f\xb7\xaa\xdc\xcb\x3c\xc3\x92\xd5\xa8\xe7\x05\x3c\x17\xba\x22\x94\x65\xf8\x92\x67\xa8\x06\x15\x31\x05\xda\x70\x21\x32\xfc\x4d\xf4\x3d\xfc\xbe\xc7\x08\x00\x3f\x24\xa1\x0f\x40\x69\x44\xfd\x68\x16\xcc\xe2\xc8\x0b\xfd\xc4\x1a\x26\x5e\x94\x04\x93\xf9\xe4\x32\x73\x03\x0d\xbd\xd6\xcd\xad\x7a\x9d\xd5\x76\xb8\x49\xae\x9d\xdd\xef\xc4\xb4\xc0\xdf\xcd\xae\xd9\x92\x87\xc4\x9b\x34\xe8\x8e\x92\x77\xe1\xf6\x09\x01\x4d\x6f\x32\x0f\xe2\x24\xa5\x7e\x1a\x4c\xa3\x14\x68\x46\x76\x3e\xf5\xe7\xc1\x3c\x9a\x5d\x26\x6e\xb8\x21\xf0\x31\x0e\x83\xc9\x03\x90\x8e\x83\xf9\xec\x01\x70\xdb\x27\xda\x62\x79\x2e\xce\x6b\xb0\x2e\x13\x37\x7c\x4c\x9c\x8f\xcb\xee\x5d\x79\x7c\xc2\xe3\x81\x2a\x5b\xe9\xac\xde\x74\x95\x3d\x06\x69\xff\x0f\xb1\x37\x0d\xd8\x17\xfb\xaf\xd6\xf8\xf5\x8b\x7d\x9a\x06\x93\x74\x66\x87\x69\x98\x3c\x2b\xf8\x9e\xe7\x0b\x45\xaf\x80\x41\x43\xd8\xd1\xc5\xfd\x06\xa8\x79\x6e\x8a\x0c\x3b\x70\x8c\x0a\xc6\xb7\x85\x81\xaa\x25\x41\x14\x45\x83\x07\xdc\x45\x8c\xc1\xd1\x06\xbe\x32\x2c\x34\x41\x92\xc6\xaf\x00\xeb\xe4\xf8\x8c\x1a\xfb\xd3\xbe\x30\xdd\x4d\xdb\x83\x28\xe2\xd5\xcf\x0c\xde\x19\x3f\x12\xc3\x24\x3d\x2e\xc7\x60\x78\x5e\xdf\x0d\xcc\xbf\x6f\x1b\x8c\x38\x1c\xad\x70\xe1\x7d\xf4\x2f\x23\x28\xb8\x36\x77\xf9\xc1\xda\x00\xb9\xa6\xa1\x10\x65\x42\x54\x24\xcf\xb9\xdc\x36\x65\xb7\x73\xab\xa8\x76\xde\x8f\x29\x18\xc9\x07\x0a\x6c\xf2\x0e\x1b\x49\x76\xcc\xb7\xae\xb0\x47\xc7\xe1\x27\xb0\x2c\xc7\xe6\x05\xb1\xd7\x72\x5c\xcb\xda\x8f\x02\xcb\x2d\x8d\xa5\xb1\x2f\xde\x21\x78\x65\xef\x0c\xc5\x2a\x46\x40\x24\x95\x25\xc3\x25\xb2\xa3\x1e\x68\x95\x0b\x23\x08\xd1\x35\x37\xb4\x40\xf6\xfa\x38\x19\xb5\x67\x8b\x91\x68\x76\x34\xf2\xd0\x86\x08\xcd\x16\x68\xb4\x29\x85\x28\x6b\x30\x9d\xff\x70\x6b\x76\x9b\x28\xcb\x1a\xf8\xc0\x56\xe1\xcf\x3b\x39\x9a\x3c\xf6\x10\xaf\x89\xfc\xba\x60\x90\x4a\xb4\x55\x3b\x9d\xae\x18\xe7\xf3\x12\x4e\x44\x76\xaa\x64\x17\x7d\x73\xac\xa0\xdd\xdf\xba\x90\x77\xa0\x72\xf0\x5a\x0d\xdc\xb3\xcf\x24\xcd\xd9\x86\xec\x85\xe9\xa5\xbb\x0f\x31\x7c\x86\x6d\xd5\xfe\x23\x6b\xe7\xa6\xff\x6b\x4f\x14\x73\xb7\x39\x58\x17\xe8\x34\xfa\xcc\x0e\x8d\xc2\x98\x1c\x2d\x5c\x1d\x5b\x45\x04\x74\xaf\x14\x93\x06\x2d\x51\x3c\xf5\x50\xc7\xbf\x54\x44\x6e\xd9\xfd\x80\x59\xd8\x0b\x50\x2c\xbf\xe7\xbd\xca\xc0\x1d\x3e\xef\x5e\x52\xc8\x01\xe1\xfa\x07\x22\xf6\xec\x7a\x80\x7d\xf4\xbf\x91\xdc\xef\xd6\x4c\x2d\x22\x04\xdf\x95\x3b\xfd\xc5\xb5\x06\xab\xba\xe9\x87\x5b\xed\x83\xd1\xf6\xf7\xe0\x0d\xd2\x3e\xb6\xc3\x3f\x01\x00\x00\xff\xff\x0a\x0c\x99\x1a\x0e\x0b\x00\x00"
|
||||||
|
|
||||||
// views_stats_html returns raw, uncompressed file data.
|
// views_stats_html returns raw, uncompressed file data.
|
||||||
func views_stats_html() []byte {
|
func views_stats_html() []byte {
|
||||||
|
@ -3,6 +3,7 @@ package mod
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path"
|
||||||
|
|
||||||
"github.com/coreos/etcd/mod/dashboard"
|
"github.com/coreos/etcd/mod/dashboard"
|
||||||
"github.com/coreos/etcd/mod/lock"
|
"github.com/coreos/etcd/mod/lock"
|
||||||
@ -11,11 +12,18 @@ import (
|
|||||||
|
|
||||||
var ServeMux *http.Handler
|
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) {
|
func HttpHandler() (handler http.Handler) {
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
|
r.HandleFunc("/dashboard", addSlash)
|
||||||
r.PathPrefix("/dashboard/").Handler(http.StripPrefix("/dashboard/", dashboard.HttpHandler()))
|
r.PathPrefix("/dashboard/").Handler(http.StripPrefix("/dashboard/", dashboard.HttpHandler()))
|
||||||
|
|
||||||
// TODO: Use correct addr.
|
// TODO: Use correct addr.
|
||||||
|
r.HandleFunc("/lock", addSlash)
|
||||||
r.PathPrefix("/lock").Handler(http.StripPrefix("/lock", lock.NewHandler("127.0.0.1:4001")))
|
r.PathPrefix("/lock").Handler(http.StripPrefix("/lock", lock.NewHandler("127.0.0.1:4001")))
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
27
profile.go
Normal file
27
profile.go
Normal file
@ -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)
|
||||||
|
}()
|
||||||
|
}
|
169
server/config.go
169
server/config.go
@ -49,6 +49,7 @@ type Config struct {
|
|||||||
BindAddr string `toml:"bind_addr" env:"ETCD_BIND_ADDR"`
|
BindAddr string `toml:"bind_addr" env:"ETCD_BIND_ADDR"`
|
||||||
CAFile string `toml:"ca_file" env:"ETCD_CA_FILE"`
|
CAFile string `toml:"ca_file" env:"ETCD_CA_FILE"`
|
||||||
CertFile string `toml:"cert_file" env:"ETCD_CERT_FILE"`
|
CertFile string `toml:"cert_file" env:"ETCD_CERT_FILE"`
|
||||||
|
CPUProfileFile string
|
||||||
CorsOrigins []string `toml:"cors" env:"ETCD_CORS"`
|
CorsOrigins []string `toml:"cors" env:"ETCD_CORS"`
|
||||||
DataDir string `toml:"data_dir" env:"ETCD_DATA_DIR"`
|
DataDir string `toml:"data_dir" env:"ETCD_DATA_DIR"`
|
||||||
Force bool
|
Force bool
|
||||||
@ -61,6 +62,8 @@ type Config struct {
|
|||||||
Name string `toml:"name" env:"ETCD_NAME"`
|
Name string `toml:"name" env:"ETCD_NAME"`
|
||||||
Snapshot bool `toml:"snapshot" env:"ETCD_SNAPSHOT"`
|
Snapshot bool `toml:"snapshot" env:"ETCD_SNAPSHOT"`
|
||||||
SnapshotCount int `toml:"snapshot_count" env:"ETCD_SNAPSHOTCOUNT"`
|
SnapshotCount int `toml:"snapshot_count" env:"ETCD_SNAPSHOTCOUNT"`
|
||||||
|
ShowHelp bool
|
||||||
|
ShowVersion bool
|
||||||
Verbose bool `toml:"verbose" env:"ETCD_VERBOSE"`
|
Verbose bool `toml:"verbose" env:"ETCD_VERBOSE"`
|
||||||
VeryVerbose bool `toml:"very_verbose" env:"ETCD_VERY_VERBOSE"`
|
VeryVerbose bool `toml:"very_verbose" env:"ETCD_VERY_VERBOSE"`
|
||||||
|
|
||||||
@ -117,15 +120,6 @@ func (c *Config) Load(arguments []string) error {
|
|||||||
return err
|
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.
|
// Loads peers if a peer file was specified.
|
||||||
if err := c.LoadPeersFile(); err != nil {
|
if err := c.LoadPeersFile(); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -133,7 +127,7 @@ func (c *Config) Load(arguments []string) error {
|
|||||||
|
|
||||||
// Sanitize all the input fields.
|
// Sanitize all the input fields.
|
||||||
if err := c.Sanitize(); err != nil {
|
if err := c.Sanitize(); err != nil {
|
||||||
return fmt.Errorf("sanitize:", err)
|
return fmt.Errorf("sanitize: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -195,100 +189,85 @@ func (c *Config) loadEnv(target interface{}) error {
|
|||||||
return nil
|
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.
|
// Loads configuration from command line flags.
|
||||||
func (c *Config) LoadFlags(arguments []string) error {
|
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 := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
|
||||||
f.SetOutput(ioutil.Discard)
|
f.SetOutput(ioutil.Discard)
|
||||||
|
|
||||||
f.BoolVar(&c.Force, "f", false, "force new node configuration if existing is found (WARNING: data loss!)")
|
f.BoolVar(&c.ShowHelp, "h", false, "")
|
||||||
f.BoolVar(&c.Force, "force", false, "force new node configuration if existing is found (WARNING: data loss!)")
|
f.BoolVar(&c.ShowHelp, "help", false, "")
|
||||||
|
f.BoolVar(&c.ShowVersion, "version", false, "")
|
||||||
|
|
||||||
f.BoolVar(&c.Verbose, "v", c.Verbose, "verbose logging")
|
f.BoolVar(&c.Force, "f", false, "")
|
||||||
f.BoolVar(&c.VeryVerbose, "vv", c.Verbose, "very verbose logging")
|
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.BoolVar(&c.Verbose, "v", c.Verbose, "")
|
||||||
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.VeryVerbose, "vv", c.Verbose, "")
|
||||||
|
|
||||||
f.StringVar(&c.Name, "name", c.Name, "the node name (required)")
|
f.StringVar(&peers, "peers", "", "")
|
||||||
f.StringVar(&c.Addr, "addr", c.Addr, "the advertised public hostname:port for etcd client communication")
|
f.StringVar(&c.PeersFile, "peers-file", c.PeersFile, "")
|
||||||
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(&c.Peer.CAFile, "peer-ca-file", c.Peer.CAFile, "the path of the CAFile")
|
f.StringVar(&c.Name, "name", c.Name, "")
|
||||||
f.StringVar(&c.Peer.CertFile, "peer-cert-file", c.Peer.CertFile, "the cert file of the server")
|
f.StringVar(&c.Addr, "addr", c.Addr, "")
|
||||||
f.StringVar(&c.Peer.KeyFile, "peer-key-file", c.Peer.KeyFile, "the key file of the server")
|
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.CAFile, "ca-file", c.CAFile, "")
|
||||||
f.StringVar(&c.CertFile, "cert-file", c.CertFile, "the cert file of the client")
|
f.StringVar(&c.CertFile, "cert-file", c.CertFile, "")
|
||||||
f.StringVar(&c.KeyFile, "key-file", c.KeyFile, "the key file of the client")
|
f.StringVar(&c.KeyFile, "key-file", c.KeyFile, "")
|
||||||
|
|
||||||
f.StringVar(&c.DataDir, "data-dir", c.DataDir, "the directory to store log and snapshot")
|
f.StringVar(&c.Peer.CAFile, "peer-ca-file", c.Peer.CAFile, "")
|
||||||
f.IntVar(&c.MaxResultBuffer, "max-result-buffer", c.MaxResultBuffer, "the max size of result buffer")
|
f.StringVar(&c.Peer.CertFile, "peer-cert-file", c.Peer.CertFile, "")
|
||||||
f.IntVar(&c.MaxRetryAttempts, "max-retry-attempts", c.MaxRetryAttempts, "the max retry attempts when trying to join a cluster")
|
f.StringVar(&c.Peer.KeyFile, "peer-key-file", c.Peer.KeyFile, "")
|
||||||
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.BoolVar(&c.Snapshot, "snapshot", c.Snapshot, "open or close snapshot")
|
f.StringVar(&c.DataDir, "data-dir", c.DataDir, "")
|
||||||
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.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.
|
f.BoolVar(&c.Snapshot, "snapshot", c.Snapshot, "")
|
||||||
var path string
|
f.IntVar(&c.SnapshotCount, "snapshot-count", c.SnapshotCount, "")
|
||||||
f.StringVar(&path, "config", "", "path to config file")
|
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.
|
// Convert some parameters to lists.
|
||||||
if peers != "" {
|
if peers != "" {
|
||||||
@ -479,15 +458,3 @@ func sanitizeBindAddr(bindAddr string, addr string) (string, error) {
|
|||||||
|
|
||||||
return net.JoinHostPort(bindAddr, aport), nil
|
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
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -113,6 +113,27 @@ func TestConfigEnv(t *testing.T) {
|
|||||||
assert.Equal(t, c.Peer.BindAddr, "127.0.0.1:7003", "")
|
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.
|
// Ensures that the "force config" flag can be parsed.
|
||||||
func TestConfigForceFlag(t *testing.T) {
|
func TestConfigForceFlag(t *testing.T) {
|
||||||
c := NewConfig()
|
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.
|
// Ensures that a the Peer Listen Host file flag can be parsed.
|
||||||
func TestConfigPeerBindAddrFlag(t *testing.T) {
|
func TestConfigPeerBindAddrFlag(t *testing.T) {
|
||||||
c := NewConfig()
|
c := NewConfig()
|
||||||
@ -456,118 +485,165 @@ func TestConfigCLIArgsOverrideEnvVar(t *testing.T) {
|
|||||||
//--------------------------------------
|
//--------------------------------------
|
||||||
|
|
||||||
func TestConfigDeprecatedAddrFlag(t *testing.T) {
|
func TestConfigDeprecatedAddrFlag(t *testing.T) {
|
||||||
|
_, stderr := capture(func() {
|
||||||
c := NewConfig()
|
c := NewConfig()
|
||||||
err := c.LoadDeprecatedFlags([]string{"-c", "127.0.0.1:4002"})
|
err := c.LoadFlags([]string{"-c", "127.0.0.1:4002"})
|
||||||
assert.Equal(t, err.Error(), "[deprecated] use -addr, not -c", "")
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c.Addr, "127.0.0.1:4002", "")
|
assert.Equal(t, c.Addr, "127.0.0.1:4002")
|
||||||
|
})
|
||||||
|
assert.Equal(t, stderr, "[deprecated] use -addr, not -c")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigDeprecatedBindAddrFlag(t *testing.T) {
|
func TestConfigDeprecatedBindAddrFlag(t *testing.T) {
|
||||||
|
_, stderr := capture(func() {
|
||||||
c := NewConfig()
|
c := NewConfig()
|
||||||
err := c.LoadDeprecatedFlags([]string{"-cl", "127.0.0.1:4003"})
|
err := c.LoadFlags([]string{"-cl", "127.0.0.1:4003"})
|
||||||
assert.Equal(t, err.Error(), "[deprecated] use -bind-addr, not -cl", "")
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c.BindAddr, "127.0.0.1:4003", "")
|
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) {
|
func TestConfigDeprecatedCAFileFlag(t *testing.T) {
|
||||||
|
_, stderr := capture(func() {
|
||||||
c := NewConfig()
|
c := NewConfig()
|
||||||
err := c.LoadDeprecatedFlags([]string{"-clientCAFile", "/tmp/file.ca"})
|
err := c.LoadFlags([]string{"-clientCAFile", "/tmp/file.ca"})
|
||||||
assert.Equal(t, err.Error(), "[deprecated] use -ca-file, not -clientCAFile", "")
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c.CAFile, "/tmp/file.ca", "")
|
assert.Equal(t, c.CAFile, "/tmp/file.ca", "")
|
||||||
|
})
|
||||||
|
assert.Equal(t, stderr, "[deprecated] use -ca-file, not -clientCAFile", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigDeprecatedCertFileFlag(t *testing.T) {
|
func TestConfigDeprecatedCertFileFlag(t *testing.T) {
|
||||||
|
_, stderr := capture(func() {
|
||||||
c := NewConfig()
|
c := NewConfig()
|
||||||
err := c.LoadDeprecatedFlags([]string{"-clientCert", "/tmp/file.cert"})
|
err := c.LoadFlags([]string{"-clientCert", "/tmp/file.cert"})
|
||||||
assert.Equal(t, err.Error(), "[deprecated] use -cert-file, not -clientCert", "")
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c.CertFile, "/tmp/file.cert", "")
|
assert.Equal(t, c.CertFile, "/tmp/file.cert", "")
|
||||||
|
})
|
||||||
|
assert.Equal(t, stderr, "[deprecated] use -cert-file, not -clientCert", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigDeprecatedKeyFileFlag(t *testing.T) {
|
func TestConfigDeprecatedKeyFileFlag(t *testing.T) {
|
||||||
|
_, stderr := capture(func() {
|
||||||
c := NewConfig()
|
c := NewConfig()
|
||||||
err := c.LoadDeprecatedFlags([]string{"-clientKey", "/tmp/file.key"})
|
err := c.LoadFlags([]string{"-clientKey", "/tmp/file.key"})
|
||||||
assert.Equal(t, err.Error(), "[deprecated] use -key-file, not -clientKey", "")
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c.KeyFile, "/tmp/file.key", "")
|
assert.Equal(t, c.KeyFile, "/tmp/file.key", "")
|
||||||
|
})
|
||||||
|
assert.Equal(t, stderr, "[deprecated] use -key-file, not -clientKey", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigDeprecatedPeersFlag(t *testing.T) {
|
func TestConfigDeprecatedPeersFlag(t *testing.T) {
|
||||||
|
_, stderr := capture(func() {
|
||||||
c := NewConfig()
|
c := NewConfig()
|
||||||
err := c.LoadDeprecatedFlags([]string{"-C", "coreos.com:4001,coreos.com:4002"})
|
err := c.LoadFlags([]string{"-C", "coreos.com:4001,coreos.com:4002"})
|
||||||
assert.Equal(t, err.Error(), "[deprecated] use -peers, not -C", "")
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c.Peers, []string{"coreos.com:4001", "coreos.com:4002"}, "")
|
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) {
|
func TestConfigDeprecatedPeersFileFlag(t *testing.T) {
|
||||||
|
_, stderr := capture(func() {
|
||||||
c := NewConfig()
|
c := NewConfig()
|
||||||
err := c.LoadDeprecatedFlags([]string{"-CF", "/tmp/machines"})
|
err := c.LoadFlags([]string{"-CF", "/tmp/machines"})
|
||||||
assert.Equal(t, err.Error(), "[deprecated] use -peers-file, not -CF", "")
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c.PeersFile, "/tmp/machines", "")
|
assert.Equal(t, c.PeersFile, "/tmp/machines", "")
|
||||||
|
})
|
||||||
|
assert.Equal(t, stderr, "[deprecated] use -peers-file, not -CF", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigDeprecatedMaxClusterSizeFlag(t *testing.T) {
|
func TestConfigDeprecatedMaxClusterSizeFlag(t *testing.T) {
|
||||||
|
_, stderr := capture(func() {
|
||||||
c := NewConfig()
|
c := NewConfig()
|
||||||
err := c.LoadDeprecatedFlags([]string{"-maxsize", "5"})
|
err := c.LoadFlags([]string{"-maxsize", "5"})
|
||||||
assert.Equal(t, err.Error(), "[deprecated] use -max-cluster-size, not -maxsize", "")
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c.MaxClusterSize, 5, "")
|
assert.Equal(t, c.MaxClusterSize, 5, "")
|
||||||
|
})
|
||||||
|
assert.Equal(t, stderr, "[deprecated] use -max-cluster-size, not -maxsize", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigDeprecatedMaxResultBufferFlag(t *testing.T) {
|
func TestConfigDeprecatedMaxResultBufferFlag(t *testing.T) {
|
||||||
|
_, stderr := capture(func() {
|
||||||
c := NewConfig()
|
c := NewConfig()
|
||||||
err := c.LoadDeprecatedFlags([]string{"-m", "512"})
|
err := c.LoadFlags([]string{"-m", "512"})
|
||||||
assert.Equal(t, err.Error(), "[deprecated] use -max-result-buffer, not -m", "")
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c.MaxResultBuffer, 512, "")
|
assert.Equal(t, c.MaxResultBuffer, 512, "")
|
||||||
|
})
|
||||||
|
assert.Equal(t, stderr, "[deprecated] use -max-result-buffer, not -m", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigDeprecatedMaxRetryAttemptsFlag(t *testing.T) {
|
func TestConfigDeprecatedMaxRetryAttemptsFlag(t *testing.T) {
|
||||||
|
_, stderr := capture(func() {
|
||||||
c := NewConfig()
|
c := NewConfig()
|
||||||
err := c.LoadDeprecatedFlags([]string{"-r", "10"})
|
err := c.LoadFlags([]string{"-r", "10"})
|
||||||
assert.Equal(t, err.Error(), "[deprecated] use -max-retry-attempts, not -r", "")
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c.MaxRetryAttempts, 10, "")
|
assert.Equal(t, c.MaxRetryAttempts, 10, "")
|
||||||
|
})
|
||||||
|
assert.Equal(t, stderr, "[deprecated] use -max-retry-attempts, not -r", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigDeprecatedNameFlag(t *testing.T) {
|
func TestConfigDeprecatedNameFlag(t *testing.T) {
|
||||||
|
_, stderr := capture(func() {
|
||||||
c := NewConfig()
|
c := NewConfig()
|
||||||
err := c.LoadDeprecatedFlags([]string{"-n", "test-name"})
|
err := c.LoadFlags([]string{"-n", "test-name"})
|
||||||
assert.Equal(t, err.Error(), "[deprecated] use -name, not -n", "")
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c.Name, "test-name", "")
|
assert.Equal(t, c.Name, "test-name", "")
|
||||||
|
})
|
||||||
|
assert.Equal(t, stderr, "[deprecated] use -name, not -n", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigDeprecatedPeerAddrFlag(t *testing.T) {
|
func TestConfigDeprecatedPeerAddrFlag(t *testing.T) {
|
||||||
|
_, stderr := capture(func() {
|
||||||
c := NewConfig()
|
c := NewConfig()
|
||||||
err := c.LoadDeprecatedFlags([]string{"-s", "localhost:7002"})
|
err := c.LoadFlags([]string{"-s", "localhost:7002"})
|
||||||
assert.Equal(t, err.Error(), "[deprecated] use -peer-addr, not -s", "")
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c.Peer.Addr, "localhost:7002", "")
|
assert.Equal(t, c.Peer.Addr, "localhost:7002", "")
|
||||||
|
})
|
||||||
|
assert.Equal(t, stderr, "[deprecated] use -peer-addr, not -s", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigDeprecatedPeerBindAddrFlag(t *testing.T) {
|
func TestConfigDeprecatedPeerBindAddrFlag(t *testing.T) {
|
||||||
|
_, stderr := capture(func() {
|
||||||
c := NewConfig()
|
c := NewConfig()
|
||||||
err := c.LoadDeprecatedFlags([]string{"-sl", "127.0.0.1:4003"})
|
err := c.LoadFlags([]string{"-sl", "127.0.0.1:4003"})
|
||||||
assert.Equal(t, err.Error(), "[deprecated] use -peer-bind-addr, not -sl", "")
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c.Peer.BindAddr, "127.0.0.1:4003", "")
|
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) {
|
func TestConfigDeprecatedPeerCAFileFlag(t *testing.T) {
|
||||||
|
_, stderr := capture(func() {
|
||||||
c := NewConfig()
|
c := NewConfig()
|
||||||
err := c.LoadDeprecatedFlags([]string{"-serverCAFile", "/tmp/peer/file.ca"})
|
err := c.LoadFlags([]string{"-serverCAFile", "/tmp/peer/file.ca"})
|
||||||
assert.Equal(t, err.Error(), "[deprecated] use -peer-ca-file, not -serverCAFile", "")
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c.Peer.CAFile, "/tmp/peer/file.ca", "")
|
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) {
|
func TestConfigDeprecatedPeerCertFileFlag(t *testing.T) {
|
||||||
|
_, stderr := capture(func() {
|
||||||
c := NewConfig()
|
c := NewConfig()
|
||||||
err := c.LoadDeprecatedFlags([]string{"-serverCert", "/tmp/peer/file.cert"})
|
err := c.LoadFlags([]string{"-serverCert", "/tmp/peer/file.cert"})
|
||||||
assert.Equal(t, err.Error(), "[deprecated] use -peer-cert-file, not -serverCert", "")
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c.Peer.CertFile, "/tmp/peer/file.cert", "")
|
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) {
|
func TestConfigDeprecatedPeerKeyFileFlag(t *testing.T) {
|
||||||
|
_, stderr := capture(func() {
|
||||||
c := NewConfig()
|
c := NewConfig()
|
||||||
err := c.LoadDeprecatedFlags([]string{"-serverKey", "/tmp/peer/file.key"})
|
err := c.LoadFlags([]string{"-serverKey", "/tmp/peer/file.key"})
|
||||||
assert.Equal(t, err.Error(), "[deprecated] use -peer-key-file, not -serverKey", "")
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c.Peer.KeyFile, "/tmp/peer/file.key", "")
|
assert.Equal(t, c.Peer.KeyFile, "/tmp/peer/file.key", "")
|
||||||
|
})
|
||||||
|
assert.Equal(t, stderr, "[deprecated] use -peer-key-file, not -serverKey", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//--------------------------------------
|
//--------------------------------------
|
||||||
// Helpers
|
// Helpers
|
||||||
//--------------------------------------
|
//--------------------------------------
|
||||||
@ -588,3 +664,27 @@ func withTempFile(content string, fn func(string)) {
|
|||||||
defer os.Remove(f.Name())
|
defer os.Remove(f.Name())
|
||||||
fn(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
server/cors_handler.go
Normal file
78
server/cors_handler.go
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
etcdErr "github.com/coreos/etcd/error"
|
etcdErr "github.com/coreos/etcd/error"
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -16,7 +16,7 @@ import (
|
|||||||
etcdErr "github.com/coreos/etcd/error"
|
etcdErr "github.com/coreos/etcd/error"
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
etcdErr "github.com/coreos/etcd/error"
|
etcdErr "github.com/coreos/etcd/error"
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ package server
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
type raftServerStats struct {
|
type raftServerStats struct {
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -17,7 +16,7 @@ import (
|
|||||||
"github.com/coreos/etcd/server/v2"
|
"github.com/coreos/etcd/server/v2"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
_ "github.com/coreos/etcd/store/v2"
|
_ "github.com/coreos/etcd/store/v2"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -32,14 +31,18 @@ type Server struct {
|
|||||||
url string
|
url string
|
||||||
tlsConf *TLSConfig
|
tlsConf *TLSConfig
|
||||||
tlsInfo *TLSInfo
|
tlsInfo *TLSInfo
|
||||||
corsOrigins map[string]bool
|
router *mux.Router
|
||||||
|
corsHandler *corsHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new Server.
|
// Creates a new Server.
|
||||||
func New(name string, urlStr string, bindAddr string, tlsConf *TLSConfig, tlsInfo *TLSInfo, peerServer *PeerServer, registry *Registry, store store.Store) *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{
|
s := &Server{
|
||||||
Server: http.Server{
|
Server: http.Server{
|
||||||
Handler: mux.NewRouter(),
|
Handler: cors,
|
||||||
TLSConfig: &tlsConf.Server,
|
TLSConfig: &tlsConf.Server,
|
||||||
Addr: bindAddr,
|
Addr: bindAddr,
|
||||||
},
|
},
|
||||||
@ -50,6 +53,8 @@ func New(name string, urlStr string, bindAddr string, tlsConf *TLSConfig, tlsInf
|
|||||||
tlsConf: tlsConf,
|
tlsConf: tlsConf,
|
||||||
tlsInfo: tlsInfo,
|
tlsInfo: tlsInfo,
|
||||||
peerServer: peerServer,
|
peerServer: peerServer,
|
||||||
|
router: r,
|
||||||
|
corsHandler: cors,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install the routes.
|
// Install the routes.
|
||||||
@ -124,7 +129,7 @@ func (s *Server) installV2() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) installMod() {
|
func (s *Server) installMod() {
|
||||||
r := s.Handler.(*mux.Router)
|
r := s.router
|
||||||
r.PathPrefix("/mod").Handler(http.StripPrefix("/mod", mod.HttpHandler()))
|
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.
|
// Adds a server handler to the router.
|
||||||
func (s *Server) handleFunc(path string, f func(http.ResponseWriter, *http.Request) error) *mux.Route {
|
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.
|
// Wrap the standard HandleFunc interface to pass in the server reference.
|
||||||
return r.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) {
|
return r.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) {
|
||||||
// Log request.
|
// Log request.
|
||||||
log.Debugf("[recv] %s %s %s [%s]", req.Method, s.url, req.URL.Path, req.RemoteAddr)
|
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.
|
// Execute handler function and return error if necessary.
|
||||||
if err := f(w, req); err != nil {
|
if err := f(w, req); err != nil {
|
||||||
if etcdErr, ok := err.(*etcdErr.Error); ok {
|
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.
|
// OriginAllowed determines whether the server will allow a given CORS origin.
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determines whether the server will allow a given CORS origin.
|
|
||||||
func (s *Server) OriginAllowed(origin string) bool {
|
func (s *Server) OriginAllowed(origin string) bool {
|
||||||
return s.corsOrigins["*"] || s.corsOrigins[origin]
|
return s.corsHandler.OriginAllowed(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.
|
// Handler to return the current version of etcd.
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ func (info TLSInfo) Config() (TLSConfig, error) {
|
|||||||
|
|
||||||
// Both the key and cert must be present.
|
// Both the key and cert must be present.
|
||||||
if info.KeyFile == "" || info.CertFile == "" {
|
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)
|
tlsCert, err := tls.LoadX509KeyPair(info.CertFile, info.KeyFile)
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Timeout for setup internal raft http connection
|
// Timeout for setup internal raft http connection
|
||||||
|
57
server/usage.go
Normal file
57
server/usage.go
Normal file
@ -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)
|
||||||
|
}
|
@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
etcdErr "github.com/coreos/etcd/error"
|
etcdErr "github.com/coreos/etcd/error"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ package v1
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
etcdErr "github.com/coreos/etcd/error"
|
etcdErr "github.com/coreos/etcd/error"
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
etcdErr "github.com/coreos/etcd/error"
|
etcdErr "github.com/coreos/etcd/error"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ package v2
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A lookup of factories by version.
|
// A lookup of factories by version.
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -3,7 +3,7 @@ package v2
|
|||||||
import (
|
import (
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -3,7 +3,7 @@ package v2
|
|||||||
import (
|
import (
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
"github.com/coreos/go-raft"
|
"github.com/coreos/raft"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ func TestMultiNodeKillAllAndRecovery(t *testing.T) {
|
|||||||
t.Fatalf("Recovery error: %s", err)
|
t.Fatalf("Recovery error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.Index != 16 {
|
if result.ModifiedIndex != 16 {
|
||||||
t.Fatalf("recovery failed! [%d/16]", result.Index)
|
t.Fatalf("recovery failed! [%d/16]", result.ModifiedIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,13 +35,13 @@ func TestRemoveNode(t *testing.T) {
|
|||||||
fmt.Println("send remove to node3 and wait for its exiting")
|
fmt.Println("send remove to node3 and wait for its exiting")
|
||||||
etcds[2].Wait()
|
etcds[2].Wait()
|
||||||
|
|
||||||
resp, err := c.Get("_etcd/machines")
|
resp, err := c.Get("_etcd/machines", false)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(resp) != 2 {
|
if len(resp.Kvs) != 2 {
|
||||||
t.Fatal("cannot remove peer")
|
t.Fatal("cannot remove peer")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,14 +59,14 @@ func TestRemoveNode(t *testing.T) {
|
|||||||
|
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
resp, err = c.Get("_etcd/machines")
|
resp, err = c.Get("_etcd/machines", false)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(resp) != 3 {
|
if len(resp.Kvs) != 3 {
|
||||||
t.Fatalf("add peer fails #1 (%d != 3)", len(resp))
|
t.Fatalf("add peer fails #1 (%d != 3)", len(resp.Kvs))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,13 +78,13 @@ func TestRemoveNode(t *testing.T) {
|
|||||||
|
|
||||||
client.Do(rmReq)
|
client.Do(rmReq)
|
||||||
|
|
||||||
resp, err := c.Get("_etcd/machines")
|
resp, err := c.Get("_etcd/machines", false)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(resp) != 2 {
|
if len(resp.Kvs) != 2 {
|
||||||
t.Fatal("cannot remove peer")
|
t.Fatal("cannot remove peer")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,14 +102,14 @@ func TestRemoveNode(t *testing.T) {
|
|||||||
|
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
resp, err = c.Get("_etcd/machines")
|
resp, err = c.Get("_etcd/machines", false)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(resp) != 3 {
|
if len(resp.Kvs) != 3 {
|
||||||
t.Fatalf("add peer fails #2 (%d != 3)", len(resp))
|
t.Fatalf("add peer fails #2 (%d != 3)", len(resp.Kvs))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,14 +50,12 @@ func TestSingleNodeRecovery(t *testing.T) {
|
|||||||
|
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
results, err := c.Get("foo")
|
result, err = c.Get("foo", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("get fail: " + err.Error())
|
t.Fatal("get fail: " + err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
result = results[0]
|
|
||||||
|
|
||||||
if err != nil || result.Key != "/foo" || result.Value != "bar" || result.TTL > 99 {
|
if err != nil || result.Key != "/foo" || result.Value != "bar" || result.TTL > 99 {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -51,9 +51,9 @@ func TestSingleNode(t *testing.T) {
|
|||||||
// Add a test-and-set test
|
// Add a test-and-set test
|
||||||
|
|
||||||
// First, we'll test we can change the value if we get it write
|
// 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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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
|
// 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 {
|
if err == nil {
|
||||||
t.Fatalf("Set 4 expecting error when setting key with incorrect previous value")
|
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")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ func CreateCluster(size int, procAttr *os.ProcAttr, ssl bool) ([][]string, []*os
|
|||||||
|
|
||||||
sslServer2 := []string{"-peer-ca-file=../../fixtures/ca/ca.crt",
|
sslServer2 := []string{"-peer-ca-file=../../fixtures/ca/ca.crt",
|
||||||
"-peer-cert-file=../../fixtures/ca/server2.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++ {
|
for i := 0; i < size; i++ {
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build darwin freebsd linux netbsd openbsd
|
|
||||||
|
|
||||||
package ipv4_test
|
package ipv4_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -75,6 +73,10 @@ func writeThenReadDatagram(t *testing.T, i int, c *ipv4.RawConn, wb []byte, src,
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isUnicast(ip net.IP) bool {
|
||||||
|
return ip.To4() != nil && (ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsGlobalUnicast())
|
||||||
|
}
|
||||||
|
|
||||||
// LoopbackInterface returns a logical network interface for loopback
|
// LoopbackInterface returns a logical network interface for loopback
|
||||||
// tests.
|
// tests.
|
||||||
func loopbackInterface() *net.Interface {
|
func loopbackInterface() *net.Interface {
|
||||||
@ -83,9 +85,25 @@ func loopbackInterface() *net.Interface {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for _, ifi := range ift {
|
for _, ifi := range ift {
|
||||||
if ifi.Flags&net.FlagLoopback != 0 {
|
if ifi.Flags&net.FlagLoopback == 0 || ifi.Flags&net.FlagUp == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ifat, err := ifi.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, ifa := range ifat {
|
||||||
|
switch ifa := ifa.(type) {
|
||||||
|
case *net.IPAddr:
|
||||||
|
if isUnicast(ifa.IP) {
|
||||||
return &ifi
|
return &ifi
|
||||||
}
|
}
|
||||||
|
case *net.IPNet:
|
||||||
|
if isUnicast(ifa.IP) {
|
||||||
|
return &ifi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -94,31 +112,24 @@ func loopbackInterface() *net.Interface {
|
|||||||
// enabled network interface. It also returns a unicast IPv4 address
|
// enabled network interface. It also returns a unicast IPv4 address
|
||||||
// that can be used for listening on ifi.
|
// that can be used for listening on ifi.
|
||||||
func isMulticastAvailable(ifi *net.Interface) (net.IP, bool) {
|
func isMulticastAvailable(ifi *net.Interface) (net.IP, bool) {
|
||||||
if ifi.Flags&net.FlagUp == 0 || ifi.Flags&net.FlagMulticast == 0 {
|
if ifi == nil || ifi.Flags&net.FlagUp == 0 || ifi.Flags&net.FlagMulticast == 0 {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
ifat, err := ifi.Addrs()
|
ifat, err := ifi.Addrs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
if len(ifat) == 0 {
|
for _, ifa := range ifat {
|
||||||
|
switch ifa := ifa.(type) {
|
||||||
|
case *net.IPAddr:
|
||||||
|
if isUnicast(ifa.IP) {
|
||||||
|
return ifa.IP, true
|
||||||
|
}
|
||||||
|
case *net.IPNet:
|
||||||
|
if isUnicast(ifa.IP) {
|
||||||
|
return ifa.IP, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
var ip net.IP
|
|
||||||
for _, ifa := range ifat {
|
|
||||||
switch v := ifa.(type) {
|
|
||||||
case *net.IPAddr:
|
|
||||||
ip = v.IP
|
|
||||||
case *net.IPNet:
|
|
||||||
ip = v.IP
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ip.To4() == nil {
|
|
||||||
ip = nil
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return ip, true
|
|
||||||
}
|
|
||||||
|
@ -2,41 +2,41 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build darwin freebsd linux netbsd openbsd
|
|
||||||
|
|
||||||
package ipv4_test
|
package ipv4_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.google.com/p/go.net/ipv4"
|
"code.google.com/p/go.net/ipv4"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var udpMultipleGroupListenerTests = []struct {
|
var udpMultipleGroupListenerTests = []net.Addr{
|
||||||
gaddr *net.UDPAddr
|
&net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}, // see RFC 4727
|
||||||
}{
|
&net.UDPAddr{IP: net.IPv4(224, 0, 0, 250)},
|
||||||
{&net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}}, // see RFC 4727
|
&net.UDPAddr{IP: net.IPv4(224, 0, 0, 254)},
|
||||||
{&net.UDPAddr{IP: net.IPv4(224, 0, 0, 250)}}, // see RFC 4727
|
|
||||||
{&net.UDPAddr{IP: net.IPv4(224, 0, 0, 254)}}, // see RFC 4727
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUDPSingleConnWithMultipleGroupListeners(t *testing.T) {
|
func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "plan9", "windows":
|
||||||
|
t.Skipf("not supported on %q", runtime.GOOS)
|
||||||
|
}
|
||||||
if testing.Short() || !*testExternal {
|
if testing.Short() || !*testExternal {
|
||||||
t.Skip("to avoid external network")
|
t.Skip("to avoid external network")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range udpMultipleGroupListenerTests {
|
for _, gaddr := range udpMultipleGroupListenerTests {
|
||||||
// listen to a wildcard address with no reusable port
|
c, err := net.ListenPacket("udp4", "0.0.0.0:0") // wildcard address with no reusable port
|
||||||
c, err := net.ListenPacket("udp4", "0.0.0.0:0")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("net.ListenPacket failed: %v", err)
|
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
p := ipv4.NewPacketConn(c)
|
p := ipv4.NewPacketConn(c)
|
||||||
|
|
||||||
var mift []*net.Interface
|
var mift []*net.Interface
|
||||||
|
|
||||||
ift, err := net.Interfaces()
|
ift, err := net.Interfaces()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("net.Interfaces failed: %v", err)
|
t.Fatalf("net.Interfaces failed: %v", err)
|
||||||
@ -45,34 +45,36 @@ func TestUDPSingleConnWithMultipleGroupListeners(t *testing.T) {
|
|||||||
if _, ok := isMulticastAvailable(&ifi); !ok {
|
if _, ok := isMulticastAvailable(&ifi); !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := p.JoinGroup(&ifi, tt.gaddr); err != nil {
|
if err := p.JoinGroup(&ifi, gaddr); err != nil {
|
||||||
t.Fatalf("ipv4.PacketConn.JoinGroup %v on %v failed: %v", tt.gaddr, ifi, err)
|
t.Fatalf("ipv4.PacketConn.JoinGroup %v on %v failed: %v", gaddr, ifi, err)
|
||||||
}
|
}
|
||||||
mift = append(mift, &ift[i])
|
mift = append(mift, &ift[i])
|
||||||
}
|
}
|
||||||
for _, ifi := range mift {
|
for _, ifi := range mift {
|
||||||
if err := p.LeaveGroup(ifi, tt.gaddr); err != nil {
|
if err := p.LeaveGroup(ifi, gaddr); err != nil {
|
||||||
t.Fatalf("ipv4.PacketConn.LeaveGroup %v on %v failed: %v", tt.gaddr, ifi, err)
|
t.Fatalf("ipv4.PacketConn.LeaveGroup %v on %v failed: %v", gaddr, ifi, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUDPMultipleConnWithMultipleGroupListeners(t *testing.T) {
|
func TestUDPMultiplePacketConnWithMultipleGroupListeners(t *testing.T) {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "plan9", "windows":
|
||||||
|
t.Skipf("not supported on %q", runtime.GOOS)
|
||||||
|
}
|
||||||
if testing.Short() || !*testExternal {
|
if testing.Short() || !*testExternal {
|
||||||
t.Skip("to avoid external network")
|
t.Skip("to avoid external network")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range udpMultipleGroupListenerTests {
|
for _, gaddr := range udpMultipleGroupListenerTests {
|
||||||
// listen to a group address, actually a wildcard address
|
c1, err := net.ListenPacket("udp4", "224.0.0.0:1024") // wildcard address with reusable port
|
||||||
// with reusable port
|
|
||||||
c1, err := net.ListenPacket("udp4", "224.0.0.0:1024") // see RFC 4727
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("net.ListenPacket failed: %v", err)
|
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||||
}
|
}
|
||||||
defer c1.Close()
|
defer c1.Close()
|
||||||
|
|
||||||
c2, err := net.ListenPacket("udp4", "224.0.0.0:1024") // see RFC 4727
|
c2, err := net.ListenPacket("udp4", "224.0.0.0:1024") // wildcard address with reusable port
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("net.ListenPacket failed: %v", err)
|
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||||
}
|
}
|
||||||
@ -81,8 +83,8 @@ func TestUDPMultipleConnWithMultipleGroupListeners(t *testing.T) {
|
|||||||
var ps [2]*ipv4.PacketConn
|
var ps [2]*ipv4.PacketConn
|
||||||
ps[0] = ipv4.NewPacketConn(c1)
|
ps[0] = ipv4.NewPacketConn(c1)
|
||||||
ps[1] = ipv4.NewPacketConn(c2)
|
ps[1] = ipv4.NewPacketConn(c2)
|
||||||
|
|
||||||
var mift []*net.Interface
|
var mift []*net.Interface
|
||||||
|
|
||||||
ift, err := net.Interfaces()
|
ift, err := net.Interfaces()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("net.Interfaces failed: %v", err)
|
t.Fatalf("net.Interfaces failed: %v", err)
|
||||||
@ -92,70 +94,32 @@ func TestUDPMultipleConnWithMultipleGroupListeners(t *testing.T) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, p := range ps {
|
for _, p := range ps {
|
||||||
if err := p.JoinGroup(&ifi, tt.gaddr); err != nil {
|
if err := p.JoinGroup(&ifi, gaddr); err != nil {
|
||||||
t.Fatalf("ipv4.PacketConn.JoinGroup %v on %v failed: %v", tt.gaddr, ifi, err)
|
t.Fatalf("ipv4.PacketConn.JoinGroup %v on %v failed: %v", gaddr, ifi, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mift = append(mift, &ift[i])
|
mift = append(mift, &ift[i])
|
||||||
}
|
}
|
||||||
for _, ifi := range mift {
|
for _, ifi := range mift {
|
||||||
for _, p := range ps {
|
for _, p := range ps {
|
||||||
if err := p.LeaveGroup(ifi, tt.gaddr); err != nil {
|
if err := p.LeaveGroup(ifi, gaddr); err != nil {
|
||||||
t.Fatalf("ipv4.PacketConn.LeaveGroup %v on %v failed: %v", tt.gaddr, ifi, err)
|
t.Fatalf("ipv4.PacketConn.LeaveGroup %v on %v failed: %v", gaddr, ifi, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIPSingleConnWithSingleGroupListener(t *testing.T) {
|
func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
|
||||||
if testing.Short() || !*testExternal {
|
switch runtime.GOOS {
|
||||||
t.Skip("to avoid external network")
|
case "plan9", "windows":
|
||||||
|
t.Skipf("not supported on %q", runtime.GOOS)
|
||||||
}
|
}
|
||||||
if os.Getuid() != 0 {
|
|
||||||
t.Skip("must be root")
|
|
||||||
}
|
|
||||||
|
|
||||||
// listen to a wildcard address
|
|
||||||
c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("net.ListenPacket failed: %v", err)
|
|
||||||
}
|
|
||||||
defer c.Close()
|
|
||||||
|
|
||||||
r, err := ipv4.NewRawConn(c)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("ipv4.RawConn failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
gaddr := &net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
|
|
||||||
var mift []*net.Interface
|
|
||||||
ift, err := net.Interfaces()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("net.Interfaces failed: %v", err)
|
|
||||||
}
|
|
||||||
for i, ifi := range ift {
|
|
||||||
if _, ok := isMulticastAvailable(&ifi); !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := r.JoinGroup(&ifi, gaddr); err != nil {
|
|
||||||
t.Fatalf("ipv4.RawConn.JoinGroup on %v failed: %v", ifi, err)
|
|
||||||
}
|
|
||||||
mift = append(mift, &ift[i])
|
|
||||||
}
|
|
||||||
for _, ifi := range mift {
|
|
||||||
if err := r.LeaveGroup(ifi, gaddr); err != nil {
|
|
||||||
t.Fatalf("ipv4.RawConn.LeaveGroup on %v failed: %v", ifi, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUDPPerInterfaceSingleConnWithSingleGroupListener(t *testing.T) {
|
|
||||||
if testing.Short() || !*testExternal {
|
if testing.Short() || !*testExternal {
|
||||||
t.Skip("to avoid external network")
|
t.Skip("to avoid external network")
|
||||||
}
|
}
|
||||||
|
|
||||||
gaddr := &net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
|
gaddr := net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
|
||||||
type ml struct {
|
type ml struct {
|
||||||
c *ipv4.PacketConn
|
c *ipv4.PacketConn
|
||||||
ifi *net.Interface
|
ifi *net.Interface
|
||||||
@ -171,26 +135,29 @@ func TestUDPPerInterfaceSingleConnWithSingleGroupListener(t *testing.T) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// listen to a unicast interface address
|
c, err := net.ListenPacket("udp4", ip.String()+":"+"1024") // unicast address with non-reusable port
|
||||||
c, err := net.ListenPacket("udp4", ip.String()+":"+"1024") // see RFC 4727
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("net.ListenPacket with %v failed: %v", ip, err)
|
t.Fatalf("net.ListenPacket with %v failed: %v", ip, err)
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
p := ipv4.NewPacketConn(c)
|
p := ipv4.NewPacketConn(c)
|
||||||
if err := p.JoinGroup(&ifi, gaddr); err != nil {
|
if err := p.JoinGroup(&ifi, &gaddr); err != nil {
|
||||||
t.Fatalf("ipv4.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
t.Fatalf("ipv4.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
||||||
}
|
}
|
||||||
mlt = append(mlt, &ml{p, &ift[i]})
|
mlt = append(mlt, &ml{p, &ift[i]})
|
||||||
}
|
}
|
||||||
for _, m := range mlt {
|
for _, m := range mlt {
|
||||||
if err := m.c.LeaveGroup(m.ifi, gaddr); err != nil {
|
if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil {
|
||||||
t.Fatalf("ipv4.PacketConn.LeaveGroup on %v failed: %v", m.ifi, err)
|
t.Fatalf("ipv4.PacketConn.LeaveGroup on %v failed: %v", m.ifi, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIPPerInterfaceSingleConnWithSingleGroupListener(t *testing.T) {
|
func TestIPSingleRawConnWithSingleGroupListener(t *testing.T) {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "plan9", "windows":
|
||||||
|
t.Skipf("not supported on %q", runtime.GOOS)
|
||||||
|
}
|
||||||
if testing.Short() || !*testExternal {
|
if testing.Short() || !*testExternal {
|
||||||
t.Skip("to avoid external network")
|
t.Skip("to avoid external network")
|
||||||
}
|
}
|
||||||
@ -198,7 +165,52 @@ func TestIPPerInterfaceSingleConnWithSingleGroupListener(t *testing.T) {
|
|||||||
t.Skip("must be root")
|
t.Skip("must be root")
|
||||||
}
|
}
|
||||||
|
|
||||||
gaddr := &net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
|
c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") // wildcard address
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
r, err := ipv4.NewRawConn(c)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ipv4.RawConn failed: %v", err)
|
||||||
|
}
|
||||||
|
gaddr := net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
|
||||||
|
var mift []*net.Interface
|
||||||
|
|
||||||
|
ift, err := net.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("net.Interfaces failed: %v", err)
|
||||||
|
}
|
||||||
|
for i, ifi := range ift {
|
||||||
|
if _, ok := isMulticastAvailable(&ifi); !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := r.JoinGroup(&ifi, &gaddr); err != nil {
|
||||||
|
t.Fatalf("ipv4.RawConn.JoinGroup on %v failed: %v", ifi, err)
|
||||||
|
}
|
||||||
|
mift = append(mift, &ift[i])
|
||||||
|
}
|
||||||
|
for _, ifi := range mift {
|
||||||
|
if err := r.LeaveGroup(ifi, &gaddr); err != nil {
|
||||||
|
t.Fatalf("ipv4.RawConn.LeaveGroup on %v failed: %v", ifi, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIPPerInterfaceSingleRawConnWithSingleGroupListener(t *testing.T) {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "plan9", "windows":
|
||||||
|
t.Skipf("not supported on %q", runtime.GOOS)
|
||||||
|
}
|
||||||
|
if testing.Short() || !*testExternal {
|
||||||
|
t.Skip("to avoid external network")
|
||||||
|
}
|
||||||
|
if os.Getuid() != 0 {
|
||||||
|
t.Skip("must be root")
|
||||||
|
}
|
||||||
|
|
||||||
|
gaddr := net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
|
||||||
type ml struct {
|
type ml struct {
|
||||||
c *ipv4.RawConn
|
c *ipv4.RawConn
|
||||||
ifi *net.Interface
|
ifi *net.Interface
|
||||||
@ -214,8 +226,7 @@ func TestIPPerInterfaceSingleConnWithSingleGroupListener(t *testing.T) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// listen to a unicast interface address
|
c, err := net.ListenPacket("ip4:253", ip.String()) // unicast address
|
||||||
c, err := net.ListenPacket("ip4:253", ip.String()) // see RFC 4727
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("net.ListenPacket with %v failed: %v", ip, err)
|
t.Fatalf("net.ListenPacket with %v failed: %v", ip, err)
|
||||||
}
|
}
|
||||||
@ -224,13 +235,13 @@ func TestIPPerInterfaceSingleConnWithSingleGroupListener(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("ipv4.NewRawConn failed: %v", err)
|
t.Fatalf("ipv4.NewRawConn failed: %v", err)
|
||||||
}
|
}
|
||||||
if err := r.JoinGroup(&ifi, gaddr); err != nil {
|
if err := r.JoinGroup(&ifi, &gaddr); err != nil {
|
||||||
t.Fatalf("ipv4.RawConn.JoinGroup on %v failed: %v", ifi, err)
|
t.Fatalf("ipv4.RawConn.JoinGroup on %v failed: %v", ifi, err)
|
||||||
}
|
}
|
||||||
mlt = append(mlt, &ml{r, &ift[i]})
|
mlt = append(mlt, &ml{r, &ift[i]})
|
||||||
}
|
}
|
||||||
for _, m := range mlt {
|
for _, m := range mlt {
|
||||||
if err := m.c.LeaveGroup(m.ifi, gaddr); err != nil {
|
if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil {
|
||||||
t.Fatalf("ipv4.RawConn.LeaveGroup on %v failed: %v", m.ifi, err)
|
t.Fatalf("ipv4.RawConn.LeaveGroup on %v failed: %v", m.ifi, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ func slicePacket(b []byte) (h, p []byte, err error) {
|
|||||||
// Checksum = platform sets an appropriate value if Checksum is zero
|
// Checksum = platform sets an appropriate value if Checksum is zero
|
||||||
// Src = platform sets an appropriate value if Src is nil
|
// Src = platform sets an appropriate value if Src is nil
|
||||||
// Dst = <must be specified>
|
// Dst = <must be specified>
|
||||||
// h.Options = optional
|
// Options = optional
|
||||||
func (c *packetHandler) WriteTo(h *Header, p []byte, cm *ControlMessage) error {
|
func (c *packetHandler) WriteTo(h *Header, p []byte, cm *ControlMessage) error {
|
||||||
if !c.ok() {
|
if !c.ok() {
|
||||||
return syscall.EINVAL
|
return syscall.EINVAL
|
||||||
|
@ -21,26 +21,27 @@ func (typ ICMPType) String() string {
|
|||||||
// packets.
|
// packets.
|
||||||
type ICMPFilter struct {
|
type ICMPFilter struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
rawICMPFilter
|
sysICMPFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set sets the ICMP type and filter action to the filter.
|
// Set sets the ICMP type and filter action to the filter.
|
||||||
func (f *ICMPFilter) Set(typ ICMPType, block bool) {
|
func (f *ICMPFilter) Set(typ ICMPType, block bool) {
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
|
||||||
f.set(typ, block)
|
f.set(typ, block)
|
||||||
|
f.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetAll sets the filter action to the filter.
|
// SetAll sets the filter action to the filter.
|
||||||
func (f *ICMPFilter) SetAll(block bool) {
|
func (f *ICMPFilter) SetAll(block bool) {
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
|
||||||
f.setAll(block)
|
f.setAll(block)
|
||||||
|
f.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// WillBlock reports whether the ICMP type will be blocked.
|
// WillBlock reports whether the ICMP type will be blocked.
|
||||||
func (f *ICMPFilter) WillBlock(typ ICMPType) bool {
|
func (f *ICMPFilter) WillBlock(typ ICMPType) bool {
|
||||||
f.mu.RLock()
|
f.mu.RLock()
|
||||||
defer f.mu.RUnlock()
|
ok := f.willBlock(typ)
|
||||||
return f.willBlock(typ)
|
f.mu.RUnlock()
|
||||||
|
return ok
|
||||||
}
|
}
|
||||||
|
@ -6,13 +6,11 @@
|
|||||||
|
|
||||||
package ipv6
|
package ipv6
|
||||||
|
|
||||||
import "syscall"
|
type sysICMPFilter struct {
|
||||||
|
Filt [8]uint32
|
||||||
type rawICMPFilter struct {
|
|
||||||
syscall.ICMPv6Filter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *rawICMPFilter) set(typ ICMPType, block bool) {
|
func (f *sysICMPFilter) set(typ ICMPType, block bool) {
|
||||||
if block {
|
if block {
|
||||||
f.Filt[typ>>5] &^= 1 << (uint32(typ) & 31)
|
f.Filt[typ>>5] &^= 1 << (uint32(typ) & 31)
|
||||||
} else {
|
} else {
|
||||||
@ -20,7 +18,7 @@ func (f *rawICMPFilter) set(typ ICMPType, block bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *rawICMPFilter) setAll(block bool) {
|
func (f *sysICMPFilter) setAll(block bool) {
|
||||||
for i := range f.Filt {
|
for i := range f.Filt {
|
||||||
if block {
|
if block {
|
||||||
f.Filt[i] = 0
|
f.Filt[i] = 0
|
||||||
@ -30,6 +28,6 @@ func (f *rawICMPFilter) setAll(block bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *rawICMPFilter) willBlock(typ ICMPType) bool {
|
func (f *sysICMPFilter) willBlock(typ ICMPType) bool {
|
||||||
return f.Filt[typ>>5]&(1<<(uint32(typ)&31)) == 0
|
return f.Filt[typ>>5]&(1<<(uint32(typ)&31)) == 0
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,11 @@
|
|||||||
|
|
||||||
package ipv6
|
package ipv6
|
||||||
|
|
||||||
import "syscall"
|
type sysICMPFilter struct {
|
||||||
|
Data [8]uint32
|
||||||
type rawICMPFilter struct {
|
|
||||||
syscall.ICMPv6Filter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *rawICMPFilter) set(typ ICMPType, block bool) {
|
func (f *sysICMPFilter) set(typ ICMPType, block bool) {
|
||||||
if block {
|
if block {
|
||||||
f.Data[typ>>5] |= 1 << (uint32(typ) & 31)
|
f.Data[typ>>5] |= 1 << (uint32(typ) & 31)
|
||||||
} else {
|
} else {
|
||||||
@ -18,7 +16,7 @@ func (f *rawICMPFilter) set(typ ICMPType, block bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *rawICMPFilter) setAll(block bool) {
|
func (f *sysICMPFilter) setAll(block bool) {
|
||||||
for i := range f.Data {
|
for i := range f.Data {
|
||||||
if block {
|
if block {
|
||||||
f.Data[i] = 1<<32 - 1
|
f.Data[i] = 1<<32 - 1
|
||||||
@ -28,6 +26,6 @@ func (f *rawICMPFilter) setAll(block bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *rawICMPFilter) willBlock(typ ICMPType) bool {
|
func (f *sysICMPFilter) willBlock(typ ICMPType) bool {
|
||||||
return f.Data[typ>>5]&(1<<(uint32(typ)&31)) != 0
|
return f.Data[typ>>5]&(1<<(uint32(typ)&31)) != 0
|
||||||
}
|
}
|
||||||
|
@ -4,19 +4,19 @@
|
|||||||
|
|
||||||
package ipv6
|
package ipv6
|
||||||
|
|
||||||
type rawICMPFilter struct {
|
type sysICMPFilter struct {
|
||||||
// TODO(mikio): Implement this
|
// TODO(mikio): Implement this
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *rawICMPFilter) set(typ ICMPType, block bool) {
|
func (f *sysICMPFilter) set(typ ICMPType, block bool) {
|
||||||
// TODO(mikio): Implement this
|
// TODO(mikio): Implement this
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *rawICMPFilter) setAll(block bool) {
|
func (f *sysICMPFilter) setAll(block bool) {
|
||||||
// TODO(mikio): Implement this
|
// TODO(mikio): Implement this
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *rawICMPFilter) willBlock(typ ICMPType) bool {
|
func (f *sysICMPFilter) willBlock(typ ICMPType) bool {
|
||||||
// TODO(mikio): Implement this
|
// TODO(mikio): Implement this
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -4,19 +4,19 @@
|
|||||||
|
|
||||||
package ipv6
|
package ipv6
|
||||||
|
|
||||||
type rawICMPFilter struct {
|
type sysICMPFilter struct {
|
||||||
// TODO(mikio): Implement this
|
// TODO(mikio): Implement this
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *rawICMPFilter) set(typ ICMPType, block bool) {
|
func (f *sysICMPFilter) set(typ ICMPType, block bool) {
|
||||||
// TODO(mikio): Implement this
|
// TODO(mikio): Implement this
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *rawICMPFilter) setAll(block bool) {
|
func (f *sysICMPFilter) setAll(block bool) {
|
||||||
// TODO(mikio): Implement this
|
// TODO(mikio): Implement this
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *rawICMPFilter) willBlock(typ ICMPType) bool {
|
func (f *sysICMPFilter) willBlock(typ ICMPType) bool {
|
||||||
// TODO(mikio): Implement this
|
// TODO(mikio): Implement this
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,22 @@ package ipv6_test
|
|||||||
import (
|
import (
|
||||||
"code.google.com/p/go.net/ipv6"
|
"code.google.com/p/go.net/ipv6"
|
||||||
"errors"
|
"errors"
|
||||||
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ipv6PseudoHeaderLen = 2*net.IPv6len + 8
|
||||||
|
ianaProtocolIPv6ICMP = 58
|
||||||
|
)
|
||||||
|
|
||||||
|
func ipv6PseudoHeader(src, dst net.IP, nextHeader int) []byte {
|
||||||
|
b := make([]byte, ipv6PseudoHeaderLen)
|
||||||
|
copy(b[:net.IPv6len], src)
|
||||||
|
copy(b[net.IPv6len:], dst)
|
||||||
|
b[len(b)-1] = byte(nextHeader)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
// icmpMessage represents an ICMP message.
|
// icmpMessage represents an ICMP message.
|
||||||
type icmpMessage struct {
|
type icmpMessage struct {
|
||||||
Type ipv6.ICMPType // type
|
Type ipv6.ICMPType // type
|
||||||
@ -25,8 +39,11 @@ type icmpMessageBody interface {
|
|||||||
|
|
||||||
// Marshal returns the binary enconding of the ICMP echo request or
|
// Marshal returns the binary enconding of the ICMP echo request or
|
||||||
// reply message m.
|
// reply message m.
|
||||||
func (m *icmpMessage) Marshal() ([]byte, error) {
|
func (m *icmpMessage) Marshal(psh []byte) ([]byte, error) {
|
||||||
b := []byte{byte(m.Type), byte(m.Code), 0, 0}
|
b := []byte{byte(m.Type), byte(m.Code), 0, 0}
|
||||||
|
if psh != nil {
|
||||||
|
b = append(psh, b...)
|
||||||
|
}
|
||||||
if m.Body != nil && m.Body.Len() != 0 {
|
if m.Body != nil && m.Body.Len() != 0 {
|
||||||
mb, err := m.Body.Marshal()
|
mb, err := m.Body.Marshal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -34,10 +51,11 @@ func (m *icmpMessage) Marshal() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
b = append(b, mb...)
|
b = append(b, mb...)
|
||||||
}
|
}
|
||||||
switch m.Type {
|
if psh == nil {
|
||||||
case ipv6.ICMPTypeEchoRequest, ipv6.ICMPTypeEchoReply:
|
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
off, l := 2*net.IPv6len, len(b)-len(psh)
|
||||||
|
b[off], b[off+1], b[off+2], b[off+3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
|
||||||
csumcv := len(b) - 1 // checksum coverage
|
csumcv := len(b) - 1 // checksum coverage
|
||||||
s := uint32(0)
|
s := uint32(0)
|
||||||
for i := 0; i < csumcv; i += 2 {
|
for i := 0; i < csumcv; i += 2 {
|
||||||
@ -50,9 +68,9 @@ func (m *icmpMessage) Marshal() ([]byte, error) {
|
|||||||
s = s + s>>16
|
s = s + s>>16
|
||||||
// Place checksum back in header; using ^= avoids the
|
// Place checksum back in header; using ^= avoids the
|
||||||
// assumption the checksum bytes are zero.
|
// assumption the checksum bytes are zero.
|
||||||
b[2] ^= byte(^s)
|
b[len(psh)+2] ^= byte(^s)
|
||||||
b[3] ^= byte(^s >> 8)
|
b[len(psh)+3] ^= byte(^s >> 8)
|
||||||
return b, nil
|
return b[len(psh):], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseICMPMessage parses b as an ICMP message.
|
// parseICMPMessage parses b as an ICMP message.
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
|
func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
|
||||||
@ -44,15 +45,22 @@ func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
p := ipv6.NewPacketConn(c)
|
p := ipv6.NewPacketConn(c)
|
||||||
|
defer p.Close()
|
||||||
if err := p.JoinGroup(ifi, dst); err != nil {
|
if err := p.JoinGroup(ifi, dst); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
||||||
}
|
}
|
||||||
if err := p.SetMulticastInterface(ifi); err != nil {
|
if err := p.SetMulticastInterface(ifi); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.SetMulticastInterface failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.SetMulticastInterface failed: %v", err)
|
||||||
}
|
}
|
||||||
|
if _, err := p.MulticastInterface(); err != nil {
|
||||||
|
t.Fatalf("ipv6.PacketConn.MulticastInterface failed: %v", err)
|
||||||
|
}
|
||||||
if err := p.SetMulticastLoopback(true); err != nil {
|
if err := p.SetMulticastLoopback(true); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.SetMulticastLoopback failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.SetMulticastLoopback failed: %v", err)
|
||||||
}
|
}
|
||||||
|
if _, err := p.MulticastLoopback(); err != nil {
|
||||||
|
t.Fatalf("ipv6.PacketConn.MulticastLoopback failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
cm := ipv6.ControlMessage{
|
cm := ipv6.ControlMessage{
|
||||||
TrafficClass: DiffServAF11 | CongestionExperienced,
|
TrafficClass: DiffServAF11 | CongestionExperienced,
|
||||||
@ -64,6 +72,9 @@ func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
|
|||||||
if err := p.SetControlMessage(cf, toggle); err != nil {
|
if err := p.SetControlMessage(cf, toggle); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
|
||||||
}
|
}
|
||||||
|
if err := p.SetDeadline(time.Now().Add(time.Millisecond * 200)); err != nil {
|
||||||
|
t.Fatalf("ipv6.PacketConn.SetDeadline failed: %v", err)
|
||||||
|
}
|
||||||
cm.HopLimit = i + 1
|
cm.HopLimit = i + 1
|
||||||
if _, err := p.WriteTo([]byte("HELLO-R-U-THERE"), &cm, dst); err != nil {
|
if _, err := p.WriteTo([]byte("HELLO-R-U-THERE"), &cm, dst); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
|
||||||
@ -104,16 +115,24 @@ func TestPacketConnReadWriteMulticastICMP(t *testing.T) {
|
|||||||
t.Fatalf("net.ResolveIPAddr failed: %v", err)
|
t.Fatalf("net.ResolveIPAddr failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pshicmp := ipv6PseudoHeader(c.LocalAddr().(*net.IPAddr).IP, dst.IP, ianaProtocolIPv6ICMP)
|
||||||
p := ipv6.NewPacketConn(c)
|
p := ipv6.NewPacketConn(c)
|
||||||
|
defer p.Close()
|
||||||
if err := p.JoinGroup(ifi, dst); err != nil {
|
if err := p.JoinGroup(ifi, dst); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
||||||
}
|
}
|
||||||
if err := p.SetMulticastInterface(ifi); err != nil {
|
if err := p.SetMulticastInterface(ifi); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.SetMulticastInterface failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.SetMulticastInterface failed: %v", err)
|
||||||
}
|
}
|
||||||
|
if _, err := p.MulticastInterface(); err != nil {
|
||||||
|
t.Fatalf("ipv6.PacketConn.MulticastInterface failed: %v", err)
|
||||||
|
}
|
||||||
if err := p.SetMulticastLoopback(true); err != nil {
|
if err := p.SetMulticastLoopback(true); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.SetMulticastLoopback failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.SetMulticastLoopback failed: %v", err)
|
||||||
}
|
}
|
||||||
|
if _, err := p.MulticastLoopback(); err != nil {
|
||||||
|
t.Fatalf("ipv6.PacketConn.MulticastLoopback failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
cm := ipv6.ControlMessage{
|
cm := ipv6.ControlMessage{
|
||||||
TrafficClass: DiffServAF11 | CongestionExperienced,
|
TrafficClass: DiffServAF11 | CongestionExperienced,
|
||||||
@ -128,20 +147,35 @@ func TestPacketConnReadWriteMulticastICMP(t *testing.T) {
|
|||||||
t.Fatalf("ipv6.PacketConn.SetICMPFilter failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.SetICMPFilter failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var psh []byte
|
||||||
for i, toggle := range []bool{true, false, true} {
|
for i, toggle := range []bool{true, false, true} {
|
||||||
|
if toggle {
|
||||||
|
psh = nil
|
||||||
|
if err := p.SetChecksum(true, 2); err != nil {
|
||||||
|
t.Fatalf("ipv6.PacketConn.SetChecksum failed: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
psh = pshicmp
|
||||||
|
// Some platforms never allow to disable the
|
||||||
|
// kernel checksum processing.
|
||||||
|
p.SetChecksum(false, -1)
|
||||||
|
}
|
||||||
wb, err := (&icmpMessage{
|
wb, err := (&icmpMessage{
|
||||||
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
|
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
|
||||||
Body: &icmpEcho{
|
Body: &icmpEcho{
|
||||||
ID: os.Getpid() & 0xffff, Seq: i + 1,
|
ID: os.Getpid() & 0xffff, Seq: i + 1,
|
||||||
Data: []byte("HELLO-R-U-THERE"),
|
Data: []byte("HELLO-R-U-THERE"),
|
||||||
},
|
},
|
||||||
}).Marshal()
|
}).Marshal(psh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("icmpMessage.Marshal failed: %v", err)
|
t.Fatalf("icmpMessage.Marshal failed: %v", err)
|
||||||
}
|
}
|
||||||
if err := p.SetControlMessage(cf, toggle); err != nil {
|
if err := p.SetControlMessage(cf, toggle); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
|
||||||
}
|
}
|
||||||
|
if err := p.SetDeadline(time.Now().Add(time.Millisecond * 200)); err != nil {
|
||||||
|
t.Fatalf("ipv6.PacketConn.SetDeadline failed: %v", err)
|
||||||
|
}
|
||||||
cm.HopLimit = i + 1
|
cm.HopLimit = i + 1
|
||||||
if _, err := p.WriteTo(wb, &cm, dst); err != nil {
|
if _, err := p.WriteTo(wb, &cm, dst); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
|
||||||
|
@ -59,7 +59,7 @@ func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUDPMultipleConnWithMultipleGroupListeners(t *testing.T) {
|
func TestUDPMultiplePacketConnWithMultipleGroupListeners(t *testing.T) {
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "plan9", "windows":
|
case "plan9", "windows":
|
||||||
t.Skipf("not supported on %q", runtime.GOOS)
|
t.Skipf("not supported on %q", runtime.GOOS)
|
||||||
@ -120,7 +120,7 @@ func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
|
|||||||
t.Skip("ipv6 is not supported")
|
t.Skip("ipv6 is not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
gaddr := &net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
|
gaddr := net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
|
||||||
type ml struct {
|
type ml struct {
|
||||||
c *ipv6.PacketConn
|
c *ipv6.PacketConn
|
||||||
ifi *net.Interface
|
ifi *net.Interface
|
||||||
@ -142,13 +142,13 @@ func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
p := ipv6.NewPacketConn(c)
|
p := ipv6.NewPacketConn(c)
|
||||||
if err := p.JoinGroup(&ifi, gaddr); err != nil {
|
if err := p.JoinGroup(&ifi, &gaddr); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
||||||
}
|
}
|
||||||
mlt = append(mlt, &ml{p, &ift[i]})
|
mlt = append(mlt, &ml{p, &ift[i]})
|
||||||
}
|
}
|
||||||
for _, m := range mlt {
|
for _, m := range mlt {
|
||||||
if err := m.c.LeaveGroup(m.ifi, gaddr); err != nil {
|
if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.LeaveGroup on %v failed: %v", m.ifi, err)
|
t.Fatalf("ipv6.PacketConn.LeaveGroup on %v failed: %v", m.ifi, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,14 +166,14 @@ func TestIPSinglePacketConnWithSingleGroupListener(t *testing.T) {
|
|||||||
t.Skip("must be root")
|
t.Skip("must be root")
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := net.ListenPacket("ip6:ipv6-icmp", "::")
|
c, err := net.ListenPacket("ip6:ipv6-icmp", "::") // wildcard address
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("net.ListenPacket failed: %v", err)
|
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
p := ipv6.NewPacketConn(c)
|
p := ipv6.NewPacketConn(c)
|
||||||
gaddr := &net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
|
gaddr := net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
|
||||||
var mift []*net.Interface
|
var mift []*net.Interface
|
||||||
|
|
||||||
ift, err := net.Interfaces()
|
ift, err := net.Interfaces()
|
||||||
@ -184,14 +184,60 @@ func TestIPSinglePacketConnWithSingleGroupListener(t *testing.T) {
|
|||||||
if _, ok := isMulticastAvailable(&ifi); !ok {
|
if _, ok := isMulticastAvailable(&ifi); !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := p.JoinGroup(&ifi, gaddr); err != nil {
|
if err := p.JoinGroup(&ifi, &gaddr); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
||||||
}
|
}
|
||||||
mift = append(mift, &ift[i])
|
mift = append(mift, &ift[i])
|
||||||
}
|
}
|
||||||
for _, ifi := range mift {
|
for _, ifi := range mift {
|
||||||
if err := p.LeaveGroup(ifi, gaddr); err != nil {
|
if err := p.LeaveGroup(ifi, &gaddr); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.LeaveGroup on %v failed: %v", ifi, err)
|
t.Fatalf("ipv6.PacketConn.LeaveGroup on %v failed: %v", ifi, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "darwin", "plan9", "windows":
|
||||||
|
t.Skipf("not supported on %q", runtime.GOOS)
|
||||||
|
}
|
||||||
|
if !supportsIPv6 {
|
||||||
|
t.Skip("ipv6 is not supported")
|
||||||
|
}
|
||||||
|
if os.Getuid() != 0 {
|
||||||
|
t.Skip("must be root")
|
||||||
|
}
|
||||||
|
|
||||||
|
gaddr := net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
|
||||||
|
type ml struct {
|
||||||
|
c *ipv6.PacketConn
|
||||||
|
ifi *net.Interface
|
||||||
|
}
|
||||||
|
var mlt []*ml
|
||||||
|
|
||||||
|
ift, err := net.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("net.Interfaces failed: %v", err)
|
||||||
|
}
|
||||||
|
for i, ifi := range ift {
|
||||||
|
ip, ok := isMulticastAvailable(&ifi)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c, err := net.ListenPacket("ip6:ipv6-icmp", fmt.Sprintf("%s%%%s", ip.String(), ifi.Name)) // unicast address
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("net.ListenPacket failed: %v", err)
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
p := ipv6.NewPacketConn(c)
|
||||||
|
if err := p.JoinGroup(&ifi, &gaddr); err != nil {
|
||||||
|
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
|
||||||
|
}
|
||||||
|
mlt = append(mlt, &ml{p, &ift[i]})
|
||||||
|
}
|
||||||
|
for _, m := range mlt {
|
||||||
|
if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil {
|
||||||
|
t.Fatalf("ipv6.PacketConn.LeaveGroup on %v failed: %v", m.ifi, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,12 +8,13 @@ package ipv6
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setIPv6Checksum(fd int, on bool, offset int) error {
|
func setIPv6Checksum(fd int, on bool, offset int) error {
|
||||||
if !on {
|
if !on {
|
||||||
offset = -1
|
offset = -1
|
||||||
}
|
}
|
||||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_CHECKSUM, offset))
|
v := int32(offset)
|
||||||
|
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptChecksum, uintptr(unsafe.Pointer(&v)), 4))
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,13 @@ package ipv6
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setIPv6Checksum(fd int, on bool, offset int) error {
|
func setIPv6Checksum(fd int, on bool, offset int) error {
|
||||||
if !on {
|
if !on {
|
||||||
offset = -1
|
offset = -1
|
||||||
}
|
}
|
||||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolReserved, syscall.IPV6_CHECKSUM, offset))
|
v := int32(offset)
|
||||||
|
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolReserved, sysSockoptChecksum, uintptr(unsafe.Pointer(&v)), 4))
|
||||||
}
|
}
|
||||||
|
@ -9,66 +9,74 @@ package ipv6
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ipv6TrafficClass(fd int) (int, error) {
|
func ipv6TrafficClass(fd int) (int, error) {
|
||||||
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_TCLASS)
|
var v int32
|
||||||
if err != nil {
|
l := sysSockoptLen(4)
|
||||||
|
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptTrafficClass, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
return 0, os.NewSyscallError("getsockopt", err)
|
return 0, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
return v, nil
|
return int(v), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setIPv6TrafficClass(fd, v int) error {
|
func setIPv6TrafficClass(fd, v int) error {
|
||||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_TCLASS, v))
|
vv := int32(v)
|
||||||
|
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptTrafficClass, uintptr(unsafe.Pointer(&vv)), 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipv6HopLimit(fd int) (int, error) {
|
func ipv6HopLimit(fd int) (int, error) {
|
||||||
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_UNICAST_HOPS)
|
var v int32
|
||||||
if err != nil {
|
l := sysSockoptLen(4)
|
||||||
|
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
return 0, os.NewSyscallError("getsockopt", err)
|
return 0, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
return v, nil
|
return int(v), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setIPv6HopLimit(fd, v int) error {
|
func setIPv6HopLimit(fd, v int) error {
|
||||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_UNICAST_HOPS, v))
|
vv := int32(v)
|
||||||
|
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, uintptr(unsafe.Pointer(&vv)), 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipv6Checksum(fd int) (bool, int, error) {
|
func ipv6Checksum(fd int) (bool, int, error) {
|
||||||
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_CHECKSUM)
|
var v int32
|
||||||
if err != nil {
|
l := sysSockoptLen(4)
|
||||||
|
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptChecksum, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
return false, 0, os.NewSyscallError("getsockopt", err)
|
return false, 0, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
on := true
|
on := true
|
||||||
if v == -1 {
|
if v == -1 {
|
||||||
on = false
|
on = false
|
||||||
}
|
}
|
||||||
return on, v, nil
|
return on, int(v), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipv6MulticastHopLimit(fd int) (int, error) {
|
func ipv6MulticastHopLimit(fd int) (int, error) {
|
||||||
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_HOPS)
|
var v int32
|
||||||
if err != nil {
|
l := sysSockoptLen(4)
|
||||||
|
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
return 0, os.NewSyscallError("getsockopt", err)
|
return 0, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
return v, nil
|
return int(v), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setIPv6MulticastHopLimit(fd, v int) error {
|
func setIPv6MulticastHopLimit(fd, v int) error {
|
||||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_HOPS, v))
|
vv := int32(v)
|
||||||
|
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, uintptr(unsafe.Pointer(&vv)), 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipv6MulticastInterface(fd int) (*net.Interface, error) {
|
func ipv6MulticastInterface(fd int) (*net.Interface, error) {
|
||||||
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_IF)
|
var v int32
|
||||||
if err != nil {
|
l := sysSockoptLen(4)
|
||||||
|
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastInterface, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
return nil, os.NewSyscallError("getsockopt", err)
|
return nil, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
if v == 0 {
|
if v == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
ifi, err := net.InterfaceByIndex(v)
|
ifi, err := net.InterfaceByIndex(int(v))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -76,39 +84,41 @@ func ipv6MulticastInterface(fd int) (*net.Interface, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setIPv6MulticastInterface(fd int, ifi *net.Interface) error {
|
func setIPv6MulticastInterface(fd int, ifi *net.Interface) error {
|
||||||
var v int
|
var v int32
|
||||||
if ifi != nil {
|
if ifi != nil {
|
||||||
v = ifi.Index
|
v = int32(ifi.Index)
|
||||||
}
|
}
|
||||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_IF, v))
|
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastInterface, uintptr(unsafe.Pointer(&v)), 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipv6MulticastLoopback(fd int) (bool, error) {
|
func ipv6MulticastLoopback(fd int) (bool, error) {
|
||||||
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_LOOP)
|
var v int32
|
||||||
if err != nil {
|
l := sysSockoptLen(4)
|
||||||
|
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastLoopback, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
return false, os.NewSyscallError("getsockopt", err)
|
return false, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
return v == 1, nil
|
return v == 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setIPv6MulticastLoopback(fd int, v bool) error {
|
func setIPv6MulticastLoopback(fd int, v bool) error {
|
||||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_LOOP, boolint(v)))
|
vv := int32(boolint(v))
|
||||||
|
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastLoopback, uintptr(unsafe.Pointer(&vv)), 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
func joinIPv6Group(fd int, ifi *net.Interface, grp net.IP) error {
|
func joinIPv6Group(fd int, ifi *net.Interface, grp net.IP) error {
|
||||||
mreq := syscall.IPv6Mreq{}
|
mreq := sysMulticastReq{}
|
||||||
copy(mreq.Multiaddr[:], grp)
|
copy(mreq.IP[:], grp)
|
||||||
if ifi != nil {
|
if ifi != nil {
|
||||||
mreq.Interface = uint32(ifi.Index)
|
mreq.IfIndex = uint32(ifi.Index)
|
||||||
}
|
}
|
||||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd, ianaProtocolIPv6, syscall.IPV6_JOIN_GROUP, &mreq))
|
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptJoinGroup, uintptr(unsafe.Pointer(&mreq)), sysSizeofMulticastReq))
|
||||||
}
|
}
|
||||||
|
|
||||||
func leaveIPv6Group(fd int, ifi *net.Interface, grp net.IP) error {
|
func leaveIPv6Group(fd int, ifi *net.Interface, grp net.IP) error {
|
||||||
mreq := syscall.IPv6Mreq{}
|
mreq := sysMulticastReq{}
|
||||||
copy(mreq.Multiaddr[:], grp)
|
copy(mreq.IP[:], grp)
|
||||||
if ifi != nil {
|
if ifi != nil {
|
||||||
mreq.Interface = uint32(ifi.Index)
|
mreq.IfIndex = uint32(ifi.Index)
|
||||||
}
|
}
|
||||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd, ianaProtocolIPv6, syscall.IPV6_LEAVE_GROUP, &mreq))
|
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptLeaveGroup, uintptr(unsafe.Pointer(&mreq)), sysSizeofMulticastReq))
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ func setIPv6TrafficClass(fd syscall.Handle, v int) error {
|
|||||||
func ipv6HopLimit(fd syscall.Handle) (int, error) {
|
func ipv6HopLimit(fd syscall.Handle) (int, error) {
|
||||||
var v int32
|
var v int32
|
||||||
l := int32(4)
|
l := int32(4)
|
||||||
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, syscall.IPV6_UNICAST_HOPS, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
|
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
return 0, os.NewSyscallError("getsockopt", err)
|
return 0, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
return int(v), nil
|
return int(v), nil
|
||||||
@ -32,7 +32,7 @@ func ipv6HopLimit(fd syscall.Handle) (int, error) {
|
|||||||
|
|
||||||
func setIPv6HopLimit(fd syscall.Handle, v int) error {
|
func setIPv6HopLimit(fd syscall.Handle, v int) error {
|
||||||
vv := int32(v)
|
vv := int32(v)
|
||||||
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_UNICAST_HOPS, (*byte)(unsafe.Pointer(&vv)), 4))
|
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, (*byte)(unsafe.Pointer(&vv)), 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipv6Checksum(fd syscall.Handle) (bool, int, error) {
|
func ipv6Checksum(fd syscall.Handle) (bool, int, error) {
|
||||||
@ -43,7 +43,7 @@ func ipv6Checksum(fd syscall.Handle) (bool, int, error) {
|
|||||||
func ipv6MulticastHopLimit(fd syscall.Handle) (int, error) {
|
func ipv6MulticastHopLimit(fd syscall.Handle) (int, error) {
|
||||||
var v int32
|
var v int32
|
||||||
l := int32(4)
|
l := int32(4)
|
||||||
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_HOPS, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
|
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
return 0, os.NewSyscallError("getsockopt", err)
|
return 0, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
return int(v), nil
|
return int(v), nil
|
||||||
@ -51,13 +51,13 @@ func ipv6MulticastHopLimit(fd syscall.Handle) (int, error) {
|
|||||||
|
|
||||||
func setIPv6MulticastHopLimit(fd syscall.Handle, v int) error {
|
func setIPv6MulticastHopLimit(fd syscall.Handle, v int) error {
|
||||||
vv := int32(v)
|
vv := int32(v)
|
||||||
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_HOPS, (*byte)(unsafe.Pointer(&vv)), 4))
|
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, (*byte)(unsafe.Pointer(&vv)), 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipv6MulticastInterface(fd syscall.Handle) (*net.Interface, error) {
|
func ipv6MulticastInterface(fd syscall.Handle) (*net.Interface, error) {
|
||||||
var v int32
|
var v int32
|
||||||
l := int32(4)
|
l := int32(4)
|
||||||
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_IF, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
|
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastInterface, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
return nil, os.NewSyscallError("getsockopt", err)
|
return nil, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
if v == 0 {
|
if v == 0 {
|
||||||
@ -75,13 +75,13 @@ func setIPv6MulticastInterface(fd syscall.Handle, ifi *net.Interface) error {
|
|||||||
if ifi != nil {
|
if ifi != nil {
|
||||||
v = int32(ifi.Index)
|
v = int32(ifi.Index)
|
||||||
}
|
}
|
||||||
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_IF, (*byte)(unsafe.Pointer(&v)), 4))
|
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastInterface, (*byte)(unsafe.Pointer(&v)), 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipv6MulticastLoopback(fd syscall.Handle) (bool, error) {
|
func ipv6MulticastLoopback(fd syscall.Handle) (bool, error) {
|
||||||
var v int32
|
var v int32
|
||||||
l := int32(4)
|
l := int32(4)
|
||||||
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_LOOP, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
|
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastLoopback, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
return false, os.NewSyscallError("getsockopt", err)
|
return false, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
return v == 1, nil
|
return v == 1, nil
|
||||||
@ -89,25 +89,25 @@ func ipv6MulticastLoopback(fd syscall.Handle) (bool, error) {
|
|||||||
|
|
||||||
func setIPv6MulticastLoopback(fd syscall.Handle, v bool) error {
|
func setIPv6MulticastLoopback(fd syscall.Handle, v bool) error {
|
||||||
vv := int32(boolint(v))
|
vv := int32(boolint(v))
|
||||||
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_LOOP, (*byte)(unsafe.Pointer(&vv)), 4))
|
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastLoopback, (*byte)(unsafe.Pointer(&vv)), 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
func joinIPv6Group(fd syscall.Handle, ifi *net.Interface, grp net.IP) error {
|
func joinIPv6Group(fd syscall.Handle, ifi *net.Interface, grp net.IP) error {
|
||||||
mreq := syscall.IPv6Mreq{}
|
mreq := sysMulticastReq{}
|
||||||
copy(mreq.Multiaddr[:], grp)
|
copy(mreq.IP[:], grp)
|
||||||
if ifi != nil {
|
if ifi != nil {
|
||||||
mreq.Interface = uint32(ifi.Index)
|
mreq.IfIndex = uint32(ifi.Index)
|
||||||
}
|
}
|
||||||
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_JOIN_GROUP, (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq))))
|
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptJoinGroup, (*byte)(unsafe.Pointer(&mreq)), int32(sysSizeofMulticastReq)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func leaveIPv6Group(fd syscall.Handle, ifi *net.Interface, grp net.IP) error {
|
func leaveIPv6Group(fd syscall.Handle, ifi *net.Interface, grp net.IP) error {
|
||||||
mreq := syscall.IPv6Mreq{}
|
mreq := sysMulticastReq{}
|
||||||
copy(mreq.Multiaddr[:], grp)
|
copy(mreq.IP[:], grp)
|
||||||
if ifi != nil {
|
if ifi != nil {
|
||||||
mreq.Interface = uint32(ifi.Index)
|
mreq.IfIndex = uint32(ifi.Index)
|
||||||
}
|
}
|
||||||
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_LEAVE_GROUP, (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq))))
|
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptLeaveGroup, (*byte)(unsafe.Pointer(&mreq)), int32(sysSizeofMulticastReq)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func setIPv6Checksum(fd syscall.Handle, on bool, offset int) error {
|
func setIPv6Checksum(fd syscall.Handle, on bool, offset int) error {
|
||||||
|
@ -8,41 +8,83 @@ package ipv6
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ipv6ReceiveTrafficClass(fd int) (bool, error) {
|
func ipv6ReceiveTrafficClass(fd int) (bool, error) {
|
||||||
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVTCLASS)
|
var v int32
|
||||||
if err != nil {
|
l := sysSockoptLen(4)
|
||||||
|
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveTrafficClass, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
return false, os.NewSyscallError("getsockopt", err)
|
return false, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
return v == 1, nil
|
return v == 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setIPv6ReceiveTrafficClass(fd int, v bool) error {
|
func setIPv6ReceiveTrafficClass(fd int, v bool) error {
|
||||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVTCLASS, boolint(v)))
|
vv := int32(boolint(v))
|
||||||
|
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveTrafficClass, uintptr(unsafe.Pointer(&vv)), 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipv6ReceiveHopLimit(fd int) (bool, error) {
|
func ipv6ReceiveHopLimit(fd int) (bool, error) {
|
||||||
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVHOPLIMIT)
|
var v int32
|
||||||
if err != nil {
|
l := sysSockoptLen(4)
|
||||||
|
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveHopLimit, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
return false, os.NewSyscallError("getsockopt", err)
|
return false, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
return v == 1, nil
|
return v == 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setIPv6ReceiveHopLimit(fd int, v bool) error {
|
func setIPv6ReceiveHopLimit(fd int, v bool) error {
|
||||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVHOPLIMIT, boolint(v)))
|
vv := int32(boolint(v))
|
||||||
|
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveHopLimit, uintptr(unsafe.Pointer(&vv)), 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipv6ReceivePacketInfo(fd int) (bool, error) {
|
func ipv6ReceivePacketInfo(fd int) (bool, error) {
|
||||||
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVPKTINFO)
|
var v int32
|
||||||
if err != nil {
|
l := sysSockoptLen(4)
|
||||||
|
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceivePacketInfo, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
return false, os.NewSyscallError("getsockopt", err)
|
return false, os.NewSyscallError("getsockopt", err)
|
||||||
}
|
}
|
||||||
return v == 1, nil
|
return v == 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setIPv6ReceivePacketInfo(fd int, v bool) error {
|
func setIPv6ReceivePacketInfo(fd int, v bool) error {
|
||||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVPKTINFO, boolint(v)))
|
vv := int32(boolint(v))
|
||||||
|
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptReceivePacketInfo, uintptr(unsafe.Pointer(&vv)), 4))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ipv6PathMTU(fd int) (int, error) {
|
||||||
|
var v sysMTUInfo
|
||||||
|
l := sysSockoptLen(sysSizeofMTUInfo)
|
||||||
|
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptPathMTU, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
|
return 0, os.NewSyscallError("getsockopt", err)
|
||||||
|
}
|
||||||
|
return int(v.MTU), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ipv6ReceivePathMTU(fd int) (bool, error) {
|
||||||
|
var v int32
|
||||||
|
l := sysSockoptLen(4)
|
||||||
|
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceivePathMTU, uintptr(unsafe.Pointer(&v)), &l); err != nil {
|
||||||
|
return false, os.NewSyscallError("getsockopt", err)
|
||||||
|
}
|
||||||
|
return v == 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setIPv6ReceivePathMTU(fd int, v bool) error {
|
||||||
|
vv := int32(boolint(v))
|
||||||
|
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptReceivePathMTU, uintptr(unsafe.Pointer(&vv)), 4))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ipv6ICMPFilter(fd int) (*ICMPFilter, error) {
|
||||||
|
var v ICMPFilter
|
||||||
|
l := sysSockoptLen(sysSizeofICMPFilter)
|
||||||
|
if err := getsockopt(fd, ianaProtocolIPv6ICMP, sysSockoptICMPFilter, uintptr(unsafe.Pointer(&v.sysICMPFilter)), &l); err != nil {
|
||||||
|
return nil, os.NewSyscallError("getsockopt", err)
|
||||||
|
}
|
||||||
|
return &v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setIPv6ICMPFilter(fd int, f *ICMPFilter) error {
|
||||||
|
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6ICMP, sysSockoptICMPFilter, uintptr(unsafe.Pointer(&f.sysICMPFilter)), sysSizeofICMPFilter))
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func benchmarkUDPListener() (net.PacketConn, net.Addr, error) {
|
func benchmarkUDPListener() (net.PacketConn, net.Addr, error) {
|
||||||
@ -106,6 +107,7 @@ func TestPacketConnReadWriteUnicastUDP(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
p := ipv6.NewPacketConn(c)
|
p := ipv6.NewPacketConn(c)
|
||||||
|
defer p.Close()
|
||||||
cm := ipv6.ControlMessage{
|
cm := ipv6.ControlMessage{
|
||||||
TrafficClass: DiffServAF11 | CongestionExperienced,
|
TrafficClass: DiffServAF11 | CongestionExperienced,
|
||||||
}
|
}
|
||||||
@ -120,10 +122,16 @@ func TestPacketConnReadWriteUnicastUDP(t *testing.T) {
|
|||||||
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
|
||||||
}
|
}
|
||||||
cm.HopLimit = i + 1
|
cm.HopLimit = i + 1
|
||||||
|
if err := p.SetWriteDeadline(time.Now().Add(time.Millisecond * 100)); err != nil {
|
||||||
|
t.Fatalf("ipv6.PacketConn.SetWriteDeadline failed: %v", err)
|
||||||
|
}
|
||||||
if _, err := p.WriteTo([]byte("HELLO-R-U-THERE"), &cm, dst); err != nil {
|
if _, err := p.WriteTo([]byte("HELLO-R-U-THERE"), &cm, dst); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
|
||||||
}
|
}
|
||||||
b := make([]byte, 128)
|
b := make([]byte, 128)
|
||||||
|
if err := p.SetReadDeadline(time.Now().Add(time.Millisecond * 100)); err != nil {
|
||||||
|
t.Fatalf("ipv6.PacketConn.SetReadDeadline failed: %v", err)
|
||||||
|
}
|
||||||
if _, cm, _, err := p.ReadFrom(b); err != nil {
|
if _, cm, _, err := p.ReadFrom(b); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
|
||||||
} else {
|
} else {
|
||||||
@ -155,7 +163,9 @@ func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
|
|||||||
t.Fatalf("net.ResolveIPAddr failed: %v", err)
|
t.Fatalf("net.ResolveIPAddr failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pshicmp := ipv6PseudoHeader(c.LocalAddr().(*net.IPAddr).IP, dst.IP, ianaProtocolIPv6ICMP)
|
||||||
p := ipv6.NewPacketConn(c)
|
p := ipv6.NewPacketConn(c)
|
||||||
|
defer p.Close()
|
||||||
cm := ipv6.ControlMessage{TrafficClass: DiffServAF11 | CongestionExperienced}
|
cm := ipv6.ControlMessage{TrafficClass: DiffServAF11 | CongestionExperienced}
|
||||||
cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU
|
cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU
|
||||||
ifi := loopbackInterface()
|
ifi := loopbackInterface()
|
||||||
@ -170,14 +180,26 @@ func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
|
|||||||
t.Fatalf("ipv6.PacketConn.SetICMPFilter failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.SetICMPFilter failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var psh []byte
|
||||||
for i, toggle := range []bool{true, false, true} {
|
for i, toggle := range []bool{true, false, true} {
|
||||||
|
if toggle {
|
||||||
|
psh = nil
|
||||||
|
if err := p.SetChecksum(true, 2); err != nil {
|
||||||
|
t.Fatalf("ipv6.PacketConn.SetChecksum failed: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
psh = pshicmp
|
||||||
|
// Some platforms never allow to disable the
|
||||||
|
// kernel checksum processing.
|
||||||
|
p.SetChecksum(false, -1)
|
||||||
|
}
|
||||||
wb, err := (&icmpMessage{
|
wb, err := (&icmpMessage{
|
||||||
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
|
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
|
||||||
Body: &icmpEcho{
|
Body: &icmpEcho{
|
||||||
ID: os.Getpid() & 0xffff, Seq: i + 1,
|
ID: os.Getpid() & 0xffff, Seq: i + 1,
|
||||||
Data: []byte("HELLO-R-U-THERE"),
|
Data: []byte("HELLO-R-U-THERE"),
|
||||||
},
|
},
|
||||||
}).Marshal()
|
}).Marshal(psh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("icmpMessage.Marshal failed: %v", err)
|
t.Fatalf("icmpMessage.Marshal failed: %v", err)
|
||||||
}
|
}
|
||||||
@ -185,10 +207,16 @@ func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
|
|||||||
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
|
||||||
}
|
}
|
||||||
cm.HopLimit = i + 1
|
cm.HopLimit = i + 1
|
||||||
|
if err := p.SetWriteDeadline(time.Now().Add(time.Millisecond * 100)); err != nil {
|
||||||
|
t.Fatalf("ipv6.PacketConn.SetWriteDeadline failed: %v", err)
|
||||||
|
}
|
||||||
if _, err := p.WriteTo(wb, &cm, dst); err != nil {
|
if _, err := p.WriteTo(wb, &cm, dst); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
|
||||||
}
|
}
|
||||||
b := make([]byte, 128)
|
b := make([]byte, 128)
|
||||||
|
if err := p.SetReadDeadline(time.Now().Add(time.Millisecond * 100)); err != nil {
|
||||||
|
t.Fatalf("ipv6.PacketConn.SetReadDeadline failed: %v", err)
|
||||||
|
}
|
||||||
if n, cm, _, err := p.ReadFrom(b); err != nil {
|
if n, cm, _, err := p.ReadFrom(b); err != nil {
|
||||||
t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
|
t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
|
||||||
} else {
|
} else {
|
||||||
|
@ -99,19 +99,18 @@ var publicSuffixTestCases = []struct {
|
|||||||
{"www.xxx.yyy.zzz.pb.ao", "pb.ao"},
|
{"www.xxx.yyy.zzz.pb.ao", "pb.ao"},
|
||||||
|
|
||||||
// The .ar rules are:
|
// The .ar rules are:
|
||||||
// *.ar
|
// ar
|
||||||
// !congresodelalengua3.ar
|
// com.ar
|
||||||
// !educ.ar
|
// edu.ar
|
||||||
// !gobiernoelectronico.ar
|
// gob.ar
|
||||||
// !mecon.ar
|
// int.ar
|
||||||
// !nacion.ar
|
// mil.ar
|
||||||
// !nic.ar
|
// net.ar
|
||||||
// !promocion.ar
|
// org.ar
|
||||||
// !retina.ar
|
// tur.ar
|
||||||
// !uba.ar
|
|
||||||
// blogspot.com.ar
|
// blogspot.com.ar
|
||||||
{"ar", "ar"},
|
{"ar", "ar"},
|
||||||
{"www.ar", "www.ar"},
|
{"www.ar", "ar"},
|
||||||
{"nic.ar", "ar"},
|
{"nic.ar", "ar"},
|
||||||
{"www.nic.ar", "ar"},
|
{"www.nic.ar", "ar"},
|
||||||
{"com.ar", "com.ar"},
|
{"com.ar", "com.ar"},
|
||||||
@ -376,12 +375,12 @@ var eTLDPlusOneTestCases = []struct {
|
|||||||
{"city.kobe.jp", "city.kobe.jp"},
|
{"city.kobe.jp", "city.kobe.jp"},
|
||||||
{"www.city.kobe.jp", "city.kobe.jp"},
|
{"www.city.kobe.jp", "city.kobe.jp"},
|
||||||
// TLD with a wildcard rule and exceptions.
|
// TLD with a wildcard rule and exceptions.
|
||||||
{"om", ""},
|
{"ck", ""},
|
||||||
{"test.om", ""},
|
{"test.ck", ""},
|
||||||
{"b.test.om", "b.test.om"},
|
{"b.test.ck", "b.test.ck"},
|
||||||
{"a.b.test.om", "b.test.om"},
|
{"a.b.test.ck", "b.test.ck"},
|
||||||
{"songfest.om", "songfest.om"},
|
{"www.ck", "www.ck"},
|
||||||
{"www.songfest.om", "songfest.om"},
|
{"www.www.ck", "www.ck"},
|
||||||
// US K12.
|
// US K12.
|
||||||
{"us", ""},
|
{"us", ""},
|
||||||
{"test.us", "test.us"},
|
{"test.us", "test.us"},
|
||||||
|
13736
third_party/code.google.com/p/go.net/publicsuffix/table.go
vendored
13736
third_party/code.google.com/p/go.net/publicsuffix/table.go
vendored
File diff suppressed because it is too large
Load Diff
@ -148,16 +148,15 @@ var rules = [...]string{
|
|||||||
"pb.ao",
|
"pb.ao",
|
||||||
"it.ao",
|
"it.ao",
|
||||||
"aq",
|
"aq",
|
||||||
"*.ar",
|
"ar",
|
||||||
"!congresodelalengua3.ar",
|
"com.ar",
|
||||||
"!educ.ar",
|
"edu.ar",
|
||||||
"!gobiernoelectronico.ar",
|
"gob.ar",
|
||||||
"!mecon.ar",
|
"int.ar",
|
||||||
"!nacion.ar",
|
"mil.ar",
|
||||||
"!nic.ar",
|
"net.ar",
|
||||||
"!promocion.ar",
|
"org.ar",
|
||||||
"!retina.ar",
|
"tur.ar",
|
||||||
"!uba.ar",
|
|
||||||
"e164.arpa",
|
"e164.arpa",
|
||||||
"in-addr.arpa",
|
"in-addr.arpa",
|
||||||
"ip6.arpa",
|
"ip6.arpa",
|
||||||
@ -179,6 +178,7 @@ var rules = [...]string{
|
|||||||
"gov.au",
|
"gov.au",
|
||||||
"asn.au",
|
"asn.au",
|
||||||
"id.au",
|
"id.au",
|
||||||
|
"csiro.au",
|
||||||
"info.au",
|
"info.au",
|
||||||
"conf.au",
|
"conf.au",
|
||||||
"oz.au",
|
"oz.au",
|
||||||
@ -3770,12 +3770,16 @@ var rules = [...]string{
|
|||||||
"info.nf",
|
"info.nf",
|
||||||
"other.nf",
|
"other.nf",
|
||||||
"store.nf",
|
"store.nf",
|
||||||
"ac.ng",
|
"ng",
|
||||||
"com.ng",
|
"com.ng",
|
||||||
"edu.ng",
|
"edu.ng",
|
||||||
"gov.ng",
|
"name.ng",
|
||||||
"net.ng",
|
"net.ng",
|
||||||
"org.ng",
|
"org.ng",
|
||||||
|
"sch.ng",
|
||||||
|
"gov.ng",
|
||||||
|
"mil.ng",
|
||||||
|
"mobi.ng",
|
||||||
"*.ni",
|
"*.ni",
|
||||||
"nl",
|
"nl",
|
||||||
"bv.nl",
|
"bv.nl",
|
||||||
@ -4544,7 +4548,16 @@ var rules = [...]string{
|
|||||||
"com.nr",
|
"com.nr",
|
||||||
"nu",
|
"nu",
|
||||||
"*.nz",
|
"*.nz",
|
||||||
"*.om",
|
"om",
|
||||||
|
"co.om",
|
||||||
|
"com.om",
|
||||||
|
"edu.om",
|
||||||
|
"gov.om",
|
||||||
|
"med.om",
|
||||||
|
"museum.om",
|
||||||
|
"net.om",
|
||||||
|
"org.om",
|
||||||
|
"pro.om",
|
||||||
"!mediaphone.om",
|
"!mediaphone.om",
|
||||||
"!nawrastelecom.om",
|
"!nawrastelecom.om",
|
||||||
"!nawras.om",
|
"!nawras.om",
|
||||||
@ -5143,7 +5156,12 @@ var rules = [...]string{
|
|||||||
"saotome.st",
|
"saotome.st",
|
||||||
"store.st",
|
"store.st",
|
||||||
"su",
|
"su",
|
||||||
"*.sv",
|
"sv",
|
||||||
|
"com.sv",
|
||||||
|
"edu.sv",
|
||||||
|
"gob.sv",
|
||||||
|
"org.sv",
|
||||||
|
"red.sv",
|
||||||
"sx",
|
"sx",
|
||||||
"gov.sx",
|
"gov.sx",
|
||||||
"sy",
|
"sy",
|
||||||
@ -5709,7 +5727,79 @@ var rules = [...]string{
|
|||||||
"*.za",
|
"*.za",
|
||||||
"*.zm",
|
"*.zm",
|
||||||
"*.zw",
|
"*.zw",
|
||||||
|
"xn--80asehdb",
|
||||||
|
"xn--80aswg",
|
||||||
|
"xn--ngbc5azd",
|
||||||
|
"xn--unup4y",
|
||||||
|
"xn--vhquv",
|
||||||
|
"camera",
|
||||||
|
"clothing",
|
||||||
|
"lighting",
|
||||||
|
"singles",
|
||||||
|
"ventures",
|
||||||
|
"voyage",
|
||||||
|
"guru",
|
||||||
|
"holdings",
|
||||||
|
"equipment",
|
||||||
|
"bike",
|
||||||
|
"estate",
|
||||||
|
"tattoo",
|
||||||
|
"xn--3ds443g",
|
||||||
|
"xn--fiq228c5hs",
|
||||||
|
"land",
|
||||||
|
"plumbing",
|
||||||
|
"contractors",
|
||||||
|
"sexy",
|
||||||
|
"menu",
|
||||||
|
"xn--rhqv96g",
|
||||||
|
"uno",
|
||||||
|
"gallery",
|
||||||
|
"technology",
|
||||||
|
"xn--3bst00m",
|
||||||
|
"reviews",
|
||||||
|
"guide",
|
||||||
|
"xn--6qq986b3xl",
|
||||||
|
"graphics",
|
||||||
|
"construction",
|
||||||
|
"onl",
|
||||||
|
"xn--q9jyb4c",
|
||||||
|
"diamonds",
|
||||||
|
"kiwi",
|
||||||
|
"enterprises",
|
||||||
|
"today",
|
||||||
|
"futbol",
|
||||||
|
"photography",
|
||||||
|
"tips",
|
||||||
|
"directory",
|
||||||
|
"kitchen",
|
||||||
|
"xn--6frz82g",
|
||||||
|
"kim",
|
||||||
|
"xn--cg4bki",
|
||||||
|
"monash",
|
||||||
|
"wed",
|
||||||
|
"pink",
|
||||||
|
"ruhr",
|
||||||
|
"buzz",
|
||||||
|
"careers",
|
||||||
|
"shoes",
|
||||||
|
"xn--4gbrim",
|
||||||
|
"career",
|
||||||
|
"otsuka",
|
||||||
|
"xn--fiq64b",
|
||||||
"cloudfront.net",
|
"cloudfront.net",
|
||||||
|
"compute.amazonaws.com",
|
||||||
|
"us-east-1.amazonaws.com",
|
||||||
|
"compute-1.amazonaws.com",
|
||||||
|
"z-1.compute-1.amazonaws.com",
|
||||||
|
"z-2.compute-1.amazonaws.com",
|
||||||
|
"ap-northeast-1.compute.amazonaws.com",
|
||||||
|
"ap-southeast-1.compute.amazonaws.com",
|
||||||
|
"ap-southeast-2.compute.amazonaws.com",
|
||||||
|
"eu-west-1.compute.amazonaws.com",
|
||||||
|
"sa-east-1.compute.amazonaws.com",
|
||||||
|
"us-gov-west-1.compute.amazonaws.com",
|
||||||
|
"us-west-1.compute.amazonaws.com",
|
||||||
|
"us-west-2.compute.amazonaws.com",
|
||||||
"elasticbeanstalk.com",
|
"elasticbeanstalk.com",
|
||||||
"elb.amazonaws.com",
|
"elb.amazonaws.com",
|
||||||
"s3.amazonaws.com",
|
"s3.amazonaws.com",
|
||||||
@ -5760,6 +5850,8 @@ var rules = [...]string{
|
|||||||
"uy.com",
|
"uy.com",
|
||||||
"za.com",
|
"za.com",
|
||||||
"c.la",
|
"c.la",
|
||||||
|
"cloudcontrolled.com",
|
||||||
|
"cloudcontrolapp.com",
|
||||||
"co.ca",
|
"co.ca",
|
||||||
"co.nl",
|
"co.nl",
|
||||||
"co.no",
|
"co.no",
|
||||||
@ -6043,7 +6135,13 @@ var rules = [...]string{
|
|||||||
"webhop.org",
|
"webhop.org",
|
||||||
"worse-than.tv",
|
"worse-than.tv",
|
||||||
"writesthisblog.com",
|
"writesthisblog.com",
|
||||||
|
"a.ssl.fastly.net",
|
||||||
|
"b.ssl.fastly.net",
|
||||||
|
"global.ssl.fastly.net",
|
||||||
|
"a.prod.fastly.net",
|
||||||
|
"global.prod.fastly.net",
|
||||||
"github.io",
|
"github.io",
|
||||||
|
"ro.com",
|
||||||
"appspot.com",
|
"appspot.com",
|
||||||
"blogspot.be",
|
"blogspot.be",
|
||||||
"blogspot.bj",
|
"blogspot.bj",
|
||||||
@ -6088,6 +6186,8 @@ var rules = [...]string{
|
|||||||
"codespot.com",
|
"codespot.com",
|
||||||
"googleapis.com",
|
"googleapis.com",
|
||||||
"googlecode.com",
|
"googlecode.com",
|
||||||
|
"herokuapp.com",
|
||||||
|
"herokussl.com",
|
||||||
"iki.fi",
|
"iki.fi",
|
||||||
"biz.at",
|
"biz.at",
|
||||||
"info.at",
|
"info.at",
|
||||||
@ -6130,6 +6230,7 @@ var nodeLabels = [...]string{
|
|||||||
"bg",
|
"bg",
|
||||||
"bh",
|
"bh",
|
||||||
"bi",
|
"bi",
|
||||||
|
"bike",
|
||||||
"biz",
|
"biz",
|
||||||
"bj",
|
"bj",
|
||||||
"bm",
|
"bm",
|
||||||
@ -6138,10 +6239,14 @@ var nodeLabels = [...]string{
|
|||||||
"br",
|
"br",
|
||||||
"bs",
|
"bs",
|
||||||
"bt",
|
"bt",
|
||||||
|
"buzz",
|
||||||
"bw",
|
"bw",
|
||||||
"by",
|
"by",
|
||||||
"bz",
|
"bz",
|
||||||
"ca",
|
"ca",
|
||||||
|
"camera",
|
||||||
|
"career",
|
||||||
|
"careers",
|
||||||
"cat",
|
"cat",
|
||||||
"cc",
|
"cc",
|
||||||
"cd",
|
"cd",
|
||||||
@ -6151,10 +6256,13 @@ var nodeLabels = [...]string{
|
|||||||
"ci",
|
"ci",
|
||||||
"ck",
|
"ck",
|
||||||
"cl",
|
"cl",
|
||||||
|
"clothing",
|
||||||
"cm",
|
"cm",
|
||||||
"cn",
|
"cn",
|
||||||
"co",
|
"co",
|
||||||
"com",
|
"com",
|
||||||
|
"construction",
|
||||||
|
"contractors",
|
||||||
"coop",
|
"coop",
|
||||||
"cr",
|
"cr",
|
||||||
"cu",
|
"cu",
|
||||||
@ -6164,6 +6272,8 @@ var nodeLabels = [...]string{
|
|||||||
"cy",
|
"cy",
|
||||||
"cz",
|
"cz",
|
||||||
"de",
|
"de",
|
||||||
|
"diamonds",
|
||||||
|
"directory",
|
||||||
"dj",
|
"dj",
|
||||||
"dk",
|
"dk",
|
||||||
"dm",
|
"dm",
|
||||||
@ -6173,8 +6283,11 @@ var nodeLabels = [...]string{
|
|||||||
"edu",
|
"edu",
|
||||||
"ee",
|
"ee",
|
||||||
"eg",
|
"eg",
|
||||||
|
"enterprises",
|
||||||
|
"equipment",
|
||||||
"er",
|
"er",
|
||||||
"es",
|
"es",
|
||||||
|
"estate",
|
||||||
"et",
|
"et",
|
||||||
"eu",
|
"eu",
|
||||||
"fi",
|
"fi",
|
||||||
@ -6183,7 +6296,9 @@ var nodeLabels = [...]string{
|
|||||||
"fm",
|
"fm",
|
||||||
"fo",
|
"fo",
|
||||||
"fr",
|
"fr",
|
||||||
|
"futbol",
|
||||||
"ga",
|
"ga",
|
||||||
|
"gallery",
|
||||||
"gd",
|
"gd",
|
||||||
"ge",
|
"ge",
|
||||||
"gf",
|
"gf",
|
||||||
@ -6197,14 +6312,18 @@ var nodeLabels = [...]string{
|
|||||||
"gp",
|
"gp",
|
||||||
"gq",
|
"gq",
|
||||||
"gr",
|
"gr",
|
||||||
|
"graphics",
|
||||||
"gs",
|
"gs",
|
||||||
"gt",
|
"gt",
|
||||||
"gu",
|
"gu",
|
||||||
|
"guide",
|
||||||
|
"guru",
|
||||||
"gw",
|
"gw",
|
||||||
"gy",
|
"gy",
|
||||||
"hk",
|
"hk",
|
||||||
"hm",
|
"hm",
|
||||||
"hn",
|
"hn",
|
||||||
|
"holdings",
|
||||||
"hr",
|
"hr",
|
||||||
"ht",
|
"ht",
|
||||||
"hu",
|
"hu",
|
||||||
@ -6229,6 +6348,9 @@ var nodeLabels = [...]string{
|
|||||||
"kg",
|
"kg",
|
||||||
"kh",
|
"kh",
|
||||||
"ki",
|
"ki",
|
||||||
|
"kim",
|
||||||
|
"kitchen",
|
||||||
|
"kiwi",
|
||||||
"km",
|
"km",
|
||||||
"kn",
|
"kn",
|
||||||
"kp",
|
"kp",
|
||||||
@ -6237,9 +6359,11 @@ var nodeLabels = [...]string{
|
|||||||
"ky",
|
"ky",
|
||||||
"kz",
|
"kz",
|
||||||
"la",
|
"la",
|
||||||
|
"land",
|
||||||
"lb",
|
"lb",
|
||||||
"lc",
|
"lc",
|
||||||
"li",
|
"li",
|
||||||
|
"lighting",
|
||||||
"lk",
|
"lk",
|
||||||
"lr",
|
"lr",
|
||||||
"ls",
|
"ls",
|
||||||
@ -6251,6 +6375,7 @@ var nodeLabels = [...]string{
|
|||||||
"mc",
|
"mc",
|
||||||
"md",
|
"md",
|
||||||
"me",
|
"me",
|
||||||
|
"menu",
|
||||||
"mg",
|
"mg",
|
||||||
"mh",
|
"mh",
|
||||||
"mil",
|
"mil",
|
||||||
@ -6260,6 +6385,7 @@ var nodeLabels = [...]string{
|
|||||||
"mn",
|
"mn",
|
||||||
"mo",
|
"mo",
|
||||||
"mobi",
|
"mobi",
|
||||||
|
"monash",
|
||||||
"mp",
|
"mp",
|
||||||
"mq",
|
"mq",
|
||||||
"mr",
|
"mr",
|
||||||
@ -6287,14 +6413,19 @@ var nodeLabels = [...]string{
|
|||||||
"nu",
|
"nu",
|
||||||
"nz",
|
"nz",
|
||||||
"om",
|
"om",
|
||||||
|
"onl",
|
||||||
"org",
|
"org",
|
||||||
|
"otsuka",
|
||||||
"pa",
|
"pa",
|
||||||
"pe",
|
"pe",
|
||||||
"pf",
|
"pf",
|
||||||
"pg",
|
"pg",
|
||||||
"ph",
|
"ph",
|
||||||
|
"photography",
|
||||||
|
"pink",
|
||||||
"pk",
|
"pk",
|
||||||
"pl",
|
"pl",
|
||||||
|
"plumbing",
|
||||||
"pm",
|
"pm",
|
||||||
"pn",
|
"pn",
|
||||||
"post",
|
"post",
|
||||||
@ -6306,18 +6437,23 @@ var nodeLabels = [...]string{
|
|||||||
"py",
|
"py",
|
||||||
"qa",
|
"qa",
|
||||||
"re",
|
"re",
|
||||||
|
"reviews",
|
||||||
"ro",
|
"ro",
|
||||||
"rs",
|
"rs",
|
||||||
"ru",
|
"ru",
|
||||||
|
"ruhr",
|
||||||
"rw",
|
"rw",
|
||||||
"sa",
|
"sa",
|
||||||
"sb",
|
"sb",
|
||||||
"sc",
|
"sc",
|
||||||
"sd",
|
"sd",
|
||||||
"se",
|
"se",
|
||||||
|
"sexy",
|
||||||
"sg",
|
"sg",
|
||||||
"sh",
|
"sh",
|
||||||
|
"shoes",
|
||||||
"si",
|
"si",
|
||||||
|
"singles",
|
||||||
"sk",
|
"sk",
|
||||||
"sl",
|
"sl",
|
||||||
"sm",
|
"sm",
|
||||||
@ -6330,18 +6466,22 @@ var nodeLabels = [...]string{
|
|||||||
"sx",
|
"sx",
|
||||||
"sy",
|
"sy",
|
||||||
"sz",
|
"sz",
|
||||||
|
"tattoo",
|
||||||
"tc",
|
"tc",
|
||||||
"td",
|
"td",
|
||||||
|
"technology",
|
||||||
"tel",
|
"tel",
|
||||||
"tf",
|
"tf",
|
||||||
"tg",
|
"tg",
|
||||||
"th",
|
"th",
|
||||||
|
"tips",
|
||||||
"tj",
|
"tj",
|
||||||
"tk",
|
"tk",
|
||||||
"tl",
|
"tl",
|
||||||
"tm",
|
"tm",
|
||||||
"tn",
|
"tn",
|
||||||
"to",
|
"to",
|
||||||
|
"today",
|
||||||
"tr",
|
"tr",
|
||||||
"travel",
|
"travel",
|
||||||
"tt",
|
"tt",
|
||||||
@ -6351,23 +6491,37 @@ var nodeLabels = [...]string{
|
|||||||
"ua",
|
"ua",
|
||||||
"ug",
|
"ug",
|
||||||
"uk",
|
"uk",
|
||||||
|
"uno",
|
||||||
"us",
|
"us",
|
||||||
"uy",
|
"uy",
|
||||||
"uz",
|
"uz",
|
||||||
"va",
|
"va",
|
||||||
"vc",
|
"vc",
|
||||||
"ve",
|
"ve",
|
||||||
|
"ventures",
|
||||||
"vg",
|
"vg",
|
||||||
"vi",
|
"vi",
|
||||||
"vn",
|
"vn",
|
||||||
|
"voyage",
|
||||||
"vu",
|
"vu",
|
||||||
|
"wed",
|
||||||
"wf",
|
"wf",
|
||||||
"ws",
|
"ws",
|
||||||
|
"xn--3bst00m",
|
||||||
|
"xn--3ds443g",
|
||||||
"xn--3e0b707e",
|
"xn--3e0b707e",
|
||||||
"xn--45brj9c",
|
"xn--45brj9c",
|
||||||
|
"xn--4gbrim",
|
||||||
"xn--54b7fta0cc",
|
"xn--54b7fta0cc",
|
||||||
|
"xn--6frz82g",
|
||||||
|
"xn--6qq986b3xl",
|
||||||
|
"xn--80asehdb",
|
||||||
|
"xn--80aswg",
|
||||||
"xn--90a3ac",
|
"xn--90a3ac",
|
||||||
|
"xn--cg4bki",
|
||||||
"xn--clchc0ea0b2g2a9gcd",
|
"xn--clchc0ea0b2g2a9gcd",
|
||||||
|
"xn--fiq228c5hs",
|
||||||
|
"xn--fiq64b",
|
||||||
"xn--fiqs8s",
|
"xn--fiqs8s",
|
||||||
"xn--fiqz9s",
|
"xn--fiqz9s",
|
||||||
"xn--fpcrj9c3d",
|
"xn--fpcrj9c3d",
|
||||||
@ -6392,13 +6546,18 @@ var nodeLabels = [...]string{
|
|||||||
"xn--mgbqly7c0a67fbc",
|
"xn--mgbqly7c0a67fbc",
|
||||||
"xn--mgbqly7cvafr",
|
"xn--mgbqly7cvafr",
|
||||||
"xn--mgbtf8fl",
|
"xn--mgbtf8fl",
|
||||||
|
"xn--ngbc5azd",
|
||||||
"xn--nnx388a",
|
"xn--nnx388a",
|
||||||
"xn--node",
|
"xn--node",
|
||||||
"xn--o3cw4h",
|
"xn--o3cw4h",
|
||||||
"xn--ogbpf8fl",
|
"xn--ogbpf8fl",
|
||||||
"xn--p1ai",
|
"xn--p1ai",
|
||||||
"xn--pgbs0dh",
|
"xn--pgbs0dh",
|
||||||
|
"xn--q9jyb4c",
|
||||||
|
"xn--rhqv96g",
|
||||||
"xn--s9brj9c",
|
"xn--s9brj9c",
|
||||||
|
"xn--unup4y",
|
||||||
|
"xn--vhquv",
|
||||||
"xn--wgbh1c",
|
"xn--wgbh1c",
|
||||||
"xn--wgbl6a",
|
"xn--wgbl6a",
|
||||||
"xn--xkc2al3hye2a",
|
"xn--xkc2al3hye2a",
|
||||||
@ -6545,15 +6704,13 @@ var nodeLabels = [...]string{
|
|||||||
"og",
|
"og",
|
||||||
"pb",
|
"pb",
|
||||||
"com",
|
"com",
|
||||||
"congresodelalengua3",
|
"edu",
|
||||||
"educ",
|
"gob",
|
||||||
"gobiernoelectronico",
|
"int",
|
||||||
"mecon",
|
"mil",
|
||||||
"nacion",
|
"net",
|
||||||
"nic",
|
"org",
|
||||||
"promocion",
|
"tur",
|
||||||
"retina",
|
|
||||||
"uba",
|
|
||||||
"blogspot",
|
"blogspot",
|
||||||
"e164",
|
"e164",
|
||||||
"in-addr",
|
"in-addr",
|
||||||
@ -6574,6 +6731,7 @@ var nodeLabels = [...]string{
|
|||||||
"asn",
|
"asn",
|
||||||
"com",
|
"com",
|
||||||
"conf",
|
"conf",
|
||||||
|
"csiro",
|
||||||
"edu",
|
"edu",
|
||||||
"gov",
|
"gov",
|
||||||
"id",
|
"id",
|
||||||
@ -6910,6 +7068,8 @@ var nodeLabels = [...]string{
|
|||||||
"blogspot",
|
"blogspot",
|
||||||
"br",
|
"br",
|
||||||
"cechire",
|
"cechire",
|
||||||
|
"cloudcontrolapp",
|
||||||
|
"cloudcontrolled",
|
||||||
"cn",
|
"cn",
|
||||||
"codespot",
|
"codespot",
|
||||||
"de",
|
"de",
|
||||||
@ -6994,6 +7154,8 @@ var nodeLabels = [...]string{
|
|||||||
"googlecode",
|
"googlecode",
|
||||||
"gotdns",
|
"gotdns",
|
||||||
"gr",
|
"gr",
|
||||||
|
"herokuapp",
|
||||||
|
"herokussl",
|
||||||
"hobby-site",
|
"hobby-site",
|
||||||
"homelinux",
|
"homelinux",
|
||||||
"homeunix",
|
"homeunix",
|
||||||
@ -7066,6 +7228,7 @@ var nodeLabels = [...]string{
|
|||||||
"operaunite",
|
"operaunite",
|
||||||
"qc",
|
"qc",
|
||||||
"rhcloud",
|
"rhcloud",
|
||||||
|
"ro",
|
||||||
"ru",
|
"ru",
|
||||||
"sa",
|
"sa",
|
||||||
"saves-the-whales",
|
"saves-the-whales",
|
||||||
@ -7082,6 +7245,8 @@ var nodeLabels = [...]string{
|
|||||||
"uy",
|
"uy",
|
||||||
"writesthisblog",
|
"writesthisblog",
|
||||||
"za",
|
"za",
|
||||||
|
"compute",
|
||||||
|
"compute-1",
|
||||||
"elb",
|
"elb",
|
||||||
"s3",
|
"s3",
|
||||||
"s3-ap-northeast-1",
|
"s3-ap-northeast-1",
|
||||||
@ -7102,6 +7267,17 @@ var nodeLabels = [...]string{
|
|||||||
"s3-website-us-gov-west-1",
|
"s3-website-us-gov-west-1",
|
||||||
"s3-website-us-west-1",
|
"s3-website-us-west-1",
|
||||||
"s3-website-us-west-2",
|
"s3-website-us-west-2",
|
||||||
|
"us-east-1",
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-southeast-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-west-1",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-gov-west-1",
|
||||||
|
"us-west-1",
|
||||||
|
"us-west-2",
|
||||||
|
"z-1",
|
||||||
|
"z-2",
|
||||||
"ac",
|
"ac",
|
||||||
"co",
|
"co",
|
||||||
"ed",
|
"ed",
|
||||||
@ -10279,6 +10455,7 @@ var nodeLabels = [...]string{
|
|||||||
"dynalias",
|
"dynalias",
|
||||||
"dynathome",
|
"dynathome",
|
||||||
"endofinternet",
|
"endofinternet",
|
||||||
|
"fastly",
|
||||||
"from-az",
|
"from-az",
|
||||||
"from-co",
|
"from-co",
|
||||||
"from-la",
|
"from-la",
|
||||||
@ -10309,6 +10486,13 @@ var nodeLabels = [...]string{
|
|||||||
"uk",
|
"uk",
|
||||||
"webhop",
|
"webhop",
|
||||||
"za",
|
"za",
|
||||||
|
"prod",
|
||||||
|
"ssl",
|
||||||
|
"a",
|
||||||
|
"global",
|
||||||
|
"a",
|
||||||
|
"b",
|
||||||
|
"global",
|
||||||
"arts",
|
"arts",
|
||||||
"com",
|
"com",
|
||||||
"firm",
|
"firm",
|
||||||
@ -10319,12 +10503,15 @@ var nodeLabels = [...]string{
|
|||||||
"rec",
|
"rec",
|
||||||
"store",
|
"store",
|
||||||
"web",
|
"web",
|
||||||
"ac",
|
|
||||||
"com",
|
"com",
|
||||||
"edu",
|
"edu",
|
||||||
"gov",
|
"gov",
|
||||||
|
"mil",
|
||||||
|
"mobi",
|
||||||
|
"name",
|
||||||
"net",
|
"net",
|
||||||
"org",
|
"org",
|
||||||
|
"sch",
|
||||||
"blogspot",
|
"blogspot",
|
||||||
"bv",
|
"bv",
|
||||||
"co",
|
"co",
|
||||||
@ -11106,12 +11293,21 @@ var nodeLabels = [...]string{
|
|||||||
"shacknet",
|
"shacknet",
|
||||||
"co",
|
"co",
|
||||||
"blogspot",
|
"blogspot",
|
||||||
|
"co",
|
||||||
|
"com",
|
||||||
|
"edu",
|
||||||
|
"gov",
|
||||||
|
"med",
|
||||||
"mediaphone",
|
"mediaphone",
|
||||||
|
"museum",
|
||||||
"nawras",
|
"nawras",
|
||||||
"nawrastelecom",
|
"nawrastelecom",
|
||||||
|
"net",
|
||||||
"omanmobile",
|
"omanmobile",
|
||||||
"omanpost",
|
"omanpost",
|
||||||
"omantel",
|
"omantel",
|
||||||
|
"org",
|
||||||
|
"pro",
|
||||||
"rakpetroleum",
|
"rakpetroleum",
|
||||||
"siemens",
|
"siemens",
|
||||||
"songfest",
|
"songfest",
|
||||||
@ -11727,6 +11923,11 @@ var nodeLabels = [...]string{
|
|||||||
"principe",
|
"principe",
|
||||||
"saotome",
|
"saotome",
|
||||||
"store",
|
"store",
|
||||||
|
"com",
|
||||||
|
"edu",
|
||||||
|
"gob",
|
||||||
|
"org",
|
||||||
|
"red",
|
||||||
"gov",
|
"gov",
|
||||||
"com",
|
"com",
|
||||||
"edu",
|
"edu",
|
||||||
|
@ -245,7 +245,7 @@ func TestWithTwoProtocol(t *testing.T) {
|
|||||||
func TestWithBadProtocol(t *testing.T) {
|
func TestWithBadProtocol(t *testing.T) {
|
||||||
_, err := testWithProtocol(t, []string{"test"})
|
_, err := testWithProtocol(t, []string{"test"})
|
||||||
if err != ErrBadStatus {
|
if err != ErrBadStatus {
|
||||||
t.Errorf("SubProto: expected %q, got %q", ErrBadStatus)
|
t.Errorf("SubProto: expected %v, got %v", ErrBadStatus, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
2
third_party/deps
vendored
2
third_party/deps
vendored
@ -1,6 +1,6 @@
|
|||||||
packages="
|
packages="
|
||||||
github.com/BurntSushi/toml
|
github.com/BurntSushi/toml
|
||||||
github.com/coreos/go-raft
|
github.com/coreos/raft
|
||||||
github.com/coreos/go-etcd
|
github.com/coreos/go-etcd
|
||||||
github.com/coreos/go-log/log
|
github.com/coreos/go-log/log
|
||||||
github.com/coreos/go-systemd
|
github.com/coreos/go-systemd
|
||||||
|
1
third_party/github.com/coreos/go-etcd/.gitignore
vendored
Normal file
1
third_party/github.com/coreos/go-etcd/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
config.json
|
43
third_party/github.com/coreos/go-etcd/README.md
vendored
43
third_party/github.com/coreos/go-etcd/README.md
vendored
@ -1,12 +1,8 @@
|
|||||||
# go-etcd
|
# go-etcd
|
||||||
|
|
||||||
golang client library for etcd
|
The official etcd v0.2 client library for Go.
|
||||||
|
|
||||||
This etcd client library is under heavy development. Check back soon for more
|
For usage, please refer to: [go-etcd/etcd](http://godoc.org/github.com/coreos/go-etcd/etcd).
|
||||||
docs. In the meantime, check out [etcd](https://github.com/coreos/etcd) for
|
|
||||||
details on the client protocol.
|
|
||||||
|
|
||||||
For usage see example below or look at godoc: [go-etcd/etcd](http://godoc.org/github.com/coreos/go-etcd/etcd)
|
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
@ -14,37 +10,6 @@ For usage see example below or look at godoc: [go-etcd/etcd](http://godoc.org/gi
|
|||||||
go get github.com/coreos/go-etcd/etcd
|
go get github.com/coreos/go-etcd/etcd
|
||||||
```
|
```
|
||||||
|
|
||||||
## Examples
|
## License
|
||||||
|
|
||||||
Returning error values are not showed for the sake of simplicity, but you
|
See LICENSE file.
|
||||||
should check them.
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/coreos/go-etcd/etcd"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
c := etcd.NewClient() // default binds to http://0.0.0.0:4001
|
|
||||||
|
|
||||||
// SET the value "bar" to the key "foo" with zero TTL
|
|
||||||
// returns a: *Response
|
|
||||||
res, _ := c.Set("foo", "bar", 0)
|
|
||||||
fmt.Printf("set response: %+v\n", res)
|
|
||||||
|
|
||||||
// GET the value that is stored for the key "foo"
|
|
||||||
// return a slice: []*Response
|
|
||||||
values, _ := c.Get("foo")
|
|
||||||
for i, res := range values { // .. and print them out
|
|
||||||
fmt.Printf("[%d] get response: %+v\n", i, res)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DELETE the key "foo"
|
|
||||||
// returns a: *Response
|
|
||||||
res, _ = c.Delete("foo")
|
|
||||||
fmt.Printf("delete response: %+v\n", res)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
11
third_party/github.com/coreos/go-etcd/etcd/add_child.go
vendored
Normal file
11
third_party/github.com/coreos/go-etcd/etcd/add_child.go
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package etcd
|
||||||
|
|
||||||
|
// Add a new directory with a random etcd-generated key under the given path.
|
||||||
|
func (c *Client) AddChildDir(key string, ttl uint64) (*Response, error) {
|
||||||
|
return c.post(key, "", ttl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a new file with a random etcd-generated key under the given path.
|
||||||
|
func (c *Client) AddChild(key string, value string, ttl uint64) (*Response, error) {
|
||||||
|
return c.post(key, value, ttl)
|
||||||
|
}
|
73
third_party/github.com/coreos/go-etcd/etcd/add_child_test.go
vendored
Normal file
73
third_party/github.com/coreos/go-etcd/etcd/add_child_test.go
vendored
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package etcd
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestAddChild(t *testing.T) {
|
||||||
|
c := NewClient(nil)
|
||||||
|
defer func() {
|
||||||
|
c.DeleteAll("fooDir")
|
||||||
|
c.DeleteAll("nonexistentDir")
|
||||||
|
}()
|
||||||
|
|
||||||
|
c.SetDir("fooDir", 5)
|
||||||
|
|
||||||
|
_, err := c.AddChild("fooDir", "v0", 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = c.AddChild("fooDir", "v1", 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.Get("fooDir", true)
|
||||||
|
// The child with v0 should proceed the child with v1 because it's added
|
||||||
|
// earlier, so it should have a lower key.
|
||||||
|
if !(len(resp.Kvs) == 2 && (resp.Kvs[0].Value == "v0" && resp.Kvs[1].Value == "v1")) {
|
||||||
|
t.Fatalf("AddChild 1 failed. There should be two chlidren whose values are v0 and v1, respectively."+
|
||||||
|
" The response was: %#v", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creating a child under a nonexistent directory should succeed.
|
||||||
|
// The directory should be created.
|
||||||
|
resp, err = c.AddChild("nonexistentDir", "foo", 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddChildDir(t *testing.T) {
|
||||||
|
c := NewClient(nil)
|
||||||
|
defer func() {
|
||||||
|
c.DeleteAll("fooDir")
|
||||||
|
c.DeleteAll("nonexistentDir")
|
||||||
|
}()
|
||||||
|
|
||||||
|
c.SetDir("fooDir", 5)
|
||||||
|
|
||||||
|
_, err := c.AddChildDir("fooDir", 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = c.AddChildDir("fooDir", 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.Get("fooDir", true)
|
||||||
|
// The child with v0 should proceed the child with v1 because it's added
|
||||||
|
// earlier, so it should have a lower key.
|
||||||
|
if !(len(resp.Kvs) == 2 && (len(resp.Kvs[0].KVPairs) == 0 && len(resp.Kvs[1].KVPairs) == 0)) {
|
||||||
|
t.Fatalf("AddChildDir 1 failed. There should be two chlidren whose values are v0 and v1, respectively."+
|
||||||
|
" The response was: %#v", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creating a child under a nonexistent directory should succeed.
|
||||||
|
// The directory should be created.
|
||||||
|
resp, err = c.AddChildDir("nonexistentDir", 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
304
third_party/github.com/coreos/go-etcd/etcd/client.go
vendored
304
third_party/github.com/coreos/go-etcd/etcd/client.go
vendored
@ -2,12 +2,16 @@ package etcd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -17,25 +21,43 @@ const (
|
|||||||
HTTPS
|
HTTPS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// See SetConsistency for how to use these constants.
|
||||||
|
const (
|
||||||
|
// Using strings rather than iota because the consistency level
|
||||||
|
// could be persisted to disk, so it'd be better to use
|
||||||
|
// human-readable values.
|
||||||
|
STRONG_CONSISTENCY = "STRONG"
|
||||||
|
WEAK_CONSISTENCY = "WEAK"
|
||||||
|
)
|
||||||
|
|
||||||
type Cluster struct {
|
type Cluster struct {
|
||||||
Leader string
|
Leader string `json:"leader"`
|
||||||
Machines []string
|
Machines []string `json:"machines"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
CertFile string
|
CertFile string `json:"certFile"`
|
||||||
KeyFile string
|
KeyFile string `json:"keyFile"`
|
||||||
Scheme string
|
Scheme string `json:"scheme"`
|
||||||
Timeout time.Duration
|
Timeout time.Duration `json:"timeout"`
|
||||||
|
Consistency string `json: "consistency"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
cluster Cluster
|
cluster Cluster `json:"cluster"`
|
||||||
config Config
|
config Config `json:"config"`
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
|
persistence io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup a basic conf and cluster
|
type options map[string]interface{}
|
||||||
|
|
||||||
|
// An internally-used data structure that represents a mapping
|
||||||
|
// between valid options and their kinds
|
||||||
|
type validOptions map[string]reflect.Kind
|
||||||
|
|
||||||
|
// NewClient create a basic client that is configured to be used
|
||||||
|
// with the given machine list.
|
||||||
func NewClient(machines []string) *Client {
|
func NewClient(machines []string) *Client {
|
||||||
// if an empty slice was sent in then just assume localhost
|
// if an empty slice was sent in then just assume localhost
|
||||||
if len(machines) == 0 {
|
if len(machines) == 0 {
|
||||||
@ -53,30 +75,168 @@ func NewClient(machines []string) *Client {
|
|||||||
Scheme: "http",
|
Scheme: "http",
|
||||||
// default timeout is one second
|
// default timeout is one second
|
||||||
Timeout: time.Second,
|
Timeout: time.Second,
|
||||||
|
// default consistency level is STRONG
|
||||||
|
Consistency: STRONG_CONSISTENCY,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client := &Client{
|
||||||
|
cluster: cluster,
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := setupHttpClient(client)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClientFile creates a client from a given file path.
|
||||||
|
// The given file is expected to use the JSON format.
|
||||||
|
func NewClientFile(fpath string) (*Client, error) {
|
||||||
|
fi, err := os.Open(fpath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := fi.Close(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return NewClientReader(fi)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClientReader creates a Client configured from a given reader.
|
||||||
|
// The config is expected to use the JSON format.
|
||||||
|
func NewClientReader(reader io.Reader) (*Client, error) {
|
||||||
|
var client Client
|
||||||
|
|
||||||
|
b, err := ioutil.ReadAll(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(b, &client)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = setupHttpClient(&client)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupHttpClient(client *Client) error {
|
||||||
|
if client.config.CertFile != "" && client.config.KeyFile != "" {
|
||||||
|
err := client.SetCertAndKey(client.config.CertFile, client.config.KeyFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
client.config.CertFile = ""
|
||||||
|
client.config.KeyFile = ""
|
||||||
tr := &http.Transport{
|
tr := &http.Transport{
|
||||||
Dial: dialTimeout,
|
Dial: dialTimeout,
|
||||||
TLSClientConfig: &tls.Config{
|
TLSClientConfig: &tls.Config{
|
||||||
InsecureSkipVerify: true,
|
InsecureSkipVerify: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
client.httpClient = &http.Client{Transport: tr}
|
||||||
return &Client{
|
|
||||||
cluster: cluster,
|
|
||||||
config: config,
|
|
||||||
httpClient: &http.Client{Transport: tr},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) SetCertAndKey(cert string, key string) (bool, error) {
|
// SetPersistence sets a writer to which the config will be
|
||||||
|
// written every time it's changed.
|
||||||
|
func (c *Client) SetPersistence(writer io.Writer) {
|
||||||
|
c.persistence = writer
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetConsistency changes the consistency level of the client.
|
||||||
|
//
|
||||||
|
// When consistency is set to STRONG_CONSISTENCY, all requests,
|
||||||
|
// including GET, are sent to the leader. This means that, assuming
|
||||||
|
// the absence of leader failures, GET requests are guaranteed to see
|
||||||
|
// the changes made by previous requests.
|
||||||
|
//
|
||||||
|
// When consistency is set to WEAK_CONSISTENCY, other requests
|
||||||
|
// are still sent to the leader, but GET requests are sent to a
|
||||||
|
// random server from the server pool. This reduces the read
|
||||||
|
// load on the leader, but it's not guaranteed that the GET requests
|
||||||
|
// will see changes made by previous requests (they might have not
|
||||||
|
// yet been committed on non-leader servers).
|
||||||
|
func (c *Client) SetConsistency(consistency string) error {
|
||||||
|
if !(consistency == STRONG_CONSISTENCY || consistency == WEAK_CONSISTENCY) {
|
||||||
|
return errors.New("The argument must be either STRONG_CONSISTENCY or WEAK_CONSISTENCY.")
|
||||||
|
}
|
||||||
|
c.config.Consistency = consistency
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements the Marshaller interface
|
||||||
|
// as defined by the standard JSON package.
|
||||||
|
func (c *Client) MarshalJSON() ([]byte, error) {
|
||||||
|
b, err := json.Marshal(struct {
|
||||||
|
Config Config `json:"config"`
|
||||||
|
Cluster Cluster `json:"cluster"`
|
||||||
|
}{
|
||||||
|
Config: c.config,
|
||||||
|
Cluster: c.cluster,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements the Unmarshaller interface
|
||||||
|
// as defined by the standard JSON package.
|
||||||
|
func (c *Client) UnmarshalJSON(b []byte) error {
|
||||||
|
temp := struct {
|
||||||
|
Config Config `json: "config"`
|
||||||
|
Cluster Cluster `json: "cluster"`
|
||||||
|
}{}
|
||||||
|
err := json.Unmarshal(b, &temp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.cluster = temp.Cluster
|
||||||
|
c.config = temp.Config
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// saveConfig saves the current config using c.persistence.
|
||||||
|
func (c *Client) saveConfig() error {
|
||||||
|
if c.persistence != nil {
|
||||||
|
b, err := json.Marshal(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = c.persistence.Write(b)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) SetCertAndKey(cert string, key string) error {
|
||||||
if cert != "" && key != "" {
|
if cert != "" && key != "" {
|
||||||
tlsCert, err := tls.LoadX509KeyPair(cert, key)
|
tlsCert, err := tls.LoadX509KeyPair(cert, key)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tr := &http.Transport{
|
tr := &http.Transport{
|
||||||
@ -88,24 +248,27 @@ func (c *Client) SetCertAndKey(cert string, key string) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.httpClient = &http.Client{Transport: tr}
|
c.httpClient = &http.Client{Transport: tr}
|
||||||
return true, nil
|
c.saveConfig()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return false, errors.New("Require both cert and key path")
|
return errors.New("Require both cert and key path")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) SetScheme(scheme int) (bool, error) {
|
func (c *Client) SetScheme(scheme int) error {
|
||||||
if scheme == HTTP {
|
if scheme == HTTP {
|
||||||
c.config.Scheme = "http"
|
c.config.Scheme = "http"
|
||||||
return true, nil
|
c.saveConfig()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
if scheme == HTTPS {
|
if scheme == HTTPS {
|
||||||
c.config.Scheme = "https"
|
c.config.Scheme = "https"
|
||||||
return true, nil
|
c.saveConfig()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return false, errors.New("Unknown Scheme")
|
return errors.New("Unknown Scheme")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to sync from the given machine
|
// SetCluster updates config using the given machine list.
|
||||||
func (c *Client) SetCluster(machines []string) bool {
|
func (c *Client) SetCluster(machines []string) bool {
|
||||||
success := c.internalSyncCluster(machines)
|
success := c.internalSyncCluster(machines)
|
||||||
return success
|
return success
|
||||||
@ -115,13 +278,13 @@ func (c *Client) GetCluster() []string {
|
|||||||
return c.cluster.Machines
|
return c.cluster.Machines
|
||||||
}
|
}
|
||||||
|
|
||||||
// sycn cluster information using the existing machine list
|
// SyncCluster updates config using the internal machine list.
|
||||||
func (c *Client) SyncCluster() bool {
|
func (c *Client) SyncCluster() bool {
|
||||||
success := c.internalSyncCluster(c.cluster.Machines)
|
success := c.internalSyncCluster(c.cluster.Machines)
|
||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
|
|
||||||
// sync cluster information by providing machine list
|
// internalSyncCluster syncs cluster information using the given machine list.
|
||||||
func (c *Client) internalSyncCluster(machines []string) bool {
|
func (c *Client) internalSyncCluster(machines []string) bool {
|
||||||
for _, machine := range machines {
|
for _, machine := range machines {
|
||||||
httpPath := c.createHttpPath(machine, version+"/machines")
|
httpPath := c.createHttpPath(machine, version+"/machines")
|
||||||
@ -146,16 +309,19 @@ func (c *Client) internalSyncCluster(machines []string) bool {
|
|||||||
c.cluster.Leader = c.cluster.Machines[0]
|
c.cluster.Leader = c.cluster.Machines[0]
|
||||||
|
|
||||||
logger.Debug("sync.machines ", c.cluster.Machines)
|
logger.Debug("sync.machines ", c.cluster.Machines)
|
||||||
|
c.saveConfig()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// serverName should contain both hostName and port
|
// createHttpPath creates a complete HTTP URL.
|
||||||
|
// serverName should contain both the host name and a port number, if any.
|
||||||
func (c *Client) createHttpPath(serverName string, _path string) string {
|
func (c *Client) createHttpPath(serverName string, _path string) string {
|
||||||
u, _ := url.Parse(serverName)
|
u, _ := url.Parse(serverName)
|
||||||
u.Path = path.Join(u.Path, "/", _path)
|
u.Path = path.Join(u.Path, "/", _path)
|
||||||
|
|
||||||
if u.Scheme == "" {
|
if u.Scheme == "" {
|
||||||
u.Scheme = "http"
|
u.Scheme = "http"
|
||||||
}
|
}
|
||||||
@ -167,18 +333,6 @@ func dialTimeout(network, addr string) (net.Conn, error) {
|
|||||||
return net.DialTimeout(network, addr, time.Second)
|
return net.DialTimeout(network, addr, time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) getHttpPath(s ...string) string {
|
|
||||||
u, _ := url.Parse(c.cluster.Leader)
|
|
||||||
|
|
||||||
u.Path = path.Join(u.Path, "/", version)
|
|
||||||
|
|
||||||
for _, seg := range s {
|
|
||||||
u.Path = path.Join(u.Path, seg)
|
|
||||||
}
|
|
||||||
|
|
||||||
return u.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) updateLeader(httpPath string) {
|
func (c *Client) updateLeader(httpPath string) {
|
||||||
u, _ := url.Parse(httpPath)
|
u, _ := url.Parse(httpPath)
|
||||||
|
|
||||||
@ -191,77 +345,5 @@ func (c *Client) updateLeader(httpPath string) {
|
|||||||
|
|
||||||
logger.Debugf("update.leader[%s,%s]", c.cluster.Leader, leader)
|
logger.Debugf("update.leader[%s,%s]", c.cluster.Leader, leader)
|
||||||
c.cluster.Leader = leader
|
c.cluster.Leader = leader
|
||||||
}
|
c.saveConfig()
|
||||||
|
|
||||||
// Wrap GET, POST and internal error handling
|
|
||||||
func (c *Client) sendRequest(method string, _path string, body string) (*http.Response, error) {
|
|
||||||
|
|
||||||
var resp *http.Response
|
|
||||||
var err error
|
|
||||||
var req *http.Request
|
|
||||||
|
|
||||||
retry := 0
|
|
||||||
// if we connect to a follower, we will retry until we found a leader
|
|
||||||
for {
|
|
||||||
|
|
||||||
httpPath := c.getHttpPath(_path)
|
|
||||||
|
|
||||||
logger.Debug("send.request.to ", httpPath)
|
|
||||||
if body == "" {
|
|
||||||
|
|
||||||
req, _ = http.NewRequest(method, httpPath, nil)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
req, _ = http.NewRequest(method, httpPath, strings.NewReader(body))
|
|
||||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err = c.httpClient.Do(req)
|
|
||||||
|
|
||||||
logger.Debug("recv.response.from ", httpPath)
|
|
||||||
// network error, change a machine!
|
|
||||||
if err != nil {
|
|
||||||
retry++
|
|
||||||
if retry > 2*len(c.cluster.Machines) {
|
|
||||||
return nil, errors.New("Cannot reach servers")
|
|
||||||
}
|
|
||||||
num := retry % len(c.cluster.Machines)
|
|
||||||
logger.Debug("update.leader[", c.cluster.Leader, ",", c.cluster.Machines[num], "]")
|
|
||||||
c.cluster.Leader = c.cluster.Machines[num]
|
|
||||||
time.Sleep(time.Millisecond * 200)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp != nil {
|
|
||||||
if resp.StatusCode == http.StatusTemporaryRedirect {
|
|
||||||
httpPath := resp.Header.Get("Location")
|
|
||||||
|
|
||||||
resp.Body.Close()
|
|
||||||
|
|
||||||
if httpPath == "" {
|
|
||||||
return nil, errors.New("Cannot get redirection location")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.updateLeader(httpPath)
|
|
||||||
logger.Debug("send.redirect")
|
|
||||||
// try to connect the leader
|
|
||||||
continue
|
|
||||||
} else if resp.StatusCode == http.StatusInternalServerError {
|
|
||||||
resp.Body.Close()
|
|
||||||
|
|
||||||
retry++
|
|
||||||
if retry > 2*len(c.cluster.Machines) {
|
|
||||||
return nil, errors.New("Cannot reach servers")
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
logger.Debug("send.return.response ", httpPath)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
logger.Debug("error.from ", httpPath, " ", err.Error())
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return resp, nil
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package etcd
|
package etcd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
|
||||||
"net/url"
|
|
||||||
"net"
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
// To pass this test, we need to create a cluster of 3 machines
|
// To pass this test, we need to create a cluster of 3 machines
|
||||||
@ -19,7 +21,7 @@ func TestSync(t *testing.T) {
|
|||||||
t.Fatal("cannot sync machines")
|
t.Fatal("cannot sync machines")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, m := range(c.GetCluster()) {
|
for _, m := range c.GetCluster() {
|
||||||
u, err := url.Parse(m)
|
u, err := url.Parse(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -56,3 +58,37 @@ func TestSync(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPersistence(t *testing.T) {
|
||||||
|
c := NewClient(nil)
|
||||||
|
c.SyncCluster()
|
||||||
|
|
||||||
|
fo, err := os.Create("config.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := fo.Close(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
c.SetPersistence(fo)
|
||||||
|
err = c.saveConfig()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c2, err := NewClientFile("config.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that the two clients have the same config
|
||||||
|
b1, _ := json.Marshal(c)
|
||||||
|
b2, _ := json.Marshal(c2)
|
||||||
|
|
||||||
|
if string(b1) != string(b2) {
|
||||||
|
t.Fatalf("The two configs should be equal!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
18
third_party/github.com/coreos/go-etcd/etcd/compare_and_swap.go
vendored
Normal file
18
third_party/github.com/coreos/go-etcd/etcd/compare_and_swap.go
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package etcd
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func (c *Client) CompareAndSwap(key string, value string, ttl uint64, prevValue string, prevIndex uint64) (*Response, error) {
|
||||||
|
if prevValue == "" && prevIndex == 0 {
|
||||||
|
return nil, fmt.Errorf("You must give either prevValue or prevIndex.")
|
||||||
|
}
|
||||||
|
|
||||||
|
options := options{}
|
||||||
|
if prevValue != "" {
|
||||||
|
options["prevValue"] = prevValue
|
||||||
|
}
|
||||||
|
if prevIndex != 0 {
|
||||||
|
options["prevIndex"] = prevIndex
|
||||||
|
}
|
||||||
|
return c.put(key, value, ttl, options)
|
||||||
|
}
|
51
third_party/github.com/coreos/go-etcd/etcd/compare_and_swap_test.go
vendored
Normal file
51
third_party/github.com/coreos/go-etcd/etcd/compare_and_swap_test.go
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package etcd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCompareAndSwap(t *testing.T) {
|
||||||
|
c := NewClient(nil)
|
||||||
|
defer func() {
|
||||||
|
c.DeleteAll("foo")
|
||||||
|
}()
|
||||||
|
|
||||||
|
c.Set("foo", "bar", 5)
|
||||||
|
|
||||||
|
// This should succeed
|
||||||
|
resp, err := c.CompareAndSwap("foo", "bar2", 5, "bar", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !(resp.Value == "bar2" && resp.PrevValue == "bar" &&
|
||||||
|
resp.Key == "/foo" && resp.TTL == 5) {
|
||||||
|
t.Fatalf("CompareAndSwap 1 failed: %#v", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should fail because it gives an incorrect prevValue
|
||||||
|
resp, err = c.CompareAndSwap("foo", "bar3", 5, "xxx", 0)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("CompareAndSwap 2 should have failed. The response is: %#v", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = c.Set("foo", "bar", 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should succeed
|
||||||
|
resp, err = c.CompareAndSwap("foo", "bar2", 5, "", resp.ModifiedIndex)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !(resp.Value == "bar2" && resp.PrevValue == "bar" &&
|
||||||
|
resp.Key == "/foo" && resp.TTL == 5) {
|
||||||
|
t.Fatalf("CompareAndSwap 1 failed: %#v", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should fail because it gives an incorrect prevIndex
|
||||||
|
resp, err = c.CompareAndSwap("foo", "bar3", 5, "", 29817514)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("CompareAndSwap 2 should have failed. The response is: %#v", resp)
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,8 @@ var logger *log.Logger
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
setLogger(log.PriErr)
|
setLogger(log.PriErr)
|
||||||
|
// Uncomment the following line if you want to see lots of logs
|
||||||
|
// OpenDebug()
|
||||||
}
|
}
|
||||||
|
|
||||||
func OpenDebug() {
|
func OpenDebug() {
|
||||||
|
@ -1,40 +1,17 @@
|
|||||||
package etcd
|
package etcd
|
||||||
|
|
||||||
import (
|
// DeleteAll deletes everything under the given key. If the key
|
||||||
"encoding/json"
|
// points to a file, the file will be deleted. If the key points
|
||||||
"io/ioutil"
|
// to a directory, then everything under the directory, include
|
||||||
"net/http"
|
// all child directories, will be deleted.
|
||||||
"path"
|
func (c *Client) DeleteAll(key string) (*Response, error) {
|
||||||
)
|
return c.delete(key, options{
|
||||||
|
"recursive": true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes the given key. If the key points to a
|
||||||
|
// directory, the method will fail.
|
||||||
func (c *Client) Delete(key string) (*Response, error) {
|
func (c *Client) Delete(key string) (*Response, error) {
|
||||||
|
return c.delete(key, nil)
|
||||||
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
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,18 +5,60 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestDelete(t *testing.T) {
|
func TestDelete(t *testing.T) {
|
||||||
|
|
||||||
c := NewClient(nil)
|
c := NewClient(nil)
|
||||||
|
defer func() {
|
||||||
|
c.DeleteAll("foo")
|
||||||
|
}()
|
||||||
|
|
||||||
c.Set("foo", "bar", 100)
|
c.Set("foo", "bar", 5)
|
||||||
result, err := c.Delete("foo")
|
resp, err := c.Delete("foo")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.PrevValue != "bar" || result.Value != "" {
|
if !(resp.PrevValue == "bar" && resp.Value == "") {
|
||||||
t.Fatalf("Delete failed with %s %s", result.PrevValue,
|
t.Fatalf("Delete failed with %s %s", resp.PrevValue,
|
||||||
result.Value)
|
resp.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resp, err = c.Delete("foo")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Delete should have failed because the key foo did not exist. "+
|
||||||
|
"The response was: %v", resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteAll(t *testing.T) {
|
||||||
|
c := NewClient(nil)
|
||||||
|
defer func() {
|
||||||
|
c.DeleteAll("foo")
|
||||||
|
c.DeleteAll("fooDir")
|
||||||
|
}()
|
||||||
|
|
||||||
|
c.Set("foo", "bar", 5)
|
||||||
|
resp, err := c.DeleteAll("foo")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(resp.PrevValue == "bar" && resp.Value == "") {
|
||||||
|
t.Fatalf("DeleteAll 1 failed: %#v", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SetDir("fooDir", 5)
|
||||||
|
c.Set("fooDir/foo", "bar", 5)
|
||||||
|
resp, err = c.DeleteAll("fooDir")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(resp.PrevValue == "" && resp.Value == "") {
|
||||||
|
t.Fatalf("DeleteAll 2 failed: %#v", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = c.DeleteAll("foo")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("DeleteAll should have failed because the key foo did not exist. "+
|
||||||
|
"The response was: %v", resp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,82 +1,23 @@
|
|||||||
package etcd
|
package etcd
|
||||||
|
|
||||||
import (
|
// GetDir gets the all contents under the given key.
|
||||||
"encoding/json"
|
// If the key points to a file, the file is returned.
|
||||||
"io/ioutil"
|
// If the key points to a directory, everything under it is returnd,
|
||||||
"net/http"
|
// including all contents under all child directories.
|
||||||
"path"
|
func (c *Client) GetAll(key string, sort bool) (*Response, error) {
|
||||||
)
|
return c.get(key, options{
|
||||||
|
"recursive": true,
|
||||||
func (c *Client) Get(key string) ([]*Response, error) {
|
"sorted": sort,
|
||||||
logger.Debugf("get %s [%s]", key, c.cluster.Leader)
|
})
|
||||||
resp, err := c.sendRequest("GET", path.Join("keys", key), "")
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := ioutil.ReadAll(resp.Body)
|
// Get gets the file or directory associated with the given key.
|
||||||
|
// If the key points to a directory, files and directories under
|
||||||
resp.Body.Close()
|
// it will be returned in sorted or unsorted order, depending on
|
||||||
|
// the sort flag. Note that contents under child directories
|
||||||
if err != nil {
|
// will not be returned. To get those contents, use GetAll.
|
||||||
return nil, err
|
func (c *Client) Get(key string, sort bool) (*Response, error) {
|
||||||
}
|
return c.get(key, options{
|
||||||
|
"sorted": sort,
|
||||||
if resp.StatusCode != http.StatusOK {
|
})
|
||||||
|
|
||||||
return nil, handleError(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
return convertGetResponse(b)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
@ -1,46 +1,99 @@
|
|||||||
package etcd
|
package etcd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
|
|
||||||
c := NewClient(nil)
|
c := NewClient(nil)
|
||||||
|
defer func() {
|
||||||
|
c.DeleteAll("foo")
|
||||||
|
}()
|
||||||
|
|
||||||
c.Set("foo", "bar", 100)
|
c.Set("foo", "bar", 5)
|
||||||
|
|
||||||
// wait for commit
|
result, err := c.Get("foo", false)
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
|
|
||||||
results, err := c.Get("foo")
|
|
||||||
|
|
||||||
if err != nil || results[0].Key != "/foo" || results[0].Value != "bar" {
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Fatalf("Get failed with %s %s %v", results[0].Key, results[0].Value, results[0].TTL)
|
|
||||||
|
if result.Key != "/foo" || result.Value != "bar" {
|
||||||
|
t.Fatalf("Get failed with %s %s %v", result.Key, result.Value, result.TTL)
|
||||||
}
|
}
|
||||||
|
|
||||||
results, err = c.Get("goo")
|
result, err = c.Get("goo", false)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("should not be able to get non-exist key")
|
t.Fatalf("should not be able to get non-exist key")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
results, err = c.GetFrom("foo", "0.0.0.0:4001")
|
func TestGetAll(t *testing.T) {
|
||||||
|
c := NewClient(nil)
|
||||||
|
defer func() {
|
||||||
|
c.DeleteAll("fooDir")
|
||||||
|
}()
|
||||||
|
|
||||||
|
c.SetDir("fooDir", 5)
|
||||||
|
c.Set("fooDir/k0", "v0", 5)
|
||||||
|
c.Set("fooDir/k1", "v1", 5)
|
||||||
|
|
||||||
|
// Return kv-pairs in sorted order
|
||||||
|
result, err := c.Get("fooDir", true)
|
||||||
|
|
||||||
if err != nil || results[0].Key != "/foo" || results[0].Value != "bar" {
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Fatalf("Get failed with %s %s %v", results[0].Key, results[0].Value, results[0].TTL)
|
|
||||||
|
expected := kvPairs{
|
||||||
|
KeyValuePair{
|
||||||
|
Key: "/fooDir/k0",
|
||||||
|
Value: "v0",
|
||||||
|
},
|
||||||
|
KeyValuePair{
|
||||||
|
Key: "/fooDir/k1",
|
||||||
|
Value: "v1",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
results, err = c.GetFrom("foo", "0.0.0.0:4009")
|
if !reflect.DeepEqual(result.Kvs, expected) {
|
||||||
|
t.Fatalf("(actual) %v != (expected) %v", result.Kvs, expected)
|
||||||
|
}
|
||||||
|
|
||||||
if err == nil {
|
// Test the `recursive` option
|
||||||
t.Fatal("should not get from port 4009")
|
c.SetDir("fooDir/childDir", 5)
|
||||||
|
c.Set("fooDir/childDir/k2", "v2", 5)
|
||||||
|
|
||||||
|
// Return kv-pairs in sorted order
|
||||||
|
result, err = c.GetAll("fooDir", true)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected = kvPairs{
|
||||||
|
KeyValuePair{
|
||||||
|
Key: "/fooDir/childDir",
|
||||||
|
Dir: true,
|
||||||
|
KVPairs: kvPairs{
|
||||||
|
KeyValuePair{
|
||||||
|
Key: "/fooDir/childDir/k2",
|
||||||
|
Value: "v2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
KeyValuePair{
|
||||||
|
Key: "/fooDir/k0",
|
||||||
|
Value: "v0",
|
||||||
|
},
|
||||||
|
KeyValuePair{
|
||||||
|
Key: "/fooDir/k1",
|
||||||
|
Value: "v1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(result.Kvs, expected) {
|
||||||
|
t.Fatalf("(actual) %v != (expected) %v", result.Kvs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
package etcd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestList(t *testing.T) {
|
|
||||||
c := NewClient(nil)
|
|
||||||
|
|
||||||
c.Set("foo_list/foo", "bar", 100)
|
|
||||||
c.Set("foo_list/fooo", "barbar", 100)
|
|
||||||
c.Set("foo_list/foooo/foo", "barbarbar", 100)
|
|
||||||
// wait for commit
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
|
|
||||||
_, err := c.Get("foo_list")
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
290
third_party/github.com/coreos/go-etcd/etcd/requests.go
vendored
Normal file
290
third_party/github.com/coreos/go-etcd/etcd/requests.go
vendored
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
package etcd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Valid options for GET, PUT, POST, DELETE
|
||||||
|
// Using CAPITALIZED_UNDERSCORE to emphasize that these
|
||||||
|
// values are meant to be used as constants.
|
||||||
|
var (
|
||||||
|
VALID_GET_OPTIONS = validOptions{
|
||||||
|
"recursive": reflect.Bool,
|
||||||
|
"consistent": reflect.Bool,
|
||||||
|
"sorted": reflect.Bool,
|
||||||
|
"wait": reflect.Bool,
|
||||||
|
"waitIndex": reflect.Uint64,
|
||||||
|
}
|
||||||
|
|
||||||
|
VALID_PUT_OPTIONS = validOptions{
|
||||||
|
"prevValue": reflect.String,
|
||||||
|
"prevIndex": reflect.Uint64,
|
||||||
|
"prevExist": reflect.Bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
VALID_POST_OPTIONS = validOptions{}
|
||||||
|
|
||||||
|
VALID_DELETE_OPTIONS = validOptions{
|
||||||
|
"recursive": reflect.Bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
curlChan chan string
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetCurlChan sets a channel to which cURL commands which can be used to
|
||||||
|
// re-produce requests are sent. This is useful for debugging.
|
||||||
|
func SetCurlChan(c chan string) {
|
||||||
|
curlChan = c
|
||||||
|
}
|
||||||
|
|
||||||
|
// get issues a GET request
|
||||||
|
func (c *Client) get(key string, options options) (*Response, error) {
|
||||||
|
logger.Debugf("get %s [%s]", key, c.cluster.Leader)
|
||||||
|
|
||||||
|
p := path.Join("keys", key)
|
||||||
|
// If consistency level is set to STRONG, append
|
||||||
|
// the `consistent` query string.
|
||||||
|
if c.config.Consistency == STRONG_CONSISTENCY {
|
||||||
|
options["consistent"] = true
|
||||||
|
}
|
||||||
|
if options != nil {
|
||||||
|
str, err := optionsToString(options, VALID_GET_OPTIONS)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p += str
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.sendRequest("GET", p, url.Values{})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// put issues a PUT request
|
||||||
|
func (c *Client) put(key string, value string, ttl uint64, options options) (*Response, error) {
|
||||||
|
logger.Debugf("put %s, %s, ttl: %d, [%s]", key, value, ttl, c.cluster.Leader)
|
||||||
|
v := url.Values{}
|
||||||
|
|
||||||
|
if value != "" {
|
||||||
|
v.Set("value", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ttl > 0 {
|
||||||
|
v.Set("ttl", fmt.Sprintf("%v", ttl))
|
||||||
|
}
|
||||||
|
|
||||||
|
p := path.Join("keys", key)
|
||||||
|
if options != nil {
|
||||||
|
str, err := optionsToString(options, VALID_PUT_OPTIONS)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p += str
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.sendRequest("PUT", p, v)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// post issues a POST request
|
||||||
|
func (c *Client) post(key string, value string, ttl uint64) (*Response, error) {
|
||||||
|
logger.Debugf("post %s, %s, ttl: %d, [%s]", key, value, ttl, c.cluster.Leader)
|
||||||
|
v := url.Values{}
|
||||||
|
|
||||||
|
if value != "" {
|
||||||
|
v.Set("value", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ttl > 0 {
|
||||||
|
v.Set("ttl", fmt.Sprintf("%v", ttl))
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.sendRequest("POST", path.Join("keys", key), v)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete issues a DELETE request
|
||||||
|
func (c *Client) delete(key string, options options) (*Response, error) {
|
||||||
|
logger.Debugf("delete %s [%s]", key, c.cluster.Leader)
|
||||||
|
v := url.Values{}
|
||||||
|
|
||||||
|
p := path.Join("keys", key)
|
||||||
|
if options != nil {
|
||||||
|
str, err := optionsToString(options, VALID_DELETE_OPTIONS)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p += str
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.sendRequest("DELETE", p, v)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendRequest sends a HTTP request and returns a Response as defined by etcd
|
||||||
|
func (c *Client) sendRequest(method string, _path string, values url.Values) (*Response, error) {
|
||||||
|
var body string = values.Encode()
|
||||||
|
var resp *http.Response
|
||||||
|
var req *http.Request
|
||||||
|
|
||||||
|
retry := 0
|
||||||
|
// if we connect to a follower, we will retry until we found a leader
|
||||||
|
for {
|
||||||
|
var httpPath string
|
||||||
|
|
||||||
|
// If _path has schema already, then it's assumed to be
|
||||||
|
// a complete URL and therefore needs no further processing.
|
||||||
|
u, err := url.Parse(_path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Scheme != "" {
|
||||||
|
httpPath = _path
|
||||||
|
} else {
|
||||||
|
if method == "GET" && c.config.Consistency == WEAK_CONSISTENCY {
|
||||||
|
// If it's a GET and consistency level is set to WEAK,
|
||||||
|
// then use a random machine.
|
||||||
|
httpPath = c.getHttpPath(true, _path)
|
||||||
|
} else {
|
||||||
|
// Else use the leader.
|
||||||
|
httpPath = c.getHttpPath(false, _path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a cURL command if curlChan is set
|
||||||
|
if curlChan != nil {
|
||||||
|
command := fmt.Sprintf("curl -X %s %s", method, httpPath)
|
||||||
|
for key, value := range values {
|
||||||
|
command += fmt.Sprintf(" -d %s=%s", key, value[0])
|
||||||
|
}
|
||||||
|
curlChan <- command
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug("send.request.to ", httpPath, " | method ", method)
|
||||||
|
if body == "" {
|
||||||
|
|
||||||
|
req, _ = http.NewRequest(method, httpPath, nil)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
req, _ = http.NewRequest(method, httpPath, strings.NewReader(body))
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = c.httpClient.Do(req)
|
||||||
|
|
||||||
|
logger.Debug("recv.response.from ", httpPath)
|
||||||
|
// network error, change a machine!
|
||||||
|
if err != nil {
|
||||||
|
retry++
|
||||||
|
if retry > 2*len(c.cluster.Machines) {
|
||||||
|
return nil, errors.New("Cannot reach servers")
|
||||||
|
}
|
||||||
|
num := retry % len(c.cluster.Machines)
|
||||||
|
logger.Debug("update.leader[", c.cluster.Leader, ",", c.cluster.Machines[num], "]")
|
||||||
|
c.cluster.Leader = c.cluster.Machines[num]
|
||||||
|
time.Sleep(time.Millisecond * 200)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp != nil {
|
||||||
|
if resp.StatusCode == http.StatusTemporaryRedirect {
|
||||||
|
httpPath := resp.Header.Get("Location")
|
||||||
|
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
if httpPath == "" {
|
||||||
|
return nil, errors.New("Cannot get redirection location")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.updateLeader(httpPath)
|
||||||
|
logger.Debug("send.redirect")
|
||||||
|
// try to connect the leader
|
||||||
|
continue
|
||||||
|
} else if resp.StatusCode == http.StatusInternalServerError {
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
retry++
|
||||||
|
if retry > 2*len(c.cluster.Machines) {
|
||||||
|
return nil, errors.New("Cannot reach servers")
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
logger.Debug("send.return.response ", httpPath)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
logger.Debug("error.from ", httpPath, " ", err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert HTTP response to etcd response
|
||||||
|
b, err := ioutil.ReadAll(resp.Body)
|
||||||
|
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(resp.StatusCode == http.StatusOK ||
|
||||||
|
resp.StatusCode == http.StatusCreated) {
|
||||||
|
return nil, handleError(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
var result Response
|
||||||
|
|
||||||
|
err = json.Unmarshal(b, &result)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) getHttpPath(random bool, s ...string) string {
|
||||||
|
var machine string
|
||||||
|
if random {
|
||||||
|
machine = c.cluster.Machines[rand.Intn(len(c.cluster.Machines))]
|
||||||
|
} else {
|
||||||
|
machine = c.cluster.Leader
|
||||||
|
}
|
||||||
|
|
||||||
|
fullPath := machine + "/" + version
|
||||||
|
for _, seg := range s {
|
||||||
|
fullPath = fullPath + "/" + seg
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullPath
|
||||||
|
}
|
@ -11,6 +11,7 @@ type Response struct {
|
|||||||
Dir bool `json:"dir,omitempty"`
|
Dir bool `json:"dir,omitempty"`
|
||||||
PrevValue string `json:"prevValue,omitempty"`
|
PrevValue string `json:"prevValue,omitempty"`
|
||||||
Value string `json:"value,omitempty"`
|
Value string `json:"value,omitempty"`
|
||||||
|
Kvs kvPairs `json:"kvs,omitempty"`
|
||||||
|
|
||||||
// If the key did not exist before the action,
|
// If the key did not exist before the action,
|
||||||
// this field should be set to true
|
// this field should be set to true
|
||||||
@ -22,5 +23,29 @@ type Response struct {
|
|||||||
TTL int64 `json:"ttl,omitempty"`
|
TTL int64 `json:"ttl,omitempty"`
|
||||||
|
|
||||||
// The command index of the raft machine when the command is executed
|
// The command index of the raft machine when the command is executed
|
||||||
Index uint64 `json:"index"`
|
ModifiedIndex uint64 `json:"modifiedIndex"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// When user list a directory, we add all the node into key-value pair slice
|
||||||
|
type KeyValuePair struct {
|
||||||
|
Key string `json:"key, omitempty"`
|
||||||
|
Value string `json:"value,omitempty"`
|
||||||
|
Dir bool `json:"dir,omitempty"`
|
||||||
|
KVPairs kvPairs `json:"kvs,omitempty"`
|
||||||
|
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]
|
||||||
}
|
}
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
package etcd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"path"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Client) Set(key string, value string, ttl uint64) (*Response, error) {
|
|
||||||
logger.Debugf("set %s, %s, ttl: %d, [%s]", key, value, ttl, c.cluster.Leader)
|
|
||||||
v := url.Values{}
|
|
||||||
v.Set("value", value)
|
|
||||||
|
|
||||||
if ttl > 0 {
|
|
||||||
v.Set("ttl", fmt.Sprintf("%v", ttl))
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := c.sendRequest("POST", path.Join("keys", key), v.Encode())
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := ioutil.ReadAll(resp.Body)
|
|
||||||
|
|
||||||
resp.Body.Close()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
|
|
||||||
return nil, handleError(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
return convertSetResponse(b)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTo sets the value of the key to a given machine address.
|
|
||||||
// If the given machine is not available or is not leader it returns an error
|
|
||||||
// Mainly use for testing purpose.
|
|
||||||
func (c *Client) SetTo(key string, value string, ttl uint64, addr string) (*Response, error) {
|
|
||||||
v := url.Values{}
|
|
||||||
v.Set("value", value)
|
|
||||||
|
|
||||||
if ttl > 0 {
|
|
||||||
v.Set("ttl", fmt.Sprintf("%v", ttl))
|
|
||||||
}
|
|
||||||
|
|
||||||
httpPath := c.createHttpPath(addr, path.Join(version, "keys", key))
|
|
||||||
|
|
||||||
resp, err := c.httpClient.PostForm(httpPath, v)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := ioutil.ReadAll(resp.Body)
|
|
||||||
|
|
||||||
resp.Body.Close()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return nil, handleError(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
return convertSetResponse(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert byte stream to response.
|
|
||||||
func convertSetResponse(b []byte) (*Response, error) {
|
|
||||||
var result Response
|
|
||||||
|
|
||||||
err := json.Unmarshal(b, &result)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &result, nil
|
|
||||||
}
|
|
43
third_party/github.com/coreos/go-etcd/etcd/set_curl_chan_test.go
vendored
Normal file
43
third_party/github.com/coreos/go-etcd/etcd/set_curl_chan_test.go
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package etcd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSetCurlChan(t *testing.T) {
|
||||||
|
c := NewClient(nil)
|
||||||
|
defer func() {
|
||||||
|
c.DeleteAll("foo")
|
||||||
|
}()
|
||||||
|
|
||||||
|
curlChan := make(chan string, 1)
|
||||||
|
SetCurlChan(curlChan)
|
||||||
|
|
||||||
|
_, err := c.Set("foo", "bar", 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := fmt.Sprintf("curl -X PUT %s/v2/keys/foo -d value=bar -d ttl=5",
|
||||||
|
c.cluster.Leader)
|
||||||
|
actual := <-curlChan
|
||||||
|
if expected != actual {
|
||||||
|
t.Fatalf(`Command "%s" is not equal to expected value "%s"`,
|
||||||
|
actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SetConsistency(STRONG_CONSISTENCY)
|
||||||
|
_, err = c.Get("foo", false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected = fmt.Sprintf("curl -X GET %s/v2/keys/foo?consistent=true&sorted=false",
|
||||||
|
c.cluster.Leader)
|
||||||
|
actual = <-curlChan
|
||||||
|
if expected != actual {
|
||||||
|
t.Fatalf(`Command "%s" is not equal to expected value "%s"`,
|
||||||
|
actual, expected)
|
||||||
|
}
|
||||||
|
}
|
@ -1,42 +0,0 @@
|
|||||||
package etcd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSet(t *testing.T) {
|
|
||||||
c := NewClient(nil)
|
|
||||||
|
|
||||||
result, err := c.Set("foo", "bar", 100)
|
|
||||||
|
|
||||||
if err != nil || result.Key != "/foo" || result.Value != "bar" || result.TTL != 99 {
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Fatalf("Set 1 failed with %s %s %v", result.Key, result.Value, result.TTL)
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
|
|
||||||
result, err = c.Set("foo", "bar", 100)
|
|
||||||
|
|
||||||
if err != nil || result.Key != "/foo" || result.Value != "bar" || result.PrevValue != "bar" || result.TTL != 99 {
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
t.Fatalf("Set 2 failed with %s %s %v", result.Key, result.Value, result.TTL)
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err = c.SetTo("toFoo", "bar", 100, "0.0.0.0:4001")
|
|
||||||
|
|
||||||
if err != nil || result.Key != "/toFoo" || result.Value != "bar" || result.TTL != 99 {
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Fatalf("SetTo failed with %s %s %v", result.Key, result.Value, result.TTL)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
43
third_party/github.com/coreos/go-etcd/etcd/set_update_create.go
vendored
Normal file
43
third_party/github.com/coreos/go-etcd/etcd/set_update_create.go
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package etcd
|
||||||
|
|
||||||
|
// SetDir sets the given key to a directory.
|
||||||
|
func (c *Client) SetDir(key string, ttl uint64) (*Response, error) {
|
||||||
|
return c.put(key, "", ttl, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateDir updates the given key to a directory. It succeeds only if the
|
||||||
|
// given key already exists.
|
||||||
|
func (c *Client) UpdateDir(key string, ttl uint64) (*Response, error) {
|
||||||
|
return c.put(key, "", ttl, options{
|
||||||
|
"prevExist": true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateDir creates a directory under the given key. It succeeds only if
|
||||||
|
// the given key does not yet exist.
|
||||||
|
func (c *Client) CreateDir(key string, ttl uint64) (*Response, error) {
|
||||||
|
return c.put(key, "", ttl, options{
|
||||||
|
"prevExist": false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the given key to the given value.
|
||||||
|
func (c *Client) Set(key string, value string, ttl uint64) (*Response, error) {
|
||||||
|
return c.put(key, value, ttl, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update updates the given key to the given value. It succeeds only if the
|
||||||
|
// given key already exists.
|
||||||
|
func (c *Client) Update(key string, value string, ttl uint64) (*Response, error) {
|
||||||
|
return c.put(key, value, ttl, options{
|
||||||
|
"prevExist": true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create creates a file with the given value under the given key. It succeeds
|
||||||
|
// only if the given key does not yet exist.
|
||||||
|
func (c *Client) Create(key string, value string, ttl uint64) (*Response, error) {
|
||||||
|
return c.put(key, value, ttl, options{
|
||||||
|
"prevExist": false,
|
||||||
|
})
|
||||||
|
}
|
183
third_party/github.com/coreos/go-etcd/etcd/set_update_create_test.go
vendored
Normal file
183
third_party/github.com/coreos/go-etcd/etcd/set_update_create_test.go
vendored
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
package etcd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSet(t *testing.T) {
|
||||||
|
c := NewClient(nil)
|
||||||
|
defer func() {
|
||||||
|
c.DeleteAll("foo")
|
||||||
|
}()
|
||||||
|
|
||||||
|
resp, err := c.Set("foo", "bar", 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if resp.Key != "/foo" || resp.Value != "bar" || resp.TTL != 5 {
|
||||||
|
t.Fatalf("Set 1 failed: %#v", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = c.Set("foo", "bar2", 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !(resp.Key == "/foo" && resp.Value == "bar2" &&
|
||||||
|
resp.PrevValue == "bar" && resp.TTL == 5) {
|
||||||
|
t.Fatalf("Set 2 failed: %#v", resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdate(t *testing.T) {
|
||||||
|
c := NewClient(nil)
|
||||||
|
defer func() {
|
||||||
|
c.DeleteAll("foo")
|
||||||
|
c.DeleteAll("nonexistent")
|
||||||
|
}()
|
||||||
|
|
||||||
|
resp, err := c.Set("foo", "bar", 5)
|
||||||
|
t.Logf("%#v", resp)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should succeed.
|
||||||
|
resp, err = c.Update("foo", "wakawaka", 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(resp.Action == "update" && resp.Key == "/foo" &&
|
||||||
|
resp.PrevValue == "bar" && resp.TTL == 5) {
|
||||||
|
t.Fatalf("Update 1 failed: %#v", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should fail because the key does not exist.
|
||||||
|
resp, err = c.Update("nonexistent", "whatever", 5)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("The key %v did not exist, so the update should have failed."+
|
||||||
|
"The response was: %#v", resp.Key, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreate(t *testing.T) {
|
||||||
|
c := NewClient(nil)
|
||||||
|
defer func() {
|
||||||
|
c.DeleteAll("newKey")
|
||||||
|
}()
|
||||||
|
|
||||||
|
newKey := "/newKey"
|
||||||
|
newValue := "/newValue"
|
||||||
|
|
||||||
|
// This should succeed
|
||||||
|
resp, err := c.Create(newKey, newValue, 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(resp.Action == "create" && resp.Key == newKey &&
|
||||||
|
resp.Value == newValue && resp.PrevValue == "" && resp.TTL == 5) {
|
||||||
|
t.Fatalf("Create 1 failed: %#v", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should fail, because the key is already there
|
||||||
|
resp, err = c.Create(newKey, newValue, 5)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("The key %v did exist, so the creation should have failed."+
|
||||||
|
"The response was: %#v", resp.Key, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetDir(t *testing.T) {
|
||||||
|
c := NewClient(nil)
|
||||||
|
defer func() {
|
||||||
|
c.DeleteAll("foo")
|
||||||
|
c.DeleteAll("fooDir")
|
||||||
|
}()
|
||||||
|
|
||||||
|
resp, err := c.SetDir("fooDir", 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !(resp.Key == "/fooDir" && resp.Value == "" && resp.TTL == 5) {
|
||||||
|
t.Fatalf("SetDir 1 failed: %#v", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should fail because /fooDir already points to a directory
|
||||||
|
resp, err = c.SetDir("/fooDir", 5)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("fooDir already points to a directory, so SetDir should have failed."+
|
||||||
|
"The response was: %#v", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = c.Set("foo", "bar", 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should succeed
|
||||||
|
resp, err = c.SetDir("foo", 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !(resp.Key == "/foo" && resp.Value == "" &&
|
||||||
|
resp.PrevValue == "bar" && resp.TTL == 5) {
|
||||||
|
t.Fatalf("SetDir 2 failed: %#v", resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateDir(t *testing.T) {
|
||||||
|
c := NewClient(nil)
|
||||||
|
defer func() {
|
||||||
|
c.DeleteAll("fooDir")
|
||||||
|
}()
|
||||||
|
|
||||||
|
resp, err := c.SetDir("fooDir", 5)
|
||||||
|
t.Logf("%#v", resp)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should succeed.
|
||||||
|
resp, err = c.UpdateDir("fooDir", 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(resp.Action == "update" && resp.Key == "/fooDir" &&
|
||||||
|
resp.Value == "" && resp.PrevValue == "" && resp.TTL == 5) {
|
||||||
|
t.Fatalf("UpdateDir 1 failed: %#v", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should fail because the key does not exist.
|
||||||
|
resp, err = c.UpdateDir("nonexistentDir", 5)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("The key %v did not exist, so the update should have failed."+
|
||||||
|
"The response was: %#v", resp.Key, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateDir(t *testing.T) {
|
||||||
|
c := NewClient(nil)
|
||||||
|
defer func() {
|
||||||
|
c.DeleteAll("fooDir")
|
||||||
|
}()
|
||||||
|
|
||||||
|
// This should succeed
|
||||||
|
resp, err := c.CreateDir("fooDir", 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(resp.Action == "create" && resp.Key == "/fooDir" &&
|
||||||
|
resp.Value == "" && resp.PrevValue == "" && resp.TTL == 5) {
|
||||||
|
t.Fatalf("CreateDir 1 failed: %#v", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should fail, because the key is already there
|
||||||
|
resp, err = c.CreateDir("fooDir", 5)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("The key %v did exist, so the creation should have failed."+
|
||||||
|
"The response was: %#v", resp.Key, resp)
|
||||||
|
}
|
||||||
|
}
|
@ -1,56 +0,0 @@
|
|||||||
package etcd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"path"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Client) TestAndSet(key string, prevValue string, value string, ttl uint64) (*Response, bool, error) {
|
|
||||||
logger.Debugf("set %s, %s[%s], ttl: %d, [%s]", key, value, prevValue, ttl, c.cluster.Leader)
|
|
||||||
v := url.Values{}
|
|
||||||
v.Set("value", value)
|
|
||||||
v.Set("prevValue", prevValue)
|
|
||||||
|
|
||||||
if ttl > 0 {
|
|
||||||
v.Set("ttl", fmt.Sprintf("%v", ttl))
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := c.sendRequest("POST", path.Join("keys", key), v.Encode())
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := ioutil.ReadAll(resp.Body)
|
|
||||||
|
|
||||||
resp.Body.Close()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return nil, false, handleError(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
var result Response
|
|
||||||
|
|
||||||
err = json.Unmarshal(b, &result)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.PrevValue == prevValue && result.Value == value {
|
|
||||||
|
|
||||||
return &result, true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &result, false, nil
|
|
||||||
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package etcd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTestAndSet(t *testing.T) {
|
|
||||||
c := NewClient(nil)
|
|
||||||
|
|
||||||
c.Set("foo_testAndSet", "bar", 100)
|
|
||||||
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
|
|
||||||
results := make(chan bool, 3)
|
|
||||||
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
testAndSet("foo_testAndSet", "bar", "barbar", results, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
count := 0
|
|
||||||
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
result := <-results
|
|
||||||
if result {
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if count != 1 {
|
|
||||||
t.Fatalf("test and set fails %v", count)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func testAndSet(key string, prevValue string, value string, ch chan bool, c *Client) {
|
|
||||||
_, success, _ := c.TestAndSet(key, prevValue, value, 0)
|
|
||||||
ch <- success
|
|
||||||
}
|
|
33
third_party/github.com/coreos/go-etcd/etcd/utils.go
vendored
Normal file
33
third_party/github.com/coreos/go-etcd/etcd/utils.go
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// Utility functions
|
||||||
|
|
||||||
|
package etcd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Convert options to a string of HTML parameters
|
||||||
|
func optionsToString(options options, vops validOptions) (string, error) {
|
||||||
|
p := "?"
|
||||||
|
v := url.Values{}
|
||||||
|
for opKey, opVal := range options {
|
||||||
|
// Check if the given option is valid (that it exists)
|
||||||
|
kind := vops[opKey]
|
||||||
|
if kind == reflect.Invalid {
|
||||||
|
return "", fmt.Errorf("Invalid option: %v", opKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the given option is of the valid type
|
||||||
|
t := reflect.TypeOf(opVal)
|
||||||
|
if kind != t.Kind() {
|
||||||
|
return "", fmt.Errorf("Option %s should be of %v kind, not of %v kind.",
|
||||||
|
opKey, kind, t.Kind())
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Set(opKey, fmt.Sprintf("%v", opVal))
|
||||||
|
}
|
||||||
|
p += v.Encode()
|
||||||
|
return p, nil
|
||||||
|
}
|
@ -1,3 +1,3 @@
|
|||||||
package etcd
|
package etcd
|
||||||
|
|
||||||
const version = "v1"
|
const version = "v2"
|
||||||
|
124
third_party/github.com/coreos/go-etcd/etcd/watch.go
vendored
124
third_party/github.com/coreos/go-etcd/etcd/watch.go
vendored
@ -1,42 +1,47 @@
|
|||||||
package etcd
|
package etcd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"path"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type respAndErr struct {
|
|
||||||
resp *http.Response
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Errors introduced by the Watch command.
|
// Errors introduced by the Watch command.
|
||||||
var (
|
var (
|
||||||
ErrWatchStoppedByUser = errors.New("Watch stopped by the user via stop channel")
|
ErrWatchStoppedByUser = errors.New("Watch stopped by the user via stop channel")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Watch any change under the given prefix.
|
// WatchAll returns the first change under the given prefix since the given index. To
|
||||||
// When a sinceIndex is given, watch will try to scan from that index to the last index
|
// watch for the latest change, set waitIndex = 0.
|
||||||
// and will return any changes under the given prefix during the history
|
//
|
||||||
|
// If the prefix points to a directory, any change under it, including all child directories,
|
||||||
|
// will be returned.
|
||||||
|
//
|
||||||
// If a receiver channel is given, it will be a long-term watch. Watch will block at the
|
// If a receiver channel is given, it will be a long-term watch. Watch will block at the
|
||||||
// channel. And after someone receive the channel, it will go on to watch that prefix.
|
// channel. And after someone receive the channel, it will go on to watch that prefix.
|
||||||
// If a stop channel is given, client can close long-term watch using the stop channel
|
// If a stop channel is given, client can close long-term watch using the stop channel
|
||||||
|
func (c *Client) WatchAll(prefix string, waitIndex uint64, receiver chan *Response, stop chan bool) (*Response, error) {
|
||||||
|
return c.watch(prefix, waitIndex, true, receiver, stop)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) Watch(prefix string, sinceIndex uint64, receiver chan *Response, stop chan bool) (*Response, error) {
|
// Watch returns the first change to the given key since the given index. To
|
||||||
|
// watch for the latest change, set waitIndex = 0.
|
||||||
|
//
|
||||||
|
// If a receiver channel is given, it will be a long-term watch. Watch will block at the
|
||||||
|
// channel. And after someone receive the channel, it will go on to watch that
|
||||||
|
// prefix. If a stop channel is given, client can close long-term watch using
|
||||||
|
// the stop channel
|
||||||
|
func (c *Client) Watch(key string, waitIndex uint64, receiver chan *Response, stop chan bool) (*Response, error) {
|
||||||
|
return c.watch(key, waitIndex, false, receiver, stop)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) watch(prefix string, waitIndex uint64, recursive bool, receiver chan *Response, stop chan bool) (*Response, error) {
|
||||||
logger.Debugf("watch %s [%s]", prefix, c.cluster.Leader)
|
logger.Debugf("watch %s [%s]", prefix, c.cluster.Leader)
|
||||||
if receiver == nil {
|
if receiver == nil {
|
||||||
return c.watchOnce(prefix, sinceIndex, stop)
|
return c.watchOnce(prefix, waitIndex, recursive, stop)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
for {
|
for {
|
||||||
resp, err := c.watchOnce(prefix, sinceIndex, stop)
|
resp, err := c.watchOnce(prefix, waitIndex, recursive, stop)
|
||||||
if resp != nil {
|
if resp != nil {
|
||||||
sinceIndex = resp.Index + 1
|
waitIndex = resp.ModifiedIndex + 1
|
||||||
receiver <- resp
|
receiver <- resp
|
||||||
} else {
|
} else {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -49,70 +54,37 @@ func (c *Client) Watch(prefix string, sinceIndex uint64, receiver chan *Response
|
|||||||
|
|
||||||
// helper func
|
// helper func
|
||||||
// return when there is change under the given prefix
|
// return when there is change under the given prefix
|
||||||
func (c *Client) watchOnce(key string, sinceIndex uint64, stop chan bool) (*Response, error) {
|
func (c *Client) watchOnce(key string, waitIndex uint64, recursive bool, stop chan bool) (*Response, error) {
|
||||||
|
|
||||||
var resp *http.Response
|
respChan := make(chan *Response)
|
||||||
var err error
|
errChan := make(chan error)
|
||||||
|
|
||||||
if stop != nil {
|
|
||||||
ch := make(chan respAndErr)
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
resp, err = c.sendWatchRequest(key, sinceIndex)
|
options := options{
|
||||||
|
"wait": true,
|
||||||
|
}
|
||||||
|
if waitIndex > 0 {
|
||||||
|
options["waitIndex"] = waitIndex
|
||||||
|
}
|
||||||
|
if recursive {
|
||||||
|
options["recursive"] = true
|
||||||
|
}
|
||||||
|
|
||||||
ch <- respAndErr{resp, err}
|
resp, err := c.get(key, options)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
errChan <- err
|
||||||
|
}
|
||||||
|
|
||||||
|
respChan <- resp
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// select at stop or continue to receive
|
|
||||||
select {
|
select {
|
||||||
|
case resp := <-respChan:
|
||||||
case res := <-ch:
|
return resp, nil
|
||||||
resp, err = res.resp, res.err
|
case err := <-errChan:
|
||||||
|
return nil, err
|
||||||
case <-stop:
|
case <-stop:
|
||||||
resp, err = nil, ErrWatchStoppedByUser
|
return nil, ErrWatchStoppedByUser
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
resp, err = c.sendWatchRequest(key, sinceIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := ioutil.ReadAll(resp.Body)
|
|
||||||
|
|
||||||
resp.Body.Close()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
|
|
||||||
return nil, handleError(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
var result Response
|
|
||||||
|
|
||||||
err = json.Unmarshal(b, &result)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) sendWatchRequest(key string, sinceIndex uint64) (*http.Response, error) {
|
|
||||||
if sinceIndex == 0 {
|
|
||||||
resp, err := c.sendRequest("GET", path.Join("watch", key), "")
|
|
||||||
return resp, err
|
|
||||||
} else {
|
|
||||||
v := url.Values{}
|
|
||||||
v.Set("index", fmt.Sprintf("%v", sinceIndex))
|
|
||||||
resp, err := c.sendRequest("POST", path.Join("watch", key), v.Encode())
|
|
||||||
return resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,31 +8,34 @@ import (
|
|||||||
|
|
||||||
func TestWatch(t *testing.T) {
|
func TestWatch(t *testing.T) {
|
||||||
c := NewClient(nil)
|
c := NewClient(nil)
|
||||||
|
defer func() {
|
||||||
|
c.DeleteAll("watch_foo")
|
||||||
|
}()
|
||||||
|
|
||||||
go setHelper("bar", c)
|
go setHelper("watch_foo", "bar", c)
|
||||||
|
|
||||||
result, err := c.Watch("watch_foo", 0, nil, nil)
|
resp, err := c.Watch("watch_foo", 0, nil, nil)
|
||||||
|
|
||||||
if err != nil || result.Key != "/watch_foo/foo" || result.Value != "bar" {
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Fatalf("Watch failed with %s %s %v %v", result.Key, result.Value, result.TTL, result.Index)
|
if !(resp.Key == "/watch_foo" && resp.Value == "bar") {
|
||||||
|
t.Fatalf("Watch 1 failed: %#v", resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err = c.Watch("watch_foo", result.Index, nil, nil)
|
go setHelper("watch_foo", "bar", c)
|
||||||
|
|
||||||
if err != nil || result.Key != "/watch_foo/foo" || result.Value != "bar" {
|
resp, err = c.Watch("watch_foo", resp.ModifiedIndex, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Fatalf("Watch with Index failed with %s %s %v %v", result.Key, result.Value, result.TTL, result.Index)
|
if !(resp.Key == "/watch_foo" && resp.Value == "bar") {
|
||||||
|
t.Fatalf("Watch 2 failed: %#v", resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
ch := make(chan *Response, 10)
|
ch := make(chan *Response, 10)
|
||||||
stop := make(chan bool, 1)
|
stop := make(chan bool, 1)
|
||||||
|
|
||||||
go setLoop("bar", c)
|
go setLoop("watch_foo", "bar", c)
|
||||||
|
|
||||||
go receiver(ch, stop)
|
go receiver(ch, stop)
|
||||||
|
|
||||||
@ -42,16 +45,55 @@ func TestWatch(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setHelper(value string, c *Client) {
|
func TestWatchAll(t *testing.T) {
|
||||||
time.Sleep(time.Second)
|
c := NewClient(nil)
|
||||||
c.Set("watch_foo/foo", value, 100)
|
defer func() {
|
||||||
|
c.DeleteAll("watch_foo")
|
||||||
|
}()
|
||||||
|
|
||||||
|
go setHelper("watch_foo/foo", "bar", c)
|
||||||
|
|
||||||
|
resp, err := c.WatchAll("watch_foo", 0, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !(resp.Key == "/watch_foo/foo" && resp.Value == "bar") {
|
||||||
|
t.Fatalf("WatchAll 1 failed: %#v", resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setLoop(value string, c *Client) {
|
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(key, value, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setLoop(key, value string, c *Client) {
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
newValue := fmt.Sprintf("%s_%v", value, i)
|
newValue := fmt.Sprintf("%s_%v", value, i)
|
||||||
c.Set("watch_foo/foo", newValue, 100)
|
c.Set(key, newValue, 100)
|
||||||
time.Sleep(time.Second / 10)
|
time.Sleep(time.Second / 10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/coreos/go-etcd/etcd"
|
|
||||||
)
|
|
||||||
|
|
||||||
var count = 0
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
good := 0
|
|
||||||
bad := 0
|
|
||||||
|
|
||||||
ch := make(chan bool, 10)
|
|
||||||
// set up a lock
|
|
||||||
c := etcd.NewClient()
|
|
||||||
c.Set("lock", "unlock", 0)
|
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
go t(i, ch, etcd.NewClient())
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
if <-ch {
|
|
||||||
good++
|
|
||||||
} else {
|
|
||||||
bad++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Println("good: ", good, "bad: ", bad)
|
|
||||||
}
|
|
||||||
|
|
||||||
func t(num int, ch chan bool, c *etcd.Client) {
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
if lock(c) {
|
|
||||||
// a stupid spin lock
|
|
||||||
count++
|
|
||||||
fmt.Println(num, " got the lock and update count to", count)
|
|
||||||
unlock(c)
|
|
||||||
fmt.Println(num, " released the lock")
|
|
||||||
} else {
|
|
||||||
ch <- false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ch <- true
|
|
||||||
}
|
|
||||||
|
|
||||||
// A stupid spin lock
|
|
||||||
func lock(c *etcd.Client) bool {
|
|
||||||
for {
|
|
||||||
_, success, _ := c.TestAndSet("lock", "unlock", "lock", 0)
|
|
||||||
|
|
||||||
if success != true {
|
|
||||||
fmt.Println("tried lock failed!")
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func unlock(c *etcd.Client) {
|
|
||||||
for {
|
|
||||||
_, err := c.Set("lock", "unlock", 0)
|
|
||||||
if err == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/coreos/go-etcd/etcd"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var count = 0
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
ch := make(chan bool, 10)
|
|
||||||
// set up a lock
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
go t(i, ch, etcd.NewClient())
|
|
||||||
}
|
|
||||||
start := time.Now()
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
<-ch
|
|
||||||
}
|
|
||||||
fmt.Println(time.Now().Sub(start), ": ", 100*50, "commands")
|
|
||||||
}
|
|
||||||
|
|
||||||
func t(num int, ch chan bool, c *etcd.Client) {
|
|
||||||
c.SyncCluster()
|
|
||||||
for i := 0; i < 50; i++ {
|
|
||||||
str := fmt.Sprintf("foo_%d", num*i)
|
|
||||||
c.Set(str, "10", 0)
|
|
||||||
}
|
|
||||||
ch <- true
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
Example script from the sync-cluster bug https://github.com/coreos/go-etcd/issues/27
|
|
||||||
|
|
||||||
TODO: turn this into a test case
|
|
@ -1,51 +0,0 @@
|
|||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/coreos/go-etcd/etcd"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
fmt.Println("etcd-client started")
|
|
||||||
c := etcd.NewClient(nil)
|
|
||||||
c.SetCluster([]string{
|
|
||||||
"http://127.0.0.1:4001",
|
|
||||||
"http://127.0.0.1:4002",
|
|
||||||
"http://127.0.0.1:4003",
|
|
||||||
})
|
|
||||||
|
|
||||||
ticker := time.NewTicker(time.Second * 3)
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case d := <-ticker.C:
|
|
||||||
n := d.Second()
|
|
||||||
if n <= 0 {
|
|
||||||
n = 60
|
|
||||||
}
|
|
||||||
|
|
||||||
for ok := c.SyncCluster(); ok == false; {
|
|
||||||
fmt.Println("SyncCluster failed, trying again")
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := c.Set("foo", "exp_"+strconv.Itoa(n), 0)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("set error", err)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("set %+v\n", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
ss, err := c.Get("foo")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("get error", err)
|
|
||||||
} else {
|
|
||||||
fmt.Println(len(ss))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
170
third_party/github.com/coreos/go-raft/timer.go
vendored
170
third_party/github.com/coreos/go-raft/timer.go
vendored
@ -1,170 +0,0 @@
|
|||||||
package raft
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/rand"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
// Typedefs
|
|
||||||
//
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
type timer struct {
|
|
||||||
fireChan chan time.Time
|
|
||||||
stopChan chan bool
|
|
||||||
state int
|
|
||||||
|
|
||||||
rand *rand.Rand
|
|
||||||
minDuration time.Duration
|
|
||||||
maxDuration time.Duration
|
|
||||||
internalTimer *time.Timer
|
|
||||||
|
|
||||||
mutex sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
STOPPED = iota
|
|
||||||
READY
|
|
||||||
RUNNING
|
|
||||||
)
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
// Constructors
|
|
||||||
//
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Creates a new timer. Panics if a non-positive duration is used.
|
|
||||||
func newTimer(minDuration time.Duration, maxDuration time.Duration) *timer {
|
|
||||||
if minDuration <= 0 {
|
|
||||||
panic("raft: Non-positive minimum duration not allowed")
|
|
||||||
} else if maxDuration <= 0 {
|
|
||||||
panic("raft: Non-positive maximum duration not allowed")
|
|
||||||
} else if minDuration > maxDuration {
|
|
||||||
panic("raft: Minimum duration cannot be greater than maximum duration")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &timer{
|
|
||||||
minDuration: minDuration,
|
|
||||||
maxDuration: maxDuration,
|
|
||||||
state: READY,
|
|
||||||
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
|
|
||||||
stopChan: make(chan bool, 1),
|
|
||||||
fireChan: make(chan time.Time),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
// Accessors
|
|
||||||
//
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Sets the minimum and maximum duration of the timer.
|
|
||||||
func (t *timer) setDuration(duration time.Duration) {
|
|
||||||
t.minDuration = duration
|
|
||||||
t.maxDuration = duration
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
// Methods
|
|
||||||
//
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Checks if the timer is currently running.
|
|
||||||
func (t *timer) running() bool {
|
|
||||||
return t.state == RUNNING
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stops the timer and closes the channel.
|
|
||||||
func (t *timer) stop() {
|
|
||||||
t.mutex.Lock()
|
|
||||||
defer t.mutex.Unlock()
|
|
||||||
|
|
||||||
if t.internalTimer != nil {
|
|
||||||
t.internalTimer.Stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.state != STOPPED {
|
|
||||||
t.state = STOPPED
|
|
||||||
|
|
||||||
// non-blocking buffer
|
|
||||||
t.stopChan <- true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change the state of timer to ready
|
|
||||||
func (t *timer) ready() {
|
|
||||||
t.mutex.Lock()
|
|
||||||
defer t.mutex.Unlock()
|
|
||||||
|
|
||||||
if t.state == RUNNING {
|
|
||||||
panic("Timer is already running")
|
|
||||||
}
|
|
||||||
t.state = READY
|
|
||||||
t.stopChan = make(chan bool, 1)
|
|
||||||
t.fireChan = make(chan time.Time)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fire at the timer
|
|
||||||
func (t *timer) fire() {
|
|
||||||
select {
|
|
||||||
case t.fireChan <- time.Now():
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the timer, this func will be blocked until the timer:
|
|
||||||
// (1) times out
|
|
||||||
// (2) stopped
|
|
||||||
// (3) fired
|
|
||||||
// Return false if stopped.
|
|
||||||
// Make sure the start func will not restart the stopped timer.
|
|
||||||
func (t *timer) start() bool {
|
|
||||||
t.mutex.Lock()
|
|
||||||
|
|
||||||
if t.state != READY {
|
|
||||||
t.mutex.Unlock()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
t.state = RUNNING
|
|
||||||
|
|
||||||
d := t.minDuration
|
|
||||||
|
|
||||||
if t.maxDuration > t.minDuration {
|
|
||||||
d += time.Duration(t.rand.Int63n(int64(t.maxDuration - t.minDuration)))
|
|
||||||
}
|
|
||||||
|
|
||||||
t.internalTimer = time.NewTimer(d)
|
|
||||||
internalTimer := t.internalTimer
|
|
||||||
|
|
||||||
t.mutex.Unlock()
|
|
||||||
|
|
||||||
// Wait for the timer channel, stop channel or fire channel.
|
|
||||||
stopped := false
|
|
||||||
select {
|
|
||||||
case <-internalTimer.C:
|
|
||||||
case <-t.fireChan:
|
|
||||||
case <-t.stopChan:
|
|
||||||
stopped = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up timer and state.
|
|
||||||
t.mutex.Lock()
|
|
||||||
t.internalTimer.Stop()
|
|
||||||
t.internalTimer = nil
|
|
||||||
if stopped {
|
|
||||||
t.state = STOPPED
|
|
||||||
} else if t.state == RUNNING {
|
|
||||||
t.state = READY
|
|
||||||
}
|
|
||||||
t.mutex.Unlock()
|
|
||||||
|
|
||||||
return !stopped
|
|
||||||
}
|
|
@ -1,86 +0,0 @@
|
|||||||
package raft
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
// Tests
|
|
||||||
//
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Ensure that we can start an election timer and it will go off in the specified duration.
|
|
||||||
func TestTimer(t *testing.T) {
|
|
||||||
timer := newTimer(5*time.Millisecond, 10*time.Millisecond)
|
|
||||||
|
|
||||||
// test timer start
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
start := time.Now()
|
|
||||||
timer.start()
|
|
||||||
|
|
||||||
duration := time.Now().Sub(start)
|
|
||||||
if duration > 12*time.Millisecond || duration < 5*time.Millisecond {
|
|
||||||
t.Fatal("Duration Error! ", duration)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// test timer stop
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
start := time.Now()
|
|
||||||
go stop(timer)
|
|
||||||
timer.start()
|
|
||||||
|
|
||||||
duration := time.Now().Sub(start)
|
|
||||||
if duration > 3*time.Millisecond {
|
|
||||||
t.Fatal("Duration Error! ", duration)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ready the timer after stop it
|
|
||||||
timer.ready()
|
|
||||||
}
|
|
||||||
|
|
||||||
// test timer fire
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
start := time.Now()
|
|
||||||
go fire(timer)
|
|
||||||
timer.start()
|
|
||||||
|
|
||||||
duration := time.Now().Sub(start)
|
|
||||||
if duration > 3*time.Millisecond {
|
|
||||||
t.Fatal("Fire Duration Error! ", duration)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resp := make(chan bool)
|
|
||||||
|
|
||||||
// play with start and stop
|
|
||||||
// make sure we can stop timer
|
|
||||||
// in all the possible seq of start and stop
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
go stop(timer)
|
|
||||||
go start(timer, resp)
|
|
||||||
ret := <-resp
|
|
||||||
if ret != false {
|
|
||||||
t.Fatal("cannot stop timer!")
|
|
||||||
}
|
|
||||||
timer.ready()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func stop(t *timer) {
|
|
||||||
time.Sleep(time.Millisecond)
|
|
||||||
t.stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
func start(t *timer, resp chan bool) {
|
|
||||||
time.Sleep(time.Millisecond)
|
|
||||||
resp <- t.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
func fire(t *timer) {
|
|
||||||
time.Sleep(time.Millisecond)
|
|
||||||
t.fire()
|
|
||||||
}
|
|
@ -12,7 +12,15 @@ const (
|
|||||||
listenFdsStart = 3
|
listenFdsStart = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
func Files() []*os.File {
|
func Files(unsetEnv bool) []*os.File {
|
||||||
|
|
||||||
|
if unsetEnv {
|
||||||
|
// there is no way to unset env in golang os package for now
|
||||||
|
// https://code.google.com/p/go/issues/detail?id=6423
|
||||||
|
defer os.Setenv("LISTEN_PID", "")
|
||||||
|
defer os.Setenv("LISTEN_FDS", "")
|
||||||
|
}
|
||||||
|
|
||||||
pid, err := strconv.Atoi(os.Getenv("LISTEN_PID"))
|
pid, err := strconv.Atoi(os.Getenv("LISTEN_PID"))
|
||||||
if err != nil || pid != os.Getpid() {
|
if err != nil || pid != os.Getpid() {
|
||||||
return nil
|
return nil
|
||||||
|
@ -25,32 +25,36 @@ type Conn struct {
|
|||||||
dispatch map[string]func(dbus.Signal)
|
dispatch map[string]func(dbus.Signal)
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *Conn {
|
func New() (*Conn, error) {
|
||||||
c := new(Conn)
|
c := new(Conn)
|
||||||
c.initConnection()
|
|
||||||
|
if err := c.initConnection(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
c.initJobs()
|
c.initJobs()
|
||||||
c.initSubscription()
|
c.initSubscription()
|
||||||
c.initDispatch()
|
c.initDispatch()
|
||||||
return c
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) initConnection() {
|
func (c *Conn) initConnection() error {
|
||||||
var err error
|
var err error
|
||||||
c.sysconn, err = dbus.SystemBusPrivate()
|
c.sysconn, err = dbus.SystemBusPrivate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.sysconn.Auth(nil)
|
err = c.sysconn.Auth(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.sysconn.Close()
|
c.sysconn.Close()
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.sysconn.Hello()
|
err = c.sysconn.Hello()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.sysconn.Close()
|
c.sysconn.Close()
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.sysobj = c.sysconn.Object("org.freedesktop.systemd1", dbus.ObjectPath("/org/freedesktop/systemd1"))
|
c.sysobj = c.sysconn.Object("org.freedesktop.systemd1", dbus.ObjectPath("/org/freedesktop/systemd1"))
|
||||||
@ -65,8 +69,10 @@ func (c *Conn) initConnection() {
|
|||||||
err = c.sysobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store()
|
err = c.sysobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.sysconn.Close()
|
c.sysconn.Close()
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) initDispatch() {
|
func (c *Conn) initDispatch() {
|
||||||
|
@ -164,3 +164,53 @@ type UnitStatus struct {
|
|||||||
JobType string // The job type as string
|
JobType string // The job type as string
|
||||||
JobPath dbus.ObjectPath // The job object path
|
JobPath dbus.ObjectPath // The job object path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EnableUnitFiles() may be used to enable one or more units in the system (by
|
||||||
|
// creating symlinks to them in /etc or /run).
|
||||||
|
//
|
||||||
|
// It takes a list of unit files to enable (either just file names or full
|
||||||
|
// absolute paths if the unit files are residing outside the usual unit
|
||||||
|
// search paths), and two booleans: the first controls whether the unit shall
|
||||||
|
// be enabled for runtime only (true, /run), or persistently (false, /etc).
|
||||||
|
// The second one controls whether symlinks pointing to other units shall
|
||||||
|
// be replaced if necessary.
|
||||||
|
//
|
||||||
|
// This call returns one boolean and an array with the changes made. The
|
||||||
|
// boolean signals whether the unit files contained any enablement
|
||||||
|
// information (i.e. an [Install]) section. The changes list consists of
|
||||||
|
// structures with three strings: the type of the change (one of symlink
|
||||||
|
// or unlink), the file name of the symlink and the destination of the
|
||||||
|
// symlink.
|
||||||
|
func (c *Conn) EnableUnitFiles(files []string, runtime bool, force bool) (bool, []EnableUnitFileChange, error) {
|
||||||
|
var carries_install_info bool
|
||||||
|
|
||||||
|
result := make([][]interface{}, 0)
|
||||||
|
err := c.sysobj.Call("EnableUnitFiles", 0, files, runtime, force).Store(&carries_install_info, &result)
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resultInterface := make([]interface{}, len(result))
|
||||||
|
for i := range result {
|
||||||
|
resultInterface[i] = result[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
changes := make([]EnableUnitFileChange, len(result))
|
||||||
|
changesInterface := make([]interface{}, len(changes))
|
||||||
|
for i := range changes {
|
||||||
|
changesInterface[i] = &changes[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dbus.Store(resultInterface, changesInterface...)
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return carries_install_info, changes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type EnableUnitFileChange struct {
|
||||||
|
Type string // Type of the change (one of symlink or unlink)
|
||||||
|
Filename string // File name of the symlink
|
||||||
|
Destination string // Destination of the symlink
|
||||||
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user