mirror of
				https://github.com/moby/moby.git
				synced 2022-11-09 12:21:53 -05:00 
			
		
		
		
	libnetwork: remove consul-related code and tests
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
		
							parent
							
								
									25594c33b9
								
							
						
					
					
						commit
						147173b099
					
				
					 24 changed files with 2 additions and 3988 deletions
				
			
		| 
						 | 
					@ -114,7 +114,7 @@ type ScopeClientCfg struct {
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	// LocalScope indicates to store the KV object in local datastore such as boltdb
 | 
						// LocalScope indicates to store the KV object in local datastore such as boltdb
 | 
				
			||||||
	LocalScope = "local"
 | 
						LocalScope = "local"
 | 
				
			||||||
	// GlobalScope indicates to store the KV object in global datastore such as consul/etcd
 | 
						// GlobalScope indicates to store the KV object in global datastore such as etcd
 | 
				
			||||||
	GlobalScope = "global"
 | 
						GlobalScope = "global"
 | 
				
			||||||
	// SwarmScope is not indicating a datastore location. It is defined here
 | 
						// SwarmScope is not indicating a datastore location. It is defined here
 | 
				
			||||||
	// along with the other two scopes just for consistency.
 | 
						// along with the other two scopes just for consistency.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,153 +0,0 @@
 | 
				
			||||||
# Overlay Driver
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Design
 | 
					 | 
				
			||||||
TODO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Multi-Host Overlay Driver Quick Start
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This example is to provision two Docker Hosts with the **experimental** Libnetwork overlay network driver.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Pre-Requisites
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- Kernel >= 3.16
 | 
					 | 
				
			||||||
- Experimental Docker client
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Install Docker Experimental
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Follow Docker experimental installation instructions at: [https://github.com/docker/docker/tree/master/experimental](https://github.com/docker/docker/tree/master/experimental)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
To ensure you are running the experimental Docker branch, check the version and look for the experimental tag:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
$ docker -v
 | 
					 | 
				
			||||||
Docker version 1.8.0-dev, build f39b9a0, experimental
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Install and Bootstrap K/V Store
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Multi-host networking uses a pluggable Key-Value store backend to distribute states using `libkv`.
 | 
					 | 
				
			||||||
`libkv` supports multiple pluggable backends such as `consul`, `etcd` (more to come).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
In this example we will use `consul`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Install:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
$ curl -OL https://dl.bintray.com/mitchellh/consul/0.5.2_linux_amd64.zip
 | 
					 | 
				
			||||||
$ unzip 0.5.2_linux_amd64.zip
 | 
					 | 
				
			||||||
$ mv consul /usr/local/bin/
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
**host-1** Start Consul as a server in bootstrap mode:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
``` 
 | 
					 | 
				
			||||||
$ consul agent -server -bootstrap -data-dir /tmp/consul -bind=<host-1-ip-address>
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
**host-2** Start the Consul agent:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
``` 
 | 
					 | 
				
			||||||
$ consul agent -data-dir /tmp/consul -bind=<host-2-ip-address>
 | 
					 | 
				
			||||||
$ consul join <host-1-ip-address>
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Start the Docker Daemon with the Network Driver Daemon Flags
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
**host-1** Docker daemon:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
$ docker -d --kv-store=consul:localhost:8500 --label=com.docker.network.driver.overlay.bind_interface=eth0
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
**host-2** Start the Docker Daemon with the neighbor ID configuration:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
$ docker -d --kv-store=consul:localhost:8500 --label=com.docker.network.driver.overlay.bind_interface=eth0 --label=com.docker.network.driver.overlay.neighbor_ip=<host-1-ip-address>
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### QuickStart Containers Attached to a Network
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
**host-1** Start a container that publishes a service svc1 in the network dev that is managed by overlay driver.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
$ docker run -i -t --publish-service=svc1.dev.overlay debian
 | 
					 | 
				
			||||||
root@21578ff721a9:/# ip add show eth0
 | 
					 | 
				
			||||||
34: eth0: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
 | 
					 | 
				
			||||||
    link/ether 02:42:ec:41:35:bf brd ff:ff:ff:ff:ff:ff
 | 
					 | 
				
			||||||
    inet 172.21.0.16/16 scope global eth0
 | 
					 | 
				
			||||||
       valid_lft forever preferred_lft forever
 | 
					 | 
				
			||||||
    inet6 fe80::42:ecff:fe41:35bf/64 scope link
 | 
					 | 
				
			||||||
       valid_lft forever preferred_lft forever
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
**host-2** Start a container that publishes a service svc2 in the network dev that is managed by overlay driver.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
$ docker run -i -t --publish-service=svc2.dev.overlay debian
 | 
					 | 
				
			||||||
root@d217828eb876:/# ping svc1
 | 
					 | 
				
			||||||
PING svc1 (172.21.0.16): 56 data bytes
 | 
					 | 
				
			||||||
64 bytes from 172.21.0.16: icmp_seq=0 ttl=64 time=0.706 ms
 | 
					 | 
				
			||||||
64 bytes from 172.21.0.16: icmp_seq=1 ttl=64 time=0.687 ms
 | 
					 | 
				
			||||||
64 bytes from 172.21.0.16: icmp_seq=2 ttl=64 time=0.841 ms
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
### Detailed Setup
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
You can also setup networks and services and then attach a running container to them.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
**host-1**:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
docker network create -d overlay prod 
 | 
					 | 
				
			||||||
docker network ls
 | 
					 | 
				
			||||||
docker network info prod
 | 
					 | 
				
			||||||
docker service publish db1.prod
 | 
					 | 
				
			||||||
cid=$(docker run -itd -p 8000:8000 ubuntu)
 | 
					 | 
				
			||||||
docker service attach $cid db1.prod
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
**host-2**:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
docker network ls
 | 
					 | 
				
			||||||
docker network info prod
 | 
					 | 
				
			||||||
docker service publish db2.prod
 | 
					 | 
				
			||||||
cid=$(docker run -itd -p 8000:8000 ubuntu)
 | 
					 | 
				
			||||||
docker service attach $cid db2.prod
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Once a container is started, a container on `host-1` and `host-2` both containers should be able to ping one another via IP, service name, \<service name>.\<network name>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
View information about the networks and services using `ls` and `info` subcommands like so:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
$ docker service ls
 | 
					 | 
				
			||||||
SERVICE ID          NAME                  NETWORK             CONTAINER
 | 
					 | 
				
			||||||
0771deb5f84b        db2                   prod                0e54a527f22c
 | 
					 | 
				
			||||||
aea23b224acf        db1                   prod                4b0a309ca311
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
$ docker network info prod
 | 
					 | 
				
			||||||
Network Id: 5ac68be2518959b48ad102e9ec3d8f42fb2ec72056aa9592eb5abd0252203012
 | 
					 | 
				
			||||||
	Name: prod
 | 
					 | 
				
			||||||
	Type: overlay
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
$ docker service info db1.prod
 | 
					 | 
				
			||||||
Service Id: aea23b224acfd2da9b893870e0d632499188a1a4b3881515ba042928a9d3f465
 | 
					 | 
				
			||||||
	Name: db1
 | 
					 | 
				
			||||||
	Network: prod
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
To detach and unpublish a service:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
$ docker service detach $cid <service>.<network>
 | 
					 | 
				
			||||||
$ docker service unpublish <service>.<network>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Example:
 | 
					 | 
				
			||||||
$ docker service detach $cid  db2.prod
 | 
					 | 
				
			||||||
$ docker service unpublish db2.prod
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
To reiterate, this is experimental, and will be under active development.
 | 
					 | 
				
			||||||
| 
						 | 
					@ -6,13 +6,11 @@ import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/docker/docker/libnetwork/datastore"
 | 
						"github.com/docker/docker/libnetwork/datastore"
 | 
				
			||||||
	"github.com/docker/libkv/store/boltdb"
 | 
						"github.com/docker/libkv/store/boltdb"
 | 
				
			||||||
	"github.com/docker/libkv/store/consul"
 | 
					 | 
				
			||||||
	"github.com/docker/libkv/store/etcd"
 | 
						"github.com/docker/libkv/store/etcd"
 | 
				
			||||||
	"github.com/sirupsen/logrus"
 | 
						"github.com/sirupsen/logrus"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func registerKVStores() {
 | 
					func registerKVStores() {
 | 
				
			||||||
	consul.Register()
 | 
					 | 
				
			||||||
	etcd.Register()
 | 
						etcd.Register()
 | 
				
			||||||
	boltdb.Register()
 | 
						boltdb.Register()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -40,23 +40,6 @@ function net_disconnect() {
 | 
				
			||||||
	dnet_cmd $(inst_id2port ${1}) service unpublish ${2}.${3}
 | 
						dnet_cmd $(inst_id2port ${1}) service unpublish ${2}.${3}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function start_consul() {
 | 
					 | 
				
			||||||
    stop_consul
 | 
					 | 
				
			||||||
    docker run -d \
 | 
					 | 
				
			||||||
	   --name=pr_consul \
 | 
					 | 
				
			||||||
	   -p 8500:8500 \
 | 
					 | 
				
			||||||
	   -p 8300-8302:8300-8302/tcp \
 | 
					 | 
				
			||||||
	   -p 8300-8302:8300-8302/udp \
 | 
					 | 
				
			||||||
	   -h consul \
 | 
					 | 
				
			||||||
	   progrium/consul -server -bootstrap
 | 
					 | 
				
			||||||
    sleep 2
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function stop_consul() {
 | 
					 | 
				
			||||||
    echo "consul started"
 | 
					 | 
				
			||||||
    docker rm -f pr_consul || true
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
hrun() {
 | 
					hrun() {
 | 
				
			||||||
    local e E T oldIFS
 | 
					    local e E T oldIFS
 | 
				
			||||||
    [[ ! "$-" =~ e ]] || e=1
 | 
					    [[ ! "$-" =~ e ]] || e=1
 | 
				
			||||||
| 
						 | 
					@ -151,8 +134,6 @@ function start_dnet() {
 | 
				
			||||||
    neighbors=""
 | 
					    neighbors=""
 | 
				
			||||||
    if [ "$store" = "etcd" ]; then
 | 
					    if [ "$store" = "etcd" ]; then
 | 
				
			||||||
	read discovery provider address < <(parse_discovery_str etcd://${bridge_ip}:42000/custom_prefix)
 | 
						read discovery provider address < <(parse_discovery_str etcd://${bridge_ip}:42000/custom_prefix)
 | 
				
			||||||
    elif [ "$store" = "consul" ]; then
 | 
					 | 
				
			||||||
	read discovery provider address < <(parse_discovery_str consul://${bridge_ip}:8500/custom_prefix)
 | 
					 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
	if [ "$nip" != "" ]; then
 | 
						if [ "$nip" != "" ]; then
 | 
				
			||||||
	    neighbors=${nip}
 | 
						    neighbors=${nip}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +0,0 @@
 | 
				
			||||||
# -*- mode: sh -*-
 | 
					 | 
				
			||||||
#!/usr/bin/env bats
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
load helpers
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@test "Test overlay network hostmode with consul" {
 | 
					 | 
				
			||||||
    test_overlay_hostmode consul
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,56 +0,0 @@
 | 
				
			||||||
# -*- mode: sh -*-
 | 
					 | 
				
			||||||
#!/usr/bin/env bats
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
load helpers
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@test "Test overlay network with consul" {
 | 
					 | 
				
			||||||
    test_overlay consul
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@test "Test overlay network singlehost with consul" {
 | 
					 | 
				
			||||||
    test_overlay_singlehost consul
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@test "Test overlay network with dnet restart" {
 | 
					 | 
				
			||||||
    test_overlay consul skip_rm
 | 
					 | 
				
			||||||
    docker restart dnet-1-consul
 | 
					 | 
				
			||||||
    wait_for_dnet $(inst_id2port 1) dnet-1-consul
 | 
					 | 
				
			||||||
    docker restart dnet-2-consul
 | 
					 | 
				
			||||||
    wait_for_dnet $(inst_id2port 2) dnet-2-consul
 | 
					 | 
				
			||||||
    docker restart dnet-3-consul
 | 
					 | 
				
			||||||
    wait_for_dnet $(inst_id2port 3) dnet-3-consul
 | 
					 | 
				
			||||||
    test_overlay consul skip_add
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@test "Test overlay network internal network with consul" {
 | 
					 | 
				
			||||||
    test_overlay consul internal
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@test "Test overlay network with dnet ungraceful shutdown" {
 | 
					 | 
				
			||||||
    dnet_cmd $(inst_id2port 1) network create -d overlay multihost
 | 
					 | 
				
			||||||
    start=1
 | 
					 | 
				
			||||||
    end=3
 | 
					 | 
				
			||||||
    for i in `seq ${start} ${end}`;
 | 
					 | 
				
			||||||
    do
 | 
					 | 
				
			||||||
	dnet_cmd $(inst_id2port $i) container create container_${i}
 | 
					 | 
				
			||||||
	net_connect ${i} container_${i} multihost
 | 
					 | 
				
			||||||
    done
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    hrun runc $(dnet_container_name 1 consul) $(get_sbox_id 1 container_1) "ifconfig eth0"
 | 
					 | 
				
			||||||
    container_1_ip=$(echo ${output} | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # ungracefully kill dnet-1-consul container
 | 
					 | 
				
			||||||
    docker rm -f dnet-1-consul
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # forcefully unpublish the service from dnet2 when dnet1 is dead.
 | 
					 | 
				
			||||||
    dnet_cmd $(inst_id2port 2) service unpublish -f container_1.multihost
 | 
					 | 
				
			||||||
    dnet_cmd $(inst_id2port 2) container create container_1
 | 
					 | 
				
			||||||
    net_connect 2 container_1 multihost
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    hrun runc $(dnet_container_name 2 consul) $(get_sbox_id 2 container_1) "ifconfig eth0"
 | 
					 | 
				
			||||||
    container_1_new_ip=$(echo ${output} | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if [ "$container_1_ip" != "$container_1_new_ip" ]; then
 | 
					 | 
				
			||||||
	exit 1
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -52,43 +52,6 @@ function run_overlay_local_tests() {
 | 
				
			||||||
	unset cmap[dnet-3-local]
 | 
						unset cmap[dnet-3-local]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function run_overlay_consul_tests() {
 | 
					 | 
				
			||||||
	## Test overlay network with consul
 | 
					 | 
				
			||||||
	## Setup
 | 
					 | 
				
			||||||
	start_dnet 1 consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1
 | 
					 | 
				
			||||||
	cmap[dnet - 1 - consul]=dnet-1-consul
 | 
					 | 
				
			||||||
	start_dnet 2 consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1
 | 
					 | 
				
			||||||
	cmap[dnet - 2 - consul]=dnet-2-consul
 | 
					 | 
				
			||||||
	start_dnet 3 consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1
 | 
					 | 
				
			||||||
	cmap[dnet - 3 - consul]=dnet-3-consul
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	## Run the test cases
 | 
					 | 
				
			||||||
	./integration-tmp/bin/bats ./test/integration/dnet/overlay-consul.bats
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	## Teardown
 | 
					 | 
				
			||||||
	stop_dnet 1 consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1
 | 
					 | 
				
			||||||
	unset cmap[dnet-1-consul]
 | 
					 | 
				
			||||||
	stop_dnet 2 consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1
 | 
					 | 
				
			||||||
	unset cmap[dnet-2-consul]
 | 
					 | 
				
			||||||
	stop_dnet 3 consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1
 | 
					 | 
				
			||||||
	unset cmap[dnet-3-consul]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function run_overlay_consul_host_tests() {
 | 
					 | 
				
			||||||
	export _OVERLAY_HOST_MODE="true"
 | 
					 | 
				
			||||||
	## Setup
 | 
					 | 
				
			||||||
	start_dnet 1 consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1
 | 
					 | 
				
			||||||
	cmap[dnet - 1 - consul]=dnet-1-consul
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	## Run the test cases
 | 
					 | 
				
			||||||
	./integration-tmp/bin/bats ./test/integration/dnet/overlay-consul-host.bats
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	## Teardown
 | 
					 | 
				
			||||||
	stop_dnet 1 consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1
 | 
					 | 
				
			||||||
	unset cmap[dnet-1-consul]
 | 
					 | 
				
			||||||
	unset _OVERLAY_HOST_MODE
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function run_overlay_etcd_tests() {
 | 
					function run_overlay_etcd_tests() {
 | 
				
			||||||
	## Test overlay network with etcd
 | 
						## Test overlay network with etcd
 | 
				
			||||||
	start_dnet 1 etcd 1>> ${INTEGRATION_ROOT}/test.log 2>&1
 | 
						start_dnet 1 etcd 1>> ${INTEGRATION_ROOT}/test.log 2>&1
 | 
				
			||||||
| 
						 | 
					@ -113,29 +76,6 @@ function run_dnet_tests() {
 | 
				
			||||||
	./integration-tmp/bin/bats ./test/integration/dnet/dnet.bats
 | 
						./integration-tmp/bin/bats ./test/integration/dnet/dnet.bats
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function run_multi_consul_tests() {
 | 
					 | 
				
			||||||
	# Test multi node configuration with a global scope test driver backed by consul
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	## Setup
 | 
					 | 
				
			||||||
	start_dnet 1 multi_consul consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1
 | 
					 | 
				
			||||||
	cmap[dnet - 1 - multi_consul]=dnet-1-multi_consul
 | 
					 | 
				
			||||||
	start_dnet 2 multi_consul consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1
 | 
					 | 
				
			||||||
	cmap[dnet - 2 - multi_consul]=dnet-2-multi_consul
 | 
					 | 
				
			||||||
	start_dnet 3 multi_consul consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1
 | 
					 | 
				
			||||||
	cmap[dnet - 3 - multi_consul]=dnet-3-multi_consul
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	## Run the test cases
 | 
					 | 
				
			||||||
	./integration-tmp/bin/bats ./test/integration/dnet/multi.bats
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	## Teardown
 | 
					 | 
				
			||||||
	stop_dnet 1 multi_consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1
 | 
					 | 
				
			||||||
	unset cmap[dnet-1-multi_consul]
 | 
					 | 
				
			||||||
	stop_dnet 2 multi_consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1
 | 
					 | 
				
			||||||
	unset cmap[dnet-2-multi_consul]
 | 
					 | 
				
			||||||
	stop_dnet 3 multi_consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1
 | 
					 | 
				
			||||||
	unset cmap[dnet-3-multi_consul]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function run_multi_etcd_tests() {
 | 
					function run_multi_etcd_tests() {
 | 
				
			||||||
	# Test multi node configuration with a global scope test driver backed by etcd
 | 
						# Test multi node configuration with a global scope test driver backed by etcd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -178,17 +118,11 @@ fi
 | 
				
			||||||
# Suite setup
 | 
					# Suite setup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if [ -z "$SUITES" ]; then
 | 
					if [ -z "$SUITES" ]; then
 | 
				
			||||||
	suites="dnet multi_consul multi_etcd  bridge overlay_consul overlay_consul_host overlay_etcd"
 | 
						suites="dnet multi_etcd  bridge overlay_etcd"
 | 
				
			||||||
else
 | 
					else
 | 
				
			||||||
	suites="$SUITES"
 | 
						suites="$SUITES"
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if [[ ("$suites" =~ .*consul.*) || ("$suites" =~ .*bridge.*) ]]; then
 | 
					 | 
				
			||||||
	echo "Starting consul ..."
 | 
					 | 
				
			||||||
	start_consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1
 | 
					 | 
				
			||||||
	cmap[pr_consul]=pr_consul
 | 
					 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if [[ "$suites" =~ .*etcd.* ]]; then
 | 
					if [[ "$suites" =~ .*etcd.* ]]; then
 | 
				
			||||||
	echo "Starting etcd ..."
 | 
						echo "Starting etcd ..."
 | 
				
			||||||
	start_etcd 1>> ${INTEGRATION_ROOT}/test.log 2>&1
 | 
						start_etcd 1>> ${INTEGRATION_ROOT}/test.log 2>&1
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -63,7 +63,6 @@ github.com/google/btree                             479b5e81b0a93ec038d201b0b33d
 | 
				
			||||||
github.com/deckarep/golang-set                      ef32fa3046d9f249d399f98ebaf9be944430fd1d
 | 
					github.com/deckarep/golang-set                      ef32fa3046d9f249d399f98ebaf9be944430fd1d
 | 
				
			||||||
github.com/coreos/etcd                              973882f697a8db3d59815bf132c6c506434334bd # v3.3.27
 | 
					github.com/coreos/etcd                              973882f697a8db3d59815bf132c6c506434334bd # v3.3.27
 | 
				
			||||||
github.com/coreos/go-semver                         8ab6407b697782a06568d4b7f1db25550ec2e4c6 # v0.2.0
 | 
					github.com/coreos/go-semver                         8ab6407b697782a06568d4b7f1db25550ec2e4c6 # v0.2.0
 | 
				
			||||||
github.com/hashicorp/consul                         9a9cc9341bb487651a0399e3fc5e1e8a42e62dd9 # v0.5.2
 | 
					 | 
				
			||||||
github.com/miekg/dns                                6c0c4e6581f8e173cc562c8b3363ab984e4ae071 # v1.1.27
 | 
					github.com/miekg/dns                                6c0c4e6581f8e173cc562c8b3363ab984e4ae071 # v1.1.27
 | 
				
			||||||
github.com/ishidawataru/sctp                        f2269e66cdee387bd321445d5d300893449805be
 | 
					github.com/ishidawataru/sctp                        f2269e66cdee387bd321445d5d300893449805be
 | 
				
			||||||
go.etcd.io/bbolt                                    232d8fc87f50244f9c808f4745759e08a304c029 # v1.3.5
 | 
					go.etcd.io/bbolt                                    232d8fc87f50244f9c808f4745759e08a304c029 # v1.3.5
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										558
									
								
								vendor/github.com/docker/libkv/store/consul/consul.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										558
									
								
								vendor/github.com/docker/libkv/store/consul/consul.go
									
										
									
										generated
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,558 +0,0 @@
 | 
				
			||||||
package consul
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"crypto/tls"
 | 
					 | 
				
			||||||
	"errors"
 | 
					 | 
				
			||||||
	"net/http"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"sync"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/docker/libkv"
 | 
					 | 
				
			||||||
	"github.com/docker/libkv/store"
 | 
					 | 
				
			||||||
	api "github.com/hashicorp/consul/api"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const (
 | 
					 | 
				
			||||||
	// DefaultWatchWaitTime is how long we block for at a
 | 
					 | 
				
			||||||
	// time to check if the watched key has changed. This
 | 
					 | 
				
			||||||
	// affects the minimum time it takes to cancel a watch.
 | 
					 | 
				
			||||||
	DefaultWatchWaitTime = 15 * time.Second
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// RenewSessionRetryMax is the number of time we should try
 | 
					 | 
				
			||||||
	// to renew the session before giving up and throwing an error
 | 
					 | 
				
			||||||
	RenewSessionRetryMax = 5
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// MaxSessionDestroyAttempts is the maximum times we will try
 | 
					 | 
				
			||||||
	// to explicitely destroy the session attached to a lock after
 | 
					 | 
				
			||||||
	// the connectivity to the store has been lost
 | 
					 | 
				
			||||||
	MaxSessionDestroyAttempts = 5
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// defaultLockTTL is the default ttl for the consul lock
 | 
					 | 
				
			||||||
	defaultLockTTL = 20 * time.Second
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var (
 | 
					 | 
				
			||||||
	// ErrMultipleEndpointsUnsupported is thrown when there are
 | 
					 | 
				
			||||||
	// multiple endpoints specified for Consul
 | 
					 | 
				
			||||||
	ErrMultipleEndpointsUnsupported = errors.New("consul does not support multiple endpoints")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// ErrSessionRenew is thrown when the session can't be
 | 
					 | 
				
			||||||
	// renewed because the Consul version does not support sessions
 | 
					 | 
				
			||||||
	ErrSessionRenew = errors.New("cannot set or renew session for ttl, unable to operate on sessions")
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Consul is the receiver type for the
 | 
					 | 
				
			||||||
// Store interface
 | 
					 | 
				
			||||||
type Consul struct {
 | 
					 | 
				
			||||||
	sync.Mutex
 | 
					 | 
				
			||||||
	config *api.Config
 | 
					 | 
				
			||||||
	client *api.Client
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type consulLock struct {
 | 
					 | 
				
			||||||
	lock    *api.Lock
 | 
					 | 
				
			||||||
	renewCh chan struct{}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Register registers consul to libkv
 | 
					 | 
				
			||||||
func Register() {
 | 
					 | 
				
			||||||
	libkv.AddStore(store.CONSUL, New)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// New creates a new Consul client given a list
 | 
					 | 
				
			||||||
// of endpoints and optional tls config
 | 
					 | 
				
			||||||
func New(endpoints []string, options *store.Config) (store.Store, error) {
 | 
					 | 
				
			||||||
	if len(endpoints) > 1 {
 | 
					 | 
				
			||||||
		return nil, ErrMultipleEndpointsUnsupported
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	s := &Consul{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Create Consul client
 | 
					 | 
				
			||||||
	config := api.DefaultConfig()
 | 
					 | 
				
			||||||
	s.config = config
 | 
					 | 
				
			||||||
	config.HttpClient = http.DefaultClient
 | 
					 | 
				
			||||||
	config.Address = endpoints[0]
 | 
					 | 
				
			||||||
	config.Scheme = "http"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Set options
 | 
					 | 
				
			||||||
	if options != nil {
 | 
					 | 
				
			||||||
		if options.TLS != nil {
 | 
					 | 
				
			||||||
			s.setTLS(options.TLS)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if options.ConnectionTimeout != 0 {
 | 
					 | 
				
			||||||
			s.setTimeout(options.ConnectionTimeout)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Creates a new client
 | 
					 | 
				
			||||||
	client, err := api.NewClient(config)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	s.client = client
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return s, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// SetTLS sets Consul TLS options
 | 
					 | 
				
			||||||
func (s *Consul) setTLS(tls *tls.Config) {
 | 
					 | 
				
			||||||
	s.config.HttpClient.Transport = &http.Transport{
 | 
					 | 
				
			||||||
		TLSClientConfig: tls,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	s.config.Scheme = "https"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// SetTimeout sets the timeout for connecting to Consul
 | 
					 | 
				
			||||||
func (s *Consul) setTimeout(time time.Duration) {
 | 
					 | 
				
			||||||
	s.config.WaitTime = time
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Normalize the key for usage in Consul
 | 
					 | 
				
			||||||
func (s *Consul) normalize(key string) string {
 | 
					 | 
				
			||||||
	key = store.Normalize(key)
 | 
					 | 
				
			||||||
	return strings.TrimPrefix(key, "/")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (s *Consul) renewSession(pair *api.KVPair, ttl time.Duration) error {
 | 
					 | 
				
			||||||
	// Check if there is any previous session with an active TTL
 | 
					 | 
				
			||||||
	session, err := s.getActiveSession(pair.Key)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if session == "" {
 | 
					 | 
				
			||||||
		entry := &api.SessionEntry{
 | 
					 | 
				
			||||||
			Behavior:  api.SessionBehaviorDelete, // Delete the key when the session expires
 | 
					 | 
				
			||||||
			TTL:       (ttl / 2).String(),        // Consul multiplies the TTL by 2x
 | 
					 | 
				
			||||||
			LockDelay: 1 * time.Millisecond,      // Virtually disable lock delay
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Create the key session
 | 
					 | 
				
			||||||
		session, _, err = s.client.Session().Create(entry, nil)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		lockOpts := &api.LockOptions{
 | 
					 | 
				
			||||||
			Key:     pair.Key,
 | 
					 | 
				
			||||||
			Session: session,
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Lock and ignore if lock is held
 | 
					 | 
				
			||||||
		// It's just a placeholder for the
 | 
					 | 
				
			||||||
		// ephemeral behavior
 | 
					 | 
				
			||||||
		lock, _ := s.client.LockOpts(lockOpts)
 | 
					 | 
				
			||||||
		if lock != nil {
 | 
					 | 
				
			||||||
			lock.Lock(nil)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_, _, err = s.client.Session().Renew(session, nil)
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getActiveSession checks if the key already has
 | 
					 | 
				
			||||||
// a session attached
 | 
					 | 
				
			||||||
func (s *Consul) getActiveSession(key string) (string, error) {
 | 
					 | 
				
			||||||
	pair, _, err := s.client.KV().Get(key, nil)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return "", err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if pair != nil && pair.Session != "" {
 | 
					 | 
				
			||||||
		return pair.Session, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return "", nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Get the value at "key", returns the last modified index
 | 
					 | 
				
			||||||
// to use in conjunction to CAS calls
 | 
					 | 
				
			||||||
func (s *Consul) Get(key string) (*store.KVPair, error) {
 | 
					 | 
				
			||||||
	options := &api.QueryOptions{
 | 
					 | 
				
			||||||
		AllowStale:        false,
 | 
					 | 
				
			||||||
		RequireConsistent: true,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pair, meta, err := s.client.KV().Get(s.normalize(key), options)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// If pair is nil then the key does not exist
 | 
					 | 
				
			||||||
	if pair == nil {
 | 
					 | 
				
			||||||
		return nil, store.ErrKeyNotFound
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return &store.KVPair{Key: pair.Key, Value: pair.Value, LastIndex: meta.LastIndex}, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Put a value at "key"
 | 
					 | 
				
			||||||
func (s *Consul) Put(key string, value []byte, opts *store.WriteOptions) error {
 | 
					 | 
				
			||||||
	key = s.normalize(key)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	p := &api.KVPair{
 | 
					 | 
				
			||||||
		Key:   key,
 | 
					 | 
				
			||||||
		Value: value,
 | 
					 | 
				
			||||||
		Flags: api.LockFlagValue,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if opts != nil && opts.TTL > 0 {
 | 
					 | 
				
			||||||
		// Create or renew a session holding a TTL. Operations on sessions
 | 
					 | 
				
			||||||
		// are not deterministic: creating or renewing a session can fail
 | 
					 | 
				
			||||||
		for retry := 1; retry <= RenewSessionRetryMax; retry++ {
 | 
					 | 
				
			||||||
			err := s.renewSession(p, opts.TTL)
 | 
					 | 
				
			||||||
			if err == nil {
 | 
					 | 
				
			||||||
				break
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if retry == RenewSessionRetryMax {
 | 
					 | 
				
			||||||
				return ErrSessionRenew
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_, err := s.client.KV().Put(p, nil)
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Delete a value at "key"
 | 
					 | 
				
			||||||
func (s *Consul) Delete(key string) error {
 | 
					 | 
				
			||||||
	if _, err := s.Get(key); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	_, err := s.client.KV().Delete(s.normalize(key), nil)
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Exists checks that the key exists inside the store
 | 
					 | 
				
			||||||
func (s *Consul) Exists(key string) (bool, error) {
 | 
					 | 
				
			||||||
	_, err := s.Get(key)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		if err == store.ErrKeyNotFound {
 | 
					 | 
				
			||||||
			return false, nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return false, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return true, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// List child nodes of a given directory
 | 
					 | 
				
			||||||
func (s *Consul) List(directory string) ([]*store.KVPair, error) {
 | 
					 | 
				
			||||||
	pairs, _, err := s.client.KV().List(s.normalize(directory), nil)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(pairs) == 0 {
 | 
					 | 
				
			||||||
		return nil, store.ErrKeyNotFound
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	kv := []*store.KVPair{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, pair := range pairs {
 | 
					 | 
				
			||||||
		if pair.Key == directory {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		kv = append(kv, &store.KVPair{
 | 
					 | 
				
			||||||
			Key:       pair.Key,
 | 
					 | 
				
			||||||
			Value:     pair.Value,
 | 
					 | 
				
			||||||
			LastIndex: pair.ModifyIndex,
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return kv, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// DeleteTree deletes a range of keys under a given directory
 | 
					 | 
				
			||||||
func (s *Consul) DeleteTree(directory string) error {
 | 
					 | 
				
			||||||
	if _, err := s.List(directory); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	_, err := s.client.KV().DeleteTree(s.normalize(directory), nil)
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Watch for changes on a "key"
 | 
					 | 
				
			||||||
// It returns a channel that will receive changes or pass
 | 
					 | 
				
			||||||
// on errors. Upon creation, the current value will first
 | 
					 | 
				
			||||||
// be sent to the channel. Providing a non-nil stopCh can
 | 
					 | 
				
			||||||
// be used to stop watching.
 | 
					 | 
				
			||||||
func (s *Consul) Watch(key string, stopCh <-chan struct{}) (<-chan *store.KVPair, error) {
 | 
					 | 
				
			||||||
	kv := s.client.KV()
 | 
					 | 
				
			||||||
	watchCh := make(chan *store.KVPair)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	go func() {
 | 
					 | 
				
			||||||
		defer close(watchCh)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Use a wait time in order to check if we should quit
 | 
					 | 
				
			||||||
		// from time to time.
 | 
					 | 
				
			||||||
		opts := &api.QueryOptions{WaitTime: DefaultWatchWaitTime}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for {
 | 
					 | 
				
			||||||
			// Check if we should quit
 | 
					 | 
				
			||||||
			select {
 | 
					 | 
				
			||||||
			case <-stopCh:
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			default:
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// Get the key
 | 
					 | 
				
			||||||
			pair, meta, err := kv.Get(key, opts)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// If LastIndex didn't change then it means `Get` returned
 | 
					 | 
				
			||||||
			// because of the WaitTime and the key didn't changed.
 | 
					 | 
				
			||||||
			if opts.WaitIndex == meta.LastIndex {
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			opts.WaitIndex = meta.LastIndex
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// Return the value to the channel
 | 
					 | 
				
			||||||
			// FIXME: What happens when a key is deleted?
 | 
					 | 
				
			||||||
			if pair != nil {
 | 
					 | 
				
			||||||
				watchCh <- &store.KVPair{
 | 
					 | 
				
			||||||
					Key:       pair.Key,
 | 
					 | 
				
			||||||
					Value:     pair.Value,
 | 
					 | 
				
			||||||
					LastIndex: pair.ModifyIndex,
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return watchCh, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// WatchTree watches for changes on a "directory"
 | 
					 | 
				
			||||||
// It returns a channel that will receive changes or pass
 | 
					 | 
				
			||||||
// on errors. Upon creating a watch, the current childs values
 | 
					 | 
				
			||||||
// will be sent to the channel .Providing a non-nil stopCh can
 | 
					 | 
				
			||||||
// be used to stop watching.
 | 
					 | 
				
			||||||
func (s *Consul) WatchTree(directory string, stopCh <-chan struct{}) (<-chan []*store.KVPair, error) {
 | 
					 | 
				
			||||||
	kv := s.client.KV()
 | 
					 | 
				
			||||||
	watchCh := make(chan []*store.KVPair)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	go func() {
 | 
					 | 
				
			||||||
		defer close(watchCh)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Use a wait time in order to check if we should quit
 | 
					 | 
				
			||||||
		// from time to time.
 | 
					 | 
				
			||||||
		opts := &api.QueryOptions{WaitTime: DefaultWatchWaitTime}
 | 
					 | 
				
			||||||
		for {
 | 
					 | 
				
			||||||
			// Check if we should quit
 | 
					 | 
				
			||||||
			select {
 | 
					 | 
				
			||||||
			case <-stopCh:
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			default:
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// Get all the childrens
 | 
					 | 
				
			||||||
			pairs, meta, err := kv.List(directory, opts)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// If LastIndex didn't change then it means `Get` returned
 | 
					 | 
				
			||||||
			// because of the WaitTime and the child keys didn't change.
 | 
					 | 
				
			||||||
			if opts.WaitIndex == meta.LastIndex {
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			opts.WaitIndex = meta.LastIndex
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// Return children KV pairs to the channel
 | 
					 | 
				
			||||||
			kvpairs := []*store.KVPair{}
 | 
					 | 
				
			||||||
			for _, pair := range pairs {
 | 
					 | 
				
			||||||
				if pair.Key == directory {
 | 
					 | 
				
			||||||
					continue
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				kvpairs = append(kvpairs, &store.KVPair{
 | 
					 | 
				
			||||||
					Key:       pair.Key,
 | 
					 | 
				
			||||||
					Value:     pair.Value,
 | 
					 | 
				
			||||||
					LastIndex: pair.ModifyIndex,
 | 
					 | 
				
			||||||
				})
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			watchCh <- kvpairs
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return watchCh, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NewLock returns a handle to a lock struct which can
 | 
					 | 
				
			||||||
// be used to provide mutual exclusion on a key
 | 
					 | 
				
			||||||
func (s *Consul) NewLock(key string, options *store.LockOptions) (store.Locker, error) {
 | 
					 | 
				
			||||||
	lockOpts := &api.LockOptions{
 | 
					 | 
				
			||||||
		Key: s.normalize(key),
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	lock := &consulLock{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ttl := defaultLockTTL
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if options != nil {
 | 
					 | 
				
			||||||
		// Set optional TTL on Lock
 | 
					 | 
				
			||||||
		if options.TTL != 0 {
 | 
					 | 
				
			||||||
			ttl = options.TTL
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		// Set optional value on Lock
 | 
					 | 
				
			||||||
		if options.Value != nil {
 | 
					 | 
				
			||||||
			lockOpts.Value = options.Value
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	entry := &api.SessionEntry{
 | 
					 | 
				
			||||||
		Behavior:  api.SessionBehaviorRelease, // Release the lock when the session expires
 | 
					 | 
				
			||||||
		TTL:       (ttl / 2).String(),         // Consul multiplies the TTL by 2x
 | 
					 | 
				
			||||||
		LockDelay: 1 * time.Millisecond,       // Virtually disable lock delay
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Create the key session
 | 
					 | 
				
			||||||
	session, _, err := s.client.Session().Create(entry, nil)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Place the session and renew chan on lock
 | 
					 | 
				
			||||||
	lockOpts.Session = session
 | 
					 | 
				
			||||||
	lock.renewCh = options.RenewLock
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	l, err := s.client.LockOpts(lockOpts)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Renew the session ttl lock periodically
 | 
					 | 
				
			||||||
	s.renewLockSession(entry.TTL, session, options.RenewLock)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	lock.lock = l
 | 
					 | 
				
			||||||
	return lock, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// renewLockSession is used to renew a session Lock, it takes
 | 
					 | 
				
			||||||
// a stopRenew chan which is used to explicitely stop the session
 | 
					 | 
				
			||||||
// renew process. The renew routine never stops until a signal is
 | 
					 | 
				
			||||||
// sent to this channel. If deleting the session fails because the
 | 
					 | 
				
			||||||
// connection to the store is lost, it keeps trying to delete the
 | 
					 | 
				
			||||||
// session periodically until it can contact the store, this ensures
 | 
					 | 
				
			||||||
// that the lock is not maintained indefinitely which ensures liveness
 | 
					 | 
				
			||||||
// over safety for the lock when the store becomes unavailable.
 | 
					 | 
				
			||||||
func (s *Consul) renewLockSession(initialTTL string, id string, stopRenew chan struct{}) {
 | 
					 | 
				
			||||||
	sessionDestroyAttempts := 0
 | 
					 | 
				
			||||||
	ttl, err := time.ParseDuration(initialTTL)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	go func() {
 | 
					 | 
				
			||||||
		for {
 | 
					 | 
				
			||||||
			select {
 | 
					 | 
				
			||||||
			case <-time.After(ttl / 2):
 | 
					 | 
				
			||||||
				entry, _, err := s.client.Session().Renew(id, nil)
 | 
					 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					// If an error occurs, continue until the
 | 
					 | 
				
			||||||
					// session gets destroyed explicitely or
 | 
					 | 
				
			||||||
					// the session ttl times out
 | 
					 | 
				
			||||||
					continue
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				if entry == nil {
 | 
					 | 
				
			||||||
					return
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// Handle the server updating the TTL
 | 
					 | 
				
			||||||
				ttl, _ = time.ParseDuration(entry.TTL)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			case <-stopRenew:
 | 
					 | 
				
			||||||
				// Attempt a session destroy
 | 
					 | 
				
			||||||
				_, err := s.client.Session().Destroy(id, nil)
 | 
					 | 
				
			||||||
				if err == nil {
 | 
					 | 
				
			||||||
					return
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				if sessionDestroyAttempts >= MaxSessionDestroyAttempts {
 | 
					 | 
				
			||||||
					return
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// We can't destroy the session because the store
 | 
					 | 
				
			||||||
				// is unavailable, wait for the session renew period
 | 
					 | 
				
			||||||
				sessionDestroyAttempts++
 | 
					 | 
				
			||||||
				time.Sleep(ttl / 2)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Lock attempts to acquire the lock and blocks while
 | 
					 | 
				
			||||||
// doing so. It returns a channel that is closed if our
 | 
					 | 
				
			||||||
// lock is lost or if an error occurs
 | 
					 | 
				
			||||||
func (l *consulLock) Lock(stopChan chan struct{}) (<-chan struct{}, error) {
 | 
					 | 
				
			||||||
	return l.lock.Lock(stopChan)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Unlock the "key". Calling unlock while
 | 
					 | 
				
			||||||
// not holding the lock will throw an error
 | 
					 | 
				
			||||||
func (l *consulLock) Unlock() error {
 | 
					 | 
				
			||||||
	if l.renewCh != nil {
 | 
					 | 
				
			||||||
		close(l.renewCh)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return l.lock.Unlock()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// AtomicPut put a value at "key" if the key has not been
 | 
					 | 
				
			||||||
// modified in the meantime, throws an error if this is the case
 | 
					 | 
				
			||||||
func (s *Consul) AtomicPut(key string, value []byte, previous *store.KVPair, options *store.WriteOptions) (bool, *store.KVPair, error) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	p := &api.KVPair{Key: s.normalize(key), Value: value, Flags: api.LockFlagValue}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if previous == nil {
 | 
					 | 
				
			||||||
		// Consul interprets ModifyIndex = 0 as new key.
 | 
					 | 
				
			||||||
		p.ModifyIndex = 0
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		p.ModifyIndex = previous.LastIndex
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ok, _, err := s.client.KV().CAS(p, nil)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return false, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		if previous == nil {
 | 
					 | 
				
			||||||
			return false, nil, store.ErrKeyExists
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return false, nil, store.ErrKeyModified
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pair, err := s.Get(key)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return false, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return true, pair, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// AtomicDelete deletes a value at "key" if the key has not
 | 
					 | 
				
			||||||
// been modified in the meantime, throws an error if this is the case
 | 
					 | 
				
			||||||
func (s *Consul) AtomicDelete(key string, previous *store.KVPair) (bool, error) {
 | 
					 | 
				
			||||||
	if previous == nil {
 | 
					 | 
				
			||||||
		return false, store.ErrPreviousNotSpecified
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	p := &api.KVPair{Key: s.normalize(key), ModifyIndex: previous.LastIndex, Flags: api.LockFlagValue}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Extra Get operation to check on the key
 | 
					 | 
				
			||||||
	_, err := s.Get(key)
 | 
					 | 
				
			||||||
	if err != nil && err == store.ErrKeyNotFound {
 | 
					 | 
				
			||||||
		return false, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if work, _, err := s.client.KV().DeleteCAS(p, nil); err != nil {
 | 
					 | 
				
			||||||
		return false, err
 | 
					 | 
				
			||||||
	} else if !work {
 | 
					 | 
				
			||||||
		return false, store.ErrKeyModified
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return true, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Close closes the client connection
 | 
					 | 
				
			||||||
func (s *Consul) Close() {
 | 
					 | 
				
			||||||
	return
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										354
									
								
								vendor/github.com/hashicorp/consul/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										354
									
								
								vendor/github.com/hashicorp/consul/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,354 +0,0 @@
 | 
				
			||||||
Mozilla Public License, version 2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
1. Definitions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
1.1. “Contributor”
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     means each individual or legal entity that creates, contributes to the
 | 
					 | 
				
			||||||
     creation of, or owns Covered Software.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
1.2. “Contributor Version”
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     means the combination of the Contributions of others (if any) used by a
 | 
					 | 
				
			||||||
     Contributor and that particular Contributor’s Contribution.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
1.3. “Contribution”
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     means Covered Software of a particular Contributor.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
1.4. “Covered Software”
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     means Source Code Form to which the initial Contributor has attached the
 | 
					 | 
				
			||||||
     notice in Exhibit A, the Executable Form of such Source Code Form, and
 | 
					 | 
				
			||||||
     Modifications of such Source Code Form, in each case including portions
 | 
					 | 
				
			||||||
     thereof.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
1.5. “Incompatible With Secondary Licenses”
 | 
					 | 
				
			||||||
     means
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     a. that the initial Contributor has attached the notice described in
 | 
					 | 
				
			||||||
        Exhibit B to the Covered Software; or
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     b. that the Covered Software was made available under the terms of version
 | 
					 | 
				
			||||||
        1.1 or earlier of the License, but not also under the terms of a
 | 
					 | 
				
			||||||
        Secondary License.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
1.6. “Executable Form”
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     means any form of the work other than Source Code Form.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
1.7. “Larger Work”
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     means a work that combines Covered Software with other material, in a separate
 | 
					 | 
				
			||||||
     file or files, that is not Covered Software.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
1.8. “License”
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     means this document.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
1.9. “Licensable”
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     means having the right to grant, to the maximum extent possible, whether at the
 | 
					 | 
				
			||||||
     time of the initial grant or subsequently, any and all of the rights conveyed by
 | 
					 | 
				
			||||||
     this License.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
1.10. “Modifications”
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     means any of the following:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     a. any file in Source Code Form that results from an addition to, deletion
 | 
					 | 
				
			||||||
        from, or modification of the contents of Covered Software; or
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     b. any new file in Source Code Form that contains any Covered Software.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
1.11. “Patent Claims” of a Contributor
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      means any patent claim(s), including without limitation, method, process,
 | 
					 | 
				
			||||||
      and apparatus claims, in any patent Licensable by such Contributor that
 | 
					 | 
				
			||||||
      would be infringed, but for the grant of the License, by the making,
 | 
					 | 
				
			||||||
      using, selling, offering for sale, having made, import, or transfer of
 | 
					 | 
				
			||||||
      either its Contributions or its Contributor Version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
1.12. “Secondary License”
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      means either the GNU General Public License, Version 2.0, the GNU Lesser
 | 
					 | 
				
			||||||
      General Public License, Version 2.1, the GNU Affero General Public
 | 
					 | 
				
			||||||
      License, Version 3.0, or any later versions of those licenses.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
1.13. “Source Code Form”
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      means the form of the work preferred for making modifications.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
1.14. “You” (or “Your”)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      means an individual or a legal entity exercising rights under this
 | 
					 | 
				
			||||||
      License. For legal entities, “You” includes any entity that controls, is
 | 
					 | 
				
			||||||
      controlled by, or is under common control with You. For purposes of this
 | 
					 | 
				
			||||||
      definition, “control” means (a) the power, direct or indirect, to cause
 | 
					 | 
				
			||||||
      the direction or management of such entity, whether by contract or
 | 
					 | 
				
			||||||
      otherwise, or (b) ownership of more than fifty percent (50%) of the
 | 
					 | 
				
			||||||
      outstanding shares or beneficial ownership of such entity.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
2. License Grants and Conditions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
2.1. Grants
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     Each Contributor hereby grants You a world-wide, royalty-free,
 | 
					 | 
				
			||||||
     non-exclusive license:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     a. under intellectual property rights (other than patent or trademark)
 | 
					 | 
				
			||||||
        Licensable by such Contributor to use, reproduce, make available,
 | 
					 | 
				
			||||||
        modify, display, perform, distribute, and otherwise exploit its
 | 
					 | 
				
			||||||
        Contributions, either on an unmodified basis, with Modifications, or as
 | 
					 | 
				
			||||||
        part of a Larger Work; and
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     b. under Patent Claims of such Contributor to make, use, sell, offer for
 | 
					 | 
				
			||||||
        sale, have made, import, and otherwise transfer either its Contributions
 | 
					 | 
				
			||||||
        or its Contributor Version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
2.2. Effective Date
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     The licenses granted in Section 2.1 with respect to any Contribution become
 | 
					 | 
				
			||||||
     effective for each Contribution on the date the Contributor first distributes
 | 
					 | 
				
			||||||
     such Contribution.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
2.3. Limitations on Grant Scope
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     The licenses granted in this Section 2 are the only rights granted under this
 | 
					 | 
				
			||||||
     License. No additional rights or licenses will be implied from the distribution
 | 
					 | 
				
			||||||
     or licensing of Covered Software under this License. Notwithstanding Section
 | 
					 | 
				
			||||||
     2.1(b) above, no patent license is granted by a Contributor:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     a. for any code that a Contributor has removed from Covered Software; or
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     b. for infringements caused by: (i) Your and any other third party’s
 | 
					 | 
				
			||||||
        modifications of Covered Software, or (ii) the combination of its
 | 
					 | 
				
			||||||
        Contributions with other software (except as part of its Contributor
 | 
					 | 
				
			||||||
        Version); or
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     c. under Patent Claims infringed by Covered Software in the absence of its
 | 
					 | 
				
			||||||
        Contributions.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     This License does not grant any rights in the trademarks, service marks, or
 | 
					 | 
				
			||||||
     logos of any Contributor (except as may be necessary to comply with the
 | 
					 | 
				
			||||||
     notice requirements in Section 3.4).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
2.4. Subsequent Licenses
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     No Contributor makes additional grants as a result of Your choice to
 | 
					 | 
				
			||||||
     distribute the Covered Software under a subsequent version of this License
 | 
					 | 
				
			||||||
     (see Section 10.2) or under the terms of a Secondary License (if permitted
 | 
					 | 
				
			||||||
     under the terms of Section 3.3).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
2.5. Representation
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     Each Contributor represents that the Contributor believes its Contributions
 | 
					 | 
				
			||||||
     are its original creation(s) or it has sufficient rights to grant the
 | 
					 | 
				
			||||||
     rights to its Contributions conveyed by this License.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
2.6. Fair Use
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     This License is not intended to limit any rights You have under applicable
 | 
					 | 
				
			||||||
     copyright doctrines of fair use, fair dealing, or other equivalents.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
2.7. Conditions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
 | 
					 | 
				
			||||||
     Section 2.1.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
3. Responsibilities
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
3.1. Distribution of Source Form
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     All distribution of Covered Software in Source Code Form, including any
 | 
					 | 
				
			||||||
     Modifications that You create or to which You contribute, must be under the
 | 
					 | 
				
			||||||
     terms of this License. You must inform recipients that the Source Code Form
 | 
					 | 
				
			||||||
     of the Covered Software is governed by the terms of this License, and how
 | 
					 | 
				
			||||||
     they can obtain a copy of this License. You may not attempt to alter or
 | 
					 | 
				
			||||||
     restrict the recipients’ rights in the Source Code Form.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
3.2. Distribution of Executable Form
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     If You distribute Covered Software in Executable Form then:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     a. such Covered Software must also be made available in Source Code Form,
 | 
					 | 
				
			||||||
        as described in Section 3.1, and You must inform recipients of the
 | 
					 | 
				
			||||||
        Executable Form how they can obtain a copy of such Source Code Form by
 | 
					 | 
				
			||||||
        reasonable means in a timely manner, at a charge no more than the cost
 | 
					 | 
				
			||||||
        of distribution to the recipient; and
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     b. You may distribute such Executable Form under the terms of this License,
 | 
					 | 
				
			||||||
        or sublicense it under different terms, provided that the license for
 | 
					 | 
				
			||||||
        the Executable Form does not attempt to limit or alter the recipients’
 | 
					 | 
				
			||||||
        rights in the Source Code Form under this License.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
3.3. Distribution of a Larger Work
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     You may create and distribute a Larger Work under terms of Your choice,
 | 
					 | 
				
			||||||
     provided that You also comply with the requirements of this License for the
 | 
					 | 
				
			||||||
     Covered Software. If the Larger Work is a combination of Covered Software
 | 
					 | 
				
			||||||
     with a work governed by one or more Secondary Licenses, and the Covered
 | 
					 | 
				
			||||||
     Software is not Incompatible With Secondary Licenses, this License permits
 | 
					 | 
				
			||||||
     You to additionally distribute such Covered Software under the terms of
 | 
					 | 
				
			||||||
     such Secondary License(s), so that the recipient of the Larger Work may, at
 | 
					 | 
				
			||||||
     their option, further distribute the Covered Software under the terms of
 | 
					 | 
				
			||||||
     either this License or such Secondary License(s).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
3.4. Notices
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     You may not remove or alter the substance of any license notices (including
 | 
					 | 
				
			||||||
     copyright notices, patent notices, disclaimers of warranty, or limitations
 | 
					 | 
				
			||||||
     of liability) contained within the Source Code Form of the Covered
 | 
					 | 
				
			||||||
     Software, except that You may alter any license notices to the extent
 | 
					 | 
				
			||||||
     required to remedy known factual inaccuracies.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
3.5. Application of Additional Terms
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     You may choose to offer, and to charge a fee for, warranty, support,
 | 
					 | 
				
			||||||
     indemnity or liability obligations to one or more recipients of Covered
 | 
					 | 
				
			||||||
     Software. However, You may do so only on Your own behalf, and not on behalf
 | 
					 | 
				
			||||||
     of any Contributor. You must make it absolutely clear that any such
 | 
					 | 
				
			||||||
     warranty, support, indemnity, or liability obligation is offered by You
 | 
					 | 
				
			||||||
     alone, and You hereby agree to indemnify every Contributor for any
 | 
					 | 
				
			||||||
     liability incurred by such Contributor as a result of warranty, support,
 | 
					 | 
				
			||||||
     indemnity or liability terms You offer. You may include additional
 | 
					 | 
				
			||||||
     disclaimers of warranty and limitations of liability specific to any
 | 
					 | 
				
			||||||
     jurisdiction.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
4. Inability to Comply Due to Statute or Regulation
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   If it is impossible for You to comply with any of the terms of this License
 | 
					 | 
				
			||||||
   with respect to some or all of the Covered Software due to statute, judicial
 | 
					 | 
				
			||||||
   order, or regulation then You must: (a) comply with the terms of this License
 | 
					 | 
				
			||||||
   to the maximum extent possible; and (b) describe the limitations and the code
 | 
					 | 
				
			||||||
   they affect. Such description must be placed in a text file included with all
 | 
					 | 
				
			||||||
   distributions of the Covered Software under this License. Except to the
 | 
					 | 
				
			||||||
   extent prohibited by statute or regulation, such description must be
 | 
					 | 
				
			||||||
   sufficiently detailed for a recipient of ordinary skill to be able to
 | 
					 | 
				
			||||||
   understand it.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
5. Termination
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
5.1. The rights granted under this License will terminate automatically if You
 | 
					 | 
				
			||||||
     fail to comply with any of its terms. However, if You become compliant,
 | 
					 | 
				
			||||||
     then the rights granted under this License from a particular Contributor
 | 
					 | 
				
			||||||
     are reinstated (a) provisionally, unless and until such Contributor
 | 
					 | 
				
			||||||
     explicitly and finally terminates Your grants, and (b) on an ongoing basis,
 | 
					 | 
				
			||||||
     if such Contributor fails to notify You of the non-compliance by some
 | 
					 | 
				
			||||||
     reasonable means prior to 60 days after You have come back into compliance.
 | 
					 | 
				
			||||||
     Moreover, Your grants from a particular Contributor are reinstated on an
 | 
					 | 
				
			||||||
     ongoing basis if such Contributor notifies You of the non-compliance by
 | 
					 | 
				
			||||||
     some reasonable means, this is the first time You have received notice of
 | 
					 | 
				
			||||||
     non-compliance with this License from such Contributor, and You become
 | 
					 | 
				
			||||||
     compliant prior to 30 days after Your receipt of the notice.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
5.2. If You initiate litigation against any entity by asserting a patent
 | 
					 | 
				
			||||||
     infringement claim (excluding declaratory judgment actions, counter-claims,
 | 
					 | 
				
			||||||
     and cross-claims) alleging that a Contributor Version directly or
 | 
					 | 
				
			||||||
     indirectly infringes any patent, then the rights granted to You by any and
 | 
					 | 
				
			||||||
     all Contributors for the Covered Software under Section 2.1 of this License
 | 
					 | 
				
			||||||
     shall terminate.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
 | 
					 | 
				
			||||||
     license agreements (excluding distributors and resellers) which have been
 | 
					 | 
				
			||||||
     validly granted by You or Your distributors under this License prior to
 | 
					 | 
				
			||||||
     termination shall survive termination.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
6. Disclaimer of Warranty
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   Covered Software is provided under this License on an “as is” basis, without
 | 
					 | 
				
			||||||
   warranty of any kind, either expressed, implied, or statutory, including,
 | 
					 | 
				
			||||||
   without limitation, warranties that the Covered Software is free of defects,
 | 
					 | 
				
			||||||
   merchantable, fit for a particular purpose or non-infringing. The entire
 | 
					 | 
				
			||||||
   risk as to the quality and performance of the Covered Software is with You.
 | 
					 | 
				
			||||||
   Should any Covered Software prove defective in any respect, You (not any
 | 
					 | 
				
			||||||
   Contributor) assume the cost of any necessary servicing, repair, or
 | 
					 | 
				
			||||||
   correction. This disclaimer of warranty constitutes an essential part of this
 | 
					 | 
				
			||||||
   License. No use of  any Covered Software is authorized under this License
 | 
					 | 
				
			||||||
   except under this disclaimer.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
7. Limitation of Liability
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   Under no circumstances and under no legal theory, whether tort (including
 | 
					 | 
				
			||||||
   negligence), contract, or otherwise, shall any Contributor, or anyone who
 | 
					 | 
				
			||||||
   distributes Covered Software as permitted above, be liable to You for any
 | 
					 | 
				
			||||||
   direct, indirect, special, incidental, or consequential damages of any
 | 
					 | 
				
			||||||
   character including, without limitation, damages for lost profits, loss of
 | 
					 | 
				
			||||||
   goodwill, work stoppage, computer failure or malfunction, or any and all
 | 
					 | 
				
			||||||
   other commercial damages or losses, even if such party shall have been
 | 
					 | 
				
			||||||
   informed of the possibility of such damages. This limitation of liability
 | 
					 | 
				
			||||||
   shall not apply to liability for death or personal injury resulting from such
 | 
					 | 
				
			||||||
   party’s negligence to the extent applicable law prohibits such limitation.
 | 
					 | 
				
			||||||
   Some jurisdictions do not allow the exclusion or limitation of incidental or
 | 
					 | 
				
			||||||
   consequential damages, so this exclusion and limitation may not apply to You.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
8. Litigation
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   Any litigation relating to this License may be brought only in the courts of
 | 
					 | 
				
			||||||
   a jurisdiction where the defendant maintains its principal place of business
 | 
					 | 
				
			||||||
   and such litigation shall be governed by laws of that jurisdiction, without
 | 
					 | 
				
			||||||
   reference to its conflict-of-law provisions. Nothing in this Section shall
 | 
					 | 
				
			||||||
   prevent a party’s ability to bring cross-claims or counter-claims.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
9. Miscellaneous
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   This License represents the complete agreement concerning the subject matter
 | 
					 | 
				
			||||||
   hereof. If any provision of this License is held to be unenforceable, such
 | 
					 | 
				
			||||||
   provision shall be reformed only to the extent necessary to make it
 | 
					 | 
				
			||||||
   enforceable. Any law or regulation which provides that the language of a
 | 
					 | 
				
			||||||
   contract shall be construed against the drafter shall not be used to construe
 | 
					 | 
				
			||||||
   this License against a Contributor.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
10. Versions of the License
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
10.1. New Versions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      Mozilla Foundation is the license steward. Except as provided in Section
 | 
					 | 
				
			||||||
      10.3, no one other than the license steward has the right to modify or
 | 
					 | 
				
			||||||
      publish new versions of this License. Each version will be given a
 | 
					 | 
				
			||||||
      distinguishing version number.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
10.2. Effect of New Versions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      You may distribute the Covered Software under the terms of the version of
 | 
					 | 
				
			||||||
      the License under which You originally received the Covered Software, or
 | 
					 | 
				
			||||||
      under the terms of any subsequent version published by the license
 | 
					 | 
				
			||||||
      steward.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
10.3. Modified Versions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      If you create software not governed by this License, and you want to
 | 
					 | 
				
			||||||
      create a new license for such software, you may create and use a modified
 | 
					 | 
				
			||||||
      version of this License if you rename the license and remove any
 | 
					 | 
				
			||||||
      references to the name of the license steward (except to note that such
 | 
					 | 
				
			||||||
      modified license differs from this License).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
 | 
					 | 
				
			||||||
      If You choose to distribute Source Code Form that is Incompatible With
 | 
					 | 
				
			||||||
      Secondary Licenses under the terms of this version of the License, the
 | 
					 | 
				
			||||||
      notice described in Exhibit B of this License must be attached.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Exhibit A - Source Code Form License Notice
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      This Source Code Form is subject to the
 | 
					 | 
				
			||||||
      terms of the Mozilla Public License, v.
 | 
					 | 
				
			||||||
      2.0. If a copy of the MPL was not
 | 
					 | 
				
			||||||
      distributed with this file, You can
 | 
					 | 
				
			||||||
      obtain one at
 | 
					 | 
				
			||||||
      http://mozilla.org/MPL/2.0/.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
If it is not possible or desirable to put the notice in a particular file, then
 | 
					 | 
				
			||||||
You may include the notice in a location (such as a LICENSE file in a relevant
 | 
					 | 
				
			||||||
directory) where a recipient would be likely to look for such a notice.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
You may add additional accurate notices of copyright ownership.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Exhibit B - “Incompatible With Secondary Licenses” Notice
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      This Source Code Form is “Incompatible
 | 
					 | 
				
			||||||
      With Secondary Licenses”, as defined by
 | 
					 | 
				
			||||||
      the Mozilla Public License, v. 2.0.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							
								
								
									
										85
									
								
								vendor/github.com/hashicorp/consul/README.md
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										85
									
								
								vendor/github.com/hashicorp/consul/README.md
									
										
									
										generated
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,85 +0,0 @@
 | 
				
			||||||
# Consul [](https://travis-ci.org/hashicorp/consul)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
* Website: http://www.consul.io
 | 
					 | 
				
			||||||
* IRC: `#consul` on Freenode
 | 
					 | 
				
			||||||
* Mailing list: [Google Groups](https://groups.google.com/group/consul-tool/)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Consul is a tool for service discovery and configuration. Consul is
 | 
					 | 
				
			||||||
distributed, highly available, and extremely scalable.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Consul provides several key features:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
* **Service Discovery** - Consul makes it simple for services to register
 | 
					 | 
				
			||||||
  themselves and to discover other services via a DNS or HTTP interface.
 | 
					 | 
				
			||||||
  External services such as SaaS providers can be registered as well.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
* **Health Checking** - Health Checking enables Consul to quickly alert
 | 
					 | 
				
			||||||
  operators about any issues in a cluster. The integration with service
 | 
					 | 
				
			||||||
  discovery prevents routing traffic to unhealthy hosts and enables service
 | 
					 | 
				
			||||||
  level circuit breakers.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
* **Key/Value Storage** - A flexible key/value store enables storing
 | 
					 | 
				
			||||||
  dynamic configuration, feature flagging, coordination, leader election and
 | 
					 | 
				
			||||||
  more. The simple HTTP API makes it easy to use anywhere.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
* **Multi-Datacenter** - Consul is built to be datacenter aware, and can
 | 
					 | 
				
			||||||
  support any number of regions without complex configuration.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Consul runs on Linux, Mac OS X, and Windows. It is recommended to run the
 | 
					 | 
				
			||||||
Consul servers only on Linux, however.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Quick Start
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
An extensive quick quick start is viewable on the Consul website:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
http://www.consul.io/intro/getting-started/install.html
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Documentation
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Full, comprehensive documentation is viewable on the Consul website:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
http://www.consul.io/docs
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Developing Consul
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
If you wish to work on Consul itself, you'll first need [Go](https://golang.org)
 | 
					 | 
				
			||||||
installed (version 1.4+ is _required_). Make sure you have Go properly installed,
 | 
					 | 
				
			||||||
including setting up your [GOPATH](https://golang.org/doc/code.html#GOPATH).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Next, clone this repository into `$GOPATH/src/github.com/hashicorp/consul` and
 | 
					 | 
				
			||||||
then just type `make`. In a few moments, you'll have a working `consul` executable:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
$ go get -u ./...
 | 
					 | 
				
			||||||
$ make
 | 
					 | 
				
			||||||
...
 | 
					 | 
				
			||||||
$ bin/consul
 | 
					 | 
				
			||||||
...
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
*note: `make` will also place a copy of the binary in the first part of your $GOPATH*
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
You can run tests by typing `make test`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
If you make any changes to the code, run `make format` in order to automatically
 | 
					 | 
				
			||||||
format the code according to Go standards.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Building Consul on Windows
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Make sure Go 1.4+ is installed on your system and that the Go command is in your
 | 
					 | 
				
			||||||
%PATH%.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
For building Consul on Windows, you also need to have MinGW installed.
 | 
					 | 
				
			||||||
[TDM-GCC](http://tdm-gcc.tdragon.net/) is a simple bundle installer which has all
 | 
					 | 
				
			||||||
the required tools for building Consul with MinGW.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Install TDM-GCC and make sure it has been added to your %PATH%.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
If all goes well, you should be able to build Consul by running `make.bat` from a
 | 
					 | 
				
			||||||
command prompt.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
See also [golang/winstrap](https://github.com/golang/winstrap) and
 | 
					 | 
				
			||||||
[golang/wiki/WindowsBuild](https://github.com/golang/go/wiki/WindowsBuild)
 | 
					 | 
				
			||||||
for more information of how to set up a general Go build environment on Windows
 | 
					 | 
				
			||||||
with MinGW.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							
								
								
									
										39
									
								
								vendor/github.com/hashicorp/consul/api/README.md
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										39
									
								
								vendor/github.com/hashicorp/consul/api/README.md
									
										
									
										generated
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,39 +0,0 @@
 | 
				
			||||||
Consul API client
 | 
					 | 
				
			||||||
=================
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This package provides the `api` package which attempts to
 | 
					 | 
				
			||||||
provide programmatic access to the full Consul API.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Currently, all of the Consul APIs included in version 0.3 are supported.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Documentation
 | 
					 | 
				
			||||||
=============
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The full documentation is available on [Godoc](http://godoc.org/github.com/hashicorp/consul/api)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Usage
 | 
					 | 
				
			||||||
=====
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Below is an example of using the Consul client:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```go
 | 
					 | 
				
			||||||
// Get a new client, with KV endpoints
 | 
					 | 
				
			||||||
client, _ := api.NewClient(api.DefaultConfig())
 | 
					 | 
				
			||||||
kv := client.KV()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// PUT a new KV pair
 | 
					 | 
				
			||||||
p := &api.KVPair{Key: "foo", Value: []byte("test")}
 | 
					 | 
				
			||||||
_, err := kv.Put(p, nil)
 | 
					 | 
				
			||||||
if err != nil {
 | 
					 | 
				
			||||||
    panic(err)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Lookup the pair
 | 
					 | 
				
			||||||
pair, _, err := kv.Get("foo", nil)
 | 
					 | 
				
			||||||
if err != nil {
 | 
					 | 
				
			||||||
    panic(err)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
fmt.Printf("KV: %v", pair)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							
								
								
									
										140
									
								
								vendor/github.com/hashicorp/consul/api/acl.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										140
									
								
								vendor/github.com/hashicorp/consul/api/acl.go
									
										
									
										generated
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,140 +0,0 @@
 | 
				
			||||||
package api
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const (
 | 
					 | 
				
			||||||
	// ACLCLientType is the client type token
 | 
					 | 
				
			||||||
	ACLClientType = "client"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// ACLManagementType is the management type token
 | 
					 | 
				
			||||||
	ACLManagementType = "management"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ACLEntry is used to represent an ACL entry
 | 
					 | 
				
			||||||
type ACLEntry struct {
 | 
					 | 
				
			||||||
	CreateIndex uint64
 | 
					 | 
				
			||||||
	ModifyIndex uint64
 | 
					 | 
				
			||||||
	ID          string
 | 
					 | 
				
			||||||
	Name        string
 | 
					 | 
				
			||||||
	Type        string
 | 
					 | 
				
			||||||
	Rules       string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ACL can be used to query the ACL endpoints
 | 
					 | 
				
			||||||
type ACL struct {
 | 
					 | 
				
			||||||
	c *Client
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ACL returns a handle to the ACL endpoints
 | 
					 | 
				
			||||||
func (c *Client) ACL() *ACL {
 | 
					 | 
				
			||||||
	return &ACL{c}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Create is used to generate a new token with the given parameters
 | 
					 | 
				
			||||||
func (a *ACL) Create(acl *ACLEntry, q *WriteOptions) (string, *WriteMeta, error) {
 | 
					 | 
				
			||||||
	r := a.c.newRequest("PUT", "/v1/acl/create")
 | 
					 | 
				
			||||||
	r.setWriteOptions(q)
 | 
					 | 
				
			||||||
	r.obj = acl
 | 
					 | 
				
			||||||
	rtt, resp, err := requireOK(a.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return "", nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wm := &WriteMeta{RequestTime: rtt}
 | 
					 | 
				
			||||||
	var out struct{ ID string }
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &out); err != nil {
 | 
					 | 
				
			||||||
		return "", nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return out.ID, wm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Update is used to update the rules of an existing token
 | 
					 | 
				
			||||||
func (a *ACL) Update(acl *ACLEntry, q *WriteOptions) (*WriteMeta, error) {
 | 
					 | 
				
			||||||
	r := a.c.newRequest("PUT", "/v1/acl/update")
 | 
					 | 
				
			||||||
	r.setWriteOptions(q)
 | 
					 | 
				
			||||||
	r.obj = acl
 | 
					 | 
				
			||||||
	rtt, resp, err := requireOK(a.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wm := &WriteMeta{RequestTime: rtt}
 | 
					 | 
				
			||||||
	return wm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Destroy is used to destroy a given ACL token ID
 | 
					 | 
				
			||||||
func (a *ACL) Destroy(id string, q *WriteOptions) (*WriteMeta, error) {
 | 
					 | 
				
			||||||
	r := a.c.newRequest("PUT", "/v1/acl/destroy/"+id)
 | 
					 | 
				
			||||||
	r.setWriteOptions(q)
 | 
					 | 
				
			||||||
	rtt, resp, err := requireOK(a.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wm := &WriteMeta{RequestTime: rtt}
 | 
					 | 
				
			||||||
	return wm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Clone is used to return a new token cloned from an existing one
 | 
					 | 
				
			||||||
func (a *ACL) Clone(id string, q *WriteOptions) (string, *WriteMeta, error) {
 | 
					 | 
				
			||||||
	r := a.c.newRequest("PUT", "/v1/acl/clone/"+id)
 | 
					 | 
				
			||||||
	r.setWriteOptions(q)
 | 
					 | 
				
			||||||
	rtt, resp, err := requireOK(a.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return "", nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wm := &WriteMeta{RequestTime: rtt}
 | 
					 | 
				
			||||||
	var out struct{ ID string }
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &out); err != nil {
 | 
					 | 
				
			||||||
		return "", nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return out.ID, wm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Info is used to query for information about an ACL token
 | 
					 | 
				
			||||||
func (a *ACL) Info(id string, q *QueryOptions) (*ACLEntry, *QueryMeta, error) {
 | 
					 | 
				
			||||||
	r := a.c.newRequest("GET", "/v1/acl/info/"+id)
 | 
					 | 
				
			||||||
	r.setQueryOptions(q)
 | 
					 | 
				
			||||||
	rtt, resp, err := requireOK(a.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	qm := &QueryMeta{}
 | 
					 | 
				
			||||||
	parseQueryMeta(resp, qm)
 | 
					 | 
				
			||||||
	qm.RequestTime = rtt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var entries []*ACLEntry
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &entries); err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(entries) > 0 {
 | 
					 | 
				
			||||||
		return entries[0], qm, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil, qm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// List is used to get all the ACL tokens
 | 
					 | 
				
			||||||
func (a *ACL) List(q *QueryOptions) ([]*ACLEntry, *QueryMeta, error) {
 | 
					 | 
				
			||||||
	r := a.c.newRequest("GET", "/v1/acl/list")
 | 
					 | 
				
			||||||
	r.setQueryOptions(q)
 | 
					 | 
				
			||||||
	rtt, resp, err := requireOK(a.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	qm := &QueryMeta{}
 | 
					 | 
				
			||||||
	parseQueryMeta(resp, qm)
 | 
					 | 
				
			||||||
	qm.RequestTime = rtt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var entries []*ACLEntry
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &entries); err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return entries, qm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										334
									
								
								vendor/github.com/hashicorp/consul/api/agent.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										334
									
								
								vendor/github.com/hashicorp/consul/api/agent.go
									
										
									
										generated
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,334 +0,0 @@
 | 
				
			||||||
package api
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// AgentCheck represents a check known to the agent
 | 
					 | 
				
			||||||
type AgentCheck struct {
 | 
					 | 
				
			||||||
	Node        string
 | 
					 | 
				
			||||||
	CheckID     string
 | 
					 | 
				
			||||||
	Name        string
 | 
					 | 
				
			||||||
	Status      string
 | 
					 | 
				
			||||||
	Notes       string
 | 
					 | 
				
			||||||
	Output      string
 | 
					 | 
				
			||||||
	ServiceID   string
 | 
					 | 
				
			||||||
	ServiceName string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// AgentService represents a service known to the agent
 | 
					 | 
				
			||||||
type AgentService struct {
 | 
					 | 
				
			||||||
	ID      string
 | 
					 | 
				
			||||||
	Service string
 | 
					 | 
				
			||||||
	Tags    []string
 | 
					 | 
				
			||||||
	Port    int
 | 
					 | 
				
			||||||
	Address string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// AgentMember represents a cluster member known to the agent
 | 
					 | 
				
			||||||
type AgentMember struct {
 | 
					 | 
				
			||||||
	Name        string
 | 
					 | 
				
			||||||
	Addr        string
 | 
					 | 
				
			||||||
	Port        uint16
 | 
					 | 
				
			||||||
	Tags        map[string]string
 | 
					 | 
				
			||||||
	Status      int
 | 
					 | 
				
			||||||
	ProtocolMin uint8
 | 
					 | 
				
			||||||
	ProtocolMax uint8
 | 
					 | 
				
			||||||
	ProtocolCur uint8
 | 
					 | 
				
			||||||
	DelegateMin uint8
 | 
					 | 
				
			||||||
	DelegateMax uint8
 | 
					 | 
				
			||||||
	DelegateCur uint8
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// AgentServiceRegistration is used to register a new service
 | 
					 | 
				
			||||||
type AgentServiceRegistration struct {
 | 
					 | 
				
			||||||
	ID      string   `json:",omitempty"`
 | 
					 | 
				
			||||||
	Name    string   `json:",omitempty"`
 | 
					 | 
				
			||||||
	Tags    []string `json:",omitempty"`
 | 
					 | 
				
			||||||
	Port    int      `json:",omitempty"`
 | 
					 | 
				
			||||||
	Address string   `json:",omitempty"`
 | 
					 | 
				
			||||||
	Check   *AgentServiceCheck
 | 
					 | 
				
			||||||
	Checks  AgentServiceChecks
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// AgentCheckRegistration is used to register a new check
 | 
					 | 
				
			||||||
type AgentCheckRegistration struct {
 | 
					 | 
				
			||||||
	ID        string `json:",omitempty"`
 | 
					 | 
				
			||||||
	Name      string `json:",omitempty"`
 | 
					 | 
				
			||||||
	Notes     string `json:",omitempty"`
 | 
					 | 
				
			||||||
	ServiceID string `json:",omitempty"`
 | 
					 | 
				
			||||||
	AgentServiceCheck
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// AgentServiceCheck is used to create an associated
 | 
					 | 
				
			||||||
// check for a service
 | 
					 | 
				
			||||||
type AgentServiceCheck struct {
 | 
					 | 
				
			||||||
	Script   string `json:",omitempty"`
 | 
					 | 
				
			||||||
	Interval string `json:",omitempty"`
 | 
					 | 
				
			||||||
	Timeout  string `json:",omitempty"`
 | 
					 | 
				
			||||||
	TTL      string `json:",omitempty"`
 | 
					 | 
				
			||||||
	HTTP     string `json:",omitempty"`
 | 
					 | 
				
			||||||
	Status   string `json:",omitempty"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
type AgentServiceChecks []*AgentServiceCheck
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Agent can be used to query the Agent endpoints
 | 
					 | 
				
			||||||
type Agent struct {
 | 
					 | 
				
			||||||
	c *Client
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// cache the node name
 | 
					 | 
				
			||||||
	nodeName string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Agent returns a handle to the agent endpoints
 | 
					 | 
				
			||||||
func (c *Client) Agent() *Agent {
 | 
					 | 
				
			||||||
	return &Agent{c: c}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Self is used to query the agent we are speaking to for
 | 
					 | 
				
			||||||
// information about itself
 | 
					 | 
				
			||||||
func (a *Agent) Self() (map[string]map[string]interface{}, error) {
 | 
					 | 
				
			||||||
	r := a.c.newRequest("GET", "/v1/agent/self")
 | 
					 | 
				
			||||||
	_, resp, err := requireOK(a.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var out map[string]map[string]interface{}
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &out); err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return out, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NodeName is used to get the node name of the agent
 | 
					 | 
				
			||||||
func (a *Agent) NodeName() (string, error) {
 | 
					 | 
				
			||||||
	if a.nodeName != "" {
 | 
					 | 
				
			||||||
		return a.nodeName, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	info, err := a.Self()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return "", err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	name := info["Config"]["NodeName"].(string)
 | 
					 | 
				
			||||||
	a.nodeName = name
 | 
					 | 
				
			||||||
	return name, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Checks returns the locally registered checks
 | 
					 | 
				
			||||||
func (a *Agent) Checks() (map[string]*AgentCheck, error) {
 | 
					 | 
				
			||||||
	r := a.c.newRequest("GET", "/v1/agent/checks")
 | 
					 | 
				
			||||||
	_, resp, err := requireOK(a.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var out map[string]*AgentCheck
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &out); err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return out, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Services returns the locally registered services
 | 
					 | 
				
			||||||
func (a *Agent) Services() (map[string]*AgentService, error) {
 | 
					 | 
				
			||||||
	r := a.c.newRequest("GET", "/v1/agent/services")
 | 
					 | 
				
			||||||
	_, resp, err := requireOK(a.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var out map[string]*AgentService
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &out); err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return out, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Members returns the known gossip members. The WAN
 | 
					 | 
				
			||||||
// flag can be used to query a server for WAN members.
 | 
					 | 
				
			||||||
func (a *Agent) Members(wan bool) ([]*AgentMember, error) {
 | 
					 | 
				
			||||||
	r := a.c.newRequest("GET", "/v1/agent/members")
 | 
					 | 
				
			||||||
	if wan {
 | 
					 | 
				
			||||||
		r.params.Set("wan", "1")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	_, resp, err := requireOK(a.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var out []*AgentMember
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &out); err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return out, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ServiceRegister is used to register a new service with
 | 
					 | 
				
			||||||
// the local agent
 | 
					 | 
				
			||||||
func (a *Agent) ServiceRegister(service *AgentServiceRegistration) error {
 | 
					 | 
				
			||||||
	r := a.c.newRequest("PUT", "/v1/agent/service/register")
 | 
					 | 
				
			||||||
	r.obj = service
 | 
					 | 
				
			||||||
	_, resp, err := requireOK(a.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	resp.Body.Close()
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ServiceDeregister is used to deregister a service with
 | 
					 | 
				
			||||||
// the local agent
 | 
					 | 
				
			||||||
func (a *Agent) ServiceDeregister(serviceID string) error {
 | 
					 | 
				
			||||||
	r := a.c.newRequest("PUT", "/v1/agent/service/deregister/"+serviceID)
 | 
					 | 
				
			||||||
	_, resp, err := requireOK(a.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	resp.Body.Close()
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// PassTTL is used to set a TTL check to the passing state
 | 
					 | 
				
			||||||
func (a *Agent) PassTTL(checkID, note string) error {
 | 
					 | 
				
			||||||
	return a.UpdateTTL(checkID, note, "pass")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// WarnTTL is used to set a TTL check to the warning state
 | 
					 | 
				
			||||||
func (a *Agent) WarnTTL(checkID, note string) error {
 | 
					 | 
				
			||||||
	return a.UpdateTTL(checkID, note, "warn")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// FailTTL is used to set a TTL check to the failing state
 | 
					 | 
				
			||||||
func (a *Agent) FailTTL(checkID, note string) error {
 | 
					 | 
				
			||||||
	return a.UpdateTTL(checkID, note, "fail")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// UpdateTTL is used to update the TTL of a check
 | 
					 | 
				
			||||||
func (a *Agent) UpdateTTL(checkID, note, status string) error {
 | 
					 | 
				
			||||||
	switch status {
 | 
					 | 
				
			||||||
	case "pass":
 | 
					 | 
				
			||||||
	case "warn":
 | 
					 | 
				
			||||||
	case "fail":
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return fmt.Errorf("Invalid status: %s", status)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	endpoint := fmt.Sprintf("/v1/agent/check/%s/%s", status, checkID)
 | 
					 | 
				
			||||||
	r := a.c.newRequest("PUT", endpoint)
 | 
					 | 
				
			||||||
	r.params.Set("note", note)
 | 
					 | 
				
			||||||
	_, resp, err := requireOK(a.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	resp.Body.Close()
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// CheckRegister is used to register a new check with
 | 
					 | 
				
			||||||
// the local agent
 | 
					 | 
				
			||||||
func (a *Agent) CheckRegister(check *AgentCheckRegistration) error {
 | 
					 | 
				
			||||||
	r := a.c.newRequest("PUT", "/v1/agent/check/register")
 | 
					 | 
				
			||||||
	r.obj = check
 | 
					 | 
				
			||||||
	_, resp, err := requireOK(a.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	resp.Body.Close()
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// CheckDeregister is used to deregister a check with
 | 
					 | 
				
			||||||
// the local agent
 | 
					 | 
				
			||||||
func (a *Agent) CheckDeregister(checkID string) error {
 | 
					 | 
				
			||||||
	r := a.c.newRequest("PUT", "/v1/agent/check/deregister/"+checkID)
 | 
					 | 
				
			||||||
	_, resp, err := requireOK(a.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	resp.Body.Close()
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Join is used to instruct the agent to attempt a join to
 | 
					 | 
				
			||||||
// another cluster member
 | 
					 | 
				
			||||||
func (a *Agent) Join(addr string, wan bool) error {
 | 
					 | 
				
			||||||
	r := a.c.newRequest("PUT", "/v1/agent/join/"+addr)
 | 
					 | 
				
			||||||
	if wan {
 | 
					 | 
				
			||||||
		r.params.Set("wan", "1")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	_, resp, err := requireOK(a.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	resp.Body.Close()
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ForceLeave is used to have the agent eject a failed node
 | 
					 | 
				
			||||||
func (a *Agent) ForceLeave(node string) error {
 | 
					 | 
				
			||||||
	r := a.c.newRequest("PUT", "/v1/agent/force-leave/"+node)
 | 
					 | 
				
			||||||
	_, resp, err := requireOK(a.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	resp.Body.Close()
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// EnableServiceMaintenance toggles service maintenance mode on
 | 
					 | 
				
			||||||
// for the given service ID.
 | 
					 | 
				
			||||||
func (a *Agent) EnableServiceMaintenance(serviceID, reason string) error {
 | 
					 | 
				
			||||||
	r := a.c.newRequest("PUT", "/v1/agent/service/maintenance/"+serviceID)
 | 
					 | 
				
			||||||
	r.params.Set("enable", "true")
 | 
					 | 
				
			||||||
	r.params.Set("reason", reason)
 | 
					 | 
				
			||||||
	_, resp, err := requireOK(a.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	resp.Body.Close()
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// DisableServiceMaintenance toggles service maintenance mode off
 | 
					 | 
				
			||||||
// for the given service ID.
 | 
					 | 
				
			||||||
func (a *Agent) DisableServiceMaintenance(serviceID string) error {
 | 
					 | 
				
			||||||
	r := a.c.newRequest("PUT", "/v1/agent/service/maintenance/"+serviceID)
 | 
					 | 
				
			||||||
	r.params.Set("enable", "false")
 | 
					 | 
				
			||||||
	_, resp, err := requireOK(a.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	resp.Body.Close()
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// EnableNodeMaintenance toggles node maintenance mode on for the
 | 
					 | 
				
			||||||
// agent we are connected to.
 | 
					 | 
				
			||||||
func (a *Agent) EnableNodeMaintenance(reason string) error {
 | 
					 | 
				
			||||||
	r := a.c.newRequest("PUT", "/v1/agent/maintenance")
 | 
					 | 
				
			||||||
	r.params.Set("enable", "true")
 | 
					 | 
				
			||||||
	r.params.Set("reason", reason)
 | 
					 | 
				
			||||||
	_, resp, err := requireOK(a.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	resp.Body.Close()
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// DisableNodeMaintenance toggles node maintenance mode off for the
 | 
					 | 
				
			||||||
// agent we are connected to.
 | 
					 | 
				
			||||||
func (a *Agent) DisableNodeMaintenance() error {
 | 
					 | 
				
			||||||
	r := a.c.newRequest("PUT", "/v1/agent/maintenance")
 | 
					 | 
				
			||||||
	r.params.Set("enable", "false")
 | 
					 | 
				
			||||||
	_, resp, err := requireOK(a.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	resp.Body.Close()
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										442
									
								
								vendor/github.com/hashicorp/consul/api/api.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										442
									
								
								vendor/github.com/hashicorp/consul/api/api.go
									
										
									
										generated
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,442 +0,0 @@
 | 
				
			||||||
package api
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"crypto/tls"
 | 
					 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"io"
 | 
					 | 
				
			||||||
	"log"
 | 
					 | 
				
			||||||
	"net"
 | 
					 | 
				
			||||||
	"net/http"
 | 
					 | 
				
			||||||
	"net/url"
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
	"strconv"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// QueryOptions are used to parameterize a query
 | 
					 | 
				
			||||||
type QueryOptions struct {
 | 
					 | 
				
			||||||
	// Providing a datacenter overwrites the DC provided
 | 
					 | 
				
			||||||
	// by the Config
 | 
					 | 
				
			||||||
	Datacenter string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// AllowStale allows any Consul server (non-leader) to service
 | 
					 | 
				
			||||||
	// a read. This allows for lower latency and higher throughput
 | 
					 | 
				
			||||||
	AllowStale bool
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// RequireConsistent forces the read to be fully consistent.
 | 
					 | 
				
			||||||
	// This is more expensive but prevents ever performing a stale
 | 
					 | 
				
			||||||
	// read.
 | 
					 | 
				
			||||||
	RequireConsistent bool
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// WaitIndex is used to enable a blocking query. Waits
 | 
					 | 
				
			||||||
	// until the timeout or the next index is reached
 | 
					 | 
				
			||||||
	WaitIndex uint64
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// WaitTime is used to bound the duration of a wait.
 | 
					 | 
				
			||||||
	// Defaults to that of the Config, but can be overriden.
 | 
					 | 
				
			||||||
	WaitTime time.Duration
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Token is used to provide a per-request ACL token
 | 
					 | 
				
			||||||
	// which overrides the agent's default token.
 | 
					 | 
				
			||||||
	Token string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// WriteOptions are used to parameterize a write
 | 
					 | 
				
			||||||
type WriteOptions struct {
 | 
					 | 
				
			||||||
	// Providing a datacenter overwrites the DC provided
 | 
					 | 
				
			||||||
	// by the Config
 | 
					 | 
				
			||||||
	Datacenter string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Token is used to provide a per-request ACL token
 | 
					 | 
				
			||||||
	// which overrides the agent's default token.
 | 
					 | 
				
			||||||
	Token string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// QueryMeta is used to return meta data about a query
 | 
					 | 
				
			||||||
type QueryMeta struct {
 | 
					 | 
				
			||||||
	// LastIndex. This can be used as a WaitIndex to perform
 | 
					 | 
				
			||||||
	// a blocking query
 | 
					 | 
				
			||||||
	LastIndex uint64
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Time of last contact from the leader for the
 | 
					 | 
				
			||||||
	// server servicing the request
 | 
					 | 
				
			||||||
	LastContact time.Duration
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Is there a known leader
 | 
					 | 
				
			||||||
	KnownLeader bool
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// How long did the request take
 | 
					 | 
				
			||||||
	RequestTime time.Duration
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// WriteMeta is used to return meta data about a write
 | 
					 | 
				
			||||||
type WriteMeta struct {
 | 
					 | 
				
			||||||
	// How long did the request take
 | 
					 | 
				
			||||||
	RequestTime time.Duration
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// HttpBasicAuth is used to authenticate http client with HTTP Basic Authentication
 | 
					 | 
				
			||||||
type HttpBasicAuth struct {
 | 
					 | 
				
			||||||
	// Username to use for HTTP Basic Authentication
 | 
					 | 
				
			||||||
	Username string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Password to use for HTTP Basic Authentication
 | 
					 | 
				
			||||||
	Password string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Config is used to configure the creation of a client
 | 
					 | 
				
			||||||
type Config struct {
 | 
					 | 
				
			||||||
	// Address is the address of the Consul server
 | 
					 | 
				
			||||||
	Address string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Scheme is the URI scheme for the Consul server
 | 
					 | 
				
			||||||
	Scheme string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Datacenter to use. If not provided, the default agent datacenter is used.
 | 
					 | 
				
			||||||
	Datacenter string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// HttpClient is the client to use. Default will be
 | 
					 | 
				
			||||||
	// used if not provided.
 | 
					 | 
				
			||||||
	HttpClient *http.Client
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// HttpAuth is the auth info to use for http access.
 | 
					 | 
				
			||||||
	HttpAuth *HttpBasicAuth
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// WaitTime limits how long a Watch will block. If not provided,
 | 
					 | 
				
			||||||
	// the agent default values will be used.
 | 
					 | 
				
			||||||
	WaitTime time.Duration
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Token is used to provide a per-request ACL token
 | 
					 | 
				
			||||||
	// which overrides the agent's default token.
 | 
					 | 
				
			||||||
	Token string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// DefaultConfig returns a default configuration for the client
 | 
					 | 
				
			||||||
func DefaultConfig() *Config {
 | 
					 | 
				
			||||||
	config := &Config{
 | 
					 | 
				
			||||||
		Address:    "127.0.0.1:8500",
 | 
					 | 
				
			||||||
		Scheme:     "http",
 | 
					 | 
				
			||||||
		HttpClient: http.DefaultClient,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if addr := os.Getenv("CONSUL_HTTP_ADDR"); addr != "" {
 | 
					 | 
				
			||||||
		config.Address = addr
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if token := os.Getenv("CONSUL_HTTP_TOKEN"); token != "" {
 | 
					 | 
				
			||||||
		config.Token = token
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if auth := os.Getenv("CONSUL_HTTP_AUTH"); auth != "" {
 | 
					 | 
				
			||||||
		var username, password string
 | 
					 | 
				
			||||||
		if strings.Contains(auth, ":") {
 | 
					 | 
				
			||||||
			split := strings.SplitN(auth, ":", 2)
 | 
					 | 
				
			||||||
			username = split[0]
 | 
					 | 
				
			||||||
			password = split[1]
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			username = auth
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		config.HttpAuth = &HttpBasicAuth{
 | 
					 | 
				
			||||||
			Username: username,
 | 
					 | 
				
			||||||
			Password: password,
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ssl := os.Getenv("CONSUL_HTTP_SSL"); ssl != "" {
 | 
					 | 
				
			||||||
		enabled, err := strconv.ParseBool(ssl)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			log.Printf("[WARN] client: could not parse CONSUL_HTTP_SSL: %s", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if enabled {
 | 
					 | 
				
			||||||
			config.Scheme = "https"
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if verify := os.Getenv("CONSUL_HTTP_SSL_VERIFY"); verify != "" {
 | 
					 | 
				
			||||||
		doVerify, err := strconv.ParseBool(verify)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			log.Printf("[WARN] client: could not parse CONSUL_HTTP_SSL_VERIFY: %s", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if !doVerify {
 | 
					 | 
				
			||||||
			config.HttpClient.Transport = &http.Transport{
 | 
					 | 
				
			||||||
				TLSClientConfig: &tls.Config{
 | 
					 | 
				
			||||||
					InsecureSkipVerify: true,
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return config
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Client provides a client to the Consul API
 | 
					 | 
				
			||||||
type Client struct {
 | 
					 | 
				
			||||||
	config Config
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NewClient returns a new client
 | 
					 | 
				
			||||||
func NewClient(config *Config) (*Client, error) {
 | 
					 | 
				
			||||||
	// bootstrap the config
 | 
					 | 
				
			||||||
	defConfig := DefaultConfig()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if len(config.Address) == 0 {
 | 
					 | 
				
			||||||
		config.Address = defConfig.Address
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if len(config.Scheme) == 0 {
 | 
					 | 
				
			||||||
		config.Scheme = defConfig.Scheme
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if config.HttpClient == nil {
 | 
					 | 
				
			||||||
		config.HttpClient = defConfig.HttpClient
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if parts := strings.SplitN(config.Address, "unix://", 2); len(parts) == 2 {
 | 
					 | 
				
			||||||
		config.HttpClient = &http.Client{
 | 
					 | 
				
			||||||
			Transport: &http.Transport{
 | 
					 | 
				
			||||||
				Dial: func(_, _ string) (net.Conn, error) {
 | 
					 | 
				
			||||||
					return net.Dial("unix", parts[1])
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		config.Address = parts[1]
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	client := &Client{
 | 
					 | 
				
			||||||
		config: *config,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return client, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// request is used to help build up a request
 | 
					 | 
				
			||||||
type request struct {
 | 
					 | 
				
			||||||
	config *Config
 | 
					 | 
				
			||||||
	method string
 | 
					 | 
				
			||||||
	url    *url.URL
 | 
					 | 
				
			||||||
	params url.Values
 | 
					 | 
				
			||||||
	body   io.Reader
 | 
					 | 
				
			||||||
	obj    interface{}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// setQueryOptions is used to annotate the request with
 | 
					 | 
				
			||||||
// additional query options
 | 
					 | 
				
			||||||
func (r *request) setQueryOptions(q *QueryOptions) {
 | 
					 | 
				
			||||||
	if q == nil {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if q.Datacenter != "" {
 | 
					 | 
				
			||||||
		r.params.Set("dc", q.Datacenter)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if q.AllowStale {
 | 
					 | 
				
			||||||
		r.params.Set("stale", "")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if q.RequireConsistent {
 | 
					 | 
				
			||||||
		r.params.Set("consistent", "")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if q.WaitIndex != 0 {
 | 
					 | 
				
			||||||
		r.params.Set("index", strconv.FormatUint(q.WaitIndex, 10))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if q.WaitTime != 0 {
 | 
					 | 
				
			||||||
		r.params.Set("wait", durToMsec(q.WaitTime))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if q.Token != "" {
 | 
					 | 
				
			||||||
		r.params.Set("token", q.Token)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// durToMsec converts a duration to a millisecond specified string
 | 
					 | 
				
			||||||
func durToMsec(dur time.Duration) string {
 | 
					 | 
				
			||||||
	return fmt.Sprintf("%dms", dur/time.Millisecond)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// setWriteOptions is used to annotate the request with
 | 
					 | 
				
			||||||
// additional write options
 | 
					 | 
				
			||||||
func (r *request) setWriteOptions(q *WriteOptions) {
 | 
					 | 
				
			||||||
	if q == nil {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if q.Datacenter != "" {
 | 
					 | 
				
			||||||
		r.params.Set("dc", q.Datacenter)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if q.Token != "" {
 | 
					 | 
				
			||||||
		r.params.Set("token", q.Token)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// toHTTP converts the request to an HTTP request
 | 
					 | 
				
			||||||
func (r *request) toHTTP() (*http.Request, error) {
 | 
					 | 
				
			||||||
	// Encode the query parameters
 | 
					 | 
				
			||||||
	r.url.RawQuery = r.params.Encode()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Check if we should encode the body
 | 
					 | 
				
			||||||
	if r.body == nil && r.obj != nil {
 | 
					 | 
				
			||||||
		if b, err := encodeBody(r.obj); err != nil {
 | 
					 | 
				
			||||||
			return nil, err
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			r.body = b
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Create the HTTP request
 | 
					 | 
				
			||||||
	req, err := http.NewRequest(r.method, r.url.RequestURI(), r.body)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	req.URL.Host = r.url.Host
 | 
					 | 
				
			||||||
	req.URL.Scheme = r.url.Scheme
 | 
					 | 
				
			||||||
	req.Host = r.url.Host
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Setup auth
 | 
					 | 
				
			||||||
	if r.config.HttpAuth != nil {
 | 
					 | 
				
			||||||
		req.SetBasicAuth(r.config.HttpAuth.Username, r.config.HttpAuth.Password)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return req, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// newRequest is used to create a new request
 | 
					 | 
				
			||||||
func (c *Client) newRequest(method, path string) *request {
 | 
					 | 
				
			||||||
	r := &request{
 | 
					 | 
				
			||||||
		config: &c.config,
 | 
					 | 
				
			||||||
		method: method,
 | 
					 | 
				
			||||||
		url: &url.URL{
 | 
					 | 
				
			||||||
			Scheme: c.config.Scheme,
 | 
					 | 
				
			||||||
			Host:   c.config.Address,
 | 
					 | 
				
			||||||
			Path:   path,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		params: make(map[string][]string),
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if c.config.Datacenter != "" {
 | 
					 | 
				
			||||||
		r.params.Set("dc", c.config.Datacenter)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if c.config.WaitTime != 0 {
 | 
					 | 
				
			||||||
		r.params.Set("wait", durToMsec(r.config.WaitTime))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if c.config.Token != "" {
 | 
					 | 
				
			||||||
		r.params.Set("token", r.config.Token)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return r
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// doRequest runs a request with our client
 | 
					 | 
				
			||||||
func (c *Client) doRequest(r *request) (time.Duration, *http.Response, error) {
 | 
					 | 
				
			||||||
	req, err := r.toHTTP()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return 0, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	start := time.Now()
 | 
					 | 
				
			||||||
	resp, err := c.config.HttpClient.Do(req)
 | 
					 | 
				
			||||||
	diff := time.Now().Sub(start)
 | 
					 | 
				
			||||||
	return diff, resp, err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Query is used to do a GET request against an endpoint
 | 
					 | 
				
			||||||
// and deserialize the response into an interface using
 | 
					 | 
				
			||||||
// standard Consul conventions.
 | 
					 | 
				
			||||||
func (c *Client) query(endpoint string, out interface{}, q *QueryOptions) (*QueryMeta, error) {
 | 
					 | 
				
			||||||
	r := c.newRequest("GET", endpoint)
 | 
					 | 
				
			||||||
	r.setQueryOptions(q)
 | 
					 | 
				
			||||||
	rtt, resp, err := requireOK(c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	qm := &QueryMeta{}
 | 
					 | 
				
			||||||
	parseQueryMeta(resp, qm)
 | 
					 | 
				
			||||||
	qm.RequestTime = rtt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, out); err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return qm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// write is used to do a PUT request against an endpoint
 | 
					 | 
				
			||||||
// and serialize/deserialized using the standard Consul conventions.
 | 
					 | 
				
			||||||
func (c *Client) write(endpoint string, in, out interface{}, q *WriteOptions) (*WriteMeta, error) {
 | 
					 | 
				
			||||||
	r := c.newRequest("PUT", endpoint)
 | 
					 | 
				
			||||||
	r.setWriteOptions(q)
 | 
					 | 
				
			||||||
	r.obj = in
 | 
					 | 
				
			||||||
	rtt, resp, err := requireOK(c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wm := &WriteMeta{RequestTime: rtt}
 | 
					 | 
				
			||||||
	if out != nil {
 | 
					 | 
				
			||||||
		if err := decodeBody(resp, &out); err != nil {
 | 
					 | 
				
			||||||
			return nil, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return wm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// parseQueryMeta is used to help parse query meta-data
 | 
					 | 
				
			||||||
func parseQueryMeta(resp *http.Response, q *QueryMeta) error {
 | 
					 | 
				
			||||||
	header := resp.Header
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Parse the X-Consul-Index
 | 
					 | 
				
			||||||
	index, err := strconv.ParseUint(header.Get("X-Consul-Index"), 10, 64)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return fmt.Errorf("Failed to parse X-Consul-Index: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	q.LastIndex = index
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Parse the X-Consul-LastContact
 | 
					 | 
				
			||||||
	last, err := strconv.ParseUint(header.Get("X-Consul-LastContact"), 10, 64)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return fmt.Errorf("Failed to parse X-Consul-LastContact: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	q.LastContact = time.Duration(last) * time.Millisecond
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Parse the X-Consul-KnownLeader
 | 
					 | 
				
			||||||
	switch header.Get("X-Consul-KnownLeader") {
 | 
					 | 
				
			||||||
	case "true":
 | 
					 | 
				
			||||||
		q.KnownLeader = true
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		q.KnownLeader = false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// decodeBody is used to JSON decode a body
 | 
					 | 
				
			||||||
func decodeBody(resp *http.Response, out interface{}) error {
 | 
					 | 
				
			||||||
	dec := json.NewDecoder(resp.Body)
 | 
					 | 
				
			||||||
	return dec.Decode(out)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// encodeBody is used to encode a request body
 | 
					 | 
				
			||||||
func encodeBody(obj interface{}) (io.Reader, error) {
 | 
					 | 
				
			||||||
	buf := bytes.NewBuffer(nil)
 | 
					 | 
				
			||||||
	enc := json.NewEncoder(buf)
 | 
					 | 
				
			||||||
	if err := enc.Encode(obj); err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return buf, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// requireOK is used to wrap doRequest and check for a 200
 | 
					 | 
				
			||||||
func requireOK(d time.Duration, resp *http.Response, e error) (time.Duration, *http.Response, error) {
 | 
					 | 
				
			||||||
	if e != nil {
 | 
					 | 
				
			||||||
		if resp != nil {
 | 
					 | 
				
			||||||
			resp.Body.Close()
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return d, nil, e
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if resp.StatusCode != 200 {
 | 
					 | 
				
			||||||
		var buf bytes.Buffer
 | 
					 | 
				
			||||||
		io.Copy(&buf, resp.Body)
 | 
					 | 
				
			||||||
		resp.Body.Close()
 | 
					 | 
				
			||||||
		return d, nil, fmt.Errorf("Unexpected response code: %d (%s)", resp.StatusCode, buf.Bytes())
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return d, resp, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										182
									
								
								vendor/github.com/hashicorp/consul/api/catalog.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										182
									
								
								vendor/github.com/hashicorp/consul/api/catalog.go
									
										
									
										generated
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,182 +0,0 @@
 | 
				
			||||||
package api
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Node struct {
 | 
					 | 
				
			||||||
	Node    string
 | 
					 | 
				
			||||||
	Address string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type CatalogService struct {
 | 
					 | 
				
			||||||
	Node           string
 | 
					 | 
				
			||||||
	Address        string
 | 
					 | 
				
			||||||
	ServiceID      string
 | 
					 | 
				
			||||||
	ServiceName    string
 | 
					 | 
				
			||||||
	ServiceAddress string
 | 
					 | 
				
			||||||
	ServiceTags    []string
 | 
					 | 
				
			||||||
	ServicePort    int
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type CatalogNode struct {
 | 
					 | 
				
			||||||
	Node     *Node
 | 
					 | 
				
			||||||
	Services map[string]*AgentService
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type CatalogRegistration struct {
 | 
					 | 
				
			||||||
	Node       string
 | 
					 | 
				
			||||||
	Address    string
 | 
					 | 
				
			||||||
	Datacenter string
 | 
					 | 
				
			||||||
	Service    *AgentService
 | 
					 | 
				
			||||||
	Check      *AgentCheck
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type CatalogDeregistration struct {
 | 
					 | 
				
			||||||
	Node       string
 | 
					 | 
				
			||||||
	Address    string
 | 
					 | 
				
			||||||
	Datacenter string
 | 
					 | 
				
			||||||
	ServiceID  string
 | 
					 | 
				
			||||||
	CheckID    string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Catalog can be used to query the Catalog endpoints
 | 
					 | 
				
			||||||
type Catalog struct {
 | 
					 | 
				
			||||||
	c *Client
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Catalog returns a handle to the catalog endpoints
 | 
					 | 
				
			||||||
func (c *Client) Catalog() *Catalog {
 | 
					 | 
				
			||||||
	return &Catalog{c}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (c *Catalog) Register(reg *CatalogRegistration, q *WriteOptions) (*WriteMeta, error) {
 | 
					 | 
				
			||||||
	r := c.c.newRequest("PUT", "/v1/catalog/register")
 | 
					 | 
				
			||||||
	r.setWriteOptions(q)
 | 
					 | 
				
			||||||
	r.obj = reg
 | 
					 | 
				
			||||||
	rtt, resp, err := requireOK(c.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wm := &WriteMeta{}
 | 
					 | 
				
			||||||
	wm.RequestTime = rtt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return wm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (c *Catalog) Deregister(dereg *CatalogDeregistration, q *WriteOptions) (*WriteMeta, error) {
 | 
					 | 
				
			||||||
	r := c.c.newRequest("PUT", "/v1/catalog/deregister")
 | 
					 | 
				
			||||||
	r.setWriteOptions(q)
 | 
					 | 
				
			||||||
	r.obj = dereg
 | 
					 | 
				
			||||||
	rtt, resp, err := requireOK(c.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wm := &WriteMeta{}
 | 
					 | 
				
			||||||
	wm.RequestTime = rtt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return wm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Datacenters is used to query for all the known datacenters
 | 
					 | 
				
			||||||
func (c *Catalog) Datacenters() ([]string, error) {
 | 
					 | 
				
			||||||
	r := c.c.newRequest("GET", "/v1/catalog/datacenters")
 | 
					 | 
				
			||||||
	_, resp, err := requireOK(c.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var out []string
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &out); err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return out, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Nodes is used to query all the known nodes
 | 
					 | 
				
			||||||
func (c *Catalog) Nodes(q *QueryOptions) ([]*Node, *QueryMeta, error) {
 | 
					 | 
				
			||||||
	r := c.c.newRequest("GET", "/v1/catalog/nodes")
 | 
					 | 
				
			||||||
	r.setQueryOptions(q)
 | 
					 | 
				
			||||||
	rtt, resp, err := requireOK(c.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	qm := &QueryMeta{}
 | 
					 | 
				
			||||||
	parseQueryMeta(resp, qm)
 | 
					 | 
				
			||||||
	qm.RequestTime = rtt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var out []*Node
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &out); err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return out, qm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Services is used to query for all known services
 | 
					 | 
				
			||||||
func (c *Catalog) Services(q *QueryOptions) (map[string][]string, *QueryMeta, error) {
 | 
					 | 
				
			||||||
	r := c.c.newRequest("GET", "/v1/catalog/services")
 | 
					 | 
				
			||||||
	r.setQueryOptions(q)
 | 
					 | 
				
			||||||
	rtt, resp, err := requireOK(c.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	qm := &QueryMeta{}
 | 
					 | 
				
			||||||
	parseQueryMeta(resp, qm)
 | 
					 | 
				
			||||||
	qm.RequestTime = rtt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var out map[string][]string
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &out); err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return out, qm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Service is used to query catalog entries for a given service
 | 
					 | 
				
			||||||
func (c *Catalog) Service(service, tag string, q *QueryOptions) ([]*CatalogService, *QueryMeta, error) {
 | 
					 | 
				
			||||||
	r := c.c.newRequest("GET", "/v1/catalog/service/"+service)
 | 
					 | 
				
			||||||
	r.setQueryOptions(q)
 | 
					 | 
				
			||||||
	if tag != "" {
 | 
					 | 
				
			||||||
		r.params.Set("tag", tag)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	rtt, resp, err := requireOK(c.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	qm := &QueryMeta{}
 | 
					 | 
				
			||||||
	parseQueryMeta(resp, qm)
 | 
					 | 
				
			||||||
	qm.RequestTime = rtt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var out []*CatalogService
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &out); err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return out, qm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Node is used to query for service information about a single node
 | 
					 | 
				
			||||||
func (c *Catalog) Node(node string, q *QueryOptions) (*CatalogNode, *QueryMeta, error) {
 | 
					 | 
				
			||||||
	r := c.c.newRequest("GET", "/v1/catalog/node/"+node)
 | 
					 | 
				
			||||||
	r.setQueryOptions(q)
 | 
					 | 
				
			||||||
	rtt, resp, err := requireOK(c.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	qm := &QueryMeta{}
 | 
					 | 
				
			||||||
	parseQueryMeta(resp, qm)
 | 
					 | 
				
			||||||
	qm.RequestTime = rtt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var out *CatalogNode
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &out); err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return out, qm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										104
									
								
								vendor/github.com/hashicorp/consul/api/event.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										104
									
								
								vendor/github.com/hashicorp/consul/api/event.go
									
										
									
										generated
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,104 +0,0 @@
 | 
				
			||||||
package api
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"strconv"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Event can be used to query the Event endpoints
 | 
					 | 
				
			||||||
type Event struct {
 | 
					 | 
				
			||||||
	c *Client
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// UserEvent represents an event that was fired by the user
 | 
					 | 
				
			||||||
type UserEvent struct {
 | 
					 | 
				
			||||||
	ID            string
 | 
					 | 
				
			||||||
	Name          string
 | 
					 | 
				
			||||||
	Payload       []byte
 | 
					 | 
				
			||||||
	NodeFilter    string
 | 
					 | 
				
			||||||
	ServiceFilter string
 | 
					 | 
				
			||||||
	TagFilter     string
 | 
					 | 
				
			||||||
	Version       int
 | 
					 | 
				
			||||||
	LTime         uint64
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Event returns a handle to the event endpoints
 | 
					 | 
				
			||||||
func (c *Client) Event() *Event {
 | 
					 | 
				
			||||||
	return &Event{c}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Fire is used to fire a new user event. Only the Name, Payload and Filters
 | 
					 | 
				
			||||||
// are respected. This returns the ID or an associated error. Cross DC requests
 | 
					 | 
				
			||||||
// are supported.
 | 
					 | 
				
			||||||
func (e *Event) Fire(params *UserEvent, q *WriteOptions) (string, *WriteMeta, error) {
 | 
					 | 
				
			||||||
	r := e.c.newRequest("PUT", "/v1/event/fire/"+params.Name)
 | 
					 | 
				
			||||||
	r.setWriteOptions(q)
 | 
					 | 
				
			||||||
	if params.NodeFilter != "" {
 | 
					 | 
				
			||||||
		r.params.Set("node", params.NodeFilter)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if params.ServiceFilter != "" {
 | 
					 | 
				
			||||||
		r.params.Set("service", params.ServiceFilter)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if params.TagFilter != "" {
 | 
					 | 
				
			||||||
		r.params.Set("tag", params.TagFilter)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if params.Payload != nil {
 | 
					 | 
				
			||||||
		r.body = bytes.NewReader(params.Payload)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rtt, resp, err := requireOK(e.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return "", nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wm := &WriteMeta{RequestTime: rtt}
 | 
					 | 
				
			||||||
	var out UserEvent
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &out); err != nil {
 | 
					 | 
				
			||||||
		return "", nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return out.ID, wm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// List is used to get the most recent events an agent has received.
 | 
					 | 
				
			||||||
// This list can be optionally filtered by the name. This endpoint supports
 | 
					 | 
				
			||||||
// quasi-blocking queries. The index is not monotonic, nor does it provide provide
 | 
					 | 
				
			||||||
// LastContact or KnownLeader.
 | 
					 | 
				
			||||||
func (e *Event) List(name string, q *QueryOptions) ([]*UserEvent, *QueryMeta, error) {
 | 
					 | 
				
			||||||
	r := e.c.newRequest("GET", "/v1/event/list")
 | 
					 | 
				
			||||||
	r.setQueryOptions(q)
 | 
					 | 
				
			||||||
	if name != "" {
 | 
					 | 
				
			||||||
		r.params.Set("name", name)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	rtt, resp, err := requireOK(e.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	qm := &QueryMeta{}
 | 
					 | 
				
			||||||
	parseQueryMeta(resp, qm)
 | 
					 | 
				
			||||||
	qm.RequestTime = rtt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var entries []*UserEvent
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &entries); err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return entries, qm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// IDToIndex is a bit of a hack. This simulates the index generation to
 | 
					 | 
				
			||||||
// convert an event ID into a WaitIndex.
 | 
					 | 
				
			||||||
func (e *Event) IDToIndex(uuid string) uint64 {
 | 
					 | 
				
			||||||
	lower := uuid[0:8] + uuid[9:13] + uuid[14:18]
 | 
					 | 
				
			||||||
	upper := uuid[19:23] + uuid[24:36]
 | 
					 | 
				
			||||||
	lowVal, err := strconv.ParseUint(lower, 16, 64)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		panic("Failed to convert " + lower)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	highVal, err := strconv.ParseUint(upper, 16, 64)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		panic("Failed to convert " + upper)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return lowVal ^ highVal
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										136
									
								
								vendor/github.com/hashicorp/consul/api/health.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										136
									
								
								vendor/github.com/hashicorp/consul/api/health.go
									
										
									
										generated
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,136 +0,0 @@
 | 
				
			||||||
package api
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// HealthCheck is used to represent a single check
 | 
					 | 
				
			||||||
type HealthCheck struct {
 | 
					 | 
				
			||||||
	Node        string
 | 
					 | 
				
			||||||
	CheckID     string
 | 
					 | 
				
			||||||
	Name        string
 | 
					 | 
				
			||||||
	Status      string
 | 
					 | 
				
			||||||
	Notes       string
 | 
					 | 
				
			||||||
	Output      string
 | 
					 | 
				
			||||||
	ServiceID   string
 | 
					 | 
				
			||||||
	ServiceName string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ServiceEntry is used for the health service endpoint
 | 
					 | 
				
			||||||
type ServiceEntry struct {
 | 
					 | 
				
			||||||
	Node    *Node
 | 
					 | 
				
			||||||
	Service *AgentService
 | 
					 | 
				
			||||||
	Checks  []*HealthCheck
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Health can be used to query the Health endpoints
 | 
					 | 
				
			||||||
type Health struct {
 | 
					 | 
				
			||||||
	c *Client
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Health returns a handle to the health endpoints
 | 
					 | 
				
			||||||
func (c *Client) Health() *Health {
 | 
					 | 
				
			||||||
	return &Health{c}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Node is used to query for checks belonging to a given node
 | 
					 | 
				
			||||||
func (h *Health) Node(node string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) {
 | 
					 | 
				
			||||||
	r := h.c.newRequest("GET", "/v1/health/node/"+node)
 | 
					 | 
				
			||||||
	r.setQueryOptions(q)
 | 
					 | 
				
			||||||
	rtt, resp, err := requireOK(h.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	qm := &QueryMeta{}
 | 
					 | 
				
			||||||
	parseQueryMeta(resp, qm)
 | 
					 | 
				
			||||||
	qm.RequestTime = rtt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var out []*HealthCheck
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &out); err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return out, qm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Checks is used to return the checks associated with a service
 | 
					 | 
				
			||||||
func (h *Health) Checks(service string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) {
 | 
					 | 
				
			||||||
	r := h.c.newRequest("GET", "/v1/health/checks/"+service)
 | 
					 | 
				
			||||||
	r.setQueryOptions(q)
 | 
					 | 
				
			||||||
	rtt, resp, err := requireOK(h.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	qm := &QueryMeta{}
 | 
					 | 
				
			||||||
	parseQueryMeta(resp, qm)
 | 
					 | 
				
			||||||
	qm.RequestTime = rtt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var out []*HealthCheck
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &out); err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return out, qm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Service is used to query health information along with service info
 | 
					 | 
				
			||||||
// for a given service. It can optionally do server-side filtering on a tag
 | 
					 | 
				
			||||||
// or nodes with passing health checks only.
 | 
					 | 
				
			||||||
func (h *Health) Service(service, tag string, passingOnly bool, q *QueryOptions) ([]*ServiceEntry, *QueryMeta, error) {
 | 
					 | 
				
			||||||
	r := h.c.newRequest("GET", "/v1/health/service/"+service)
 | 
					 | 
				
			||||||
	r.setQueryOptions(q)
 | 
					 | 
				
			||||||
	if tag != "" {
 | 
					 | 
				
			||||||
		r.params.Set("tag", tag)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if passingOnly {
 | 
					 | 
				
			||||||
		r.params.Set("passing", "1")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	rtt, resp, err := requireOK(h.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	qm := &QueryMeta{}
 | 
					 | 
				
			||||||
	parseQueryMeta(resp, qm)
 | 
					 | 
				
			||||||
	qm.RequestTime = rtt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var out []*ServiceEntry
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &out); err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return out, qm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// State is used to retreive all the checks in a given state.
 | 
					 | 
				
			||||||
// The wildcard "any" state can also be used for all checks.
 | 
					 | 
				
			||||||
func (h *Health) State(state string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) {
 | 
					 | 
				
			||||||
	switch state {
 | 
					 | 
				
			||||||
	case "any":
 | 
					 | 
				
			||||||
	case "warning":
 | 
					 | 
				
			||||||
	case "critical":
 | 
					 | 
				
			||||||
	case "passing":
 | 
					 | 
				
			||||||
	case "unknown":
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return nil, nil, fmt.Errorf("Unsupported state: %v", state)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	r := h.c.newRequest("GET", "/v1/health/state/"+state)
 | 
					 | 
				
			||||||
	r.setQueryOptions(q)
 | 
					 | 
				
			||||||
	rtt, resp, err := requireOK(h.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	qm := &QueryMeta{}
 | 
					 | 
				
			||||||
	parseQueryMeta(resp, qm)
 | 
					 | 
				
			||||||
	qm.RequestTime = rtt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var out []*HealthCheck
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &out); err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return out, qm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										236
									
								
								vendor/github.com/hashicorp/consul/api/kv.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										236
									
								
								vendor/github.com/hashicorp/consul/api/kv.go
									
										
									
										generated
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,236 +0,0 @@
 | 
				
			||||||
package api
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"io"
 | 
					 | 
				
			||||||
	"net/http"
 | 
					 | 
				
			||||||
	"strconv"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// KVPair is used to represent a single K/V entry
 | 
					 | 
				
			||||||
type KVPair struct {
 | 
					 | 
				
			||||||
	Key         string
 | 
					 | 
				
			||||||
	CreateIndex uint64
 | 
					 | 
				
			||||||
	ModifyIndex uint64
 | 
					 | 
				
			||||||
	LockIndex   uint64
 | 
					 | 
				
			||||||
	Flags       uint64
 | 
					 | 
				
			||||||
	Value       []byte
 | 
					 | 
				
			||||||
	Session     string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// KVPairs is a list of KVPair objects
 | 
					 | 
				
			||||||
type KVPairs []*KVPair
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// KV is used to manipulate the K/V API
 | 
					 | 
				
			||||||
type KV struct {
 | 
					 | 
				
			||||||
	c *Client
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// KV is used to return a handle to the K/V apis
 | 
					 | 
				
			||||||
func (c *Client) KV() *KV {
 | 
					 | 
				
			||||||
	return &KV{c}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Get is used to lookup a single key
 | 
					 | 
				
			||||||
func (k *KV) Get(key string, q *QueryOptions) (*KVPair, *QueryMeta, error) {
 | 
					 | 
				
			||||||
	resp, qm, err := k.getInternal(key, nil, q)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if resp == nil {
 | 
					 | 
				
			||||||
		return nil, qm, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var entries []*KVPair
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &entries); err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(entries) > 0 {
 | 
					 | 
				
			||||||
		return entries[0], qm, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil, qm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// List is used to lookup all keys under a prefix
 | 
					 | 
				
			||||||
func (k *KV) List(prefix string, q *QueryOptions) (KVPairs, *QueryMeta, error) {
 | 
					 | 
				
			||||||
	resp, qm, err := k.getInternal(prefix, map[string]string{"recurse": ""}, q)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if resp == nil {
 | 
					 | 
				
			||||||
		return nil, qm, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var entries []*KVPair
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &entries); err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return entries, qm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Keys is used to list all the keys under a prefix. Optionally,
 | 
					 | 
				
			||||||
// a separator can be used to limit the responses.
 | 
					 | 
				
			||||||
func (k *KV) Keys(prefix, separator string, q *QueryOptions) ([]string, *QueryMeta, error) {
 | 
					 | 
				
			||||||
	params := map[string]string{"keys": ""}
 | 
					 | 
				
			||||||
	if separator != "" {
 | 
					 | 
				
			||||||
		params["separator"] = separator
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	resp, qm, err := k.getInternal(prefix, params, q)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if resp == nil {
 | 
					 | 
				
			||||||
		return nil, qm, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var entries []string
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &entries); err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return entries, qm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (k *KV) getInternal(key string, params map[string]string, q *QueryOptions) (*http.Response, *QueryMeta, error) {
 | 
					 | 
				
			||||||
	r := k.c.newRequest("GET", "/v1/kv/"+key)
 | 
					 | 
				
			||||||
	r.setQueryOptions(q)
 | 
					 | 
				
			||||||
	for param, val := range params {
 | 
					 | 
				
			||||||
		r.params.Set(param, val)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	rtt, resp, err := k.c.doRequest(r)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	qm := &QueryMeta{}
 | 
					 | 
				
			||||||
	parseQueryMeta(resp, qm)
 | 
					 | 
				
			||||||
	qm.RequestTime = rtt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if resp.StatusCode == 404 {
 | 
					 | 
				
			||||||
		resp.Body.Close()
 | 
					 | 
				
			||||||
		return nil, qm, nil
 | 
					 | 
				
			||||||
	} else if resp.StatusCode != 200 {
 | 
					 | 
				
			||||||
		resp.Body.Close()
 | 
					 | 
				
			||||||
		return nil, nil, fmt.Errorf("Unexpected response code: %d", resp.StatusCode)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return resp, qm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Put is used to write a new value. Only the
 | 
					 | 
				
			||||||
// Key, Flags and Value is respected.
 | 
					 | 
				
			||||||
func (k *KV) Put(p *KVPair, q *WriteOptions) (*WriteMeta, error) {
 | 
					 | 
				
			||||||
	params := make(map[string]string, 1)
 | 
					 | 
				
			||||||
	if p.Flags != 0 {
 | 
					 | 
				
			||||||
		params["flags"] = strconv.FormatUint(p.Flags, 10)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	_, wm, err := k.put(p.Key, params, p.Value, q)
 | 
					 | 
				
			||||||
	return wm, err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// CAS is used for a Check-And-Set operation. The Key,
 | 
					 | 
				
			||||||
// ModifyIndex, Flags and Value are respected. Returns true
 | 
					 | 
				
			||||||
// on success or false on failures.
 | 
					 | 
				
			||||||
func (k *KV) CAS(p *KVPair, q *WriteOptions) (bool, *WriteMeta, error) {
 | 
					 | 
				
			||||||
	params := make(map[string]string, 2)
 | 
					 | 
				
			||||||
	if p.Flags != 0 {
 | 
					 | 
				
			||||||
		params["flags"] = strconv.FormatUint(p.Flags, 10)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	params["cas"] = strconv.FormatUint(p.ModifyIndex, 10)
 | 
					 | 
				
			||||||
	return k.put(p.Key, params, p.Value, q)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Acquire is used for a lock acquisiiton operation. The Key,
 | 
					 | 
				
			||||||
// Flags, Value and Session are respected. Returns true
 | 
					 | 
				
			||||||
// on success or false on failures.
 | 
					 | 
				
			||||||
func (k *KV) Acquire(p *KVPair, q *WriteOptions) (bool, *WriteMeta, error) {
 | 
					 | 
				
			||||||
	params := make(map[string]string, 2)
 | 
					 | 
				
			||||||
	if p.Flags != 0 {
 | 
					 | 
				
			||||||
		params["flags"] = strconv.FormatUint(p.Flags, 10)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	params["acquire"] = p.Session
 | 
					 | 
				
			||||||
	return k.put(p.Key, params, p.Value, q)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Release is used for a lock release operation. The Key,
 | 
					 | 
				
			||||||
// Flags, Value and Session are respected. Returns true
 | 
					 | 
				
			||||||
// on success or false on failures.
 | 
					 | 
				
			||||||
func (k *KV) Release(p *KVPair, q *WriteOptions) (bool, *WriteMeta, error) {
 | 
					 | 
				
			||||||
	params := make(map[string]string, 2)
 | 
					 | 
				
			||||||
	if p.Flags != 0 {
 | 
					 | 
				
			||||||
		params["flags"] = strconv.FormatUint(p.Flags, 10)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	params["release"] = p.Session
 | 
					 | 
				
			||||||
	return k.put(p.Key, params, p.Value, q)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (k *KV) put(key string, params map[string]string, body []byte, q *WriteOptions) (bool, *WriteMeta, error) {
 | 
					 | 
				
			||||||
	r := k.c.newRequest("PUT", "/v1/kv/"+key)
 | 
					 | 
				
			||||||
	r.setWriteOptions(q)
 | 
					 | 
				
			||||||
	for param, val := range params {
 | 
					 | 
				
			||||||
		r.params.Set(param, val)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	r.body = bytes.NewReader(body)
 | 
					 | 
				
			||||||
	rtt, resp, err := requireOK(k.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return false, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	qm := &WriteMeta{}
 | 
					 | 
				
			||||||
	qm.RequestTime = rtt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var buf bytes.Buffer
 | 
					 | 
				
			||||||
	if _, err := io.Copy(&buf, resp.Body); err != nil {
 | 
					 | 
				
			||||||
		return false, nil, fmt.Errorf("Failed to read response: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	res := strings.Contains(string(buf.Bytes()), "true")
 | 
					 | 
				
			||||||
	return res, qm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Delete is used to delete a single key
 | 
					 | 
				
			||||||
func (k *KV) Delete(key string, w *WriteOptions) (*WriteMeta, error) {
 | 
					 | 
				
			||||||
	_, qm, err := k.deleteInternal(key, nil, w)
 | 
					 | 
				
			||||||
	return qm, err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// DeleteCAS is used for a Delete Check-And-Set operation. The Key
 | 
					 | 
				
			||||||
// and ModifyIndex are respected. Returns true on success or false on failures.
 | 
					 | 
				
			||||||
func (k *KV) DeleteCAS(p *KVPair, q *WriteOptions) (bool, *WriteMeta, error) {
 | 
					 | 
				
			||||||
	params := map[string]string{
 | 
					 | 
				
			||||||
		"cas": strconv.FormatUint(p.ModifyIndex, 10),
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return k.deleteInternal(p.Key, params, q)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// DeleteTree is used to delete all keys under a prefix
 | 
					 | 
				
			||||||
func (k *KV) DeleteTree(prefix string, w *WriteOptions) (*WriteMeta, error) {
 | 
					 | 
				
			||||||
	_, qm, err := k.deleteInternal(prefix, map[string]string{"recurse": ""}, w)
 | 
					 | 
				
			||||||
	return qm, err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (k *KV) deleteInternal(key string, params map[string]string, q *WriteOptions) (bool, *WriteMeta, error) {
 | 
					 | 
				
			||||||
	r := k.c.newRequest("DELETE", "/v1/kv/"+key)
 | 
					 | 
				
			||||||
	r.setWriteOptions(q)
 | 
					 | 
				
			||||||
	for param, val := range params {
 | 
					 | 
				
			||||||
		r.params.Set(param, val)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	rtt, resp, err := requireOK(k.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return false, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	qm := &WriteMeta{}
 | 
					 | 
				
			||||||
	qm.RequestTime = rtt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var buf bytes.Buffer
 | 
					 | 
				
			||||||
	if _, err := io.Copy(&buf, resp.Body); err != nil {
 | 
					 | 
				
			||||||
		return false, nil, fmt.Errorf("Failed to read response: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	res := strings.Contains(string(buf.Bytes()), "true")
 | 
					 | 
				
			||||||
	return res, qm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										326
									
								
								vendor/github.com/hashicorp/consul/api/lock.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										326
									
								
								vendor/github.com/hashicorp/consul/api/lock.go
									
										
									
										generated
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,326 +0,0 @@
 | 
				
			||||||
package api
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"sync"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const (
 | 
					 | 
				
			||||||
	// DefaultLockSessionName is the Session Name we assign if none is provided
 | 
					 | 
				
			||||||
	DefaultLockSessionName = "Consul API Lock"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// DefaultLockSessionTTL is the default session TTL if no Session is provided
 | 
					 | 
				
			||||||
	// when creating a new Lock. This is used because we do not have another
 | 
					 | 
				
			||||||
	// other check to depend upon.
 | 
					 | 
				
			||||||
	DefaultLockSessionTTL = "15s"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// DefaultLockWaitTime is how long we block for at a time to check if lock
 | 
					 | 
				
			||||||
	// acquisition is possible. This affects the minimum time it takes to cancel
 | 
					 | 
				
			||||||
	// a Lock acquisition.
 | 
					 | 
				
			||||||
	DefaultLockWaitTime = 15 * time.Second
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// DefaultLockRetryTime is how long we wait after a failed lock acquisition
 | 
					 | 
				
			||||||
	// before attempting to do the lock again. This is so that once a lock-delay
 | 
					 | 
				
			||||||
	// is in affect, we do not hot loop retrying the acquisition.
 | 
					 | 
				
			||||||
	DefaultLockRetryTime = 5 * time.Second
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// LockFlagValue is a magic flag we set to indicate a key
 | 
					 | 
				
			||||||
	// is being used for a lock. It is used to detect a potential
 | 
					 | 
				
			||||||
	// conflict with a semaphore.
 | 
					 | 
				
			||||||
	LockFlagValue = 0x2ddccbc058a50c18
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var (
 | 
					 | 
				
			||||||
	// ErrLockHeld is returned if we attempt to double lock
 | 
					 | 
				
			||||||
	ErrLockHeld = fmt.Errorf("Lock already held")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// ErrLockNotHeld is returned if we attempt to unlock a lock
 | 
					 | 
				
			||||||
	// that we do not hold.
 | 
					 | 
				
			||||||
	ErrLockNotHeld = fmt.Errorf("Lock not held")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// ErrLockInUse is returned if we attempt to destroy a lock
 | 
					 | 
				
			||||||
	// that is in use.
 | 
					 | 
				
			||||||
	ErrLockInUse = fmt.Errorf("Lock in use")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// ErrLockConflict is returned if the flags on a key
 | 
					 | 
				
			||||||
	// used for a lock do not match expectation
 | 
					 | 
				
			||||||
	ErrLockConflict = fmt.Errorf("Existing key does not match lock use")
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Lock is used to implement client-side leader election. It is follows the
 | 
					 | 
				
			||||||
// algorithm as described here: https://consul.io/docs/guides/leader-election.html.
 | 
					 | 
				
			||||||
type Lock struct {
 | 
					 | 
				
			||||||
	c    *Client
 | 
					 | 
				
			||||||
	opts *LockOptions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	isHeld       bool
 | 
					 | 
				
			||||||
	sessionRenew chan struct{}
 | 
					 | 
				
			||||||
	lockSession  string
 | 
					 | 
				
			||||||
	l            sync.Mutex
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// LockOptions is used to parameterize the Lock behavior.
 | 
					 | 
				
			||||||
type LockOptions struct {
 | 
					 | 
				
			||||||
	Key         string // Must be set and have write permissions
 | 
					 | 
				
			||||||
	Value       []byte // Optional, value to associate with the lock
 | 
					 | 
				
			||||||
	Session     string // Optional, created if not specified
 | 
					 | 
				
			||||||
	SessionName string // Optional, defaults to DefaultLockSessionName
 | 
					 | 
				
			||||||
	SessionTTL  string // Optional, defaults to DefaultLockSessionTTL
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// LockKey returns a handle to a lock struct which can be used
 | 
					 | 
				
			||||||
// to acquire and release the mutex. The key used must have
 | 
					 | 
				
			||||||
// write permissions.
 | 
					 | 
				
			||||||
func (c *Client) LockKey(key string) (*Lock, error) {
 | 
					 | 
				
			||||||
	opts := &LockOptions{
 | 
					 | 
				
			||||||
		Key: key,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return c.LockOpts(opts)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// LockOpts returns a handle to a lock struct which can be used
 | 
					 | 
				
			||||||
// to acquire and release the mutex. The key used must have
 | 
					 | 
				
			||||||
// write permissions.
 | 
					 | 
				
			||||||
func (c *Client) LockOpts(opts *LockOptions) (*Lock, error) {
 | 
					 | 
				
			||||||
	if opts.Key == "" {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("missing key")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if opts.SessionName == "" {
 | 
					 | 
				
			||||||
		opts.SessionName = DefaultLockSessionName
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if opts.SessionTTL == "" {
 | 
					 | 
				
			||||||
		opts.SessionTTL = DefaultLockSessionTTL
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		if _, err := time.ParseDuration(opts.SessionTTL); err != nil {
 | 
					 | 
				
			||||||
			return nil, fmt.Errorf("invalid SessionTTL: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	l := &Lock{
 | 
					 | 
				
			||||||
		c:    c,
 | 
					 | 
				
			||||||
		opts: opts,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return l, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Lock attempts to acquire the lock and blocks while doing so.
 | 
					 | 
				
			||||||
// Providing a non-nil stopCh can be used to abort the lock attempt.
 | 
					 | 
				
			||||||
// Returns a channel that is closed if our lock is lost or an error.
 | 
					 | 
				
			||||||
// This channel could be closed at any time due to session invalidation,
 | 
					 | 
				
			||||||
// communication errors, operator intervention, etc. It is NOT safe to
 | 
					 | 
				
			||||||
// assume that the lock is held until Unlock() unless the Session is specifically
 | 
					 | 
				
			||||||
// created without any associated health checks. By default Consul sessions
 | 
					 | 
				
			||||||
// prefer liveness over safety and an application must be able to handle
 | 
					 | 
				
			||||||
// the lock being lost.
 | 
					 | 
				
			||||||
func (l *Lock) Lock(stopCh <-chan struct{}) (<-chan struct{}, error) {
 | 
					 | 
				
			||||||
	// Hold the lock as we try to acquire
 | 
					 | 
				
			||||||
	l.l.Lock()
 | 
					 | 
				
			||||||
	defer l.l.Unlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Check if we already hold the lock
 | 
					 | 
				
			||||||
	if l.isHeld {
 | 
					 | 
				
			||||||
		return nil, ErrLockHeld
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Check if we need to create a session first
 | 
					 | 
				
			||||||
	l.lockSession = l.opts.Session
 | 
					 | 
				
			||||||
	if l.lockSession == "" {
 | 
					 | 
				
			||||||
		if s, err := l.createSession(); err != nil {
 | 
					 | 
				
			||||||
			return nil, fmt.Errorf("failed to create session: %v", err)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			l.sessionRenew = make(chan struct{})
 | 
					 | 
				
			||||||
			l.lockSession = s
 | 
					 | 
				
			||||||
			session := l.c.Session()
 | 
					 | 
				
			||||||
			go session.RenewPeriodic(l.opts.SessionTTL, s, nil, l.sessionRenew)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// If we fail to acquire the lock, cleanup the session
 | 
					 | 
				
			||||||
			defer func() {
 | 
					 | 
				
			||||||
				if !l.isHeld {
 | 
					 | 
				
			||||||
					close(l.sessionRenew)
 | 
					 | 
				
			||||||
					l.sessionRenew = nil
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}()
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Setup the query options
 | 
					 | 
				
			||||||
	kv := l.c.KV()
 | 
					 | 
				
			||||||
	qOpts := &QueryOptions{
 | 
					 | 
				
			||||||
		WaitTime: DefaultLockWaitTime,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
WAIT:
 | 
					 | 
				
			||||||
	// Check if we should quit
 | 
					 | 
				
			||||||
	select {
 | 
					 | 
				
			||||||
	case <-stopCh:
 | 
					 | 
				
			||||||
		return nil, nil
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Look for an existing lock, blocking until not taken
 | 
					 | 
				
			||||||
	pair, meta, err := kv.Get(l.opts.Key, qOpts)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("failed to read lock: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if pair != nil && pair.Flags != LockFlagValue {
 | 
					 | 
				
			||||||
		return nil, ErrLockConflict
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	locked := false
 | 
					 | 
				
			||||||
	if pair != nil && pair.Session == l.lockSession {
 | 
					 | 
				
			||||||
		goto HELD
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if pair != nil && pair.Session != "" {
 | 
					 | 
				
			||||||
		qOpts.WaitIndex = meta.LastIndex
 | 
					 | 
				
			||||||
		goto WAIT
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Try to acquire the lock
 | 
					 | 
				
			||||||
	pair = l.lockEntry(l.lockSession)
 | 
					 | 
				
			||||||
	locked, _, err = kv.Acquire(pair, nil)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("failed to acquire lock: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Handle the case of not getting the lock
 | 
					 | 
				
			||||||
	if !locked {
 | 
					 | 
				
			||||||
		select {
 | 
					 | 
				
			||||||
		case <-time.After(DefaultLockRetryTime):
 | 
					 | 
				
			||||||
			goto WAIT
 | 
					 | 
				
			||||||
		case <-stopCh:
 | 
					 | 
				
			||||||
			return nil, nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
HELD:
 | 
					 | 
				
			||||||
	// Watch to ensure we maintain leadership
 | 
					 | 
				
			||||||
	leaderCh := make(chan struct{})
 | 
					 | 
				
			||||||
	go l.monitorLock(l.lockSession, leaderCh)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Set that we own the lock
 | 
					 | 
				
			||||||
	l.isHeld = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Locked! All done
 | 
					 | 
				
			||||||
	return leaderCh, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Unlock released the lock. It is an error to call this
 | 
					 | 
				
			||||||
// if the lock is not currently held.
 | 
					 | 
				
			||||||
func (l *Lock) Unlock() error {
 | 
					 | 
				
			||||||
	// Hold the lock as we try to release
 | 
					 | 
				
			||||||
	l.l.Lock()
 | 
					 | 
				
			||||||
	defer l.l.Unlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Ensure the lock is actually held
 | 
					 | 
				
			||||||
	if !l.isHeld {
 | 
					 | 
				
			||||||
		return ErrLockNotHeld
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Set that we no longer own the lock
 | 
					 | 
				
			||||||
	l.isHeld = false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Stop the session renew
 | 
					 | 
				
			||||||
	if l.sessionRenew != nil {
 | 
					 | 
				
			||||||
		defer func() {
 | 
					 | 
				
			||||||
			close(l.sessionRenew)
 | 
					 | 
				
			||||||
			l.sessionRenew = nil
 | 
					 | 
				
			||||||
		}()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Get the lock entry, and clear the lock session
 | 
					 | 
				
			||||||
	lockEnt := l.lockEntry(l.lockSession)
 | 
					 | 
				
			||||||
	l.lockSession = ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Release the lock explicitly
 | 
					 | 
				
			||||||
	kv := l.c.KV()
 | 
					 | 
				
			||||||
	_, _, err := kv.Release(lockEnt, nil)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return fmt.Errorf("failed to release lock: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Destroy is used to cleanup the lock entry. It is not necessary
 | 
					 | 
				
			||||||
// to invoke. It will fail if the lock is in use.
 | 
					 | 
				
			||||||
func (l *Lock) Destroy() error {
 | 
					 | 
				
			||||||
	// Hold the lock as we try to release
 | 
					 | 
				
			||||||
	l.l.Lock()
 | 
					 | 
				
			||||||
	defer l.l.Unlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Check if we already hold the lock
 | 
					 | 
				
			||||||
	if l.isHeld {
 | 
					 | 
				
			||||||
		return ErrLockHeld
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Look for an existing lock
 | 
					 | 
				
			||||||
	kv := l.c.KV()
 | 
					 | 
				
			||||||
	pair, _, err := kv.Get(l.opts.Key, nil)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return fmt.Errorf("failed to read lock: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Nothing to do if the lock does not exist
 | 
					 | 
				
			||||||
	if pair == nil {
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Check for possible flag conflict
 | 
					 | 
				
			||||||
	if pair.Flags != LockFlagValue {
 | 
					 | 
				
			||||||
		return ErrLockConflict
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Check if it is in use
 | 
					 | 
				
			||||||
	if pair.Session != "" {
 | 
					 | 
				
			||||||
		return ErrLockInUse
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Attempt the delete
 | 
					 | 
				
			||||||
	didRemove, _, err := kv.DeleteCAS(pair, nil)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return fmt.Errorf("failed to remove lock: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if !didRemove {
 | 
					 | 
				
			||||||
		return ErrLockInUse
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// createSession is used to create a new managed session
 | 
					 | 
				
			||||||
func (l *Lock) createSession() (string, error) {
 | 
					 | 
				
			||||||
	session := l.c.Session()
 | 
					 | 
				
			||||||
	se := &SessionEntry{
 | 
					 | 
				
			||||||
		Name: l.opts.SessionName,
 | 
					 | 
				
			||||||
		TTL:  l.opts.SessionTTL,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	id, _, err := session.Create(se, nil)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return "", err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return id, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// lockEntry returns a formatted KVPair for the lock
 | 
					 | 
				
			||||||
func (l *Lock) lockEntry(session string) *KVPair {
 | 
					 | 
				
			||||||
	return &KVPair{
 | 
					 | 
				
			||||||
		Key:     l.opts.Key,
 | 
					 | 
				
			||||||
		Value:   l.opts.Value,
 | 
					 | 
				
			||||||
		Session: session,
 | 
					 | 
				
			||||||
		Flags:   LockFlagValue,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// monitorLock is a long running routine to monitor a lock ownership
 | 
					 | 
				
			||||||
// It closes the stopCh if we lose our leadership.
 | 
					 | 
				
			||||||
func (l *Lock) monitorLock(session string, stopCh chan struct{}) {
 | 
					 | 
				
			||||||
	defer close(stopCh)
 | 
					 | 
				
			||||||
	kv := l.c.KV()
 | 
					 | 
				
			||||||
	opts := &QueryOptions{RequireConsistent: true}
 | 
					 | 
				
			||||||
WAIT:
 | 
					 | 
				
			||||||
	pair, meta, err := kv.Get(l.opts.Key, opts)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if pair != nil && pair.Session == session {
 | 
					 | 
				
			||||||
		opts.WaitIndex = meta.LastIndex
 | 
					 | 
				
			||||||
		goto WAIT
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										24
									
								
								vendor/github.com/hashicorp/consul/api/raw.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								vendor/github.com/hashicorp/consul/api/raw.go
									
										
									
										generated
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,24 +0,0 @@
 | 
				
			||||||
package api
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Raw can be used to do raw queries against custom endpoints
 | 
					 | 
				
			||||||
type Raw struct {
 | 
					 | 
				
			||||||
	c *Client
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Raw returns a handle to query endpoints
 | 
					 | 
				
			||||||
func (c *Client) Raw() *Raw {
 | 
					 | 
				
			||||||
	return &Raw{c}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Query is used to do a GET request against an endpoint
 | 
					 | 
				
			||||||
// and deserialize the response into an interface using
 | 
					 | 
				
			||||||
// standard Consul conventions.
 | 
					 | 
				
			||||||
func (raw *Raw) Query(endpoint string, out interface{}, q *QueryOptions) (*QueryMeta, error) {
 | 
					 | 
				
			||||||
	return raw.c.query(endpoint, out, q)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Write is used to do a PUT request against an endpoint
 | 
					 | 
				
			||||||
// and serialize/deserialized using the standard Consul conventions.
 | 
					 | 
				
			||||||
func (raw *Raw) Write(endpoint string, in, out interface{}, q *WriteOptions) (*WriteMeta, error) {
 | 
					 | 
				
			||||||
	return raw.c.write(endpoint, in, out, q)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										477
									
								
								vendor/github.com/hashicorp/consul/api/semaphore.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										477
									
								
								vendor/github.com/hashicorp/consul/api/semaphore.go
									
										
									
										generated
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,477 +0,0 @@
 | 
				
			||||||
package api
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"path"
 | 
					 | 
				
			||||||
	"sync"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const (
 | 
					 | 
				
			||||||
	// DefaultSemaphoreSessionName is the Session Name we assign if none is provided
 | 
					 | 
				
			||||||
	DefaultSemaphoreSessionName = "Consul API Semaphore"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// DefaultSemaphoreSessionTTL is the default session TTL if no Session is provided
 | 
					 | 
				
			||||||
	// when creating a new Semaphore. This is used because we do not have another
 | 
					 | 
				
			||||||
	// other check to depend upon.
 | 
					 | 
				
			||||||
	DefaultSemaphoreSessionTTL = "15s"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// DefaultSemaphoreWaitTime is how long we block for at a time to check if semaphore
 | 
					 | 
				
			||||||
	// acquisition is possible. This affects the minimum time it takes to cancel
 | 
					 | 
				
			||||||
	// a Semaphore acquisition.
 | 
					 | 
				
			||||||
	DefaultSemaphoreWaitTime = 15 * time.Second
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// DefaultSemaphoreKey is the key used within the prefix to
 | 
					 | 
				
			||||||
	// use for coordination between all the contenders.
 | 
					 | 
				
			||||||
	DefaultSemaphoreKey = ".lock"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// SemaphoreFlagValue is a magic flag we set to indicate a key
 | 
					 | 
				
			||||||
	// is being used for a semaphore. It is used to detect a potential
 | 
					 | 
				
			||||||
	// conflict with a lock.
 | 
					 | 
				
			||||||
	SemaphoreFlagValue = 0xe0f69a2baa414de0
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var (
 | 
					 | 
				
			||||||
	// ErrSemaphoreHeld is returned if we attempt to double lock
 | 
					 | 
				
			||||||
	ErrSemaphoreHeld = fmt.Errorf("Semaphore already held")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// ErrSemaphoreNotHeld is returned if we attempt to unlock a semaphore
 | 
					 | 
				
			||||||
	// that we do not hold.
 | 
					 | 
				
			||||||
	ErrSemaphoreNotHeld = fmt.Errorf("Semaphore not held")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// ErrSemaphoreInUse is returned if we attempt to destroy a semaphore
 | 
					 | 
				
			||||||
	// that is in use.
 | 
					 | 
				
			||||||
	ErrSemaphoreInUse = fmt.Errorf("Semaphore in use")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// ErrSemaphoreConflict is returned if the flags on a key
 | 
					 | 
				
			||||||
	// used for a semaphore do not match expectation
 | 
					 | 
				
			||||||
	ErrSemaphoreConflict = fmt.Errorf("Existing key does not match semaphore use")
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Semaphore is used to implement a distributed semaphore
 | 
					 | 
				
			||||||
// using the Consul KV primitives.
 | 
					 | 
				
			||||||
type Semaphore struct {
 | 
					 | 
				
			||||||
	c    *Client
 | 
					 | 
				
			||||||
	opts *SemaphoreOptions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	isHeld       bool
 | 
					 | 
				
			||||||
	sessionRenew chan struct{}
 | 
					 | 
				
			||||||
	lockSession  string
 | 
					 | 
				
			||||||
	l            sync.Mutex
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// SemaphoreOptions is used to parameterize the Semaphore
 | 
					 | 
				
			||||||
type SemaphoreOptions struct {
 | 
					 | 
				
			||||||
	Prefix      string // Must be set and have write permissions
 | 
					 | 
				
			||||||
	Limit       int    // Must be set, and be positive
 | 
					 | 
				
			||||||
	Value       []byte // Optional, value to associate with the contender entry
 | 
					 | 
				
			||||||
	Session     string // OPtional, created if not specified
 | 
					 | 
				
			||||||
	SessionName string // Optional, defaults to DefaultLockSessionName
 | 
					 | 
				
			||||||
	SessionTTL  string // Optional, defaults to DefaultLockSessionTTL
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// semaphoreLock is written under the DefaultSemaphoreKey and
 | 
					 | 
				
			||||||
// is used to coordinate between all the contenders.
 | 
					 | 
				
			||||||
type semaphoreLock struct {
 | 
					 | 
				
			||||||
	// Limit is the integer limit of holders. This is used to
 | 
					 | 
				
			||||||
	// verify that all the holders agree on the value.
 | 
					 | 
				
			||||||
	Limit int
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Holders is a list of all the semaphore holders.
 | 
					 | 
				
			||||||
	// It maps the session ID to true. It is used as a set effectively.
 | 
					 | 
				
			||||||
	Holders map[string]bool
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// SemaphorePrefix is used to created a Semaphore which will operate
 | 
					 | 
				
			||||||
// at the given KV prefix and uses the given limit for the semaphore.
 | 
					 | 
				
			||||||
// The prefix must have write privileges, and the limit must be agreed
 | 
					 | 
				
			||||||
// upon by all contenders.
 | 
					 | 
				
			||||||
func (c *Client) SemaphorePrefix(prefix string, limit int) (*Semaphore, error) {
 | 
					 | 
				
			||||||
	opts := &SemaphoreOptions{
 | 
					 | 
				
			||||||
		Prefix: prefix,
 | 
					 | 
				
			||||||
		Limit:  limit,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return c.SemaphoreOpts(opts)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// SemaphoreOpts is used to create a Semaphore with the given options.
 | 
					 | 
				
			||||||
// The prefix must have write privileges, and the limit must be agreed
 | 
					 | 
				
			||||||
// upon by all contenders. If a Session is not provided, one will be created.
 | 
					 | 
				
			||||||
func (c *Client) SemaphoreOpts(opts *SemaphoreOptions) (*Semaphore, error) {
 | 
					 | 
				
			||||||
	if opts.Prefix == "" {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("missing prefix")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if opts.Limit <= 0 {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("semaphore limit must be positive")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if opts.SessionName == "" {
 | 
					 | 
				
			||||||
		opts.SessionName = DefaultSemaphoreSessionName
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if opts.SessionTTL == "" {
 | 
					 | 
				
			||||||
		opts.SessionTTL = DefaultSemaphoreSessionTTL
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		if _, err := time.ParseDuration(opts.SessionTTL); err != nil {
 | 
					 | 
				
			||||||
			return nil, fmt.Errorf("invalid SessionTTL: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	s := &Semaphore{
 | 
					 | 
				
			||||||
		c:    c,
 | 
					 | 
				
			||||||
		opts: opts,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return s, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Acquire attempts to reserve a slot in the semaphore, blocking until
 | 
					 | 
				
			||||||
// success, interrupted via the stopCh or an error is encounted.
 | 
					 | 
				
			||||||
// Providing a non-nil stopCh can be used to abort the attempt.
 | 
					 | 
				
			||||||
// On success, a channel is returned that represents our slot.
 | 
					 | 
				
			||||||
// This channel could be closed at any time due to session invalidation,
 | 
					 | 
				
			||||||
// communication errors, operator intervention, etc. It is NOT safe to
 | 
					 | 
				
			||||||
// assume that the slot is held until Release() unless the Session is specifically
 | 
					 | 
				
			||||||
// created without any associated health checks. By default Consul sessions
 | 
					 | 
				
			||||||
// prefer liveness over safety and an application must be able to handle
 | 
					 | 
				
			||||||
// the session being lost.
 | 
					 | 
				
			||||||
func (s *Semaphore) Acquire(stopCh <-chan struct{}) (<-chan struct{}, error) {
 | 
					 | 
				
			||||||
	// Hold the lock as we try to acquire
 | 
					 | 
				
			||||||
	s.l.Lock()
 | 
					 | 
				
			||||||
	defer s.l.Unlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Check if we already hold the semaphore
 | 
					 | 
				
			||||||
	if s.isHeld {
 | 
					 | 
				
			||||||
		return nil, ErrSemaphoreHeld
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Check if we need to create a session first
 | 
					 | 
				
			||||||
	s.lockSession = s.opts.Session
 | 
					 | 
				
			||||||
	if s.lockSession == "" {
 | 
					 | 
				
			||||||
		if sess, err := s.createSession(); err != nil {
 | 
					 | 
				
			||||||
			return nil, fmt.Errorf("failed to create session: %v", err)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			s.sessionRenew = make(chan struct{})
 | 
					 | 
				
			||||||
			s.lockSession = sess
 | 
					 | 
				
			||||||
			session := s.c.Session()
 | 
					 | 
				
			||||||
			go session.RenewPeriodic(s.opts.SessionTTL, sess, nil, s.sessionRenew)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// If we fail to acquire the lock, cleanup the session
 | 
					 | 
				
			||||||
			defer func() {
 | 
					 | 
				
			||||||
				if !s.isHeld {
 | 
					 | 
				
			||||||
					close(s.sessionRenew)
 | 
					 | 
				
			||||||
					s.sessionRenew = nil
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}()
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Create the contender entry
 | 
					 | 
				
			||||||
	kv := s.c.KV()
 | 
					 | 
				
			||||||
	made, _, err := kv.Acquire(s.contenderEntry(s.lockSession), nil)
 | 
					 | 
				
			||||||
	if err != nil || !made {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("failed to make contender entry: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Setup the query options
 | 
					 | 
				
			||||||
	qOpts := &QueryOptions{
 | 
					 | 
				
			||||||
		WaitTime: DefaultSemaphoreWaitTime,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
WAIT:
 | 
					 | 
				
			||||||
	// Check if we should quit
 | 
					 | 
				
			||||||
	select {
 | 
					 | 
				
			||||||
	case <-stopCh:
 | 
					 | 
				
			||||||
		return nil, nil
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Read the prefix
 | 
					 | 
				
			||||||
	pairs, meta, err := kv.List(s.opts.Prefix, qOpts)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("failed to read prefix: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Decode the lock
 | 
					 | 
				
			||||||
	lockPair := s.findLock(pairs)
 | 
					 | 
				
			||||||
	if lockPair.Flags != SemaphoreFlagValue {
 | 
					 | 
				
			||||||
		return nil, ErrSemaphoreConflict
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	lock, err := s.decodeLock(lockPair)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Verify we agree with the limit
 | 
					 | 
				
			||||||
	if lock.Limit != s.opts.Limit {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("semaphore limit conflict (lock: %d, local: %d)",
 | 
					 | 
				
			||||||
			lock.Limit, s.opts.Limit)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Prune the dead holders
 | 
					 | 
				
			||||||
	s.pruneDeadHolders(lock, pairs)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Check if the lock is held
 | 
					 | 
				
			||||||
	if len(lock.Holders) >= lock.Limit {
 | 
					 | 
				
			||||||
		qOpts.WaitIndex = meta.LastIndex
 | 
					 | 
				
			||||||
		goto WAIT
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Create a new lock with us as a holder
 | 
					 | 
				
			||||||
	lock.Holders[s.lockSession] = true
 | 
					 | 
				
			||||||
	newLock, err := s.encodeLock(lock, lockPair.ModifyIndex)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Attempt the acquisition
 | 
					 | 
				
			||||||
	didSet, _, err := kv.CAS(newLock, nil)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("failed to update lock: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if !didSet {
 | 
					 | 
				
			||||||
		// Update failed, could have been a race with another contender,
 | 
					 | 
				
			||||||
		// retry the operation
 | 
					 | 
				
			||||||
		goto WAIT
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Watch to ensure we maintain ownership of the slot
 | 
					 | 
				
			||||||
	lockCh := make(chan struct{})
 | 
					 | 
				
			||||||
	go s.monitorLock(s.lockSession, lockCh)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Set that we own the lock
 | 
					 | 
				
			||||||
	s.isHeld = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Acquired! All done
 | 
					 | 
				
			||||||
	return lockCh, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Release is used to voluntarily give up our semaphore slot. It is
 | 
					 | 
				
			||||||
// an error to call this if the semaphore has not been acquired.
 | 
					 | 
				
			||||||
func (s *Semaphore) Release() error {
 | 
					 | 
				
			||||||
	// Hold the lock as we try to release
 | 
					 | 
				
			||||||
	s.l.Lock()
 | 
					 | 
				
			||||||
	defer s.l.Unlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Ensure the lock is actually held
 | 
					 | 
				
			||||||
	if !s.isHeld {
 | 
					 | 
				
			||||||
		return ErrSemaphoreNotHeld
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Set that we no longer own the lock
 | 
					 | 
				
			||||||
	s.isHeld = false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Stop the session renew
 | 
					 | 
				
			||||||
	if s.sessionRenew != nil {
 | 
					 | 
				
			||||||
		defer func() {
 | 
					 | 
				
			||||||
			close(s.sessionRenew)
 | 
					 | 
				
			||||||
			s.sessionRenew = nil
 | 
					 | 
				
			||||||
		}()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Get and clear the lock session
 | 
					 | 
				
			||||||
	lockSession := s.lockSession
 | 
					 | 
				
			||||||
	s.lockSession = ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Remove ourselves as a lock holder
 | 
					 | 
				
			||||||
	kv := s.c.KV()
 | 
					 | 
				
			||||||
	key := path.Join(s.opts.Prefix, DefaultSemaphoreKey)
 | 
					 | 
				
			||||||
READ:
 | 
					 | 
				
			||||||
	pair, _, err := kv.Get(key, nil)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if pair == nil {
 | 
					 | 
				
			||||||
		pair = &KVPair{}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	lock, err := s.decodeLock(pair)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Create a new lock without us as a holder
 | 
					 | 
				
			||||||
	if _, ok := lock.Holders[lockSession]; ok {
 | 
					 | 
				
			||||||
		delete(lock.Holders, lockSession)
 | 
					 | 
				
			||||||
		newLock, err := s.encodeLock(lock, pair.ModifyIndex)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Swap the locks
 | 
					 | 
				
			||||||
		didSet, _, err := kv.CAS(newLock, nil)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return fmt.Errorf("failed to update lock: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if !didSet {
 | 
					 | 
				
			||||||
			goto READ
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Destroy the contender entry
 | 
					 | 
				
			||||||
	contenderKey := path.Join(s.opts.Prefix, lockSession)
 | 
					 | 
				
			||||||
	if _, err := kv.Delete(contenderKey, nil); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Destroy is used to cleanup the semaphore entry. It is not necessary
 | 
					 | 
				
			||||||
// to invoke. It will fail if the semaphore is in use.
 | 
					 | 
				
			||||||
func (s *Semaphore) Destroy() error {
 | 
					 | 
				
			||||||
	// Hold the lock as we try to acquire
 | 
					 | 
				
			||||||
	s.l.Lock()
 | 
					 | 
				
			||||||
	defer s.l.Unlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Check if we already hold the semaphore
 | 
					 | 
				
			||||||
	if s.isHeld {
 | 
					 | 
				
			||||||
		return ErrSemaphoreHeld
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// List for the semaphore
 | 
					 | 
				
			||||||
	kv := s.c.KV()
 | 
					 | 
				
			||||||
	pairs, _, err := kv.List(s.opts.Prefix, nil)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return fmt.Errorf("failed to read prefix: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Find the lock pair, bail if it doesn't exist
 | 
					 | 
				
			||||||
	lockPair := s.findLock(pairs)
 | 
					 | 
				
			||||||
	if lockPair.ModifyIndex == 0 {
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if lockPair.Flags != SemaphoreFlagValue {
 | 
					 | 
				
			||||||
		return ErrSemaphoreConflict
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Decode the lock
 | 
					 | 
				
			||||||
	lock, err := s.decodeLock(lockPair)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Prune the dead holders
 | 
					 | 
				
			||||||
	s.pruneDeadHolders(lock, pairs)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Check if there are any holders
 | 
					 | 
				
			||||||
	if len(lock.Holders) > 0 {
 | 
					 | 
				
			||||||
		return ErrSemaphoreInUse
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Attempt the delete
 | 
					 | 
				
			||||||
	didRemove, _, err := kv.DeleteCAS(lockPair, nil)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return fmt.Errorf("failed to remove semaphore: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if !didRemove {
 | 
					 | 
				
			||||||
		return ErrSemaphoreInUse
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// createSession is used to create a new managed session
 | 
					 | 
				
			||||||
func (s *Semaphore) createSession() (string, error) {
 | 
					 | 
				
			||||||
	session := s.c.Session()
 | 
					 | 
				
			||||||
	se := &SessionEntry{
 | 
					 | 
				
			||||||
		Name:     s.opts.SessionName,
 | 
					 | 
				
			||||||
		TTL:      s.opts.SessionTTL,
 | 
					 | 
				
			||||||
		Behavior: SessionBehaviorDelete,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	id, _, err := session.Create(se, nil)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return "", err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return id, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// contenderEntry returns a formatted KVPair for the contender
 | 
					 | 
				
			||||||
func (s *Semaphore) contenderEntry(session string) *KVPair {
 | 
					 | 
				
			||||||
	return &KVPair{
 | 
					 | 
				
			||||||
		Key:     path.Join(s.opts.Prefix, session),
 | 
					 | 
				
			||||||
		Value:   s.opts.Value,
 | 
					 | 
				
			||||||
		Session: session,
 | 
					 | 
				
			||||||
		Flags:   SemaphoreFlagValue,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// findLock is used to find the KV Pair which is used for coordination
 | 
					 | 
				
			||||||
func (s *Semaphore) findLock(pairs KVPairs) *KVPair {
 | 
					 | 
				
			||||||
	key := path.Join(s.opts.Prefix, DefaultSemaphoreKey)
 | 
					 | 
				
			||||||
	for _, pair := range pairs {
 | 
					 | 
				
			||||||
		if pair.Key == key {
 | 
					 | 
				
			||||||
			return pair
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return &KVPair{Flags: SemaphoreFlagValue}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// decodeLock is used to decode a semaphoreLock from an
 | 
					 | 
				
			||||||
// entry in Consul
 | 
					 | 
				
			||||||
func (s *Semaphore) decodeLock(pair *KVPair) (*semaphoreLock, error) {
 | 
					 | 
				
			||||||
	// Handle if there is no lock
 | 
					 | 
				
			||||||
	if pair == nil || pair.Value == nil {
 | 
					 | 
				
			||||||
		return &semaphoreLock{
 | 
					 | 
				
			||||||
			Limit:   s.opts.Limit,
 | 
					 | 
				
			||||||
			Holders: make(map[string]bool),
 | 
					 | 
				
			||||||
		}, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	l := &semaphoreLock{}
 | 
					 | 
				
			||||||
	if err := json.Unmarshal(pair.Value, l); err != nil {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("lock decoding failed: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return l, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// encodeLock is used to encode a semaphoreLock into a KVPair
 | 
					 | 
				
			||||||
// that can be PUT
 | 
					 | 
				
			||||||
func (s *Semaphore) encodeLock(l *semaphoreLock, oldIndex uint64) (*KVPair, error) {
 | 
					 | 
				
			||||||
	enc, err := json.Marshal(l)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("lock encoding failed: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	pair := &KVPair{
 | 
					 | 
				
			||||||
		Key:         path.Join(s.opts.Prefix, DefaultSemaphoreKey),
 | 
					 | 
				
			||||||
		Value:       enc,
 | 
					 | 
				
			||||||
		Flags:       SemaphoreFlagValue,
 | 
					 | 
				
			||||||
		ModifyIndex: oldIndex,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return pair, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// pruneDeadHolders is used to remove all the dead lock holders
 | 
					 | 
				
			||||||
func (s *Semaphore) pruneDeadHolders(lock *semaphoreLock, pairs KVPairs) {
 | 
					 | 
				
			||||||
	// Gather all the live holders
 | 
					 | 
				
			||||||
	alive := make(map[string]struct{}, len(pairs))
 | 
					 | 
				
			||||||
	for _, pair := range pairs {
 | 
					 | 
				
			||||||
		if pair.Session != "" {
 | 
					 | 
				
			||||||
			alive[pair.Session] = struct{}{}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Remove any holders that are dead
 | 
					 | 
				
			||||||
	for holder := range lock.Holders {
 | 
					 | 
				
			||||||
		if _, ok := alive[holder]; !ok {
 | 
					 | 
				
			||||||
			delete(lock.Holders, holder)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// monitorLock is a long running routine to monitor a semaphore ownership
 | 
					 | 
				
			||||||
// It closes the stopCh if we lose our slot.
 | 
					 | 
				
			||||||
func (s *Semaphore) monitorLock(session string, stopCh chan struct{}) {
 | 
					 | 
				
			||||||
	defer close(stopCh)
 | 
					 | 
				
			||||||
	kv := s.c.KV()
 | 
					 | 
				
			||||||
	opts := &QueryOptions{RequireConsistent: true}
 | 
					 | 
				
			||||||
WAIT:
 | 
					 | 
				
			||||||
	pairs, meta, err := kv.List(s.opts.Prefix, opts)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	lockPair := s.findLock(pairs)
 | 
					 | 
				
			||||||
	lock, err := s.decodeLock(lockPair)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	s.pruneDeadHolders(lock, pairs)
 | 
					 | 
				
			||||||
	if _, ok := lock.Holders[session]; ok {
 | 
					 | 
				
			||||||
		opts.WaitIndex = meta.LastIndex
 | 
					 | 
				
			||||||
		goto WAIT
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										201
									
								
								vendor/github.com/hashicorp/consul/api/session.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										201
									
								
								vendor/github.com/hashicorp/consul/api/session.go
									
										
									
										generated
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,201 +0,0 @@
 | 
				
			||||||
package api
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const (
 | 
					 | 
				
			||||||
	// SessionBehaviorRelease is the default behavior and causes
 | 
					 | 
				
			||||||
	// all associated locks to be released on session invalidation.
 | 
					 | 
				
			||||||
	SessionBehaviorRelease = "release"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// SessionBehaviorDelete is new in Consul 0.5 and changes the
 | 
					 | 
				
			||||||
	// behavior to delete all associated locks on session invalidation.
 | 
					 | 
				
			||||||
	// It can be used in a way similar to Ephemeral Nodes in ZooKeeper.
 | 
					 | 
				
			||||||
	SessionBehaviorDelete = "delete"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// SessionEntry represents a session in consul
 | 
					 | 
				
			||||||
type SessionEntry struct {
 | 
					 | 
				
			||||||
	CreateIndex uint64
 | 
					 | 
				
			||||||
	ID          string
 | 
					 | 
				
			||||||
	Name        string
 | 
					 | 
				
			||||||
	Node        string
 | 
					 | 
				
			||||||
	Checks      []string
 | 
					 | 
				
			||||||
	LockDelay   time.Duration
 | 
					 | 
				
			||||||
	Behavior    string
 | 
					 | 
				
			||||||
	TTL         string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Session can be used to query the Session endpoints
 | 
					 | 
				
			||||||
type Session struct {
 | 
					 | 
				
			||||||
	c *Client
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Session returns a handle to the session endpoints
 | 
					 | 
				
			||||||
func (c *Client) Session() *Session {
 | 
					 | 
				
			||||||
	return &Session{c}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// CreateNoChecks is like Create but is used specifically to create
 | 
					 | 
				
			||||||
// a session with no associated health checks.
 | 
					 | 
				
			||||||
func (s *Session) CreateNoChecks(se *SessionEntry, q *WriteOptions) (string, *WriteMeta, error) {
 | 
					 | 
				
			||||||
	body := make(map[string]interface{})
 | 
					 | 
				
			||||||
	body["Checks"] = []string{}
 | 
					 | 
				
			||||||
	if se != nil {
 | 
					 | 
				
			||||||
		if se.Name != "" {
 | 
					 | 
				
			||||||
			body["Name"] = se.Name
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if se.Node != "" {
 | 
					 | 
				
			||||||
			body["Node"] = se.Node
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if se.LockDelay != 0 {
 | 
					 | 
				
			||||||
			body["LockDelay"] = durToMsec(se.LockDelay)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if se.Behavior != "" {
 | 
					 | 
				
			||||||
			body["Behavior"] = se.Behavior
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if se.TTL != "" {
 | 
					 | 
				
			||||||
			body["TTL"] = se.TTL
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return s.create(body, q)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Create makes a new session. Providing a session entry can
 | 
					 | 
				
			||||||
// customize the session. It can also be nil to use defaults.
 | 
					 | 
				
			||||||
func (s *Session) Create(se *SessionEntry, q *WriteOptions) (string, *WriteMeta, error) {
 | 
					 | 
				
			||||||
	var obj interface{}
 | 
					 | 
				
			||||||
	if se != nil {
 | 
					 | 
				
			||||||
		body := make(map[string]interface{})
 | 
					 | 
				
			||||||
		obj = body
 | 
					 | 
				
			||||||
		if se.Name != "" {
 | 
					 | 
				
			||||||
			body["Name"] = se.Name
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if se.Node != "" {
 | 
					 | 
				
			||||||
			body["Node"] = se.Node
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if se.LockDelay != 0 {
 | 
					 | 
				
			||||||
			body["LockDelay"] = durToMsec(se.LockDelay)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if len(se.Checks) > 0 {
 | 
					 | 
				
			||||||
			body["Checks"] = se.Checks
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if se.Behavior != "" {
 | 
					 | 
				
			||||||
			body["Behavior"] = se.Behavior
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if se.TTL != "" {
 | 
					 | 
				
			||||||
			body["TTL"] = se.TTL
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return s.create(obj, q)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (s *Session) create(obj interface{}, q *WriteOptions) (string, *WriteMeta, error) {
 | 
					 | 
				
			||||||
	var out struct{ ID string }
 | 
					 | 
				
			||||||
	wm, err := s.c.write("/v1/session/create", obj, &out, q)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return "", nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return out.ID, wm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Destroy invalides a given session
 | 
					 | 
				
			||||||
func (s *Session) Destroy(id string, q *WriteOptions) (*WriteMeta, error) {
 | 
					 | 
				
			||||||
	wm, err := s.c.write("/v1/session/destroy/"+id, nil, nil, q)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return wm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Renew renews the TTL on a given session
 | 
					 | 
				
			||||||
func (s *Session) Renew(id string, q *WriteOptions) (*SessionEntry, *WriteMeta, error) {
 | 
					 | 
				
			||||||
	var entries []*SessionEntry
 | 
					 | 
				
			||||||
	wm, err := s.c.write("/v1/session/renew/"+id, nil, &entries, q)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(entries) > 0 {
 | 
					 | 
				
			||||||
		return entries[0], wm, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil, wm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// RenewPeriodic is used to periodically invoke Session.Renew on a
 | 
					 | 
				
			||||||
// session until a doneCh is closed. This is meant to be used in a long running
 | 
					 | 
				
			||||||
// goroutine to ensure a session stays valid.
 | 
					 | 
				
			||||||
func (s *Session) RenewPeriodic(initialTTL string, id string, q *WriteOptions, doneCh chan struct{}) error {
 | 
					 | 
				
			||||||
	ttl, err := time.ParseDuration(initialTTL)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	waitDur := ttl / 2
 | 
					 | 
				
			||||||
	lastRenewTime := time.Now()
 | 
					 | 
				
			||||||
	var lastErr error
 | 
					 | 
				
			||||||
	for {
 | 
					 | 
				
			||||||
		if time.Since(lastRenewTime) > ttl {
 | 
					 | 
				
			||||||
			return lastErr
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		select {
 | 
					 | 
				
			||||||
		case <-time.After(waitDur):
 | 
					 | 
				
			||||||
			entry, _, err := s.Renew(id, q)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				waitDur = time.Second
 | 
					 | 
				
			||||||
				lastErr = err
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if entry == nil {
 | 
					 | 
				
			||||||
				waitDur = time.Second
 | 
					 | 
				
			||||||
				lastErr = fmt.Errorf("No SessionEntry returned")
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// Handle the server updating the TTL
 | 
					 | 
				
			||||||
			ttl, _ = time.ParseDuration(entry.TTL)
 | 
					 | 
				
			||||||
			waitDur = ttl / 2
 | 
					 | 
				
			||||||
			lastRenewTime = time.Now()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		case <-doneCh:
 | 
					 | 
				
			||||||
			// Attempt a session destroy
 | 
					 | 
				
			||||||
			s.Destroy(id, q)
 | 
					 | 
				
			||||||
			return nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Info looks up a single session
 | 
					 | 
				
			||||||
func (s *Session) Info(id string, q *QueryOptions) (*SessionEntry, *QueryMeta, error) {
 | 
					 | 
				
			||||||
	var entries []*SessionEntry
 | 
					 | 
				
			||||||
	qm, err := s.c.query("/v1/session/info/"+id, &entries, q)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(entries) > 0 {
 | 
					 | 
				
			||||||
		return entries[0], qm, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil, qm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// List gets sessions for a node
 | 
					 | 
				
			||||||
func (s *Session) Node(node string, q *QueryOptions) ([]*SessionEntry, *QueryMeta, error) {
 | 
					 | 
				
			||||||
	var entries []*SessionEntry
 | 
					 | 
				
			||||||
	qm, err := s.c.query("/v1/session/node/"+node, &entries, q)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return entries, qm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// List gets all active sessions
 | 
					 | 
				
			||||||
func (s *Session) List(q *QueryOptions) ([]*SessionEntry, *QueryMeta, error) {
 | 
					 | 
				
			||||||
	var entries []*SessionEntry
 | 
					 | 
				
			||||||
	qm, err := s.c.query("/v1/session/list", &entries, q)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return entries, qm, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										43
									
								
								vendor/github.com/hashicorp/consul/api/status.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										43
									
								
								vendor/github.com/hashicorp/consul/api/status.go
									
										
									
										generated
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,43 +0,0 @@
 | 
				
			||||||
package api
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Status can be used to query the Status endpoints
 | 
					 | 
				
			||||||
type Status struct {
 | 
					 | 
				
			||||||
	c *Client
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Status returns a handle to the status endpoints
 | 
					 | 
				
			||||||
func (c *Client) Status() *Status {
 | 
					 | 
				
			||||||
	return &Status{c}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Leader is used to query for a known leader
 | 
					 | 
				
			||||||
func (s *Status) Leader() (string, error) {
 | 
					 | 
				
			||||||
	r := s.c.newRequest("GET", "/v1/status/leader")
 | 
					 | 
				
			||||||
	_, resp, err := requireOK(s.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return "", err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var leader string
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &leader); err != nil {
 | 
					 | 
				
			||||||
		return "", err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return leader, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Peers is used to query for a known raft peers
 | 
					 | 
				
			||||||
func (s *Status) Peers() ([]string, error) {
 | 
					 | 
				
			||||||
	r := s.c.newRequest("GET", "/v1/status/peers")
 | 
					 | 
				
			||||||
	_, resp, err := requireOK(s.c.doRequest(r))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer resp.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var peers []string
 | 
					 | 
				
			||||||
	if err := decodeBody(resp, &peers); err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return peers, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue