From c60b7d45ed8c4a5120c29239d8f29fc4c1822845 Mon Sep 17 00:00:00 2001 From: Preetha Appan Date: Mon, 13 Nov 2017 14:58:36 -0600 Subject: [PATCH] Vendor update to consul template 0.19.4 --- .../hashicorp/consul-template/config/retry.go | 15 +++-- .../consul-template/config/template.go | 30 +++++---- .../dependency/catalog_node.go | 2 +- .../dependency/catalog_nodes.go | 6 +- .../dependency/catalog_service.go | 2 +- .../consul-template/dependency/client_set.go | 2 +- .../consul-template/dependency/dependency.go | 15 +++-- .../dependency/health_service.go | 7 +- .../consul-template/manager/dedup.go | 17 ++++- .../consul-template/manager/renderer.go | 65 +++++++++++++++---- .../consul-template/manager/runner.go | 19 +++--- .../consul-template/template/funcs.go | 19 ++++++ .../consul-template/template/template.go | 1 + .../consul-template/version/version.go | 12 ++++ vendor/vendor.json | 42 +++++++----- 15 files changed, 184 insertions(+), 70 deletions(-) create mode 100644 vendor/github.com/hashicorp/consul-template/version/version.go diff --git a/vendor/github.com/hashicorp/consul-template/config/retry.go b/vendor/github.com/hashicorp/consul-template/config/retry.go index 8215e7ee2..cb239c3ad 100644 --- a/vendor/github.com/hashicorp/consul-template/config/retry.go +++ b/vendor/github.com/hashicorp/consul-template/config/retry.go @@ -114,14 +114,19 @@ func (c *RetryConfig) RetryFunc() RetryFunc { return false, 0 } - base := math.Pow(2, float64(retry)) - sleep := time.Duration(base) * TimeDurationVal(c.Backoff) - + baseSleep := TimeDurationVal(c.Backoff) maxSleep := TimeDurationVal(c.MaxBackoff) - if maxSleep > 0 && maxSleep < sleep { - return true, maxSleep + + if maxSleep > 0 { + attemptsTillMaxBackoff := int(math.Log2(maxSleep.Seconds() / baseSleep.Seconds())) + if retry > attemptsTillMaxBackoff { + return true, maxSleep + } } + base := math.Pow(2, float64(retry)) + sleep := time.Duration(base) * baseSleep + return true, sleep } } diff --git a/vendor/github.com/hashicorp/consul-template/config/template.go b/vendor/github.com/hashicorp/consul-template/config/template.go index 982162d08..63eb99c5f 100644 --- a/vendor/github.com/hashicorp/consul-template/config/template.go +++ b/vendor/github.com/hashicorp/consul-template/config/template.go @@ -10,11 +10,6 @@ import ( ) const ( - // DefaultTemplateFilePerms are the default file permissions for templates - // rendered onto disk when a specific file permission has not already been - // specified. - DefaultTemplateFilePerms = 0644 - // DefaultTemplateCommandTimeout is the amount of time to wait for a command // to return. DefaultTemplateCommandTimeout = 30 * time.Second @@ -25,10 +20,6 @@ var ( // are empty. ErrTemplateStringEmpty = errors.New("template: cannot be empty") - // ErrTemplateInvalidFormat is the error returned with the template is not - // a valid format. - ErrTemplateInvalidFormat = errors.New("template: invalid format") - // configTemplateRe is the pattern to split the config template syntax. configTemplateRe = regexp.MustCompile("([a-zA-Z]:)?([^:]+)") ) @@ -52,6 +43,10 @@ type TemplateConfig struct { // must be specified, but not both. Contents *string `mapstructure:"contents"` + // CreateDestDirs tells Consul Template to create the parent directories of + // the destination path if they do not exist. The default value is true. + CreateDestDirs *bool `mapstructure:"create_dest_dirs"` + // Destination is the location on disk where the template should be rendered. // This is required unless running in debug/dry mode. Destination *string `mapstructure:"destination"` @@ -107,6 +102,8 @@ func (c *TemplateConfig) Copy() *TemplateConfig { o.Contents = c.Contents + o.CreateDestDirs = c.CreateDestDirs + o.Destination = c.Destination o.ErrMissingKey = c.ErrMissingKey @@ -163,6 +160,10 @@ func (c *TemplateConfig) Merge(o *TemplateConfig) *TemplateConfig { r.Contents = o.Contents } + if o.CreateDestDirs != nil { + r.CreateDestDirs = o.CreateDestDirs + } + if o.Destination != nil { r.Destination = o.Destination } @@ -217,6 +218,10 @@ func (c *TemplateConfig) Finalize() { c.Contents = String("") } + if c.CreateDestDirs == nil { + c.CreateDestDirs = Bool(true) + } + if c.Destination == nil { c.Destination = String("") } @@ -239,7 +244,7 @@ func (c *TemplateConfig) Finalize() { c.Exec.Finalize() if c.Perms == nil { - c.Perms = FileMode(DefaultTemplateFilePerms) + c.Perms = FileMode(0) } if c.Source == nil { @@ -271,6 +276,7 @@ func (c *TemplateConfig) GoString() string { "Command:%s, "+ "CommandTimeout:%s, "+ "Contents:%s, "+ + "CreateDestDirs:%s, "+ "Destination:%s, "+ "ErrMissingKey:%s, "+ "Exec:%#v, "+ @@ -284,6 +290,7 @@ func (c *TemplateConfig) GoString() string { StringGoString(c.Command), TimeDurationGoString(c.CommandTimeout), StringGoString(c.Contents), + BoolGoString(c.CreateDestDirs), StringGoString(c.Destination), BoolGoString(c.ErrMissingKey), c.Exec, @@ -399,7 +406,8 @@ func ParseTemplateConfig(s string) (*TemplateConfig, error) { case 3: source, destination, command = parts[0], parts[1], parts[2] default: - return nil, ErrTemplateInvalidFormat + source, destination = parts[0], parts[1] + command = strings.Join(parts[2:], ":") } var sourcePtr, destinationPtr, commandPtr *string diff --git a/vendor/github.com/hashicorp/consul-template/dependency/catalog_node.go b/vendor/github.com/hashicorp/consul-template/dependency/catalog_node.go index 115837bad..d8fb1f665 100644 --- a/vendor/github.com/hashicorp/consul-template/dependency/catalog_node.go +++ b/vendor/github.com/hashicorp/consul-template/dependency/catalog_node.go @@ -16,7 +16,7 @@ var ( _ Dependency = (*CatalogNodeQuery)(nil) // CatalogNodeQueryRe is the regular expression to use. - CatalogNodeQueryRe = regexp.MustCompile(`\A` + nameRe + dcRe + `\z`) + CatalogNodeQueryRe = regexp.MustCompile(`\A` + nodeNameRe + dcRe + `\z`) ) func init() { diff --git a/vendor/github.com/hashicorp/consul-template/dependency/catalog_nodes.go b/vendor/github.com/hashicorp/consul-template/dependency/catalog_nodes.go index 845aae929..d570cf0fa 100644 --- a/vendor/github.com/hashicorp/consul-template/dependency/catalog_nodes.go +++ b/vendor/github.com/hashicorp/consul-template/dependency/catalog_nodes.go @@ -92,7 +92,11 @@ func (d *CatalogNodesQuery) Fetch(clients *ClientSet, opts *QueryOptions) (inter Meta: node.Meta, }) } - sort.Stable(ByNode(nodes)) + + // Sort unless the user explicitly asked for nearness + if d.near == "" { + sort.Stable(ByNode(nodes)) + } rm := &ResponseMetadata{ LastIndex: qm.LastIndex, diff --git a/vendor/github.com/hashicorp/consul-template/dependency/catalog_service.go b/vendor/github.com/hashicorp/consul-template/dependency/catalog_service.go index e12739bc6..a56516915 100644 --- a/vendor/github.com/hashicorp/consul-template/dependency/catalog_service.go +++ b/vendor/github.com/hashicorp/consul-template/dependency/catalog_service.go @@ -15,7 +15,7 @@ var ( _ Dependency = (*CatalogServiceQuery)(nil) // CatalogServiceQueryRe is the regular expression to use. - CatalogServiceQueryRe = regexp.MustCompile(`\A` + tagRe + nameRe + dcRe + nearRe + `\z`) + CatalogServiceQueryRe = regexp.MustCompile(`\A` + tagRe + serviceNameRe + dcRe + nearRe + `\z`) ) func init() { diff --git a/vendor/github.com/hashicorp/consul-template/dependency/client_set.go b/vendor/github.com/hashicorp/consul-template/dependency/client_set.go index 647c68b76..c6cbbb7e9 100644 --- a/vendor/github.com/hashicorp/consul-template/dependency/client_set.go +++ b/vendor/github.com/hashicorp/consul-template/dependency/client_set.go @@ -310,7 +310,7 @@ func (c *ClientSet) Consul() *consulapi.Client { return c.consul.client } -// Vault returns the Consul client for this set. +// Vault returns the Vault client for this set. func (c *ClientSet) Vault() *vaultapi.Client { c.RLock() defer c.RUnlock() diff --git a/vendor/github.com/hashicorp/consul-template/dependency/dependency.go b/vendor/github.com/hashicorp/consul-template/dependency/dependency.go index 9cac1f26f..04c16b1a1 100644 --- a/vendor/github.com/hashicorp/consul-template/dependency/dependency.go +++ b/vendor/github.com/hashicorp/consul-template/dependency/dependency.go @@ -11,13 +11,14 @@ import ( ) const ( - dcRe = `(@(?P[[:word:]\.\-\_]+))?` - keyRe = `/?(?P[^@]+)` - filterRe = `(\|(?P[[:word:]\,]+))?` - nameRe = `(?P[[:word:]\-\_]+)` - nearRe = `(~(?P[[:word:]\.\-\_]+))?` - prefixRe = `/?(?P[^@]+)` - tagRe = `((?P[[:word:]\.\-\_]+)\.)?` + dcRe = `(@(?P[[:word:]\.\-\_]+))?` + keyRe = `/?(?P[^@]+)` + filterRe = `(\|(?P[[:word:]\,]+))?` + serviceNameRe = `(?P[[:word:]\-\_]+)` + nodeNameRe = `(?P[[:word:]\.\-\_]+)` + nearRe = `(~(?P[[:word:]\.\-\_]+))?` + prefixRe = `/?(?P[^@]+)` + tagRe = `((?P[[:word:]\.\-\_]+)\.)?` ) type Type int diff --git a/vendor/github.com/hashicorp/consul-template/dependency/health_service.go b/vendor/github.com/hashicorp/consul-template/dependency/health_service.go index 8edcb8154..50112d7d7 100644 --- a/vendor/github.com/hashicorp/consul-template/dependency/health_service.go +++ b/vendor/github.com/hashicorp/consul-template/dependency/health_service.go @@ -29,7 +29,7 @@ var ( _ Dependency = (*HealthServiceQuery)(nil) // HealthServiceQueryRe is the regular expression to use. - HealthServiceQueryRe = regexp.MustCompile(`\A` + tagRe + nameRe + dcRe + nearRe + filterRe + `\z`) + HealthServiceQueryRe = regexp.MustCompile(`\A` + tagRe + serviceNameRe + dcRe + nearRe + filterRe + `\z`) ) func init() { @@ -174,7 +174,10 @@ func (d *HealthServiceQuery) Fetch(clients *ClientSet, opts *QueryOptions) (inte log.Printf("[TRACE] %s: returned %d results after filtering", d, len(list)) - sort.Stable(ByNodeThenID(list)) + // Sort unless the user explicitly asked for nearness + if d.near == "" { + sort.Stable(ByNodeThenID(list)) + } rm := &ResponseMetadata{ LastIndex: qm.LastIndex, diff --git a/vendor/github.com/hashicorp/consul-template/manager/dedup.go b/vendor/github.com/hashicorp/consul-template/manager/dedup.go index 816fe14be..7b085688c 100644 --- a/vendor/github.com/hashicorp/consul-template/manager/dedup.go +++ b/vendor/github.com/hashicorp/consul-template/manager/dedup.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/consul-template/config" dep "github.com/hashicorp/consul-template/dependency" "github.com/hashicorp/consul-template/template" + "github.com/hashicorp/consul-template/version" consulapi "github.com/hashicorp/consul/api" ) @@ -33,8 +34,14 @@ const ( templateDataFlag = 0x22b9a127a2c03520 ) -// templateData is GOB encoded share the depdency values +// templateData is GOB encoded share the dependency values type templateData struct { + // Version is the version of Consul Template which created this template data. + // This is important because users may be running multiple versions of CT + // with the same templates. This provides a nicer upgrade path. + Version string + + // Data is the actual template data. Data map[string]interface{} } @@ -197,7 +204,8 @@ func (d *DedupManager) UpdateDeps(t *template.Template, deps []dep.Dependency) e // Package up the dependency data td := templateData{ - Data: make(map[string]interface{}), + Version: version.Version, + Data: make(map[string]interface{}), } for _, dp := range deps { // Skip any dependencies that can't be shared @@ -409,6 +417,11 @@ func (d *DedupManager) parseData(path string, raw []byte) { path, err) return } + if td.Version != version.Version { + log.Printf("[WARN] (dedup) created with different version (%s vs %s)", + td.Version, version.Version) + return + } log.Printf("[INFO] (dedup) loading %d dependencies from '%s'", len(td.Data), path) diff --git a/vendor/github.com/hashicorp/consul-template/manager/renderer.go b/vendor/github.com/hashicorp/consul-template/manager/renderer.go index 8ec1fc34a..ce1778212 100644 --- a/vendor/github.com/hashicorp/consul-template/manager/renderer.go +++ b/vendor/github.com/hashicorp/consul-template/manager/renderer.go @@ -11,14 +11,30 @@ import ( "github.com/pkg/errors" ) +const ( + // DefaultFilePerms are the default file permissions for files rendered onto + // disk when a specific file permission has not already been specified. + DefaultFilePerms = 0644 +) + +var ( + // ErrNoParentDir is the error returned with the parent directory is missing + // and the user disabled it. + ErrNoParentDir = errors.New("parent directory is missing") + + // ErrMissingDest is the error returned with the destination is empty. + ErrMissingDest = errors.New("missing destination") +) + // RenderInput is used as input to the render function. type RenderInput struct { - Backup bool - Contents []byte - Dry bool - DryStream io.Writer - Path string - Perms os.FileMode + Backup bool + Contents []byte + CreateDestDirs bool + Dry bool + DryStream io.Writer + Path string + Perms os.FileMode } // RenderResult is returned and stored. It contains the status of the render @@ -58,7 +74,7 @@ func Render(i *RenderInput) (*RenderResult, error) { if i.Dry { fmt.Fprintf(i.DryStream, "> %s\n%s", i.Path, i.Contents) } else { - if err := AtomicWrite(i.Path, i.Contents, i.Perms, i.Backup); err != nil { + if err := AtomicWrite(i.Path, i.CreateDestDirs, i.Contents, i.Perms, i.Backup); err != nil { return nil, errors.Wrap(err, "failed writing file") } } @@ -85,15 +101,19 @@ func Render(i *RenderInput) (*RenderResult, error) { // // If no errors occur, the Tempfile is "renamed" (moved) to the destination // path. -func AtomicWrite(path string, contents []byte, perms os.FileMode, backup bool) error { +func AtomicWrite(path string, createDestDirs bool, contents []byte, perms os.FileMode, backup bool) error { if path == "" { - return fmt.Errorf("missing destination") + return ErrMissingDest } parent := filepath.Dir(path) if _, err := os.Stat(parent); os.IsNotExist(err) { - if err := os.MkdirAll(parent, 0755); err != nil { - return err + if createDestDirs { + if err := os.MkdirAll(parent, 0755); err != nil { + return err + } + } else { + return ErrNoParentDir } } @@ -115,6 +135,22 @@ func AtomicWrite(path string, contents []byte, perms os.FileMode, backup bool) e return err } + // If the user did not explicitly set permissions, attempt to lookup the + // current permissions on the file. If the file does not exist, fall back to + // the default. Otherwise, inherit the current permissions. + if perms == 0 { + currentInfo, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + perms = DefaultFilePerms + } else { + return err + } + } else { + perms = currentInfo.Mode() + } + } + if err := os.Chmod(f.Name(), perms); err != nil { return err } @@ -158,5 +194,10 @@ func copyFile(src, dst string) error { d.Close() return err } - return d.Close() + if err := d.Close(); err != nil { + return err + } + + // io.Copy can restrict file permissions based on umask. + return os.Chmod(dst, stat.Mode()) } diff --git a/vendor/github.com/hashicorp/consul-template/manager/runner.go b/vendor/github.com/hashicorp/consul-template/manager/runner.go index f22f38f5e..942e1861e 100644 --- a/vendor/github.com/hashicorp/consul-template/manager/runner.go +++ b/vendor/github.com/hashicorp/consul-template/manager/runner.go @@ -43,7 +43,7 @@ type Runner struct { // outStream and errStream are the io.Writer streams where the runner will // write information. These can be modified by calling SetOutStream and - // SetErrStream accordingly. + // SetErrStream accordingly. // inStream is the ioReader where the runner will read information. outStream, errStream io.Writer @@ -740,12 +740,13 @@ func (r *Runner) runTemplate(tmpl *template.Template, runCtx *templateRunCtx) (* // Render the template, taking dry mode into account result, err := Render(&RenderInput{ - Backup: config.BoolVal(templateConfig.Backup), - Contents: result.Output, - Dry: r.dry, - DryStream: r.outStream, - Path: config.StringVal(templateConfig.Destination), - Perms: config.FileModeVal(templateConfig.Perms), + Backup: config.BoolVal(templateConfig.Backup), + Contents: result.Output, + CreateDestDirs: config.BoolVal(templateConfig.CreateDestDirs), + Dry: r.dry, + DryStream: r.outStream, + Path: config.StringVal(templateConfig.Destination), + Perms: config.FileModeVal(templateConfig.Perms), }) if err != nil { return nil, errors.Wrap(err, "error rendering "+templateConfig.Display()) @@ -1252,14 +1253,14 @@ func newWatcher(c *config.Config, clients *dep.ClientSet, once bool) (*watch.Wat Clients: clients, MaxStale: config.TimeDurationVal(c.MaxStale), Once: once, - RenewVault: config.StringPresent(c.Vault.Token) && config.BoolVal(c.Vault.RenewToken), + RenewVault: clients.Vault().Token() != "" && config.BoolVal(c.Vault.RenewToken), RetryFuncConsul: watch.RetryFunc(c.Consul.Retry.RetryFunc()), // TODO: Add a sane default retry - right now this only affects "local" // dependencies like reading a file from disk. RetryFuncDefault: nil, RetryFuncVault: watch.RetryFunc(c.Vault.Retry.RetryFunc()), VaultGrace: config.TimeDurationVal(c.Vault.Grace), - VaultToken: config.StringVal(c.Vault.Token), + VaultToken: clients.Vault().Token(), }) if err != nil { return nil, errors.Wrap(err, "runner") diff --git a/vendor/github.com/hashicorp/consul-template/template/funcs.go b/vendor/github.com/hashicorp/consul-template/template/funcs.go index 3a73682ec..fa7eeec44 100644 --- a/vendor/github.com/hashicorp/consul-template/template/funcs.go +++ b/vendor/github.com/hashicorp/consul-template/template/funcs.go @@ -655,6 +655,25 @@ func in(l, v interface{}) (bool, error) { return false, nil } +// Indent prefixes each line of a string with the specified number of spaces +func indent(spaces int, s string) (string, error) { + var output, prefix []byte + var sp bool + var size int + prefix = []byte(strings.Repeat(" ", spaces)) + sp = true + for _, c := range []byte(s) { + if sp && c != '\n' { + output = append(output, prefix...) + size += spaces + } + output = append(output, c) + sp = c == '\n' + size += 1 + } + return string(output[:size]), nil +} + // loop accepts varying parameters and differs its behavior. If given one // parameter, loop will return a goroutine that begins at 0 and loops until the // given int, increasing the index by 1 each iteration. If given two parameters, diff --git a/vendor/github.com/hashicorp/consul-template/template/template.go b/vendor/github.com/hashicorp/consul-template/template/template.go index 6f9e50a2c..88150d46c 100644 --- a/vendor/github.com/hashicorp/consul-template/template/template.go +++ b/vendor/github.com/hashicorp/consul-template/template/template.go @@ -233,6 +233,7 @@ func funcMap(i *funcMapInput) template.FuncMap { "executeTemplate": executeTemplateFunc(i.t), "explode": explode, "in": in, + "indent": indent, "loop": loop, "join": join, "trimSpace": trimSpace, diff --git a/vendor/github.com/hashicorp/consul-template/version/version.go b/vendor/github.com/hashicorp/consul-template/version/version.go new file mode 100644 index 000000000..28f4e9e99 --- /dev/null +++ b/vendor/github.com/hashicorp/consul-template/version/version.go @@ -0,0 +1,12 @@ +package version + +import "fmt" + +const Version = "0.19.5.dev" + +var ( + Name string + GitCommit string + + HumanVersion = fmt.Sprintf("%s v%s (%s)", Name, Version, GitCommit) +) diff --git a/vendor/vendor.json b/vendor/vendor.json index 0a5c5cb65..2bde4ecdc 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -709,44 +709,50 @@ { "checksumSHA1": "Nu2j1GusM7ZH0uYrGzqr1K7yH7I=", "path": "github.com/hashicorp/consul-template/child", - "revision": "16b6f8c417d88c88e9b88af6235da2582397c709", - "revisionTime": "2017-10-03T21:31:50Z" + "revision": "26d029ad37335b3827a9fde5569b2c5e10dcac8f", + "revisionTime": "2017-10-31T14:25:17Z" }, { - "checksumSHA1": "nkbwT6jsnaXDiXrJFjCge9W5ULo=", + "checksumSHA1": "qKAxyhYnUpKzZ5KpA6aOiIHHqqg=", "path": "github.com/hashicorp/consul-template/config", - "revision": "16b6f8c417d88c88e9b88af6235da2582397c709", - "revisionTime": "2017-10-03T21:31:50Z" + "revision": "26d029ad37335b3827a9fde5569b2c5e10dcac8f", + "revisionTime": "2017-10-31T14:25:17Z" }, { - "checksumSHA1": "ki5mjKALz3JrAee3mYUNl8pFJnU=", + "checksumSHA1": "gZUb/+jEn+2hdO/lmQSKcYuOB/o=", "path": "github.com/hashicorp/consul-template/dependency", - "revision": "16b6f8c417d88c88e9b88af6235da2582397c709", - "revisionTime": "2017-10-03T21:31:50Z" + "revision": "26d029ad37335b3827a9fde5569b2c5e10dcac8f", + "revisionTime": "2017-10-31T14:25:17Z" }, { - "checksumSHA1": "yXXJXYYs/OPVJW2ErBiQVaLxSt0=", + "checksumSHA1": "JVwx9FW1/nxRvg1lEeydBhaf3No=", "path": "github.com/hashicorp/consul-template/manager", - "revision": "16b6f8c417d88c88e9b88af6235da2582397c709", - "revisionTime": "2017-10-03T21:31:50Z" + "revision": "26d029ad37335b3827a9fde5569b2c5e10dcac8f", + "revisionTime": "2017-10-31T14:25:17Z" }, { "checksumSHA1": "YSEUV/9/k85XciRKu0cngxdjZLE=", "path": "github.com/hashicorp/consul-template/signals", - "revision": "16b6f8c417d88c88e9b88af6235da2582397c709", - "revisionTime": "2017-10-03T21:31:50Z" + "revision": "26d029ad37335b3827a9fde5569b2c5e10dcac8f", + "revisionTime": "2017-10-31T14:25:17Z" }, { - "checksumSHA1": "zSvJlNfZS3fCRlFaZ7r9Q+N17T8=", + "checksumSHA1": "N9qobVzScLbTEnGE7MgFnnTbGBw=", "path": "github.com/hashicorp/consul-template/template", - "revision": "16b6f8c417d88c88e9b88af6235da2582397c709", - "revisionTime": "2017-10-03T21:31:50Z" + "revision": "26d029ad37335b3827a9fde5569b2c5e10dcac8f", + "revisionTime": "2017-10-31T14:25:17Z" + }, + { + "checksumSHA1": "NB5+D4AuCNV9Bsqh3YFdPi4AJ6U=", + "path": "github.com/hashicorp/consul-template/version", + "revision": "26d029ad37335b3827a9fde5569b2c5e10dcac8f", + "revisionTime": "2017-10-31T14:25:17Z" }, { "checksumSHA1": "b4+Y+02pY2Y5620F9ALzKg8Zmdw=", "path": "github.com/hashicorp/consul-template/watch", - "revision": "16b6f8c417d88c88e9b88af6235da2582397c709", - "revisionTime": "2017-10-03T21:31:50Z" + "revision": "26d029ad37335b3827a9fde5569b2c5e10dcac8f", + "revisionTime": "2017-10-31T14:25:17Z" }, { "checksumSHA1": "XLfcIX2qpRr0o26aFMjCOzvw6jo=",