github.com/blend/go-sdk@v1.20220411.3/env/vars.go (about)

     1  /*
     2  
     3  Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file.
     5  
     6  */
     7  
     8  package env
     9  
    10  import (
    11  	"encoding/base64"
    12  	"fmt"
    13  	"os"
    14  	"strconv"
    15  	"strings"
    16  	"time"
    17  
    18  	"github.com/blend/go-sdk/ex"
    19  	"github.com/blend/go-sdk/reflectutil"
    20  	"github.com/blend/go-sdk/stringutil"
    21  )
    22  
    23  // New returns a new env var set.
    24  //
    25  /* By default, it is empty. In order to populate it with the current
    26  runtime environment variables, you need to pass in options:
    27      vars := env.New(env.OptEnviron(os.Environ()...))
    28  */
    29  func New(opts ...Option) Vars {
    30  	vars := make(Vars)
    31  	for _, opt := range opts {
    32  		opt(vars)
    33  	}
    34  	return vars
    35  }
    36  
    37  // Vars is a set of environment variables.
    38  type Vars map[string]string
    39  
    40  // Get gets a variable as a string.
    41  // It mirrors os.Getenv.
    42  func (ev Vars) Get(envVar string) string {
    43  	if value, ok := ev[envVar]; ok {
    44  		return value
    45  	}
    46  	return ""
    47  }
    48  
    49  // Set sets a value for a key.
    50  func (ev Vars) Set(envVar, value string) {
    51  	ev[envVar] = value
    52  }
    53  
    54  // Restore resets an environment variable to it's environment value.
    55  func (ev Vars) Restore(key string) {
    56  	ev[key] = os.Getenv(key)
    57  }
    58  
    59  // Delete removes a key from the set.
    60  func (ev Vars) Delete(key string) {
    61  	delete(ev, key)
    62  }
    63  
    64  // String returns a string value for a given key, with an optional default vaule.
    65  func (ev Vars) String(envVar string, defaults ...string) string {
    66  	if value, hasValue := ev[envVar]; hasValue {
    67  		return value
    68  	}
    69  	for _, defaultValue := range defaults {
    70  		if defaultValue != "" {
    71  			return defaultValue
    72  		}
    73  	}
    74  	return ""
    75  }
    76  
    77  // CSV returns a string array for a given string var.
    78  func (ev Vars) CSV(envVar string, defaults ...string) []string {
    79  	if value, hasValue := ev[envVar]; hasValue && len(value) > 0 {
    80  		return strings.Split(value, ",")
    81  	}
    82  	return defaults
    83  }
    84  
    85  // Bool returns a boolean value for a key, defaulting to false.
    86  // Valid "truthy" values are `true`, `yes`, and `1`.
    87  // Everything else is false, including `REEEEEEEEEEEEEEE`.
    88  func (ev Vars) Bool(envVar string, defaults ...bool) bool {
    89  	if value, hasValue := ev[envVar]; hasValue {
    90  		boolValue, err := stringutil.ParseBool(value)
    91  		if err == nil {
    92  			return boolValue
    93  		}
    94  	}
    95  	if len(defaults) > 0 {
    96  		return defaults[0]
    97  	}
    98  	return false
    99  }
   100  
   101  // Int returns an integer value for a given key.
   102  func (ev Vars) Int(envVar string, defaults ...int) (int, error) {
   103  	if value, hasValue := ev[envVar]; hasValue {
   104  		parsedValue, err := strconv.Atoi(value)
   105  		if err != nil {
   106  			return 0, ex.New(err, ex.OptMessagef("var: %q", envVar))
   107  		}
   108  		return parsedValue, nil
   109  	}
   110  	for _, defaultValue := range defaults {
   111  		if defaultValue > 0 {
   112  			return defaultValue, nil
   113  		}
   114  	}
   115  	return 0, nil
   116  }
   117  
   118  // MustInt returns an integer value for a given key and panics if it is malformed.
   119  func (ev Vars) MustInt(envVar string, defaults ...int) int {
   120  	value, err := ev.Int(envVar, defaults...)
   121  	if err != nil {
   122  		panic(err)
   123  	}
   124  	return value
   125  }
   126  
   127  // Int32 returns an integer value for a given key.
   128  func (ev Vars) Int32(envVar string, defaults ...int32) (int32, error) {
   129  	if value, hasValue := ev[envVar]; hasValue {
   130  		parsedValue, err := strconv.ParseInt(value, 10, 32)
   131  		if err != nil {
   132  			return 0, ex.New(err, ex.OptMessagef("var: %q", envVar))
   133  		}
   134  		return int32(parsedValue), nil
   135  	}
   136  	for _, defaultValue := range defaults {
   137  		if defaultValue > 0 {
   138  			return defaultValue, nil
   139  		}
   140  	}
   141  	return 0, nil
   142  }
   143  
   144  // MustInt32 returns an integer value for a given key and panics if it is malformed.
   145  func (ev Vars) MustInt32(envVar string, defaults ...int32) int32 {
   146  	value, err := ev.Int32(envVar, defaults...)
   147  	if err != nil {
   148  		panic(err)
   149  	}
   150  	return value
   151  }
   152  
   153  // Int64 returns an int64 value for a given key.
   154  func (ev Vars) Int64(envVar string, defaults ...int64) (int64, error) {
   155  	if value, hasValue := ev[envVar]; hasValue {
   156  		parsedValue, err := strconv.ParseInt(value, 10, 64)
   157  		if err != nil {
   158  			return 0, ex.New(err, ex.OptMessagef("var: %q", envVar))
   159  		}
   160  		return parsedValue, nil
   161  	}
   162  	for _, defaultValue := range defaults {
   163  		if defaultValue > 0 {
   164  			return defaultValue, nil
   165  		}
   166  	}
   167  	return 0, nil
   168  }
   169  
   170  // MustInt64 returns an int64 value for a given key and panics if it is malformed.
   171  func (ev Vars) MustInt64(envVar string, defaults ...int64) int64 {
   172  	value, err := ev.Int64(envVar, defaults...)
   173  	if err != nil {
   174  		panic(err)
   175  	}
   176  	return value
   177  }
   178  
   179  // Uint32 returns an uint32 value for a given key.
   180  func (ev Vars) Uint32(envVar string, defaults ...uint32) (uint32, error) {
   181  	if value, hasValue := ev[envVar]; hasValue {
   182  		parsedValue, err := strconv.ParseUint(value, 10, 32)
   183  		if err != nil {
   184  			return 0, ex.New(err, ex.OptMessagef("var: %q", envVar))
   185  		}
   186  		return uint32(parsedValue), nil
   187  	}
   188  	for _, defaultValue := range defaults {
   189  		if defaultValue > 0 {
   190  			return defaultValue, nil
   191  		}
   192  	}
   193  	return 0, nil
   194  }
   195  
   196  // MustUint32 returns an uint32 value for a given key and panics if it is malformed.
   197  func (ev Vars) MustUint32(envVar string, defaults ...uint32) uint32 {
   198  	value, err := ev.Uint32(envVar, defaults...)
   199  	if err != nil {
   200  		panic(err)
   201  	}
   202  	return value
   203  }
   204  
   205  // Uint64 returns an uint64 value for a given key.
   206  func (ev Vars) Uint64(envVar string, defaults ...uint64) (uint64, error) {
   207  	if value, hasValue := ev[envVar]; hasValue {
   208  		parsedValue, err := strconv.ParseUint(value, 10, 64)
   209  		if err != nil {
   210  			return 0, ex.New(err, ex.OptMessagef("var: %q", envVar))
   211  		}
   212  		return parsedValue, nil
   213  	}
   214  	for _, defaultValue := range defaults {
   215  		if defaultValue > 0 {
   216  			return defaultValue, nil
   217  		}
   218  	}
   219  	return 0, nil
   220  }
   221  
   222  // MustUint64 returns an uint64 value for a given key and panics if it is malformed.
   223  func (ev Vars) MustUint64(envVar string, defaults ...uint64) uint64 {
   224  	value, err := ev.Uint64(envVar, defaults...)
   225  	if err != nil {
   226  		panic(err)
   227  	}
   228  	return value
   229  }
   230  
   231  // Float32 returns an float32 value for a given key.
   232  func (ev Vars) Float32(envVar string, defaults ...float32) (float32, error) {
   233  	if value, hasValue := ev[envVar]; hasValue {
   234  		parsedValue, err := strconv.ParseFloat(value, 32)
   235  		if err != nil {
   236  			return 0, ex.New(err, ex.OptMessagef("var: %q", envVar))
   237  		}
   238  		return float32(parsedValue), nil
   239  	}
   240  	for _, defaultValue := range defaults {
   241  		if defaultValue > 0 {
   242  			return defaultValue, nil
   243  		}
   244  	}
   245  	return 0, nil
   246  }
   247  
   248  // MustFloat32 returns an float64 value for a given key and panics if it is malformed.
   249  func (ev Vars) MustFloat32(envVar string, defaults ...float32) float32 {
   250  	value, err := ev.Float32(envVar, defaults...)
   251  	if err != nil {
   252  		panic(err)
   253  	}
   254  	return value
   255  }
   256  
   257  // Float64 returns an float64 value for a given key.
   258  func (ev Vars) Float64(envVar string, defaults ...float64) (float64, error) {
   259  	if value, hasValue := ev[envVar]; hasValue {
   260  		parsedValue, err := strconv.ParseFloat(value, 64)
   261  		if err != nil {
   262  			return 0, ex.New(err, ex.OptMessagef("var: %q", envVar))
   263  		}
   264  		return parsedValue, nil
   265  	}
   266  	for _, defaultValue := range defaults {
   267  		if defaultValue > 0 {
   268  			return defaultValue, nil
   269  		}
   270  	}
   271  	return 0, nil
   272  }
   273  
   274  // MustFloat64 returns an float64 value for a given key and panics if it is malformed.
   275  func (ev Vars) MustFloat64(envVar string, defaults ...float64) float64 {
   276  	value, err := ev.Float64(envVar, defaults...)
   277  	if err != nil {
   278  		panic(err)
   279  	}
   280  	return value
   281  }
   282  
   283  // Duration returns a duration value for a given key.
   284  func (ev Vars) Duration(envVar string, defaults ...time.Duration) (time.Duration, error) {
   285  	if value, hasValue := ev[envVar]; hasValue {
   286  		return time.ParseDuration(value)
   287  	}
   288  	for _, defaultValue := range defaults {
   289  		if defaultValue > 0 {
   290  			return defaultValue, nil
   291  		}
   292  	}
   293  	return 0, nil
   294  }
   295  
   296  // MustDuration returnss a duration value for a given key and panics if malformed.
   297  func (ev Vars) MustDuration(envVar string, defaults ...time.Duration) time.Duration {
   298  	value, err := ev.Duration(envVar, defaults...)
   299  	if err != nil {
   300  		panic(err)
   301  	}
   302  	return value
   303  }
   304  
   305  // Bytes returns a []byte value for a given key.
   306  func (ev Vars) Bytes(envVar string, defaults ...[]byte) []byte {
   307  	if value, hasValue := ev[envVar]; hasValue && len(value) > 0 {
   308  		return []byte(value)
   309  	}
   310  	for _, defaultValue := range defaults {
   311  		if len(defaultValue) > 0 {
   312  			return defaultValue
   313  		}
   314  	}
   315  	return nil
   316  }
   317  
   318  // Base64 returns a []byte value for a given key whose value is encoded in base64.
   319  func (ev Vars) Base64(envVar string, defaults ...[]byte) ([]byte, error) {
   320  	if value, hasValue := ev[envVar]; hasValue && len(value) > 0 {
   321  		return base64.StdEncoding.DecodeString(value)
   322  	}
   323  	for _, defaultValue := range defaults {
   324  		if len(defaultValue) > 0 {
   325  			return defaultValue, nil
   326  		}
   327  	}
   328  	return nil, nil
   329  }
   330  
   331  // MustBase64 returns a []byte value for a given key encoded with base64, and panics if malformed.
   332  func (ev Vars) MustBase64(envVar string, defaults ...[]byte) []byte {
   333  	value, err := ev.Base64(envVar, defaults...)
   334  	if err != nil {
   335  		panic(err)
   336  	}
   337  	return value
   338  }
   339  
   340  // Has returns if a key is present in the set.
   341  func (ev Vars) Has(envVar string) bool {
   342  	_, hasKey := ev[envVar]
   343  	return hasKey
   344  }
   345  
   346  // HasAll returns if all of the given vars are present in the set.
   347  func (ev Vars) HasAll(envVars ...string) bool {
   348  	if len(envVars) == 0 {
   349  		return false
   350  	}
   351  	for _, envVar := range envVars {
   352  		if !ev.Has(envVar) {
   353  			return false
   354  		}
   355  	}
   356  	return true
   357  }
   358  
   359  // HasAny returns if any of the given vars are present in the set.
   360  func (ev Vars) HasAny(envVars ...string) bool {
   361  	for _, envVar := range envVars {
   362  		if ev.Has(envVar) {
   363  			return true
   364  		}
   365  	}
   366  	return false
   367  }
   368  
   369  // Require enforces that a given set of environment variables are present.
   370  func (ev Vars) Require(keys ...string) error {
   371  	for _, key := range keys {
   372  		if !ev.Has(key) {
   373  			return fmt.Errorf("the following environment variables are required: `%s`", strings.Join(keys, ","))
   374  		}
   375  	}
   376  	return nil
   377  }
   378  
   379  // Must enforces that a given set of environment variables are present and panics
   380  // if they're not present.
   381  func (ev Vars) Must(keys ...string) {
   382  	for _, key := range keys {
   383  		if !ev.Has(key) {
   384  			panic(fmt.Sprintf("the following environment variables are required: `%s`", strings.Join(keys, ",")))
   385  		}
   386  	}
   387  }
   388  
   389  // Union returns the union of the two sets, other replacing conflicts.
   390  func (ev Vars) Union(other Vars) Vars {
   391  	newSet := New()
   392  	for key, value := range ev {
   393  		newSet[key] = value
   394  	}
   395  	for key, value := range other {
   396  		newSet[key] = value
   397  	}
   398  	return newSet
   399  }
   400  
   401  // Vars returns all the vars stored in the env var set.
   402  func (ev Vars) Vars() []string {
   403  	var envVars = make([]string, len(ev))
   404  	var index int
   405  	for envVar := range ev {
   406  		envVars[index] = envVar
   407  		index++
   408  	}
   409  	return envVars
   410  }
   411  
   412  // Raw returns a raw KEY=VALUE form of the vars.
   413  func (ev Vars) Raw() []string {
   414  	var raw []string
   415  	for key, value := range ev {
   416  		raw = append(raw, fmt.Sprintf("%s=%s", key, value))
   417  	}
   418  	return raw
   419  }
   420  
   421  // ReadInto sets an object based on the fields in the env vars set.
   422  func (ev Vars) ReadInto(obj interface{}) error {
   423  	if typed, isTyped := obj.(Unmarshaler); isTyped {
   424  		return typed.UnmarshalEnv(ev)
   425  	}
   426  	return reflectutil.PatchStrings(ReflectTagName, ev, obj)
   427  }
   428  
   429  // Expand calls os.Expand with the variable set as the environment value resolver.
   430  func (ev Vars) Expand(value string) string {
   431  	return os.Expand(value, ev.Get)
   432  }
   433  
   434  // --------------------------------------------------------------------------------
   435  // Service Specific helpers
   436  // --------------------------------------------------------------------------------
   437  
   438  // ServiceEnv is a common environment variable for the services environment.
   439  // Common values include "dev", "ci", "sandbox", "preprod", "beta", and "prod".
   440  func (ev Vars) ServiceEnv(defaults ...string) string {
   441  	return ev.String(VarServiceEnv, defaults...)
   442  }
   443  
   444  // IsProduction returns if the ServiceEnv is a production environment.
   445  func (ev Vars) IsProduction() bool {
   446  	return IsProduction(ev.ServiceEnv())
   447  }
   448  
   449  // IsProdlike returns if the ServiceEnv is "prodlike".
   450  func (ev Vars) IsProdlike() bool {
   451  	return IsProdlike(ev.ServiceEnv())
   452  }
   453  
   454  // IsDev returns if the ServiceEnv is the local development environment.
   455  func (ev Vars) IsDev() bool {
   456  	return IsDev(ev.ServiceEnv())
   457  }
   458  
   459  // IsDevlike returns if the ServiceEnv is strictly the inverse of `IsProdlike`.
   460  func (ev Vars) IsDevlike() bool {
   461  	return !IsProdlike(ev.ServiceEnv())
   462  }
   463  
   464  // ServiceName is a common environment variable for the service's name.
   465  func (ev Vars) ServiceName(defaults ...string) string {
   466  	return ev.String(VarServiceName, defaults...)
   467  }
   468  
   469  // Hostname is a common environment variable for the machine's hostname.
   470  func (ev Vars) Hostname(defaults ...string) string {
   471  	return ev.String(VarHostname, defaults...)
   472  }
   473  
   474  // Version is a common environment variable for the service version.
   475  func (ev Vars) Version(defaults ...string) string {
   476  	return ev.String(VarVersion, defaults...)
   477  }