1
2
3
4 package runtimeutil
5
6 import (
7 "fmt"
8 "os"
9 "sort"
10 "strings"
11
12 "sigs.k8s.io/kustomize/kyaml/yaml"
13 k8syaml "sigs.k8s.io/yaml"
14 )
15
16 const (
17 FunctionAnnotationKey = "config.kubernetes.io/function"
18 oldFunctionAnnotationKey = "config.k8s.io/function"
19 )
20
21 var functionAnnotationKeys = []string{FunctionAnnotationKey, oldFunctionAnnotationKey}
22
23
24 type ContainerNetworkName string
25
26 const (
27 NetworkNameNone ContainerNetworkName = "none"
28 NetworkNameHost ContainerNetworkName = "host"
29 )
30 const defaultEnvValue string = "true"
31
32
33 type ContainerEnv struct {
34
35 EnvVars map[string]string
36
37
38 VarsToExport []string
39 }
40
41
42 func (ce *ContainerEnv) GetDockerFlags() []string {
43 envs := ce.EnvVars
44 if envs == nil {
45 envs = make(map[string]string)
46 }
47
48 flags := []string{}
49
50 keys := []string{}
51 for k := range envs {
52 keys = append(keys, k)
53 }
54 sort.Strings(keys)
55 for _, key := range keys {
56 flags = append(flags, "-e", key+"="+envs[key])
57 }
58
59 for _, key := range ce.VarsToExport {
60 flags = append(flags, "-e", key)
61 }
62
63 return flags
64 }
65
66
67 func (ce *ContainerEnv) AddKeyValue(key, value string) {
68 if ce.EnvVars == nil {
69 ce.EnvVars = make(map[string]string)
70 }
71 ce.EnvVars[key] = value
72 }
73
74
75 func (ce *ContainerEnv) HasExportedKey(key string) bool {
76 for _, k := range ce.VarsToExport {
77 if k == key {
78 return true
79 }
80 }
81 return false
82 }
83
84
85 func (ce *ContainerEnv) AddKey(key string) {
86 if !ce.HasExportedKey(key) {
87 ce.VarsToExport = append(ce.VarsToExport, key)
88 }
89 }
90
91
92
93 func (ce *ContainerEnv) Raw() []string {
94 var ret []string
95 for k, v := range ce.EnvVars {
96 ret = append(ret, k+"="+v)
97 }
98
99 ret = append(ret, ce.VarsToExport...)
100 return ret
101 }
102
103
104 func NewContainerEnv() *ContainerEnv {
105 var ce ContainerEnv
106 ce.EnvVars = make(map[string]string)
107
108 ce.EnvVars["LOG_TO_STDERR"] = defaultEnvValue
109 ce.EnvVars["STRUCTURED_RESULTS"] = defaultEnvValue
110 return &ce
111 }
112
113
114
115 func NewContainerEnvFromStringSlice(envStr []string) *ContainerEnv {
116 ce := NewContainerEnv()
117 for _, e := range envStr {
118 parts := strings.SplitN(e, "=", 2)
119 if len(parts) == 1 {
120 ce.AddKey(e)
121 } else {
122 ce.AddKeyValue(parts[0], parts[1])
123 }
124 }
125 return ce
126 }
127
128
129 type FunctionSpec struct {
130 DeferFailure bool `json:"deferFailure,omitempty" yaml:"deferFailure,omitempty"`
131
132
133 Container ContainerSpec `json:"container,omitempty" yaml:"container,omitempty"`
134
135
136 Starlark StarlarkSpec `json:"starlark,omitempty" yaml:"starlark,omitempty"`
137
138
139 Exec ExecSpec `json:"exec,omitempty" yaml:"exec,omitempty"`
140 }
141
142 type ExecSpec struct {
143 Path string `json:"path,omitempty" yaml:"path,omitempty"`
144 }
145
146
147 type ContainerSpec struct {
148
149 Image string `json:"image,omitempty" yaml:"image,omitempty"`
150
151
152 Network bool `json:"network,omitempty" yaml:"network,omitempty"`
153
154
155 StorageMounts []StorageMount `json:"mounts,omitempty" yaml:"mounts,omitempty"`
156
157
158 Env []string `json:"envs,omitempty" yaml:"envs,omitempty"`
159 }
160
161
162 type StarlarkSpec struct {
163 Name string `json:"name,omitempty" yaml:"name,omitempty"`
164
165
166 Path string `json:"path,omitempty" yaml:"path,omitempty"`
167
168
169 URL string `json:"url,omitempty" yaml:"url,omitempty"`
170 }
171
172
173 type StorageMount struct {
174
175 MountType string `json:"type,omitempty" yaml:"type,omitempty"`
176
177
178
179
180
181 Src string `json:"src,omitempty" yaml:"src,omitempty"`
182
183
184 DstPath string `json:"dst,omitempty" yaml:"dst,omitempty"`
185
186
187
188 ReadWriteMode bool `json:"rw,omitempty" yaml:"rw,omitempty"`
189 }
190
191 func (s *StorageMount) String() string {
192 mode := ""
193 if !s.ReadWriteMode {
194 mode = ",readonly"
195 }
196 return fmt.Sprintf("type=%s,source=%s,target=%s%s", s.MountType, s.Src, s.DstPath, mode)
197 }
198
199
200
201
202
203
204 func GetFunctionSpec(n *yaml.RNode) (*FunctionSpec, error) {
205 meta, err := n.GetMeta()
206 if err != nil {
207 return nil, fmt.Errorf("failed to get ResourceMeta: %w", err)
208 }
209
210 fn, err := getFunctionSpecFromAnnotation(n, meta)
211 if err != nil {
212 return nil, err
213 }
214 if fn != nil {
215 return fn, nil
216 }
217
218
219 container := meta.Annotations["config.kubernetes.io/container"]
220 if container != "" {
221 return &FunctionSpec{Container: ContainerSpec{Image: container}}, nil
222 }
223 return nil, nil
224 }
225
226
227
228 func getFunctionSpecFromAnnotation(n *yaml.RNode, meta yaml.ResourceMeta) (*FunctionSpec, error) {
229 var fs FunctionSpec
230 for _, s := range functionAnnotationKeys {
231 fn := meta.Annotations[s]
232 if fn != "" {
233 if err := k8syaml.UnmarshalStrict([]byte(fn), &fs); err != nil {
234 return nil, fmt.Errorf("%s unmarshal error: %w", s, err)
235 }
236 return &fs, nil
237 }
238 }
239 n, err := n.Pipe(yaml.Lookup("metadata", "configFn"))
240 if err != nil {
241 return nil, fmt.Errorf("failed to look up metadata.configFn: %w", err)
242 }
243 if yaml.IsMissingOrNull(n) {
244 return nil, nil
245 }
246 s, err := n.String()
247 if err != nil {
248 fmt.Fprintf(os.Stderr, "configFn parse error: %v\n", err)
249 return nil, fmt.Errorf("configFn parse error: %w", err)
250 }
251 if err := k8syaml.UnmarshalStrict([]byte(s), &fs); err != nil {
252 return nil, fmt.Errorf("%s unmarshal error: %w", "configFn", err)
253 }
254 return &fs, nil
255 }
256
257 func StringToStorageMount(s string) StorageMount {
258 m := make(map[string]string)
259 options := strings.Split(s, ",")
260 for _, option := range options {
261 keyVal := strings.SplitN(option, "=", 2)
262 if len(keyVal) == 2 {
263 m[keyVal[0]] = keyVal[1]
264 }
265 }
266 var sm StorageMount
267 for key, value := range m {
268 switch {
269 case key == "type":
270 sm.MountType = value
271 case key == "src" || key == "source":
272 sm.Src = value
273 case key == "dst" || key == "target":
274 sm.DstPath = value
275 case key == "rw" && value == "true":
276 sm.ReadWriteMode = true
277 }
278 }
279 return sm
280 }
281
282
283
284
285 type IsReconcilerFilter struct {
286
287
288
289 ExcludeReconcilers bool `yaml:"excludeReconcilers,omitempty"`
290
291
292 IncludeNonReconcilers bool `yaml:"includeNonReconcilers,omitempty"`
293 }
294
295
296 func (c *IsReconcilerFilter) Filter(inputs []*yaml.RNode) ([]*yaml.RNode, error) {
297 var out []*yaml.RNode
298 for i := range inputs {
299 functionSpec, err := GetFunctionSpec(inputs[i])
300 if err != nil {
301 return nil, err
302 }
303 isFnResource := functionSpec != nil
304 if isFnResource && !c.ExcludeReconcilers {
305 out = append(out, inputs[i])
306 }
307 if !isFnResource && c.IncludeNonReconcilers {
308 out = append(out, inputs[i])
309 }
310 }
311 return out, nil
312 }
313
View as plain text