github.com/webonyx/up@v0.7.4-0.20180808230834-91b94e551323/platform/aws/runtime/runtime.go (about)

     1  package runtime
     2  
     3  import (
     4  	"os"
     5  	"time"
     6  
     7  	"github.com/apex/log"
     8  	"github.com/apex/up"
     9  	"github.com/apex/up/internal/secret"
    10  	"github.com/apex/up/internal/util"
    11  	"github.com/aws/aws-sdk-go/aws"
    12  	"github.com/aws/aws-sdk-go/aws/session"
    13  	"github.com/aws/aws-sdk-go/service/cloudwatch"
    14  	"github.com/pkg/errors"
    15  )
    16  
    17  // Runtime implementation.
    18  type Runtime struct {
    19  	config *up.Config
    20  	log    log.Interface
    21  }
    22  
    23  // Option function.
    24  type Option func(*Runtime)
    25  
    26  // New with the given options.
    27  func New(c *up.Config, options ...Option) *Runtime {
    28  	var v Runtime
    29  	v.config = c
    30  	v.log = log.Log
    31  	for _, o := range options {
    32  		o(&v)
    33  	}
    34  	return &v
    35  }
    36  
    37  // WithLogger option.
    38  func WithLogger(l log.Interface) Option {
    39  	return func(v *Runtime) {
    40  		v.log = l
    41  	}
    42  }
    43  
    44  // Init implementation.
    45  func (r *Runtime) Init(stage string) error {
    46  	os.Setenv("UP_STAGE", stage)
    47  
    48  	if os.Getenv("NODE_ENV") == "" {
    49  		os.Setenv("NODE_ENV", stage)
    50  	}
    51  
    52  	r.log.Info("loading secrets")
    53  	if err := r.loadSecrets(stage); err != nil {
    54  		return errors.Wrap(err, "loading secrets")
    55  	}
    56  
    57  	return nil
    58  }
    59  
    60  // Metric records a metric value.
    61  func (r *Runtime) Metric(name string, value float64) error {
    62  	// TODO: move
    63  	c := cloudwatch.New(session.New(aws.NewConfig()))
    64  
    65  	// TODO: conventions for Name? ByApp ?
    66  	// TODO: timeouts or delegate
    67  	// TODO: stage dim?
    68  	_, err := c.PutMetricData(&cloudwatch.PutMetricDataInput{
    69  		Namespace: aws.String("up"),
    70  		MetricData: []*cloudwatch.MetricDatum{
    71  			{
    72  				MetricName: &name,
    73  				Value:      &value,
    74  				Dimensions: []*cloudwatch.Dimension{
    75  					{
    76  						Name:  aws.String("app"),
    77  						Value: &r.config.Name,
    78  					},
    79  				},
    80  			},
    81  		},
    82  	})
    83  
    84  	return err
    85  }
    86  
    87  // loadSecrets loads secrets.
    88  func (r *Runtime) loadSecrets(stage string) error {
    89  	start := time.Now()
    90  	initialEnv := util.EnvironMap()
    91  
    92  	r.log.Info("initializing secrets")
    93  	defer func() {
    94  		r.log.WithField("duration", util.MillisecondsSince(start)).Info("initialized secrets")
    95  	}()
    96  
    97  	// TODO: all regions
    98  	secrets, err := NewSecrets(r.config.Name, stage, r.config.Regions[0]).Load()
    99  	if err != nil {
   100  		return err
   101  	}
   102  
   103  	secrets = secret.FilterByApp(secrets, r.config.Name)
   104  	stages := secret.GroupByStage(secrets)
   105  
   106  	precedence := []string{
   107  		"all",
   108  		stage,
   109  	}
   110  
   111  	for _, name := range precedence {
   112  		if secrets := stages[name]; len(secrets) > 0 {
   113  			r.log.WithFields(log.Fields{
   114  				"name":  name,
   115  				"count": len(secrets),
   116  			}).Info("initializing variables")
   117  
   118  			for _, s := range secrets {
   119  				ctx := r.log.WithFields(log.Fields{
   120  					"name":  s.Name,
   121  					"value": secret.String(s),
   122  				})
   123  
   124  				// in development we allow existing vars to override `up env`
   125  				if _, ok := initialEnv[s.Name]; ok && stage == "development" {
   126  					ctx.Debug("variable already defined")
   127  					continue
   128  				}
   129  
   130  				ctx.Info("set variable")
   131  				os.Setenv(s.Name, s.Value)
   132  			}
   133  		}
   134  	}
   135  
   136  	return nil
   137  }