@@ -3,6 +3,7 @@ package load
3
3
import (
4
4
"fmt"
5
5
"slices"
6
+ "sort"
6
7
"sync"
7
8
8
9
"go.starlark.net/starlark"
@@ -15,8 +16,8 @@ const nameInstancePlacement = "instance_placement"
15
16
// prefixQEMU is the prefix used in Starlark for the QEMU scriptlet.
16
17
const prefixQEMU = "qemu"
17
18
18
- // prefixAuthorization is the prefix used in Starlark for the Authorization scriptlet.
19
- const prefixAuthorization = "authorization"
19
+ // nameAuthorization is the name used in Starlark for the Authorization scriptlet.
20
+ const nameAuthorization = "authorization"
20
21
21
22
// compile compiles a scriptlet.
22
23
func compile (programName string , src string , preDeclared []string ) (* starlark.Program , error ) {
@@ -33,6 +34,72 @@ func compile(programName string, src string, preDeclared []string) (*starlark.Pr
33
34
return mod , nil
34
35
}
35
36
37
+ // validate validates a scriptlet by compiling it and checking the presence of required functions.
38
+ func validate (compiler func (string , string ) (* starlark.Program , error ), programName string , src string , requiredFunctions map [string ][]string ) error {
39
+ prog , err := compiler (programName , src )
40
+ if err != nil {
41
+ return err
42
+ }
43
+
44
+ thread := & starlark.Thread {Name : programName }
45
+ globals , err := prog .Init (thread , nil )
46
+ if err != nil {
47
+ return err
48
+ }
49
+
50
+ globals .Freeze ()
51
+
52
+ var notFound []string
53
+ for funName , requiredArgs := range requiredFunctions {
54
+ // The function is missing if its name is not found in the globals.
55
+ funv := globals [funName ]
56
+ if funv == nil {
57
+ notFound = append (notFound , funName )
58
+ continue
59
+ }
60
+
61
+ // The function is missing if its name is not bound to a function.
62
+ fun , ok := funv .(* starlark.Function )
63
+ if ! ok {
64
+ notFound = append (notFound , funName )
65
+ }
66
+
67
+ // Get the function arguments.
68
+ argc := fun .NumParams ()
69
+ var args []string
70
+ for i := range argc {
71
+ arg , _ := fun .Param (i )
72
+ args = append (args , arg )
73
+ }
74
+
75
+ // Return an error early if the function does not have the right arguments.
76
+ match := len (args ) == len (requiredArgs )
77
+ if match {
78
+ sort .Strings (args )
79
+ sort .Strings (requiredArgs )
80
+ for i := range args {
81
+ if args [i ] != requiredArgs [i ] {
82
+ match = false
83
+ break
84
+ }
85
+ }
86
+ }
87
+
88
+ if ! match {
89
+ return fmt .Errorf ("The function %q defines arguments %q (expected: %q)" , funName , args , requiredArgs )
90
+ }
91
+ }
92
+
93
+ switch len (notFound ) {
94
+ case 0 :
95
+ return nil
96
+ case 1 :
97
+ return fmt .Errorf ("The function %q is required but has not been found in the scriptlet" , notFound [0 ])
98
+ default :
99
+ return fmt .Errorf ("The functions %q are required but have not been found in the scriptlet" , notFound )
100
+ }
101
+ }
102
+
36
103
var programsMu sync.Mutex
37
104
var programs = make (map [string ]* starlark.Program )
38
105
@@ -89,8 +156,9 @@ func InstancePlacementCompile(name string, src string) (*starlark.Program, error
89
156
90
157
// InstancePlacementValidate validates the instance placement scriptlet.
91
158
func InstancePlacementValidate (src string ) error {
92
- _ , err := InstancePlacementCompile (nameInstancePlacement , src )
93
- return err
159
+ return validate (InstancePlacementCompile , nameInstancePlacement , src , map [string ][]string {
160
+ "instance_placement" : {"request" , "candidate_members" },
161
+ })
94
162
}
95
163
96
164
// InstancePlacementSet compiles the instance placement scriptlet into memory for use with InstancePlacementRun.
@@ -131,8 +199,9 @@ func QEMUCompile(name string, src string) (*starlark.Program, error) {
131
199
132
200
// QEMUValidate validates the QEMU scriptlet.
133
201
func QEMUValidate (src string ) error {
134
- _ , err := QEMUCompile (prefixQEMU , src )
135
- return err
202
+ return validate (QEMUCompile , prefixQEMU , src , map [string ][]string {
203
+ "qemu_hook" : {"hook_name" },
204
+ })
136
205
}
137
206
138
207
// QEMUSet compiles the QEMU scriptlet into memory for use with QEMURun.
@@ -157,17 +226,18 @@ func AuthorizationCompile(name string, src string) (*starlark.Program, error) {
157
226
158
227
// AuthorizationValidate validates the authorization scriptlet.
159
228
func AuthorizationValidate (src string ) error {
160
- _ , err := AuthorizationCompile (prefixAuthorization , src )
161
- return err
229
+ return validate (AuthorizationCompile , nameAuthorization , src , map [string ][]string {
230
+ "authorize" : {"details" , "object" , "entitlement" },
231
+ })
162
232
}
163
233
164
234
// AuthorizationSet compiles the authorization scriptlet into memory for use with AuthorizationRun.
165
235
// If empty src is provided the current program is deleted.
166
236
func AuthorizationSet (src string ) error {
167
- return set (AuthorizationCompile , prefixAuthorization , src )
237
+ return set (AuthorizationCompile , nameAuthorization , src )
168
238
}
169
239
170
240
// AuthorizationProgram returns the precompiled authorization scriptlet program.
171
241
func AuthorizationProgram () (* starlark.Program , * starlark.Thread , error ) {
172
- return program ("Authorization" , prefixAuthorization )
242
+ return program ("Authorization" , nameAuthorization )
173
243
}
0 commit comments