gddo-server/dynconfig: add package
Copied from https://go.googlesource.com/pkgsite/+/cb9361e611fe25baabab637bc260f48b345bd1da/internal/config/dynconfig
with minor modification to remove reference to derrors package.
Change-Id: Ib006861369613183ac29eb1456582e34dbd83b22
Reviewed-on: https://go-review.googlesource.com/c/gddo/+/285834
Trust: Julie Qiu <julie@golang.org>
Run-TryBot: Julie Qiu <julie@golang.org>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/gddo-server/dynconfig/dynconfig.go b/gddo-server/dynconfig/dynconfig.go
new file mode 100644
index 0000000..d75f8bc
--- /dev/null
+++ b/gddo-server/dynconfig/dynconfig.go
@@ -0,0 +1,87 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package dynconfig supports dynamic configuration for pkgsite services.
+// Dynamic configuration is read from a file and can change over the lifetime of
+// the process.
+package dynconfig
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "strings"
+
+ "cloud.google.com/go/storage"
+ "github.com/ghodss/yaml"
+)
+
+// DynamicConfig holds configuration that can change over the lifetime of the
+// process. It is loaded from a GCS file or other external source.
+type DynamicConfig struct {
+ // Fields can be added at any time, but removing or changing a field
+ // requires careful coordination with the config file contents.
+}
+
+// Read reads dynamic configuration from the given location.
+// Location may be of the form gs://bucket/object, denoting a GCS bucket.
+// Otherwise it is interpreted as a filename.
+func Read(ctx context.Context, location string) (_ *DynamicConfig, err error) {
+ defer wrap(&err, "dynconfig.Read(%q)", location)
+
+ log.Printf("reading dynamic config from %s", location)
+ var r io.ReadCloser
+ if strings.HasPrefix(location, "gs://") {
+ parts := strings.SplitN(location[5:], "/", 2)
+ if len(parts) != 2 {
+ return nil, errors.New("bad GCS URL")
+ }
+ bucket := parts[0]
+ object := parts[1]
+ if err != nil {
+ return nil, err
+ }
+ client, err := storage.NewClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ defer client.Close()
+ r, err = client.Bucket(bucket).Object(object).NewReader(ctx)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ r, err = os.Open(location)
+ if err != nil {
+ return nil, err
+ }
+ }
+ defer r.Close()
+ data, err := ioutil.ReadAll(r)
+ if err != nil {
+ return nil, err
+ }
+ return Parse(data)
+}
+
+// Parse parses yamlData as a YAML description of DynamicConfig.
+func Parse(yamlData []byte) (_ *DynamicConfig, err error) {
+ defer wrap(&err, "dynconfig.Parse(data)")
+
+ var dc DynamicConfig
+ if err := yaml.Unmarshal(yamlData, &dc); err != nil {
+ return nil, err
+ }
+ return &dc, nil
+}
+
+func wrap(errp *error, format string, args ...interface{}) {
+ if *errp != nil {
+ *errp = fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), *errp)
+ }
+}
diff --git a/go.mod b/go.mod
index 8b23dfb..d21b016 100644
--- a/go.mod
+++ b/go.mod
@@ -9,6 +9,7 @@
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.4.3-0.20170329110642-4da3e2cfbabc // indirect
github.com/garyburd/redigo v1.1.1-0.20170914051019-70e1b1943d4f
+ github.com/ghodss/yaml v1.0.0
github.com/go-stack/stack v1.6.0 // indirect
github.com/golang/lint v0.0.0-20170918230701-e5d664eb928e
github.com/golang/snappy v0.0.0-20170215233205-553a64147049
diff --git a/go.sum b/go.sum
index 84fd4e3..bf30d80 100644
--- a/go.sum
+++ b/go.sum
@@ -11,6 +11,8 @@
github.com/fsnotify/fsnotify v1.4.3-0.20170329110642-4da3e2cfbabc/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/garyburd/redigo v1.1.1-0.20170914051019-70e1b1943d4f h1:Sk0u0gIncQaQD23zAoAZs2DNi2u2l5UTLi4CmCBL5v8=
github.com/garyburd/redigo v1.1.1-0.20170914051019-70e1b1943d4f/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
+github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-stack/stack v1.6.0 h1:MmJCxYVKTJ0SplGKqFVX3SBnmaUhODHZrrFF6jMbpZk=
github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/golang/lint v0.0.0-20170918230701-e5d664eb928e h1:ior8LN6127GsA53E9mD9nH/oP/LVbJplmLH5V8o+/Uk=