blob: 249190bd51cd11e4b8da646e966250ed4043d9e5 [file] [log] [blame]
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +04001package installer
2
3import (
4 "encoding/json"
5 "fmt"
6 "strings"
7
8 "cuelang.org/go/cue"
9 "cuelang.org/go/cue/cuecontext"
10)
11
12type Kind int
13
14const (
15 KindBoolean Kind = 0
16 KindString = 1
17 KindStruct = 2
18 KindNetwork = 3
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +040019 KindNumber = 4
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040020)
21
22type Schema interface {
23 Kind() Kind
24 Fields() map[string]Schema
25}
26
27const networkSchema = `
28#Network: {
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +040029 name: string
30 ingressClass: string
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +040031 certificateIssuer: string | *""
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +040032 domain: string
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040033}
34
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +040035value: { %s }
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040036`
37
38func isNetwork(v cue.Value) bool {
39 if v.Value().Kind() != cue.StructKind {
40 return false
41 }
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +040042 s := fmt.Sprintf(networkSchema, fmt.Sprintf("%#v", v))
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040043 c := cuecontext.New()
44 u := c.CompileString(s)
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +040045 network := u.LookupPath(cue.ParsePath("#Network"))
46 vv := u.LookupPath(cue.ParsePath("value"))
47 if err := network.Subsume(vv); err == nil {
48 return true
49 }
50 return false
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040051}
52
53type basicSchema struct {
54 kind Kind
55}
56
57func (s basicSchema) Kind() Kind {
58 return s.kind
59}
60
61func (s basicSchema) Fields() map[string]Schema {
62 return nil
63}
64
65type structSchema struct {
66 fields map[string]Schema
67}
68
69func (s structSchema) Kind() Kind {
70 return KindStruct
71}
72
73func (s structSchema) Fields() map[string]Schema {
74 return s.fields
75}
76
77func NewCueSchema(v cue.Value) (Schema, error) {
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +040078 switch v.IncompleteKind() {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040079 case cue.StringKind:
80 return basicSchema{KindString}, nil
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +040081 case cue.BoolKind:
82 return basicSchema{KindBoolean}, nil
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +040083 case cue.NumberKind:
84 return basicSchema{KindNumber}, nil
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040085 case cue.StructKind:
86 if isNetwork(v) {
87 return basicSchema{KindNetwork}, nil
88 }
89 s := structSchema{make(map[string]Schema)}
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +040090 f, err := v.Fields(cue.Schema())
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040091 if err != nil {
92 return nil, err
93 }
94 for f.Next() {
95 scm, err := NewCueSchema(f.Value())
96 if err != nil {
97 return nil, err
98 }
99 s.fields[f.Selector().String()] = scm
100 }
101 return s, nil
102 default:
103 return nil, fmt.Errorf("SHOULD NOT REACH!")
104 }
105}
106
107func newSchema(schema map[string]any) (Schema, error) {
108 switch schema["type"] {
109 case "string":
110 if r, ok := schema["role"]; ok && r == "network" {
111 return basicSchema{KindNetwork}, nil
112 } else {
113 return basicSchema{KindString}, nil
114 }
115 case "object":
116 s := structSchema{make(map[string]Schema)}
117 props := schema["properties"].(map[string]any)
118 for name, schema := range props {
119 sm, _ := schema.(map[string]any)
120 scm, err := newSchema(sm)
121 if err != nil {
122 return nil, err
123 }
124 s.fields[name] = scm
125 }
126 return s, nil
127 default:
128 return nil, fmt.Errorf("SHOULD NOT REACH!")
129 }
130}
131
132func NewJSONSchema(schema string) (Schema, error) {
133 ret := make(map[string]any)
134 if err := json.NewDecoder(strings.NewReader(schema)).Decode(&ret); err != nil {
135 return nil, err
136 }
137 return newSchema(ret)
138}