* #7013 add tls config support to consul provider * #7013 add acceptance tests * #7013 use GFM tables * #7013 require one of {CONSUL_ADDRESS,CONSUL_HTTP_ADDR} when running consul acc tests
This commit is contained in:
parent
45219dbb4f
commit
c072c0dfbb
|
@ -2,6 +2,7 @@ package consul
|
|||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
consulapi "github.com/hashicorp/consul/api"
|
||||
)
|
||||
|
@ -9,8 +10,11 @@ import (
|
|||
type Config struct {
|
||||
Datacenter string `mapstructure:"datacenter"`
|
||||
Address string `mapstructure:"address"`
|
||||
Token string `mapstructure:"token"`
|
||||
Scheme string `mapstructure:"scheme"`
|
||||
Token string `mapstructure:"token"`
|
||||
CAFile string `mapstructure:"ca_file"`
|
||||
CertFile string `mapstructure:"cert_file"`
|
||||
KeyFile string `mapstructure:"key_file"`
|
||||
}
|
||||
|
||||
// Client() returns a new client for accessing consul.
|
||||
|
@ -26,9 +30,21 @@ func (c *Config) Client() (*consulapi.Client, error) {
|
|||
if c.Scheme != "" {
|
||||
config.Scheme = c.Scheme
|
||||
}
|
||||
|
||||
tlsConfig := &consulapi.TLSConfig{}
|
||||
tlsConfig.CAFile = c.CAFile
|
||||
tlsConfig.CertFile = c.CertFile
|
||||
tlsConfig.KeyFile = c.KeyFile
|
||||
cc, err := consulapi.SetupTLSConfig(tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.HttpClient.Transport.(*http.Transport).TLSClientConfig = cc
|
||||
|
||||
if c.Token != "" {
|
||||
config.Token = c.Token
|
||||
}
|
||||
|
||||
client, err := consulapi.NewClient(config)
|
||||
|
||||
log.Printf("[INFO] Consul Client configured with address: '%s', scheme: '%s', datacenter: '%s'",
|
||||
|
|
|
@ -20,11 +20,34 @@ func Provider() terraform.ResourceProvider {
|
|||
"address": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
DefaultFunc: schema.MultiEnvDefaultFunc([]string{
|
||||
"CONSUL_ADDRESS",
|
||||
"CONSUL_HTTP_ADDR",
|
||||
}, nil),
|
||||
},
|
||||
|
||||
"scheme": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("CONSUL_SCHEME", nil),
|
||||
},
|
||||
|
||||
"ca_file": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("CONSUL_CA_FILE", nil),
|
||||
},
|
||||
|
||||
"cert_file": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("CONSUL_CERT_FILE", nil),
|
||||
},
|
||||
|
||||
"key_file": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("CONSUL_KEY_FILE", nil),
|
||||
},
|
||||
|
||||
"token": &schema.Schema{
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
consulapi "github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
|
@ -18,12 +17,6 @@ func init() {
|
|||
testAccProviders = map[string]terraform.ResourceProvider{
|
||||
"consul": testAccProvider,
|
||||
}
|
||||
|
||||
// Use the demo address for the acceptance tests
|
||||
testAccProvider.ConfigureFunc = func(d *schema.ResourceData) (interface{}, error) {
|
||||
conf := consulapi.DefaultConfig()
|
||||
return consulapi.NewClient(conf)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceProvider(t *testing.T) {
|
||||
|
@ -56,8 +49,35 @@ func TestResourceProvider_Configure(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func testAccPreCheck(t *testing.T) {
|
||||
if v := os.Getenv("CONSUL_HTTP_ADDR"); v == "" {
|
||||
t.Fatal("CONSUL_HTTP_ADDR must be set for acceptance tests")
|
||||
func TestResourceProvider_ConfigureTLS(t *testing.T) {
|
||||
rp := Provider()
|
||||
|
||||
raw := map[string]interface{}{
|
||||
"address": "demo.consul.io:80",
|
||||
"ca_file": "test-fixtures/cacert.pem",
|
||||
"cert_file": "test-fixtures/usercert.pem",
|
||||
"datacenter": "nyc3",
|
||||
"key_file": "test-fixtures/userkey.pem",
|
||||
"scheme": "https",
|
||||
}
|
||||
|
||||
rawConfig, err := config.NewRawConfig(raw)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
err = rp.Configure(terraform.NewResourceConfig(rawConfig))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func testAccPreCheck(t *testing.T) {
|
||||
if v := os.Getenv("CONSUL_HTTP_ADDR"); v != "" {
|
||||
return
|
||||
}
|
||||
if v := os.Getenv("CONSUL_ADDRESS"); v != "" {
|
||||
return
|
||||
}
|
||||
t.Fatal("Either CONSUL_ADDRESS or CONSUL_HTTP_ADDR must be set for acceptance tests")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
# Running Consul for Terraform Acceptance Tests
|
||||
|
||||
## TLS
|
||||
|
||||
Some of the acceptance tests for the `consul` provider use
|
||||
TLS. To service these tests, a Consul server must be started
|
||||
with HTTPS enabled with TLS certificates.
|
||||
|
||||
### Test fixtures
|
||||
|
||||
File | Description
|
||||
--- | ---
|
||||
`agent.json.example` | Configures the Consul agent to respond to HTTPS requests, and verifies the authenticity of HTTPS requests
|
||||
`agentcert.pem` | A PEM-encoded certificate used by the Consul agent, valid only for 127.0.0.1 signed by `cacert.pem`, expires 2026
|
||||
`agentkey.pem` | A PEM-encoded private key used by the Consul agent
|
||||
`cacert.pem` | A PEM-encoded Certificate Authority, expires 2036
|
||||
`usercert.pem` | A PEM-encoded certificate used by the Terraform acceptance tests, signed by `cacert.pem`, expires 2026
|
||||
`userkey.pem` | A PEM-encoded private key used by the Terraform acceptance tests
|
||||
|
||||
### Start
|
||||
|
||||
Start a Consul server configured to serve HTTP traffic, and validate incoming
|
||||
HTTPS requests.
|
||||
|
||||
~/.go/src/github.com/hashicorp/terraform> consul agent \
|
||||
-bind 127.0.0.1 \
|
||||
-data-dir=/tmp \
|
||||
-dev \
|
||||
-config-file=builtin/providers/consul/text-fixtures/agent.json.example \
|
||||
-server
|
||||
|
||||
### Test
|
||||
|
||||
With TLS, `CONSUL_HTTP_ADDR` must match the Common Name of the agent certificate.
|
||||
|
||||
~/.go/src/github.com/hashicorp/terraform> CONSUL_CERT_FILE=test-fixtures/usercert.pem \
|
||||
CONSUL_KEY_FILE=test-fixtures/userkey.pem \
|
||||
CONSUL_CA_FILE=test-fixtures/cacert.pem \
|
||||
CONSUL_SCHEME=https \
|
||||
CONSUL_HTTP_ADDR=127.0.0.1:8943 \
|
||||
make testacc TEST=./builtin/providers/consul/
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"ca_file": "./cacert.pem",
|
||||
"cert_file": "./agentcert.pem",
|
||||
"datacenter": "dc1",
|
||||
"domain": "hashicorp.test",
|
||||
"key_file": "./agentkey.pem",
|
||||
"ports": {
|
||||
"https": 8943
|
||||
},
|
||||
"verify_incoming": true
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIEjjCCA3agAwIBAgICEAMwDQYJKoZIhvcNAQELBQAwZjELMAkGA1UEBhMCVVMx
|
||||
EzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAoMCUhhc2hpQ29ycDESMBAGA1UE
|
||||
CwwJSGFzaGlDb3JwMRowGAYDVQQDDBFIYXNoaUNvcnAgVGVzdCBDQTAgFw0xNjA4
|
||||
MTEwNTEwMDRaGA8yMDY2MDczMDA1MTAwNFowXjELMAkGA1UEBhMCVVMxEzARBgNV
|
||||
BAgMCkNhbGlmb3JuaWExEjAQBgNVBAoMCUhhc2hpQ29ycDESMBAGA1UECwwJSGFz
|
||||
aGlDb3JwMRIwEAYDVQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
||||
DwAwggEKAoIBAQDQiR2zwLdfCd95LIrKekZlDKo3YF5nMZFzR3OR1Mc8jCAaYLz/
|
||||
ZFr8hVSAsygwZ+tHzoHP0U3FxeYemPtjLAPE077C6h+v6pHiTLxOkd22GtyalgMZ
|
||||
E4ACGSogqDUvwssxxDUsG2ItzhVCB0GXTlfo/6XhApyRqvnEto+ZJ+zk6MiHnAmc
|
||||
eN9sx0c5K097+Nq7PZgtk6HOxbKSvMWEkTtHrOBrhc9lTfwVSiWHdZ2X0wpOL1Ra
|
||||
pFnBMDxnWyH2ivVMknarzKz2pBBDJwTGvsJcC1ymprqU+SRyjs75BfNv2BKJrhb4
|
||||
vBj3YEGMBEhHKtnObniGqV8W4o9jBIwocFpfAgMBAAGjggFKMIIBRjAPBgNVHREE
|
||||
CDAGhwR/AAABMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDMGCWCGSAGG
|
||||
+EIBDQQmFiRPcGVuU1NMIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYD
|
||||
VR0OBBYEFNzoTM6XceaITc2lVIDrXaYBKJolMIGRBgNVHSMEgYkwgYaAFANEnP7/
|
||||
5Iil24eKuYJTt/IJPfamoWqkaDBmMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2Fs
|
||||
aWZvcm5pYTESMBAGA1UECgwJSGFzaGlDb3JwMRIwEAYDVQQLDAlIYXNoaUNvcnAx
|
||||
GjAYBgNVBAMMEUhhc2hpQ29ycCBUZXN0IENBggIQADAOBgNVHQ8BAf8EBAMCBaAw
|
||||
HQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4IB
|
||||
AQB4p5sWE+p+sheXYgkg/PsATRMxYDPRTw0Zvd2AKOrodqF4IlibSb4mVVh0fPtx
|
||||
2VX3z/WOZb8wgXNnEUhVcijf7LgGvw/SvQGgW5mXYSCcHeb4ETFJ1yuKZj5yn5tl
|
||||
vZx1Sq/fGFkjHn3mgL+gzyQlNk1Wt0p3fLsIfpMOgpntSdRq3IUvf+W+oH5BUrTl
|
||||
WgaXUD3lkdx3R9h3uLX4nxJWpMPViPCpr3ADW9oEwoHHQbe3LC7iJI2Us/qIH73n
|
||||
Du7mUk+/HSkajjFsxnVoFCF1+RMqf1i9w7tXaAwWYT+vaP46fq3M/Bmsv/gDc5ur
|
||||
8p48hpQ61Sfj0oU38Ftzzcs+
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA0Ikds8C3XwnfeSyKynpGZQyqN2BeZzGRc0dzkdTHPIwgGmC8
|
||||
/2Ra/IVUgLMoMGfrR86Bz9FNxcXmHpj7YywDxNO+wuofr+qR4ky8TpHdthrcmpYD
|
||||
GROAAhkqIKg1L8LLMcQ1LBtiLc4VQgdBl05X6P+l4QKckar5xLaPmSfs5OjIh5wJ
|
||||
nHjfbMdHOStPe/jauz2YLZOhzsWykrzFhJE7R6zga4XPZU38FUolh3Wdl9MKTi9U
|
||||
WqRZwTA8Z1sh9or1TJJ2q8ys9qQQQycExr7CXAtcpqa6lPkkco7O+QXzb9gSia4W
|
||||
+LwY92BBjARIRyrZzm54hqlfFuKPYwSMKHBaXwIDAQABAoIBAFQxyAg3GtIITm3C
|
||||
ChdN3vYVcvQAuJy5apw8kPCkE/ziJmQAAs6qWgHyYvfDXcqNanUHb2nUe64KBKr9
|
||||
4SFdN/hT9YUEud5wuo2/pZejVPydQ8w2HPIW6WvvdQ7SWwb5gsiJC17Pf4g22GZc
|
||||
P6MzQlMURIjgYQ5/FXDStI+FiyOwDhHDbLoMaIOfToWJupd+sGREccSKOVmJdGY/
|
||||
7/n1AGvfbgUToy2sMEz7HqTtgRJW/Knko2dD3ZWh7KqFS2GUYsJ3Ake1CG7xT6sA
|
||||
4MWQvfR/+t7xSpDDU2WlNgFi9sQ8UjrIhaMYaiFhV6h2bTVeGl1tvBmbE77Z1Lne
|
||||
jcobwKECgYEA6SuDF0wuu8WnDXnCpNrd83gONy1pEp9s7vbf/GrMXGaavQmqb6x1
|
||||
sLZTXho1srqRLXGSAvbDS/yO6wRUd/CdLB8WBda3lcH9y/II1BDynEygGpipGa83
|
||||
7Ti+D2hMSMLhX1vsUcCwz3fz61wzBqvdvrjdymmivPLu3rMINd8twl0CgYEA5PQi
|
||||
jwi483icPPOc1S/5CsKnj6nJwKVQz9dDeT6QLVDCoVh5u0X9WZAAnMdrR9yZS6ax
|
||||
ZAF453DPlK6Ct7vcBPEFC1W6QrRhjJrzvRb/PLnzaRMY+YoEg2qmGreb+30jrx+4
|
||||
jkTLkz4Qag+jOdR3t4104Pix1CkpQUX0chD5u+sCgYAiuI8Bxh9jaLBSimIYqFrK
|
||||
qYL8Zm+yDTlscCi0brbVv5WlNq5BiN3RnaTWa3K5lZyOts22UUaNpyMlDfUCEztk
|
||||
WZCu9+VIkKWZXAZChe+KpMJmk3sCzxu14HA03SQW5aYnzAlptxbdHhCdaJJUmP0h
|
||||
LGgifw5zsn0tfl1noD8xJQKBgBKSSwtXJcl6CxJWoG4aihT5XSYmG5tozXlOeMao
|
||||
8ID8gA0eZCFwt/A/4gzVkDowBq9AQjteczQyzmO9FBVbQ6mS81nMBmPKxe7l0seP
|
||||
yfxfCQOI7QmwzFTsnbSlGB36NJ7L7+h6ZBj5e9NemVrjhSJ6cvSct7AB9rq4te9a
|
||||
uScpAoGBAOIjcv2lQsJ3GWBTHWCh23jC/0XPE4bJg9DjliHQDAB/Yp49oV1giWs6
|
||||
xI0SBsovtJqJxOd6F8e6HuQTt1X1kQ8Q1Itb78Wx9Rs4bvN51pxj4L+DTxLBMl5g
|
||||
xXsS+Zfm5O2HGxU5t60CsxRyF0EVNVEtgKkIiQE+ZaQ1d0WJC8RI
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1,22 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDrTCCApWgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwZjELMAkGA1UEBhMCVVMx
|
||||
EzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAoMCUhhc2hpQ29ycDESMBAGA1UE
|
||||
CwwJSGFzaGlDb3JwMRowGAYDVQQDDBFIYXNoaUNvcnAgVGVzdCBDQTAgFw0xNjA4
|
||||
MTEwNTA1MDZaGA8yMTE2MDcxODA1MDUwNlowZjELMAkGA1UEBhMCVVMxEzARBgNV
|
||||
BAgMCkNhbGlmb3JuaWExEjAQBgNVBAoMCUhhc2hpQ29ycDESMBAGA1UECwwJSGFz
|
||||
aGlDb3JwMRowGAYDVQQDDBFIYXNoaUNvcnAgVGVzdCBDQTCCASIwDQYJKoZIhvcN
|
||||
AQEBBQADggEPADCCAQoCggEBAKglYmf9Tv1u6e1ulQZNpmvUHi+D+/PBHyg9Ft60
|
||||
HiZaeBGyNPZX9uVuM1jN3o/qpwBQxhq3ojQafU3WDSU6Y0GZ1e8AcLsObeNUjma4
|
||||
eLjZy+059Vt7DKcp6LA+7JqELToK83QzqNdYuocze5v9hPt5W6Q3Dp5rsmVjOFim
|
||||
6LxcN/TAcmW+ZrykOGOT4QyYFkamp4uMJkpX1UwO3djdQF7CllnOboUUYqGyOt9e
|
||||
BBudhCsSvWpJa/wNcAH2AxzaIVu85Dmg3G0Erekcget5ewebsnhGs3emfWO/XQht
|
||||
uwKdz60mz1vAIK3UR5eYCbxnLrXM0WfcYKFqhuQpqqONWtUCAwEAAaNjMGEwHQYD
|
||||
VR0OBBYEFANEnP7/5Iil24eKuYJTt/IJPfamMB8GA1UdIwQYMBaAFANEnP7/5Iil
|
||||
24eKuYJTt/IJPfamMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0G
|
||||
CSqGSIb3DQEBCwUAA4IBAQBzzvX4GiBalxuW5YxMiuFAljNB+tW20Frz0s7bq0+Z
|
||||
1+ErQIW26NUHH14RUU4vbisX09QMm4p62oJOpo/5nW1VqsyoTCQJXaolGF6UidFy
|
||||
l/2bgOy8QbCOqrS0jt0MFQFDr9Z/m8dBgbjFzv8gfsnpxDQvi+iKkVSuzlIfcvoo
|
||||
xlwtNnrD9lSsinP4Zo8sNqjhaRbih8zhsUdd0mUDDGczw2mY2CdMmeH0wflJMEVe
|
||||
3hwR8650sCJlJfVuFUDsqy1K9T5j5NVv7i6RloeMvYOH2nwpIejE88lmjpXR6Bzw
|
||||
g8geEjKOLBN8Nmak3jSvH2IewczZKSaKNSiv/4Izut/8
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,25 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIEJjCCAw6gAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwZjELMAkGA1UEBhMCVVMx
|
||||
EzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAoMCUhhc2hpQ29ycDESMBAGA1UE
|
||||
CwwJSGFzaGlDb3JwMRowGAYDVQQDDBFIYXNoaUNvcnAgVGVzdCBDQTAgFw0xNjA4
|
||||
MTEwNTA1MzFaGA8yMDY2MDczMDA1MDUzMVowcjELMAkGA1UEBhMCVVMxEzARBgNV
|
||||
BAgMCkNhbGlmb3JuaWExEjAQBgNVBAoMCUhhc2hpQ29ycDESMBAGA1UECwwJSGFz
|
||||
aGlDb3JwMSYwJAYDVQQDDB1IYXNoaUNvcnAgVGVzdCBUZXJyYWZvcm0gVXNlcjCC
|
||||
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANrfzsj+gIM3pvuI+hdx2W5s
|
||||
hTh2YXd4Q7cByLDzXPRgI0W1BFOIxOAdHy/zqxCKQPxiibxPqDCxzPnc7mSco8e0
|
||||
zvihAysthiUmWcNdF1pIh6631SU9rE+Mis6XcW2beuh/IVloXBwI4dmSuX3Urb0D
|
||||
Aw3Rb5kCJzXUTBG/g8KriR6KyNFTu0Wb/1NcrrCnNAteQmpDuuMtx75stfoMUnlr
|
||||
xZfsCZXHVpe8GmVlwqr8Mw7NKmyeKgl0rH1Mef6+ce9BPnVBxdJMEYWl+UQfTSV+
|
||||
pWoNtQTZxEbhbMFhYi410EJ5s0Nw6lyUnXrQ2/YglikIvnyfWj/CwLTZwaXlgAkC
|
||||
AwEAAaOBzzCBzDAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIFoDAzBglghkgB
|
||||
hvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgQ2xpZW50IENlcnRpZmljYXRlMB0G
|
||||
A1UdDgQWBBRIgDbi1wLtW+u3PgrbVrNDkGfwGjAfBgNVHSMEGDAWgBQDRJz+/+SI
|
||||
pduHirmCU7fyCT32pjAOBgNVHQ8BAf8EBAMCBeAwJwYDVR0lBCAwHgYIKwYBBQUH
|
||||
AwIGCCsGAQUFBwMEBggrBgEFBQcDATANBgkqhkiG9w0BAQsFAAOCAQEAi1pDjqy1
|
||||
bN9cLknPyblWdgaO0xSXJAvBpEaFnz88wmEPOmsg1889x7jJKhrGjTpjDJMeq3kh
|
||||
ziDVCpOJesJOlUsa8ejOhMbcdiqHOWSk12bQC7dBbnAAXwO1Tr583IdLhC+ej64r
|
||||
J4dBk7/wLBx2Deh8wxW+6TrIFNCyVptcHw76K2AkO11fscqS0sL/WVxqi1mjA9rV
|
||||
KNGDIEIzqu13jZ3t0Cxc5AZ6dGHBALGNkfhjJ9SCpuPf8CmWNYHGWeIV0N4AB2SQ
|
||||
gAjRYUKY4+zU3e+lUuudgTYZIM+zark6hLUAfXTeNRk6kGHod7x/Q9NvLB4SLwlI
|
||||
DAzXJ9QHZyO1vQ==
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpgIBAAKCAQEA2t/OyP6Agzem+4j6F3HZbmyFOHZhd3hDtwHIsPNc9GAjRbUE
|
||||
U4jE4B0fL/OrEIpA/GKJvE+oMLHM+dzuZJyjx7TO+KEDKy2GJSZZw10XWkiHrrfV
|
||||
JT2sT4yKzpdxbZt66H8hWWhcHAjh2ZK5fdStvQMDDdFvmQInNdRMEb+DwquJHorI
|
||||
0VO7RZv/U1yusKc0C15CakO64y3Hvmy1+gxSeWvFl+wJlcdWl7waZWXCqvwzDs0q
|
||||
bJ4qCXSsfUx5/r5x70E+dUHF0kwRhaX5RB9NJX6lag21BNnERuFswWFiLjXQQnmz
|
||||
Q3DqXJSdetDb9iCWKQi+fJ9aP8LAtNnBpeWACQIDAQABAoIBAQCRXS8zIoQrodyP
|
||||
FkwzIfPseLqJ42WcOQ2QD+lATIEh9G+4rh5vdFh9GBpMeKLWW1wJw1AC90yW+p9O
|
||||
G0NhIv9LdXQ4gIdgN93t8miPbdZCqgUjLwiqsSkttAPEbaRxzV915mk5vivemq+V
|
||||
FvOG9Kdm7wcqODzL/DgaciMLboyNzrChltgybGKQIHAd9UFm+jE86IeeBsVcHuoL
|
||||
0rEsYFKKzgdmIizjDOWPDSzVKL+dkiZ/8rYgoe1VtGV1DRWAWU5fawDFrdOxsGCh
|
||||
Ob+rEmosTvONEQhB6GsdOQZ8++N6UTiJw32jqgieeP+Xj+K4XNG3nhP000DUIx/o
|
||||
pRnj+KDhAoGBAPWXFbGHIMwEJCUV6OUXiY9Enb3A/Jf65f7cKbvo1i/nIbhzEv3v
|
||||
LBtcUrsTmgTgNvuh3tF1RnwAUeYgedjdnALGJL16h3E0IWLAStaovoKgQyHHrtp9
|
||||
CEnOIj3PcPTFJVe+2FeV0+/kLjTHsZj9gMljzfxgswNdYfeGjXp4o1lVAoGBAOQm
|
||||
06TW3smWW0FIRyyDNBwhQjD8tg3Yn0QJ+zFP6WMk5qbu/LeJnJSevBpQt1PvJ6xQ
|
||||
kCj6Bi90jtLeuCW/8XLQjP46jQLU+3a74m3Nszgu9JVofiK6EPIsx62TGlwtIJfJ
|
||||
U4+C5D/Piw/3qy6MjDbA1NJlSE4i2hAgGA79cDvlAoGBAN2o2sSbkOdyyWjLiKPV
|
||||
BaxQowrUN2e45YONFQHsGf2sYEwJWNfm2elr/6OoAnhqIlYleGWWsuJSq5jIMRGi
|
||||
myAJ1LlL8Rkkkwl9Q07RiPl/SngfsVq0RRnQOimNpIbXtWen8b3DlkFLssSihFHw
|
||||
ZB/gu9cRNCFSVIzDXchvQAftAoGBAL0EzeOLgRhSUVhMoWrnaIzFoSkktU/TYF/m
|
||||
RQ4dvqY9NDqpVQZaJDedKwpCRSBsytmgBU9tlSJL1ugtTTM5Srhsv+MAb0MhYRSF
|
||||
pJqECS9K96ew4o+yx8dcAjJz5Sro2E/opCoJr0COmg+oiVIPbzsNl0SYVMcnaLJj
|
||||
ZItGvW1hAoGBALeVUiqLgEDNQAIvprRlpJhU/ANpKm01ja9v66cgvmg62P9fcqb+
|
||||
yYPuJ2CwFDlb70KIDys6q9sTKUFykPDiKQgAFvEBQgAyOb3kdl4SXoUPbVDhMqwB
|
||||
OfPznsXM6Y5LFNLzEi4n0QP4KsLc+wM52On5vnj7Mgvt5h2QlllPPTXy
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -18,11 +18,12 @@ type AgentCheck struct {
|
|||
|
||||
// AgentService represents a service known to the agent
|
||||
type AgentService struct {
|
||||
ID string
|
||||
Service string
|
||||
Tags []string
|
||||
Port int
|
||||
Address string
|
||||
ID string
|
||||
Service string
|
||||
Tags []string
|
||||
Port int
|
||||
Address string
|
||||
EnableTagOverride bool
|
||||
}
|
||||
|
||||
// AgentMember represents a cluster member known to the agent
|
||||
|
@ -42,13 +43,14 @@ type AgentMember struct {
|
|||
|
||||
// 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
|
||||
ID string `json:",omitempty"`
|
||||
Name string `json:",omitempty"`
|
||||
Tags []string `json:",omitempty"`
|
||||
Port int `json:",omitempty"`
|
||||
Address string `json:",omitempty"`
|
||||
EnableTagOverride bool `json:",omitempty"`
|
||||
Check *AgentServiceCheck
|
||||
Checks AgentServiceChecks
|
||||
}
|
||||
|
||||
// AgentCheckRegistration is used to register a new check
|
||||
|
@ -196,23 +198,43 @@ func (a *Agent) ServiceDeregister(serviceID string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// PassTTL is used to set a TTL check to the passing state
|
||||
// PassTTL is used to set a TTL check to the passing state.
|
||||
//
|
||||
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
|
||||
// The client interface will be removed in 0.8 or changed to use
|
||||
// UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
|
||||
func (a *Agent) PassTTL(checkID, note string) error {
|
||||
return a.UpdateTTL(checkID, note, "pass")
|
||||
return a.updateTTL(checkID, note, "pass")
|
||||
}
|
||||
|
||||
// WarnTTL is used to set a TTL check to the warning state
|
||||
// WarnTTL is used to set a TTL check to the warning state.
|
||||
//
|
||||
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
|
||||
// The client interface will be removed in 0.8 or changed to use
|
||||
// UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
|
||||
func (a *Agent) WarnTTL(checkID, note string) error {
|
||||
return a.UpdateTTL(checkID, note, "warn")
|
||||
return a.updateTTL(checkID, note, "warn")
|
||||
}
|
||||
|
||||
// FailTTL is used to set a TTL check to the failing state
|
||||
// FailTTL is used to set a TTL check to the failing state.
|
||||
//
|
||||
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
|
||||
// The client interface will be removed in 0.8 or changed to use
|
||||
// UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
|
||||
func (a *Agent) FailTTL(checkID, note string) error {
|
||||
return a.UpdateTTL(checkID, note, "fail")
|
||||
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 {
|
||||
// updateTTL is used to update the TTL of a check. This is the internal
|
||||
// method that uses the old API that's present in Consul versions prior to
|
||||
// 0.6.4. Since Consul didn't have an analogous "update" API before it seemed
|
||||
// ok to break this (former) UpdateTTL in favor of the new UpdateTTL below,
|
||||
// but keep the old Pass/Warn/Fail methods using the old API under the hood.
|
||||
//
|
||||
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
|
||||
// The client interface will be removed in 0.8 and the server endpoints will
|
||||
// be removed in 0.9.
|
||||
func (a *Agent) updateTTL(checkID, note, status string) error {
|
||||
switch status {
|
||||
case "pass":
|
||||
case "warn":
|
||||
|
@ -231,6 +253,51 @@ func (a *Agent) UpdateTTL(checkID, note, status string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// checkUpdate is the payload for a PUT for a check update.
|
||||
type checkUpdate struct {
|
||||
// Status is one of the api.Health* states: HealthPassing
|
||||
// ("passing"), HealthWarning ("warning"), or HealthCritical
|
||||
// ("critical").
|
||||
Status string
|
||||
|
||||
// Output is the information to post to the UI for operators as the
|
||||
// output of the process that decided to hit the TTL check. This is
|
||||
// different from the note field that's associated with the check
|
||||
// itself.
|
||||
Output string
|
||||
}
|
||||
|
||||
// UpdateTTL is used to update the TTL of a check. This uses the newer API
|
||||
// that was introduced in Consul 0.6.4 and later. We translate the old status
|
||||
// strings for compatibility (though a newer version of Consul will still be
|
||||
// required to use this API).
|
||||
func (a *Agent) UpdateTTL(checkID, output, status string) error {
|
||||
switch status {
|
||||
case "pass", HealthPassing:
|
||||
status = HealthPassing
|
||||
case "warn", HealthWarning:
|
||||
status = HealthWarning
|
||||
case "fail", HealthCritical:
|
||||
status = HealthCritical
|
||||
default:
|
||||
return fmt.Errorf("Invalid status: %s", status)
|
||||
}
|
||||
|
||||
endpoint := fmt.Sprintf("/v1/agent/check/update/%s", checkID)
|
||||
r := a.c.newRequest("PUT", endpoint)
|
||||
r.obj = &checkUpdate{
|
||||
Status: status,
|
||||
Output: output,
|
||||
}
|
||||
|
||||
_, 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 {
|
||||
|
|
|
@ -3,9 +3,11 @@ package api
|
|||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
|
@ -122,12 +124,58 @@ type Config struct {
|
|||
Token string
|
||||
}
|
||||
|
||||
// DefaultConfig returns a default configuration for the client
|
||||
// TLSConfig is used to generate a TLSClientConfig that's useful for talking to
|
||||
// Consul using TLS.
|
||||
type TLSConfig struct {
|
||||
// Address is the optional address of the Consul server. The port, if any
|
||||
// will be removed from here and this will be set to the ServerName of the
|
||||
// resulting config.
|
||||
Address string
|
||||
|
||||
// CAFile is the optional path to the CA certificate used for Consul
|
||||
// communication, defaults to the system bundle if not specified.
|
||||
CAFile string
|
||||
|
||||
// CertFile is the optional path to the certificate for Consul
|
||||
// communication. If this is set then you need to also set KeyFile.
|
||||
CertFile string
|
||||
|
||||
// KeyFile is the optional path to the private key for Consul communication.
|
||||
// If this is set then you need to also set CertFile.
|
||||
KeyFile string
|
||||
|
||||
// InsecureSkipVerify if set to true will disable TLS host verification.
|
||||
InsecureSkipVerify bool
|
||||
}
|
||||
|
||||
// DefaultConfig returns a default configuration for the client. By default this
|
||||
// will pool and reuse idle connections to Consul. If you have a long-lived
|
||||
// client object, this is the desired behavior and should make the most efficient
|
||||
// use of the connections to Consul. If you don't reuse a client object , which
|
||||
// is not recommended, then you may notice idle connections building up over
|
||||
// time. To avoid this, use the DefaultNonPooledConfig() instead.
|
||||
func DefaultConfig() *Config {
|
||||
return defaultConfig(cleanhttp.DefaultPooledTransport)
|
||||
}
|
||||
|
||||
// DefaultNonPooledConfig returns a default configuration for the client which
|
||||
// does not pool connections. This isn't a recommended configuration because it
|
||||
// will reconnect to Consul on every request, but this is useful to avoid the
|
||||
// accumulation of idle connections if you make many client objects during the
|
||||
// lifetime of your application.
|
||||
func DefaultNonPooledConfig() *Config {
|
||||
return defaultConfig(cleanhttp.DefaultTransport)
|
||||
}
|
||||
|
||||
// defaultConfig returns the default configuration for the client, using the
|
||||
// given function to make the transport.
|
||||
func defaultConfig(transportFn func() *http.Transport) *Config {
|
||||
config := &Config{
|
||||
Address: "127.0.0.1:8500",
|
||||
Scheme: "http",
|
||||
HttpClient: cleanhttp.DefaultClient(),
|
||||
Address: "127.0.0.1:8500",
|
||||
Scheme: "http",
|
||||
HttpClient: &http.Client{
|
||||
Transport: transportFn(),
|
||||
},
|
||||
}
|
||||
|
||||
if addr := os.Getenv("CONSUL_HTTP_ADDR"); addr != "" {
|
||||
|
@ -172,10 +220,19 @@ func DefaultConfig() *Config {
|
|||
}
|
||||
|
||||
if !doVerify {
|
||||
transport := cleanhttp.DefaultTransport()
|
||||
transport.TLSClientConfig = &tls.Config{
|
||||
tlsClientConfig, err := SetupTLSConfig(&TLSConfig{
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
|
||||
// We don't expect this to fail given that we aren't
|
||||
// parsing any of the input, but we panic just in case
|
||||
// since this doesn't have an error return.
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
transport := transportFn()
|
||||
transport.TLSClientConfig = tlsClientConfig
|
||||
config.HttpClient.Transport = transport
|
||||
}
|
||||
}
|
||||
|
@ -183,6 +240,50 @@ func DefaultConfig() *Config {
|
|||
return config
|
||||
}
|
||||
|
||||
// TLSConfig is used to generate a TLSClientConfig that's useful for talking to
|
||||
// Consul using TLS.
|
||||
func SetupTLSConfig(tlsConfig *TLSConfig) (*tls.Config, error) {
|
||||
tlsClientConfig := &tls.Config{
|
||||
InsecureSkipVerify: tlsConfig.InsecureSkipVerify,
|
||||
}
|
||||
|
||||
if tlsConfig.Address != "" {
|
||||
server := tlsConfig.Address
|
||||
hasPort := strings.LastIndex(server, ":") > strings.LastIndex(server, "]")
|
||||
if hasPort {
|
||||
var err error
|
||||
server, _, err = net.SplitHostPort(server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
tlsClientConfig.ServerName = server
|
||||
}
|
||||
|
||||
if tlsConfig.CertFile != "" && tlsConfig.KeyFile != "" {
|
||||
tlsCert, err := tls.LoadX509KeyPair(tlsConfig.CertFile, tlsConfig.KeyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsClientConfig.Certificates = []tls.Certificate{tlsCert}
|
||||
}
|
||||
|
||||
if tlsConfig.CAFile != "" {
|
||||
data, err := ioutil.ReadFile(tlsConfig.CAFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read CA file: %v", err)
|
||||
}
|
||||
|
||||
caPool := x509.NewCertPool()
|
||||
if !caPool.AppendCertsFromPEM(data) {
|
||||
return nil, fmt.Errorf("failed to parse CA certificate")
|
||||
}
|
||||
tlsClientConfig.RootCAs = caPool
|
||||
}
|
||||
|
||||
return tlsClientConfig, nil
|
||||
}
|
||||
|
||||
// Client provides a client to the Consul API
|
||||
type Client struct {
|
||||
config Config
|
||||
|
|
|
@ -6,13 +6,14 @@ type Node struct {
|
|||
}
|
||||
|
||||
type CatalogService struct {
|
||||
Node string
|
||||
Address string
|
||||
ServiceID string
|
||||
ServiceName string
|
||||
ServiceAddress string
|
||||
ServiceTags []string
|
||||
ServicePort int
|
||||
Node string
|
||||
Address string
|
||||
ServiceID string
|
||||
ServiceName string
|
||||
ServiceAddress string
|
||||
ServiceTags []string
|
||||
ServicePort int
|
||||
ServiceEnableTagOverride bool
|
||||
}
|
||||
|
||||
type CatalogNode struct {
|
||||
|
|
|
@ -4,6 +4,16 @@ import (
|
|||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
// HealthAny is special, and is used as a wild card,
|
||||
// not as a specific state.
|
||||
HealthAny = "any"
|
||||
HealthUnknown = "unknown"
|
||||
HealthPassing = "passing"
|
||||
HealthWarning = "warning"
|
||||
HealthCritical = "critical"
|
||||
)
|
||||
|
||||
// HealthCheck is used to represent a single check
|
||||
type HealthCheck struct {
|
||||
Node string
|
||||
|
@ -85,7 +95,7 @@ func (h *Health) Service(service, tag string, passingOnly bool, q *QueryOptions)
|
|||
r.params.Set("tag", tag)
|
||||
}
|
||||
if passingOnly {
|
||||
r.params.Set("passing", "1")
|
||||
r.params.Set(HealthPassing, "1")
|
||||
}
|
||||
rtt, resp, err := requireOK(h.c.doRequest(r))
|
||||
if err != nil {
|
||||
|
@ -108,11 +118,11 @@ func (h *Health) Service(service, tag string, passingOnly bool, q *QueryOptions)
|
|||
// 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":
|
||||
case HealthAny:
|
||||
case HealthWarning:
|
||||
case HealthCritical:
|
||||
case HealthPassing:
|
||||
case HealthUnknown:
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("Unsupported state: %v", state)
|
||||
}
|
||||
|
|
|
@ -23,6 +23,43 @@ type KVPair struct {
|
|||
// KVPairs is a list of KVPair objects
|
||||
type KVPairs []*KVPair
|
||||
|
||||
// KVOp constants give possible operations available in a KVTxn.
|
||||
type KVOp string
|
||||
|
||||
const (
|
||||
KVSet KVOp = "set"
|
||||
KVDelete = "delete"
|
||||
KVDeleteCAS = "delete-cas"
|
||||
KVDeleteTree = "delete-tree"
|
||||
KVCAS = "cas"
|
||||
KVLock = "lock"
|
||||
KVUnlock = "unlock"
|
||||
KVGet = "get"
|
||||
KVGetTree = "get-tree"
|
||||
KVCheckSession = "check-session"
|
||||
KVCheckIndex = "check-index"
|
||||
)
|
||||
|
||||
// KVTxnOp defines a single operation inside a transaction.
|
||||
type KVTxnOp struct {
|
||||
Verb string
|
||||
Key string
|
||||
Value []byte
|
||||
Flags uint64
|
||||
Index uint64
|
||||
Session string
|
||||
}
|
||||
|
||||
// KVTxnOps defines a set of operations to be performed inside a single
|
||||
// transaction.
|
||||
type KVTxnOps []*KVTxnOp
|
||||
|
||||
// KVTxnResponse has the outcome of a transaction.
|
||||
type KVTxnResponse struct {
|
||||
Results []*KVPair
|
||||
Errors TxnErrors
|
||||
}
|
||||
|
||||
// KV is used to manipulate the K/V API
|
||||
type KV struct {
|
||||
c *Client
|
||||
|
@ -238,3 +275,122 @@ func (k *KV) deleteInternal(key string, params map[string]string, q *WriteOption
|
|||
res := strings.Contains(string(buf.Bytes()), "true")
|
||||
return res, qm, nil
|
||||
}
|
||||
|
||||
// TxnOp is the internal format we send to Consul. It's not specific to KV,
|
||||
// though currently only KV operations are supported.
|
||||
type TxnOp struct {
|
||||
KV *KVTxnOp
|
||||
}
|
||||
|
||||
// TxnOps is a list of transaction operations.
|
||||
type TxnOps []*TxnOp
|
||||
|
||||
// TxnResult is the internal format we receive from Consul.
|
||||
type TxnResult struct {
|
||||
KV *KVPair
|
||||
}
|
||||
|
||||
// TxnResults is a list of TxnResult objects.
|
||||
type TxnResults []*TxnResult
|
||||
|
||||
// TxnError is used to return information about an operation in a transaction.
|
||||
type TxnError struct {
|
||||
OpIndex int
|
||||
What string
|
||||
}
|
||||
|
||||
// TxnErrors is a list of TxnError objects.
|
||||
type TxnErrors []*TxnError
|
||||
|
||||
// TxnResponse is the internal format we receive from Consul.
|
||||
type TxnResponse struct {
|
||||
Results TxnResults
|
||||
Errors TxnErrors
|
||||
}
|
||||
|
||||
// Txn is used to apply multiple KV operations in a single, atomic transaction.
|
||||
//
|
||||
// Note that Go will perform the required base64 encoding on the values
|
||||
// automatically because the type is a byte slice. Transactions are defined as a
|
||||
// list of operations to perform, using the KVOp constants and KVTxnOp structure
|
||||
// to define operations. If any operation fails, none of the changes are applied
|
||||
// to the state store. Note that this hides the internal raw transaction interface
|
||||
// and munges the input and output types into KV-specific ones for ease of use.
|
||||
// If there are more non-KV operations in the future we may break out a new
|
||||
// transaction API client, but it will be easy to keep this KV-specific variant
|
||||
// supported.
|
||||
//
|
||||
// Even though this is generally a write operation, we take a QueryOptions input
|
||||
// and return a QueryMeta output. If the transaction contains only read ops, then
|
||||
// Consul will fast-path it to a different endpoint internally which supports
|
||||
// consistency controls, but not blocking. If there are write operations then
|
||||
// the request will always be routed through raft and any consistency settings
|
||||
// will be ignored.
|
||||
//
|
||||
// Here's an example:
|
||||
//
|
||||
// ops := KVTxnOps{
|
||||
// &KVTxnOp{
|
||||
// Verb: KVLock,
|
||||
// Key: "test/lock",
|
||||
// Session: "adf4238a-882b-9ddc-4a9d-5b6758e4159e",
|
||||
// Value: []byte("hello"),
|
||||
// },
|
||||
// &KVTxnOp{
|
||||
// Verb: KVGet,
|
||||
// Key: "another/key",
|
||||
// },
|
||||
// }
|
||||
// ok, response, _, err := kv.Txn(&ops, nil)
|
||||
//
|
||||
// If there is a problem making the transaction request then an error will be
|
||||
// returned. Otherwise, the ok value will be true if the transaction succeeded
|
||||
// or false if it was rolled back. The response is a structured return value which
|
||||
// will have the outcome of the transaction. Its Results member will have entries
|
||||
// for each operation. Deleted keys will have a nil entry in the, and to save
|
||||
// space, the Value of each key in the Results will be nil unless the operation
|
||||
// is a KVGet. If the transaction was rolled back, the Errors member will have
|
||||
// entries referencing the index of the operation that failed along with an error
|
||||
// message.
|
||||
func (k *KV) Txn(txn KVTxnOps, q *QueryOptions) (bool, *KVTxnResponse, *QueryMeta, error) {
|
||||
r := k.c.newRequest("PUT", "/v1/txn")
|
||||
r.setQueryOptions(q)
|
||||
|
||||
// Convert into the internal format since this is an all-KV txn.
|
||||
ops := make(TxnOps, 0, len(txn))
|
||||
for _, kvOp := range txn {
|
||||
ops = append(ops, &TxnOp{KV: kvOp})
|
||||
}
|
||||
r.obj = ops
|
||||
rtt, resp, err := k.c.doRequest(r)
|
||||
if err != nil {
|
||||
return false, nil, nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
qm := &QueryMeta{}
|
||||
parseQueryMeta(resp, qm)
|
||||
qm.RequestTime = rtt
|
||||
|
||||
if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusConflict {
|
||||
var txnResp TxnResponse
|
||||
if err := decodeBody(resp, &txnResp); err != nil {
|
||||
return false, nil, nil, err
|
||||
}
|
||||
|
||||
// Convert from the internal format.
|
||||
kvResp := KVTxnResponse{
|
||||
Errors: txnResp.Errors,
|
||||
}
|
||||
for _, result := range txnResp.Results {
|
||||
kvResp.Results = append(kvResp.Results, result.KV)
|
||||
}
|
||||
return resp.StatusCode == http.StatusOK, &kvResp, qm, nil
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if _, err := io.Copy(&buf, resp.Body); err != nil {
|
||||
return false, nil, nil, fmt.Errorf("Failed to read response: %v", err)
|
||||
}
|
||||
return false, nil, nil, fmt.Errorf("Failed request: %s", buf.String())
|
||||
}
|
||||
|
|
|
@ -890,9 +890,11 @@
|
|||
"revisionTime": "2016-07-26T16:33:11Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "glOabn8rkJvz7tjz/xfX4lmt070=",
|
||||
"comment": "v0.6.3-28-g3215b87",
|
||||
"path": "github.com/hashicorp/consul/api",
|
||||
"revision": "3215b8727f44c778dd7045dcfd5ac42735c581a9"
|
||||
"revision": "d4a8a43d2b600e662a50a75be70daed5fad8dd2d",
|
||||
"revisionTime": "2016-06-04T06:35:46Z"
|
||||
},
|
||||
{
|
||||
"path": "github.com/hashicorp/errwrap",
|
||||
|
|
|
@ -46,4 +46,8 @@ The following arguments are supported:
|
|||
* `address` - (Optional) The HTTP(S) API address of the agent to use. Defaults to "127.0.0.1:8500".
|
||||
* `scheme` - (Optional) The URL scheme of the agent to use ("http" or "https"). Defaults to "http".
|
||||
* `datacenter` - (Optional) The datacenter to use. Defaults to that of the agent.
|
||||
* `token` - (Optional) The ACL token to use by default when making requests to the agent.
|
||||
* `ca_file` - (Optional) A path to a PEM-encoded certificate authority used to verify the remote agent's certificate.
|
||||
* `cert_file` - (Optional) A path to a PEM-encoded certificate provided to the remote agent; requires use of `key_file`.
|
||||
* `key_file`- (Optional) A path to a PEM-encoded private key, required if `cert_file` is specified.
|
||||
|
||||
|
|
Loading…
Reference in New Issue