pulumi/pkg/resource/config.go

113 lines
3.7 KiB
Go

// Copyright 2016 Pulumi, Inc. All rights reserved.
package resource
import (
"sort"
"github.com/golang/glog"
"github.com/pulumi/coconut/pkg/compiler"
"github.com/pulumi/coconut/pkg/compiler/binder"
"github.com/pulumi/coconut/pkg/compiler/errors"
"github.com/pulumi/coconut/pkg/compiler/symbols"
"github.com/pulumi/coconut/pkg/diag"
"github.com/pulumi/coconut/pkg/eval"
"github.com/pulumi/coconut/pkg/eval/rt"
"github.com/pulumi/coconut/pkg/tokens"
"github.com/pulumi/coconut/pkg/util/contract"
)
// ConfigMap contains a mapping from variable token to the value to poke into that variable.
type ConfigMap map[tokens.Token]interface{}
// ConfigApplier returns a compiler preexec function that applies the configuration when invoked, and copies the
// resulting configuration variables into the supplied map (if it is non-nil).
func (cfg *ConfigMap) ConfigApplier(vars map[tokens.Token]*rt.Object) compiler.Preexec {
return func(ctx *binder.Context, pkg *symbols.Package, e eval.Interpreter) {
configs := cfg.ApplyConfig(ctx, pkg, e)
contract.Assert(configs != nil)
if vars != nil {
for k, v := range configs {
vars[k] = v
}
}
}
}
// ApplyConfig applies the configuration map to an existing interpreter context. The map is simply a map of tokens --
// which must be globally settable variables (module properties or static class properties) -- to serializable constant
// values. The routine simply walks these tokens in sorted order, and assigns the constant objects. Note that, because
// we are accessing module and class members, this routine will also trigger the relevant initialization routines.
func (cfg *ConfigMap) ApplyConfig(ctx *binder.Context, pkg *symbols.Package,
e eval.Interpreter) map[tokens.Token]*rt.Object {
glog.V(5).Infof("Applying configuration values for package '%v'", pkg)
// Track all configuration variables that get set, for diagnostics and plumbing.
vars := make(map[tokens.Token]*rt.Object)
if cfg != nil {
// For each config entry, bind the token to its symbol, and then attempt to assign to it.
for _, tok := range StableConfigKeys(*cfg) {
glog.V(5).Infof("Applying configuration value for token '%v'", tok)
// Bind to the symbol; if it returns nil, this means an error has resulted, and we can skip it.
var tree diag.Diagable = nil // there is no source info for this; eventually we may assign one.
if sym := ctx.LookupSymbol(tree, tokens.Token(tok), true); sym != nil {
var ok bool
switch s := sym.(type) {
case *symbols.ModuleProperty:
ok = true
case *symbols.ClassProperty:
// class properties are ok, so long as they are static.
ok = s.Static()
default:
ok = false
}
if !ok {
ctx.Diag.Errorf(errors.ErrorIllegalConfigToken, tok)
continue // skip to the next one
}
// Load up the location as an l-value; because we don't support instance properties, this is nil.
if loc := e.LoadLocation(tree, sym, nil, true); loc != nil {
// Allocate a new constant for the value we are about to assign, and assign it to the location.
v := (*cfg)[tok]
obj := e.NewConstantObject(nil, v)
loc.Assign(tree, obj)
vars[tok] = obj
}
}
}
}
if !ctx.Diag.Success() {
ctx.Diag.Errorf(errors.ErrorConfigApplyFailure, pkg)
}
return vars
}
func StableConfigKeys(config ConfigMap) []tokens.Token {
sorted := make(configKeys, 0, len(config))
for key := range config {
sorted = append(sorted, key)
}
sort.Sort(sorted)
return sorted
}
type configKeys []tokens.Token
func (s configKeys) Len() int {
return len(s)
}
func (s configKeys) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s configKeys) Less(i, j int) bool {
return s[i] < s[j]
}