Merge pull request #8748 from christoe/christoe/8491-gzip-consul-remote-state

state/remote: Add gzip support to consul remote state (#8491)
This commit is contained in:
James Bardin 2017-03-14 15:27:46 -04:00 committed by GitHub
commit 1527eae6b7
4 changed files with 64 additions and 2 deletions

View File

@ -53,6 +53,13 @@ func New() backend.Backend {
Description: "HTTP Auth in the format of 'username:password'", Description: "HTTP Auth in the format of 'username:password'",
Default: "", // To prevent input Default: "", // To prevent input
}, },
"gzip": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Description: "Compress the state data using gzip",
Default: false,
},
}, },
} }

View File

@ -85,11 +85,15 @@ func (b *Backend) State(name string) (state.State, error) {
// Determine the path of the data // Determine the path of the data
path := b.path(name) path := b.path(name)
// Determine whether to gzip or not
gzip := b.configData.Get("gzip").(bool)
// Build the state client // Build the state client
stateMgr := &remote.State{ stateMgr := &remote.State{
Client: &RemoteClient{ Client: &RemoteClient{
Client: client, Client: client,
Path: path, Path: path,
GZip: gzip,
}, },
} }

View File

@ -1,6 +1,8 @@
package consul package consul
import ( import (
"bytes"
"compress/gzip"
"crypto/md5" "crypto/md5"
"encoding/json" "encoding/json"
"errors" "errors"
@ -22,6 +24,7 @@ const (
type RemoteClient struct { type RemoteClient struct {
Client *consulapi.Client Client *consulapi.Client
Path string Path string
GZip bool
consulLock *consulapi.Lock consulLock *consulapi.Lock
lockCh <-chan struct{} lockCh <-chan struct{}
@ -36,18 +39,37 @@ func (c *RemoteClient) Get() (*remote.Payload, error) {
return nil, nil return nil, nil
} }
payload := pair.Value
// If the payload starts with 0x1f, it's gzip, not json
if len(pair.Value) >= 1 && pair.Value[0] == '\x1f' {
if data, err := uncompressState(pair.Value); err == nil {
payload = data
} else {
return nil, err
}
}
md5 := md5.Sum(pair.Value) md5 := md5.Sum(pair.Value)
return &remote.Payload{ return &remote.Payload{
Data: pair.Value, Data: payload,
MD5: md5[:], MD5: md5[:],
}, nil }, nil
} }
func (c *RemoteClient) Put(data []byte) error { func (c *RemoteClient) Put(data []byte) error {
payload := data
if c.GZip {
if compressedState, err := compressState(data); err == nil {
payload = compressedState
} else {
return err
}
}
kv := c.Client.KV() kv := c.Client.KV()
_, err := kv.Put(&consulapi.KVPair{ _, err := kv.Put(&consulapi.KVPair{
Key: c.Path, Key: c.Path,
Value: data, Value: payload,
}, nil) }, nil)
return err return err
} }
@ -177,3 +199,31 @@ func (c *RemoteClient) Unlock(id string) error {
return err return err
} }
func compressState(data []byte) ([]byte, error) {
b := new(bytes.Buffer)
gz := gzip.NewWriter(b)
if _, err := gz.Write(data); err != nil {
return nil, err
}
if err := gz.Flush(); err != nil {
return nil, err
}
if err := gz.Close(); err != nil {
return nil, err
}
return b.Bytes(), nil
}
func uncompressState(data []byte) ([]byte, error) {
b := new(bytes.Buffer)
gz, err := gzip.NewReader(bytes.NewReader(data))
if err != nil {
return nil, err
}
b.ReadFrom(gz)
if err := gz.Close(); err != nil {
return nil, err
}
return b.Bytes(), nil
}

View File

@ -53,3 +53,4 @@ The following configuration options / environment variables are supported:
* `datacenter` - (Optional) The datacenter to use. Defaults to that of the agent. * `datacenter` - (Optional) The datacenter to use. Defaults to that of the agent.
* `http_auth` / `CONSUL_HTTP_AUTH` - (Optional) HTTP Basic Authentication credentials to be used when * `http_auth` / `CONSUL_HTTP_AUTH` - (Optional) HTTP Basic Authentication credentials to be used when
communicating with Consul, in the format of either `user` or `user:pass`. communicating with Consul, in the format of either `user` or `user:pass`.
* `gzip` - (Optional) `true` to compress the state data using gzip, or `false` (the default) to leave it uncompressed.