Documentation ¶
Overview ¶
Package runtimevar provides an easy and portable way to watch runtime configuration variables.
It provides a blocking method that returns a Snapshot of the variable value whenever a change is detected.
Subpackages contain distinct implementations of runtimevar for various providers, including Cloud and on-premise solutions. For example, "etcdvar" supports variables stored in etcd. Your application should import one of these provider-specific subpackages and use its exported function(s) to create a *Variable; do not use the New function in this package. For example:
var v *runtimevar.Variable var err error v, err = etcdvar.New("my variable", etcdClient, runtimevar.JSONDecode, nil) ...
Then, write your application code using the *Variable type. You can easily reconfigure your initialization code to choose a different provider. You can develop your application locally using filevar or constantvar, and deploy it to multiple Cloud providers. You may find http://github.com/google/wire useful for managing your initialization code.
Variable implements health.Checker; it reports as healthy when Latest will return a value without blocking.
Alternatively, you can construct a *Variable via a URL and OpenVariable. See https://github.com/eliben/gocdkx/concepts/urls/ for more information.
OpenCensus Integration ¶
OpenCensus supports tracing and metric collection for multiple languages and backend providers. See https://opencensus.io.
This API collects an OpenCensus metric "github.com/eliben/gocdkx/runtimevar/value_changes", a count of the number of times all variables have changed values, by provider.
To enable metric collection in your application, see "Exporting stats" at https://opencensus.io/quickstart/go/metrics.
Index ¶
- Variables
- func BytesDecode(ctx context.Context, b []byte, obj interface{}) error
- func GobDecode(ctx context.Context, data []byte, obj interface{}) error
- func JSONDecode(ctx context.Context, data []byte, obj interface{}) error
- func StringDecode(ctx context.Context, b []byte, obj interface{}) error
- type Decode
- type Decoder
- type Snapshot
- type URLMux
- func (mux *URLMux) OpenVariable(ctx context.Context, urlstr string) (*Variable, error)
- func (mux *URLMux) OpenVariableURL(ctx context.Context, u *url.URL) (*Variable, error)
- func (mux *URLMux) RegisterVariable(scheme string, opener VariableURLOpener)
- func (mux *URLMux) ValidVariableScheme(scheme string) bool
- func (mux *URLMux) VariableSchemes() []string
- type Variable
- type VariableURLOpener
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // StringDecoder decodes into strings. StringDecoder = NewDecoder("", StringDecode) // BytesDecoder copies the slice of bytes. BytesDecoder = NewDecoder([]byte{}, BytesDecode) )
var ErrClosed = gcerr.Newf(gcerr.FailedPrecondition, nil, "Variable has been Closed")
ErrClosed is returned from Watch when the Variable has been Closed.
var New = newVar
New is intended for use by provider implementations.
var ( // OpenCensusViews are predefined views for OpenCensus metrics. OpenCensusViews = []*view.View{ { Name: pkgName + "/value_changes", Measure: changeMeasure, Description: "Count of variable value changes by provider.", TagKeys: []tag.Key{oc.ProviderKey}, Aggregation: view.Count(), }, } )
Functions ¶
func BytesDecode ¶
BytesDecode copies the slice of bytes b into obj.
func GobDecode ¶
GobDecode can be passed to NewDecoder when decoding gobs (https://golang.org/pkg/encoding/gob/).
func JSONDecode ¶
JSONDecode can be passed to NewDecoder when decoding JSON (https://golang.org/pkg/encoding/json/).
Types ¶
type Decode ¶
Decode is a function type for unmarshaling/decoding a slice of bytes into an arbitrary type. Decode functions are used when creating a Decoder via NewDecoder. This package provides common Decode functions including GobDecode and JSONDecode.
func DecryptDecode ¶
DecryptDecode returns a decode function that can be passed to NewDecoder when decoding an encrypted message (https://godoc.org/github.com/eliben/gocdkx/secrets).
post defaults to BytesDecode. An optional decoder can be passed in to do further decode operation based on the decrypted message.
type Decoder ¶
type Decoder struct {
// contains filtered or unexported fields
}
Decoder decodes a slice of bytes into a particular Go object.
This package provides some common Decoders that you can use directly, including StringDecoder and BytesDecoder. You can also NewDecoder to construct other Decoders.
func DecoderByName ¶
DecoderByName returns a *Decoder based on decoderName.
It is intended to be used by URL openers in driver packages.
Supported values include:
- empty string: Returns the default from the URLOpener.Decoder, or BytesDecoder if URLOpener.Decoder is nil (which is true if you're using the default URLOpener).
- "bytes": Returns a BytesDecoder; Snapshot.Value will be of type []byte.
- "jsonmap": Returns a JSON decoder for a map[string]interface{}; Snapshot.Value will be of type *map[string]interface{}.
- "string": Returns StringDecoder; Snapshot.Value will be of type string.
It also supports using "decrypt+<decoderName>" (or "decrypt" for default decoder) to decrypt the data before decoding. It uses the secrets package to open a keeper by the URL string stored in a environment variable "RUNTIMEVAR_KEEPER_URL". See https://godoc.org/github.com/eliben/gocdkx/secrets#OpenKeeper for more details.
func NewDecoder ¶
NewDecoder returns a Decoder that uses fn to decode a slice of bytes into an object of type obj.
This package provides some common Decode functions, including JSONDecode and GobDecode, which can be passed to this function to create Decoders for JSON and gob values.
type Snapshot ¶
type Snapshot struct { // Value contains the value of the variable. // The type for Value depends on the provider; for most providers, it depends // on the decoder used when creating Variable. Value interface{} // UpdateTime is the time when the last change was detected. UpdateTime time.Time // contains filtered or unexported fields }
Snapshot contains a snapshot of a variable's value and metadata about it. It is intended to be read-only for users.
func (*Snapshot) As ¶
As converts i to provider-specific types. See https://godoc.org/github.com/eliben/gocdkx#hdr-As for background information, the "As" examples in this package for examples, and the provider-specific package documentation for the specific types supported for that provider.
Example ¶
package main import ( "context" "fmt" "log" "github.com/eliben/gocdkx/runtimevar" _ "github.com/eliben/gocdkx/runtimevar/gcpruntimeconfig" runtimeconfig "google.golang.org/genproto/googleapis/cloud/runtimeconfig/v1beta1" ) func main() { // This example is specific to the gcpruntimeconfig implementation; it // demonstrates access to the underlying // google.golang.org/genproto/googleapis/cloud/runtimeconfig.Variable type. // The types exposed for As by gcpruntimeconfig are documented in // https://godoc.org/github.com/eliben/gocdkx/runtimevar/gcpruntimeconfig#hdr-As ctx := context.Background() const url = "gcpruntimeconfig://proj/config/key" v, err := runtimevar.OpenVariable(ctx, url) if err != nil { log.Fatal(err) } s, err := v.Latest(ctx) if err != nil { log.Fatal(err) } var rcv *runtimeconfig.Variable if s.As(&rcv) { fmt.Println(rcv.UpdateTime) } }
Output:
type URLMux ¶
type URLMux struct {
// contains filtered or unexported fields
}
URLMux is a URL opener multiplexer. It matches the scheme of the URLs against a set of registered schemes and calls the opener that matches the URL's scheme. See https://github.com/eliben/gocdkx/concepts/urls/ for more information.
The zero value is a multiplexer with no registered schemes.
func DefaultURLMux ¶
func DefaultURLMux() *URLMux
DefaultURLMux returns the URLMux used by OpenVariable.
Driver packages can use this to register their VariableURLOpener on the mux.
func (*URLMux) OpenVariable ¶
OpenVariable calls OpenVariableURL with the URL parsed from urlstr. OpenVariable is safe to call from multiple goroutines.
func (*URLMux) OpenVariableURL ¶
OpenVariableURL dispatches the URL to the opener that is registered with the URL's scheme. OpenVariableURL is safe to call from multiple goroutines.
func (*URLMux) RegisterVariable ¶
func (mux *URLMux) RegisterVariable(scheme string, opener VariableURLOpener)
RegisterVariable registers the opener with the given scheme. If an opener already exists for the scheme, RegisterVariable panics.
func (*URLMux) ValidVariableScheme ¶
ValidVariableScheme returns true iff scheme has been registered for Variables.
func (*URLMux) VariableSchemes ¶
VariableSchemes returns a sorted slice of the registered Variable schemes.
type Variable ¶
type Variable struct {
// contains filtered or unexported fields
}
Variable provides an easy and portable way to watch runtime configuration variables. To create a Variable, use constructors found in provider-specific subpackages.
func OpenVariable ¶
OpenVariable opens the variable identified by the URL given. See the URLOpener documentation in provider-specific subpackages for details on supported URL formats, and https://github.com/eliben/gocdkx/concepts/urls for more information.
Example ¶
package main import ( "context" "fmt" "log" "github.com/eliben/gocdkx/runtimevar" _ "github.com/eliben/gocdkx/runtimevar/constantvar" ) func main() { // Connect to a Variable using a URL. // This example uses "constantvar", an in-memory implementation. // We need to add a blank import line to register the constantvar provider's // URLOpener, which implements runtimevar.VariableURLOpener: // import _ "github.com/eliben/gocdkx/runtimevar/constantvar" // constantvar registers for the "constant" scheme. // All runtimevar.OpenVariable URLs also work with "runtimevar+" or "runtimevar+variable+" prefixes, // e.g., "runtimevar+constant://..." or "runtimevar+variable+constant://...". ctx := context.Background() v, err := runtimevar.OpenVariable(ctx, "constant://?val=hello+world&decoder=string") if err != nil { log.Fatal(err) } // Now we can use the Variable as normal. snapshot, err := v.Latest(ctx) if err != nil { log.Fatal(err) } // It's safe to cast the Value to string since we used the string decoder. fmt.Printf("%s\n", snapshot.Value.(string)) }
Output: hello world
func (*Variable) CheckHealth ¶
CheckHealth returns an error unless Latest will return a good value without blocking.
func (*Variable) ErrorAs ¶
ErrorAs converts err to provider-specific types. ErrorAs panics if i is nil or not a pointer. ErrorAs returns false if err == nil. See https://godoc.org/github.com/eliben/gocdkx#hdr-As for background information.
Example ¶
package main import ( "context" "fmt" "log" "github.com/eliben/gocdkx/runtimevar" _ "github.com/eliben/gocdkx/runtimevar/gcpruntimeconfig" "google.golang.org/grpc/status" ) func main() { // This example is specific to the gcpruntimeconfig implementation; it // demonstrates access to the underlying google.golang.org/grpc/status.Status // type. // The types exposed for As by gcpruntimeconfig are documented in // https://godoc.org/github.com/eliben/gocdkx/runtimevar/gcpruntimeconfig#hdr-As ctx := context.Background() const url = "gcpruntimeconfig://proj/wrongconfig/key" v, err := runtimevar.OpenVariable(ctx, url) if err != nil { log.Fatal(err) } _, err = v.Watch(ctx) if err != nil { var s *status.Status if v.ErrorAs(err, &s) { fmt.Println(s.Code()) } } }
Output:
func (*Variable) Latest ¶
Latest is intended to be called per request, with the request context. It returns the latest good Snapshot of the variable value, blocking if no good value has ever been received. If ctx is Done, it returns the latest error indicating why no good value is available (not the ctx.Err()). You can pass an already-Done ctx to make Latest not block.
Latest returns ErrClosed if the Variable has been closed.
Example (JsonVariable) ¶
package main import ( "context" "fmt" "log" "github.com/eliben/gocdkx/runtimevar" "github.com/eliben/gocdkx/runtimevar/constantvar" _ "github.com/eliben/gocdkx/runtimevar/gcpruntimeconfig" ) func main() { // DBConfig is the sample config struct we're going to parse our JSON into. type DBConfig struct { Host string Port int Username string } // Here's our sample JSON config. const jsonConfig = `{"Host": "github.com/eliben/gocdkx", "Port": 8080, "Username": "testuser"}` // We need a Decoder that decodes raw bytes into our config. decoder := runtimevar.NewDecoder(DBConfig{}, runtimevar.JSONDecode) // Next, a construct a *Variable using a constructor from one of the // runtimevar subpackages. This example uses constantvar. v := constantvar.NewBytes([]byte(jsonConfig), decoder) defer v.Close() // Call Latest to retrieve the value. snapshot, err := v.Latest(context.Background()) if err != nil { log.Fatalf("Error in retrieving variable: %v", err) } // snapshot.Value will be of type DBConfig. fmt.Printf("Config: %+v\n", snapshot.Value.(DBConfig)) }
Output: Config: {Host:github.com/eliben/gocdkx Port:8080 Username:testuser}
Example (StringVariable) ¶
package main import ( "context" "fmt" "log" "github.com/eliben/gocdkx/runtimevar" "github.com/eliben/gocdkx/runtimevar/constantvar" _ "github.com/eliben/gocdkx/runtimevar/gcpruntimeconfig" ) func main() { // Construct a *Variable using a constructor from one of the // runtimevar subpackages. This example uses constantvar. // The variable value is of type string, so we use StringDecoder. v := constantvar.NewBytes([]byte("hello world"), runtimevar.StringDecoder) defer v.Close() // Call Latest to retrieve the value. snapshot, err := v.Latest(context.Background()) if err != nil { log.Fatalf("Error in retrieving variable: %v", err) } // snapshot.Value will be of type string. fmt.Printf("%q\n", snapshot.Value.(string)) }
Output: "hello world"
func (*Variable) Watch ¶
Watch returns when there is a new Snapshot of the current value of the variable.
The first call to Watch will block while reading the variable from the provider, and will return the resulting Snapshot or error. If an error is returned, the returned Snapshot is a zero value and should be ignored. Subsequent calls will block until the variable's value changes or a different error occurs.
Watch returns an ErrClosed error if the Variable has been closed.
Watch should not be called on the same variable from multiple goroutines concurrently. The typical use case is to call it in a single goroutine in a loop.
If the variable does not exist, Watch returns an error for which gcerrors.Code will return gcerrors.NotFound.
Alternatively, use Latest to retrieve the latest good value.
Example ¶
package main import ( "context" "log" "github.com/eliben/gocdkx/runtimevar" "github.com/eliben/gocdkx/runtimevar/constantvar" _ "github.com/eliben/gocdkx/runtimevar/gcpruntimeconfig" ) func main() { // Construct a *Variable using a constructor from one of the // runtimevar subpackages. This example uses constantvar. // The variable value is of type string, so we use StringDecoder. v := constantvar.NewBytes([]byte("hello world"), runtimevar.StringDecoder) defer v.Close() // Call Watch in a loop from a background goroutine to see all changes, // including errors. // // You can use this for logging, or to trigger behaviors when the // config changes. // // Note that Latest always returns the latest "good" config, so seeing // an error from Watch doesn't mean that Latest will return one. go func() { for { snapshot, err := v.Watch(context.Background()) if err == runtimevar.ErrClosed { // v has been closed; exit. return } if err == nil { // Casting to a string here because we used StringDecoder. log.Printf("New config: %v", snapshot.Value.(string)) } else { log.Printf("Error loading config: %v", err) // Even though there's been an error loading the config, // v.Latest will continue to return the latest "good" value. } } }() }
Output:
type VariableURLOpener ¶
type VariableURLOpener interface {
OpenVariableURL(ctx context.Context, u *url.URL) (*Variable, error)
}
VariableURLOpener represents types than can open Variables based on a URL. The opener must not modify the URL argument. OpenVariableURL must be safe to call from multiple goroutines.
This interface is generally implemented by types in driver packages.
Directories ¶
Path | Synopsis |
---|---|
Package awsparamstore provides a runtimevar implementation with variables read from AWS Systems Manager Parameter Store (https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-paramstore.html) Use OpenVariable to construct a *runtimevar.Variable.
|
Package awsparamstore provides a runtimevar implementation with variables read from AWS Systems Manager Parameter Store (https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-paramstore.html) Use OpenVariable to construct a *runtimevar.Variable. |
Package blobvar provides a runtimevar implementation with variables read from a blob.Bucket.
|
Package blobvar provides a runtimevar implementation with variables read from a blob.Bucket. |
Package constantvar provides a runtimevar implementation with Variables that never change.
|
Package constantvar provides a runtimevar implementation with Variables that never change. |
Package driver provides the interface for providers of runtimevar.
|
Package driver provides the interface for providers of runtimevar. |
Package drivertest provides a conformance test for implementations of runtimevar.
|
Package drivertest provides a conformance test for implementations of runtimevar. |
Package etcdvar provides a runtimevar implementation with variables backed by etcd.
|
Package etcdvar provides a runtimevar implementation with variables backed by etcd. |
Package filevar provides a runtimevar implementation with variables backed by the filesystem.
|
Package filevar provides a runtimevar implementation with variables backed by the filesystem. |
Package gcpruntimeconfig provides a runtimevar implementation with variables read from GCP Cloud Runtime Configurator (https://cloud.google.com/deployment-manager/runtime-configurator).
|
Package gcpruntimeconfig provides a runtimevar implementation with variables read from GCP Cloud Runtime Configurator (https://cloud.google.com/deployment-manager/runtime-configurator). |
Package httpvar provides a runtimevar implementation with variables backed by http endpoint.
|
Package httpvar provides a runtimevar implementation with variables backed by http endpoint. |