github.com/SaurabhDubey-Groww/go-cloud@v0.0.0-20221124105541-b26c29285fd8/runtimevar/runtimevar.go (about)

     1  // Copyright 2018 The Go Cloud Development Kit Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package runtimevar provides an easy and portable way to watch runtime
    16  // configuration variables. Subpackages contain driver implementations of
    17  // runtimevar for supported services.
    18  //
    19  // See https://gocloud.dev/howto/runtimevar/ for a detailed how-to guide.
    20  //
    21  // # OpenCensus Integration
    22  //
    23  // OpenCensus supports tracing and metric collection for multiple languages and
    24  // backend providers. See https://opencensus.io.
    25  //
    26  // This API collects an OpenCensus metric "gocloud.dev/runtimevar/value_changes",
    27  // a count of the number of times all variables have changed values, by driver.
    28  //
    29  // To enable metric collection in your application, see "Exporting stats" at
    30  // https://opencensus.io/quickstart/go/metrics.
    31  package runtimevar // import "gocloud.dev/runtimevar"
    32  
    33  import (
    34  	"bytes"
    35  	"context"
    36  	"encoding/gob"
    37  	"encoding/json"
    38  	"errors"
    39  	"fmt"
    40  	"net/url"
    41  	"os"
    42  	"reflect"
    43  	"strings"
    44  	"sync"
    45  	"time"
    46  
    47  	"go.opencensus.io/stats"
    48  	"go.opencensus.io/stats/view"
    49  	"go.opencensus.io/tag"
    50  	"gocloud.dev/internal/gcerr"
    51  	"gocloud.dev/internal/oc"
    52  	"gocloud.dev/internal/openurl"
    53  	"gocloud.dev/runtimevar/driver"
    54  	"gocloud.dev/secrets"
    55  )
    56  
    57  // Snapshot contains a snapshot of a variable's value and metadata about it.
    58  // It is intended to be read-only for users.
    59  type Snapshot struct {
    60  	// Value contains the value of the variable.
    61  	// The type for Value depends on the decoder used when creating the Variable.
    62  	Value interface{}
    63  
    64  	// UpdateTime is the time when the last change was detected.
    65  	UpdateTime time.Time
    66  
    67  	asFunc func(interface{}) bool
    68  }
    69  
    70  // As converts i to driver-specific types.
    71  // See https://gocloud.dev/concepts/as/ for background information, the "As"
    72  // examples in this package for examples, and the driver package
    73  // documentation for the specific types supported for that driver.
    74  func (s *Snapshot) As(i interface{}) bool {
    75  	if s.asFunc == nil {
    76  		return false
    77  	}
    78  	return s.asFunc(i)
    79  }
    80  
    81  const pkgName = "gocloud.dev/runtimevar"
    82  
    83  var (
    84  	changeMeasure = stats.Int64(pkgName+"/value_changes", "Count of variable value changes",
    85  		stats.UnitDimensionless)
    86  	// OpenCensusViews are predefined views for OpenCensus metrics.
    87  	OpenCensusViews = []*view.View{
    88  		{
    89  			Name:        pkgName + "/value_changes",
    90  			Measure:     changeMeasure,
    91  			Description: "Count of variable value changes by driver.",
    92  			TagKeys:     []tag.Key{oc.ProviderKey},
    93  			Aggregation: view.Count(),
    94  		},
    95  	}
    96  )
    97  
    98  // Variable provides an easy and portable way to watch runtime configuration
    99  // variables. To create a Variable, use constructors found in driver subpackages.
   100  type Variable struct {
   101  	dw       driver.Watcher
   102  	provider string // for metric collection; refers to driver package name
   103  
   104  	// For cancelling the background goroutine, and noticing when it has exited.
   105  	backgroundCancel context.CancelFunc
   106  	backgroundDone   chan struct{}
   107  
   108  	// haveGoodCh is closed when we get the first good value for the variable.
   109  	haveGoodCh chan struct{}
   110  	// A reference to changed at the last time Watch was called.
   111  	// Not protected by mu because it's only referenced in Watch, which is not
   112  	// supposed to be called from multiple goroutines.
   113  	lastWatch <-chan struct{}
   114  
   115  	mu       sync.RWMutex
   116  	changed  chan struct{} // closed when changing any of the other variables and replaced with a new channel
   117  	last     Snapshot
   118  	lastErr  error
   119  	lastGood Snapshot
   120  }
   121  
   122  // New is intended for use by drivers only. Do not use in application code.
   123  var New = newVar
   124  
   125  // newVar creates a new *Variable based on a specific driver implementation.
   126  func newVar(w driver.Watcher) *Variable {
   127  	ctx, cancel := context.WithCancel(context.Background())
   128  	changed := make(chan struct{})
   129  	v := &Variable{
   130  		dw:               w,
   131  		provider:         oc.ProviderName(w),
   132  		backgroundCancel: cancel,
   133  		backgroundDone:   make(chan struct{}),
   134  		haveGoodCh:       make(chan struct{}),
   135  		changed:          changed,
   136  		lastWatch:        changed,
   137  		lastErr:          gcerr.Newf(gcerr.FailedPrecondition, nil, "no value yet"),
   138  	}
   139  	go v.background(ctx)
   140  	return v
   141  }
   142  
   143  // ErrClosed is returned from Watch when the Variable has been Closed.
   144  var ErrClosed = gcerr.Newf(gcerr.FailedPrecondition, nil, "Variable has been Closed")
   145  
   146  // Watch returns when there is a new Snapshot of the current value of the
   147  // variable.
   148  //
   149  // The first call to Watch will block while reading the variable from the
   150  // driver, and will return the resulting Snapshot or error. If an error is
   151  // returned, the returned Snapshot is a zero value and should be ignored.
   152  // Subsequent calls will block until the variable's value changes or a different
   153  // error occurs.
   154  //
   155  // Watch returns an ErrClosed error if the Variable has been closed.
   156  //
   157  // Watch should not be called on the same variable from multiple goroutines
   158  // concurrently. The typical use case is to call it in a single goroutine in a
   159  // loop.
   160  //
   161  // If the variable does not exist, Watch returns an error for which
   162  // gcerrors.Code will return gcerrors.NotFound.
   163  //
   164  // Alternatively, use Latest to retrieve the latest good value.
   165  func (c *Variable) Watch(ctx context.Context) (Snapshot, error) {
   166  	// Block until there's a change since the last Watch call, signaled
   167  	// by lastWatch being closed by the background goroutine.
   168  	var ctxErr error
   169  	select {
   170  	case <-c.lastWatch:
   171  	case <-ctx.Done():
   172  		ctxErr = ctx.Err()
   173  	}
   174  	c.mu.Lock()
   175  	defer c.mu.Unlock()
   176  	if c.lastErr == ErrClosed {
   177  		return Snapshot{}, ErrClosed
   178  	} else if ctxErr != nil {
   179  		return Snapshot{}, ctxErr
   180  	}
   181  	c.lastWatch = c.changed
   182  	return c.last, c.lastErr
   183  }
   184  
   185  func (c *Variable) background(ctx context.Context) {
   186  	var curState, prevState driver.State
   187  	var wait time.Duration
   188  	for {
   189  		select {
   190  		case <-ctx.Done():
   191  			// We're shutting down; exit the goroutine.
   192  			close(c.backgroundDone)
   193  			return
   194  		case <-time.After(wait):
   195  			// Continue.
   196  		}
   197  
   198  		curState, wait = c.dw.WatchVariable(ctx, prevState)
   199  		if curState == nil {
   200  			// No change.
   201  			continue
   202  		}
   203  
   204  		// There's something new to return!
   205  		prevState = curState
   206  		_ = stats.RecordWithTags(ctx, []tag.Mutator{tag.Upsert(oc.ProviderKey, c.provider)}, changeMeasure.M(1))
   207  		// Error from RecordWithTags is not possible.
   208  
   209  		// Updates under the lock.
   210  		c.mu.Lock()
   211  		if c.lastErr == ErrClosed {
   212  			close(c.backgroundDone)
   213  			c.mu.Unlock()
   214  			return
   215  		}
   216  		if val, err := curState.Value(); err == nil {
   217  			// We got a good value!
   218  			c.last = Snapshot{
   219  				Value:      val,
   220  				UpdateTime: curState.UpdateTime(),
   221  				asFunc:     curState.As,
   222  			}
   223  			c.lastErr = nil
   224  			c.lastGood = c.last
   225  			// Close c.haveGoodCh if it's not already closed.
   226  			select {
   227  			case <-c.haveGoodCh:
   228  			default:
   229  				close(c.haveGoodCh)
   230  			}
   231  		} else {
   232  			// We got an error value.
   233  			c.last = Snapshot{}
   234  			c.lastErr = wrapError(c.dw, err)
   235  		}
   236  		close(c.changed)
   237  		c.changed = make(chan struct{})
   238  		c.mu.Unlock()
   239  	}
   240  }
   241  
   242  func (c *Variable) haveGood() bool {
   243  	select {
   244  	case <-c.haveGoodCh:
   245  		return true
   246  	default:
   247  		return false
   248  	}
   249  }
   250  
   251  // Latest is intended to be called per request, with the request context.
   252  // It returns the latest good Snapshot of the variable value, blocking if no
   253  // good value has ever been received. If ctx is Done, it returns the latest
   254  // error indicating why no good value is available (not the ctx.Err()).
   255  // You can pass an already-Done ctx to make Latest not block.
   256  //
   257  // Latest returns ErrClosed if the Variable has been closed.
   258  func (c *Variable) Latest(ctx context.Context) (Snapshot, error) {
   259  	haveGood := c.haveGood()
   260  	if !haveGood {
   261  		select {
   262  		case <-c.haveGoodCh:
   263  			haveGood = true
   264  		case <-ctx.Done():
   265  			// We don't return ctx.Err().
   266  		}
   267  	}
   268  	c.mu.RLock()
   269  	defer c.mu.RUnlock()
   270  	if haveGood && c.lastErr != ErrClosed {
   271  		return c.lastGood, nil
   272  	}
   273  	return Snapshot{}, c.lastErr
   274  }
   275  
   276  // CheckHealth returns an error unless Latest will return a good value
   277  // without blocking.
   278  func (c *Variable) CheckHealth() error {
   279  	haveGood := c.haveGood()
   280  	c.mu.RLock()
   281  	defer c.mu.RUnlock()
   282  	if haveGood && c.lastErr != ErrClosed {
   283  		return nil
   284  	}
   285  	return c.lastErr
   286  }
   287  
   288  // Close closes the Variable. The Variable is unusable after Close returns.
   289  func (c *Variable) Close() error {
   290  	// Record that we're closing. Subsequent calls to Watch/Latest will return ErrClosed.
   291  	c.mu.Lock()
   292  	if c.lastErr == ErrClosed {
   293  		c.mu.Unlock()
   294  		return ErrClosed
   295  	}
   296  	c.last = Snapshot{}
   297  	c.lastErr = ErrClosed
   298  
   299  	// Close any remaining channels to wake up any callers that are waiting on them.
   300  	close(c.changed)
   301  	// If it's the first good value, close haveGoodCh so that Latest doesn't block.
   302  	select {
   303  	case <-c.haveGoodCh:
   304  	default:
   305  		close(c.haveGoodCh)
   306  	}
   307  	c.mu.Unlock()
   308  
   309  	// Shut down the background goroutine.
   310  	c.backgroundCancel()
   311  	<-c.backgroundDone
   312  
   313  	// Close the driver.
   314  	err := c.dw.Close()
   315  	return wrapError(c.dw, err)
   316  }
   317  
   318  func wrapError(w driver.Watcher, err error) error {
   319  	if err == nil {
   320  		return nil
   321  	}
   322  	if gcerr.DoNotWrap(err) {
   323  		return err
   324  	}
   325  	return gcerr.New(w.ErrorCode(err), err, 2, "runtimevar")
   326  }
   327  
   328  // ErrorAs converts err to driver-specific types.
   329  // ErrorAs panics if i is nil or not a pointer.
   330  // ErrorAs returns false if err == nil.
   331  // See https://gocloud.dev/concepts/as/ for background information.
   332  func (c *Variable) ErrorAs(err error, i interface{}) bool {
   333  	return gcerr.ErrorAs(err, i, c.dw.ErrorAs)
   334  }
   335  
   336  // VariableURLOpener represents types than can open Variables based on a URL.
   337  // The opener must not modify the URL argument. OpenVariableURL must be safe to
   338  // call from multiple goroutines.
   339  //
   340  // This interface is generally implemented by types in driver packages.
   341  type VariableURLOpener interface {
   342  	OpenVariableURL(ctx context.Context, u *url.URL) (*Variable, error)
   343  }
   344  
   345  // URLMux is a URL opener multiplexer. It matches the scheme of the URLs
   346  // against a set of registered schemes and calls the opener that matches the
   347  // URL's scheme.
   348  // See https://gocloud.dev/concepts/urls/ for more information.
   349  //
   350  // The zero value is a multiplexer with no registered schemes.
   351  type URLMux struct {
   352  	schemes openurl.SchemeMap
   353  }
   354  
   355  // VariableSchemes returns a sorted slice of the registered Variable schemes.
   356  func (mux *URLMux) VariableSchemes() []string { return mux.schemes.Schemes() }
   357  
   358  // ValidVariableScheme returns true iff scheme has been registered for Variables.
   359  func (mux *URLMux) ValidVariableScheme(scheme string) bool { return mux.schemes.ValidScheme(scheme) }
   360  
   361  // RegisterVariable registers the opener with the given scheme. If an opener
   362  // already exists for the scheme, RegisterVariable panics.
   363  func (mux *URLMux) RegisterVariable(scheme string, opener VariableURLOpener) {
   364  	mux.schemes.Register("runtimevar", "Variable", scheme, opener)
   365  }
   366  
   367  // OpenVariable calls OpenVariableURL with the URL parsed from urlstr.
   368  // OpenVariable is safe to call from multiple goroutines.
   369  func (mux *URLMux) OpenVariable(ctx context.Context, urlstr string) (*Variable, error) {
   370  	opener, u, err := mux.schemes.FromString("Variable", urlstr)
   371  	if err != nil {
   372  		return nil, err
   373  	}
   374  	return opener.(VariableURLOpener).OpenVariableURL(ctx, u)
   375  }
   376  
   377  // OpenVariableURL dispatches the URL to the opener that is registered with the
   378  // URL's scheme. OpenVariableURL is safe to call from multiple goroutines.
   379  func (mux *URLMux) OpenVariableURL(ctx context.Context, u *url.URL) (*Variable, error) {
   380  	opener, err := mux.schemes.FromURL("Variable", u)
   381  	if err != nil {
   382  		return nil, err
   383  	}
   384  	return opener.(VariableURLOpener).OpenVariableURL(ctx, u)
   385  }
   386  
   387  var defaultURLMux = new(URLMux)
   388  
   389  // DefaultURLMux returns the URLMux used by OpenVariable.
   390  //
   391  // Driver packages can use this to register their VariableURLOpener on the mux.
   392  func DefaultURLMux() *URLMux {
   393  	return defaultURLMux
   394  }
   395  
   396  // OpenVariable opens the variable identified by the URL given.
   397  // See the URLOpener documentation in driver subpackages for
   398  // details on supported URL formats, and https://gocloud.dev/concepts/urls
   399  // for more information.
   400  func OpenVariable(ctx context.Context, urlstr string) (*Variable, error) {
   401  	return defaultURLMux.OpenVariable(ctx, urlstr)
   402  }
   403  
   404  // Decode is a function type for unmarshaling/decoding a slice of bytes into
   405  // an arbitrary type. Decode functions are used when creating a Decoder via
   406  // NewDecoder. This package provides common Decode functions including
   407  // GobDecode and JSONDecode.
   408  type Decode func(context.Context, []byte, interface{}) error
   409  
   410  // Decoder decodes a slice of bytes into a particular Go object.
   411  //
   412  // This package provides some common Decoders that you can use directly,
   413  // including StringDecoder and BytesDecoder. You can also NewDecoder to
   414  // construct other Decoders.
   415  type Decoder struct {
   416  	typ reflect.Type
   417  	fn  Decode
   418  }
   419  
   420  // NewDecoder returns a Decoder that uses fn to decode a slice of bytes into
   421  // an object of type obj.
   422  //
   423  // This package provides some common Decode functions, including JSONDecode
   424  // and GobDecode, which can be passed to this function to create Decoders for
   425  // JSON and gob values.
   426  func NewDecoder(obj interface{}, fn Decode) *Decoder {
   427  	return &Decoder{
   428  		typ: reflect.TypeOf(obj),
   429  		fn:  fn,
   430  	}
   431  }
   432  
   433  // Decode decodes b into a new instance of the target type.
   434  func (d *Decoder) Decode(ctx context.Context, b []byte) (interface{}, error) {
   435  	nv := reflect.New(d.typ).Interface()
   436  	if err := d.fn(ctx, b, nv); err != nil {
   437  		return nil, err
   438  	}
   439  	ptr := reflect.ValueOf(nv)
   440  	return ptr.Elem().Interface(), nil
   441  }
   442  
   443  var (
   444  	// StringDecoder decodes into strings.
   445  	StringDecoder = NewDecoder("", StringDecode)
   446  
   447  	// BytesDecoder copies the slice of bytes.
   448  	BytesDecoder = NewDecoder([]byte{}, BytesDecode)
   449  )
   450  
   451  // JSONDecode can be passed to NewDecoder when decoding JSON (https://golang.org/pkg/encoding/json/).
   452  func JSONDecode(ctx context.Context, data []byte, obj interface{}) error {
   453  	return json.Unmarshal(data, obj)
   454  }
   455  
   456  // GobDecode can be passed to NewDecoder when decoding gobs (https://golang.org/pkg/encoding/gob/).
   457  func GobDecode(ctx context.Context, data []byte, obj interface{}) error {
   458  	return gob.NewDecoder(bytes.NewBuffer(data)).Decode(obj)
   459  }
   460  
   461  // StringDecode decodes raw bytes b into a string.
   462  func StringDecode(ctx context.Context, b []byte, obj interface{}) error {
   463  	v := obj.(*string)
   464  	*v = string(b)
   465  	return nil
   466  }
   467  
   468  // BytesDecode copies the slice of bytes b into obj.
   469  func BytesDecode(ctx context.Context, b []byte, obj interface{}) error {
   470  	v := obj.(*[]byte)
   471  	*v = b[:]
   472  	return nil
   473  }
   474  
   475  // DecryptDecode returns a decode function that can be passed to NewDecoder when
   476  // decoding an encrypted message (https://godoc.org/gocloud.dev/secrets).
   477  //
   478  // post defaults to BytesDecode. An optional decoder can be passed in to do
   479  // further decode operation based on the decrypted message.
   480  func DecryptDecode(k *secrets.Keeper, post Decode) Decode {
   481  	return func(ctx context.Context, b []byte, obj interface{}) error {
   482  		decrypted, err := k.Decrypt(ctx, b)
   483  		if err != nil {
   484  			return err
   485  		}
   486  		if post == nil {
   487  			return BytesDecode(ctx, decrypted, obj)
   488  		}
   489  		return post(ctx, decrypted, obj)
   490  	}
   491  }
   492  
   493  // DecoderByName returns a *Decoder based on decoderName.
   494  //
   495  // It is intended to be used by URL openers in driver packages.
   496  //
   497  // Supported values include:
   498  //   - empty string: Returns the default from the URLOpener.Decoder, or
   499  //     BytesDecoder if URLOpener.Decoder is nil (which is true if you're
   500  //     using the default URLOpener).
   501  //   - "bytes": Returns a BytesDecoder; Snapshot.Value will be of type []byte.
   502  //   - "jsonmap": Returns a JSON decoder for a map[string]interface{};
   503  //     Snapshot.Value will be of type *map[string]interface{}.
   504  //   - "string": Returns StringDecoder; Snapshot.Value will be of type string.
   505  //
   506  // It also supports using "decrypt+<decoderName>" (or "decrypt" for default
   507  // decoder) to decrypt the data before decoding. It uses the secrets package to
   508  // open a keeper by the URL string stored in a environment variable
   509  // "RUNTIMEVAR_KEEPER_URL". See https://godoc.org/gocloud.dev/secrets#OpenKeeper
   510  // for more details.
   511  func DecoderByName(ctx context.Context, decoderName string, dflt *Decoder) (*Decoder, error) {
   512  	// Open a *secrets.Keeper if the decoderName contains "decrypt".
   513  	k, decoderName, err := decryptByName(ctx, decoderName)
   514  	if err != nil {
   515  		return nil, err
   516  	}
   517  
   518  	if dflt == nil {
   519  		dflt = BytesDecoder
   520  	}
   521  	switch decoderName {
   522  	case "":
   523  		return maybeDecrypt(ctx, k, dflt), nil
   524  	case "bytes":
   525  		return maybeDecrypt(ctx, k, BytesDecoder), nil
   526  	case "jsonmap":
   527  		var m map[string]interface{}
   528  		return maybeDecrypt(ctx, k, NewDecoder(&m, JSONDecode)), nil
   529  	case "string":
   530  		return maybeDecrypt(ctx, k, StringDecoder), nil
   531  	default:
   532  		return nil, fmt.Errorf("unsupported decoder %q", decoderName)
   533  	}
   534  }
   535  
   536  // decryptByName returns a *secrets.Keeper for decryption when decoderName
   537  // contains "decrypt".
   538  func decryptByName(ctx context.Context, decoderName string) (*secrets.Keeper, string, error) {
   539  	if !strings.HasPrefix(decoderName, "decrypt") {
   540  		return nil, decoderName, nil
   541  	}
   542  	keeperURL := os.Getenv("RUNTIMEVAR_KEEPER_URL")
   543  	if keeperURL == "" {
   544  		return nil, "", errors.New("environment variable RUNTIMEVAR_KEEPER_URL needed to open a *secrets.Keeper for decryption")
   545  	}
   546  	k, err := secrets.OpenKeeper(ctx, keeperURL)
   547  	if err != nil {
   548  		return nil, "", err
   549  	}
   550  	decoderName = strings.TrimPrefix(decoderName, "decrypt")
   551  	if decoderName != "" {
   552  		decoderName = strings.TrimLeftFunc(decoderName, func(r rune) bool {
   553  			return r == ' ' || r == '+'
   554  		})
   555  	}
   556  	// The parsed value is "decrypt <decoderName>".
   557  	return k, decoderName, nil
   558  }
   559  
   560  func maybeDecrypt(ctx context.Context, k *secrets.Keeper, dec *Decoder) *Decoder {
   561  	if k == nil {
   562  		return dec
   563  	}
   564  	return NewDecoder(reflect.New(dec.typ).Elem().Interface(), DecryptDecode(k, dec.fn))
   565  }