github.com/mcuadros/ascode@v1.3.1/starlark/runtime/runtime.go (about) 1 package runtime 2 3 import ( 4 "fmt" 5 osfilepath "path/filepath" 6 7 "github.com/mcuadros/ascode/starlark/module/docker" 8 "github.com/mcuadros/ascode/starlark/module/filepath" 9 "github.com/mcuadros/ascode/starlark/module/os" 10 "github.com/mcuadros/ascode/starlark/module/url" 11 "github.com/mcuadros/ascode/starlark/types" 12 "github.com/mcuadros/ascode/terraform" 13 "github.com/qri-io/starlib/encoding/base64" 14 "github.com/qri-io/starlib/encoding/csv" 15 "github.com/qri-io/starlib/encoding/json" 16 "github.com/qri-io/starlib/encoding/yaml" 17 "github.com/qri-io/starlib/http" 18 "github.com/qri-io/starlib/math" 19 "github.com/qri-io/starlib/re" 20 "github.com/qri-io/starlib/time" 21 "go.starlark.net/repl" 22 "go.starlark.net/resolve" 23 "go.starlark.net/starlark" 24 "go.starlark.net/starlarkstruct" 25 ) 26 27 func init() { 28 resolve.AllowRecursion = true 29 resolve.AllowFloat = true 30 resolve.AllowGlobalReassign = true 31 resolve.AllowLambda = true 32 resolve.AllowNestedDef = true 33 resolve.AllowSet = true 34 } 35 36 // LoadModuleFunc is a concurrency-safe and idempotent function that returns 37 // the module when is called from the `load` funcion. 38 type LoadModuleFunc func() (starlark.StringDict, error) 39 40 // Runtime represents the AsCode runtime, it defines the available modules, 41 // the predeclared globals and handles how the `load` function behaves. 42 type Runtime struct { 43 Terraform *types.Terraform 44 pm *terraform.PluginManager 45 predeclared starlark.StringDict 46 modules map[string]LoadModuleFunc 47 moduleCache map[string]*moduleCache 48 49 path string 50 } 51 52 // NewRuntime returns a new Runtime for the given terraform.PluginManager. 53 func NewRuntime(pm *terraform.PluginManager) *Runtime { 54 tf := types.NewTerraform(pm) 55 predeclared := starlark.StringDict{} 56 predeclared["tf"] = tf 57 predeclared["provisioner"] = types.BuiltinProvisioner() 58 predeclared["backend"] = types.BuiltinBackend() 59 predeclared["validate"] = types.BuiltinValidate() 60 predeclared["hcl"] = types.BuiltinHCL() 61 predeclared["fn"] = types.BuiltinFunctionAttribute() 62 predeclared["ref"] = types.BuiltinRef() 63 predeclared["evaluate"] = types.BuiltinEvaluate(predeclared) 64 predeclared["struct"] = starlark.NewBuiltin("struct", starlarkstruct.Make) 65 predeclared["module"] = starlark.NewBuiltin("module", starlarkstruct.MakeModule) 66 67 return &Runtime{ 68 Terraform: tf, 69 pm: pm, 70 moduleCache: make(map[string]*moduleCache), 71 modules: map[string]LoadModuleFunc{ 72 filepath.ModuleName: filepath.LoadModule, 73 os.ModuleName: os.LoadModule, 74 docker.ModuleName: docker.LoadModule, 75 76 "encoding/json": json.LoadModule, 77 "encoding/base64": base64.LoadModule, 78 "encoding/csv": csv.LoadModule, 79 "encoding/yaml": yaml.LoadModule, 80 "math": math.LoadModule, 81 "re": re.LoadModule, 82 "time": time.LoadModule, 83 "http": http.LoadModule, 84 "url": url.LoadModule, 85 }, 86 predeclared: predeclared, 87 } 88 } 89 90 // ExecFile parses, resolves, and executes a Starlark file. 91 func (r *Runtime) ExecFile(filename string) (starlark.StringDict, error) { 92 fullpath, _ := osfilepath.Abs(filename) 93 r.path, _ = osfilepath.Split(fullpath) 94 95 thread := &starlark.Thread{Name: "thread", Load: r.load} 96 r.setLocals(thread) 97 98 return starlark.ExecFile(thread, filename, nil, r.predeclared) 99 } 100 101 // REPL executes a read, eval, print loop. 102 func (r *Runtime) REPL() { 103 thread := &starlark.Thread{Name: "thread", Load: r.load} 104 r.setLocals(thread) 105 106 repl.REPL(thread, r.predeclared) 107 } 108 109 func (r *Runtime) setLocals(t *starlark.Thread) { 110 t.SetLocal("base_path", r.path) 111 t.SetLocal(types.PluginManagerLocal, r.pm) 112 } 113 114 func (r *Runtime) load(t *starlark.Thread, module string) (starlark.StringDict, error) { 115 if m, ok := r.modules[module]; ok { 116 return m() 117 } 118 119 filename := osfilepath.Join(r.path, module) 120 return r.loadFile(t, filename) 121 } 122 123 type moduleCache struct { 124 globals starlark.StringDict 125 err error 126 } 127 128 func (r *Runtime) loadFile(thread *starlark.Thread, module string) (starlark.StringDict, error) { 129 e, ok := r.moduleCache[module] 130 if e == nil { 131 if ok { 132 // request for package whose loading is in progress 133 return nil, fmt.Errorf("cycle in load graph") 134 } 135 136 // Add a placeholder to indicate "load in progress". 137 r.moduleCache[module] = nil 138 139 thread := &starlark.Thread{Name: "exec " + module, Load: thread.Load} 140 globals, err := starlark.ExecFile(thread, module, nil, r.predeclared) 141 142 e = &moduleCache{globals, err} 143 r.moduleCache[module] = e 144 } 145 146 return e.globals, e.err 147 }