diff --git a/backend/remote-state/consul/backend.go b/backend/remote-state/consul/backend.go index 7193bf8a96..979dc368c0 100644 --- a/backend/remote-state/consul/backend.go +++ b/backend/remote-state/consul/backend.go @@ -53,6 +53,13 @@ func New() backend.Backend { Description: "HTTP Auth in the format of 'username:password'", Default: "", // To prevent input }, + + "gzip": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Description: "Compress the state data using gzip", + Default: false, + }, }, } diff --git a/backend/remote-state/consul/backend_state.go b/backend/remote-state/consul/backend_state.go index 6e6d115f0d..0339dd79ac 100644 --- a/backend/remote-state/consul/backend_state.go +++ b/backend/remote-state/consul/backend_state.go @@ -85,11 +85,15 @@ func (b *Backend) State(name string) (state.State, error) { // Determine the path of the data path := b.path(name) + // Determine whether to gzip or not + gzip := b.configData.Get("gzip").(bool) + // Build the state client stateMgr := &remote.State{ Client: &RemoteClient{ Client: client, Path: path, + GZip: gzip, }, } diff --git a/backend/remote-state/consul/client.go b/backend/remote-state/consul/client.go index 8a26165e85..cd59711631 100644 --- a/backend/remote-state/consul/client.go +++ b/backend/remote-state/consul/client.go @@ -1,6 +1,8 @@ package consul import ( + "bytes" + "compress/gzip" "crypto/md5" "encoding/json" "errors" @@ -22,6 +24,7 @@ const ( type RemoteClient struct { Client *consulapi.Client Path string + GZip bool consulLock *consulapi.Lock lockCh <-chan struct{} @@ -36,18 +39,37 @@ func (c *RemoteClient) Get() (*remote.Payload, error) { 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) return &remote.Payload{ - Data: pair.Value, + Data: payload, MD5: md5[:], }, nil } 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() _, err := kv.Put(&consulapi.KVPair{ Key: c.Path, - Value: data, + Value: payload, }, nil) return err } @@ -177,3 +199,31 @@ func (c *RemoteClient) Unlock(id string) error { 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 +} diff --git a/website/source/docs/backends/types/consul.html.md b/website/source/docs/backends/types/consul.html.md index 59c05724e8..24d45fe143 100644 --- a/website/source/docs/backends/types/consul.html.md +++ b/website/source/docs/backends/types/consul.html.md @@ -53,3 +53,4 @@ The following configuration options / environment variables are supported: * `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 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.