blob: fcdebd48579e3f62cf3dcdb2ba16958a5d5a2eba [file] [log] [blame]
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +04001package installer
2
3import (
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +04004 "fmt"
gioefa0ed42024-06-13 12:31:43 +04005 "strings"
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +04006
7 "cuelang.org/go/cue"
8 "cuelang.org/go/cue/cuecontext"
9)
10
11type Kind int
12
13const (
gio4ece99c2024-07-18 11:05:50 +040014 KindBoolean Kind = 0
15 KindInt = 7
16 KindString = 1
17 KindStruct = 2
18 KindNetwork = 3
19 KindMultiNetwork = 10
20 KindAuth = 5
21 KindSSHKey = 6
22 KindNumber = 4
23 KindArrayString = 8
24 KindPort = 9
gio36b23b32024-08-25 12:20:54 +040025 KindVPNAuthKey = 11
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040026)
27
gio44f621b2024-04-29 09:44:38 +040028type Field struct {
29 Name string
30 Schema Schema
31}
32
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040033type Schema interface {
gio44f621b2024-04-29 09:44:38 +040034 Name() string
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040035 Kind() Kind
gio44f621b2024-04-29 09:44:38 +040036 Fields() []Field
37 Advanced() bool
gio36b23b32024-08-25 12:20:54 +040038 Meta() map[string]string
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040039}
40
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +040041var AuthSchema Schema = structSchema{
gio44f621b2024-04-29 09:44:38 +040042 name: "Auth",
43 fields: []Field{
gio36b23b32024-08-25 12:20:54 +040044 Field{"enabled", basicSchema{"Enabled", KindBoolean, false, nil}},
45 Field{"groups", basicSchema{"Groups", KindString, false, nil}},
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +040046 },
gio44f621b2024-04-29 09:44:38 +040047 advanced: false,
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +040048}
49
Giorgi Lekveishvilib6a58062024-04-02 16:49:19 +040050var SSHKeySchema Schema = structSchema{
gio44f621b2024-04-29 09:44:38 +040051 name: "SSH Key",
52 fields: []Field{
gio36b23b32024-08-25 12:20:54 +040053 Field{"public", basicSchema{"Public Key", KindString, false, nil}},
54 Field{"private", basicSchema{"Private Key", KindString, false, nil}},
Giorgi Lekveishvilib6a58062024-04-02 16:49:19 +040055 },
gio44f621b2024-04-29 09:44:38 +040056 advanced: true,
Giorgi Lekveishvilib6a58062024-04-02 16:49:19 +040057}
58
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040059const networkSchema = `
60#Network: {
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +040061 name: string
62 ingressClass: string
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +040063 certificateIssuer: string | *""
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +040064 domain: string
Giorgi Lekveishvilib59b7c22024-04-03 22:17:50 +040065 allocatePortAddr: string
gioefa0ed42024-06-13 12:31:43 +040066 reservePortAddr: string
giocdfa3722024-06-13 20:10:14 +040067 deallocatePortAddr: string
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040068}
69
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +040070value: { %s }
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040071`
72
73func isNetwork(v cue.Value) bool {
74 if v.Value().Kind() != cue.StructKind {
75 return false
76 }
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +040077 s := fmt.Sprintf(networkSchema, fmt.Sprintf("%#v", v))
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040078 c := cuecontext.New()
79 u := c.CompileString(s)
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +040080 network := u.LookupPath(cue.ParsePath("#Network"))
81 vv := u.LookupPath(cue.ParsePath("value"))
82 if err := network.Subsume(vv); err == nil {
83 return true
84 }
85 return false
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040086}
87
gio4ece99c2024-07-18 11:05:50 +040088const multiNetworkSchema = `
89#Network: {
90 name: string
91 ingressClass: string
92 certificateIssuer: string | *""
93 domain: string
94 allocatePortAddr: string
95 reservePortAddr: string
96 deallocatePortAddr: string
97}
98
99#Networks: [...#Network]
100
101value: %s
102`
103
104func isMultiNetwork(v cue.Value) bool {
105 if v.Value().IncompleteKind() != cue.ListKind {
106 return false
107 }
108 s := fmt.Sprintf(multiNetworkSchema, fmt.Sprintf("%#v", v))
109 c := cuecontext.New()
110 u := c.CompileString(s)
111 networks := u.LookupPath(cue.ParsePath("#Networks"))
112 vv := u.LookupPath(cue.ParsePath("value"))
113 if err := networks.Subsume(vv); err == nil {
114 return true
115 }
116 return false
117}
118
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400119const authSchema = `
120#Auth: {
121 enabled: bool | false
122 groups: string | *""
123}
124
125value: { %s }
126`
127
128func isAuth(v cue.Value) bool {
129 if v.Value().Kind() != cue.StructKind {
130 return false
131 }
132 s := fmt.Sprintf(authSchema, fmt.Sprintf("%#v", v))
133 c := cuecontext.New()
134 u := c.CompileString(s)
135 auth := u.LookupPath(cue.ParsePath("#Auth"))
136 vv := u.LookupPath(cue.ParsePath("value"))
137 if err := auth.Subsume(vv); err == nil {
138 return true
139 }
140 return false
141}
142
Giorgi Lekveishvilib6a58062024-04-02 16:49:19 +0400143const sshKeySchema = `
144#SSHKey: {
145 public: string
146 private: string
147}
148
149value: { %s }
150`
151
152func isSSHKey(v cue.Value) bool {
153 if v.Value().Kind() != cue.StructKind {
154 return false
155 }
156 s := fmt.Sprintf(sshKeySchema, fmt.Sprintf("%#v", v))
157 c := cuecontext.New()
158 u := c.CompileString(s)
159 sshKey := u.LookupPath(cue.ParsePath("#SSHKey"))
160 vv := u.LookupPath(cue.ParsePath("value"))
161 if err := sshKey.Subsume(vv); err == nil {
162 return true
163 }
164 return false
165}
166
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400167type basicSchema struct {
gio44f621b2024-04-29 09:44:38 +0400168 name string
169 kind Kind
170 advanced bool
gio36b23b32024-08-25 12:20:54 +0400171 meta map[string]string
gio44f621b2024-04-29 09:44:38 +0400172}
173
174func (s basicSchema) Name() string {
175 return s.name
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400176}
177
178func (s basicSchema) Kind() Kind {
179 return s.kind
180}
181
gio44f621b2024-04-29 09:44:38 +0400182func (s basicSchema) Fields() []Field {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400183 return nil
184}
185
gio44f621b2024-04-29 09:44:38 +0400186func (s basicSchema) Advanced() bool {
187 return s.advanced
188}
189
gio36b23b32024-08-25 12:20:54 +0400190func (s basicSchema) Meta() map[string]string {
191 return s.meta
192}
193
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400194type structSchema struct {
gio44f621b2024-04-29 09:44:38 +0400195 name string
196 fields []Field
197 advanced bool
198}
199
200func (s structSchema) Name() string {
201 return s.name
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400202}
203
204func (s structSchema) Kind() Kind {
205 return KindStruct
206}
207
gio44f621b2024-04-29 09:44:38 +0400208func (s structSchema) Fields() []Field {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400209 return s.fields
210}
211
gio44f621b2024-04-29 09:44:38 +0400212func (s structSchema) Advanced() bool {
213 return s.advanced
214}
215
gio36b23b32024-08-25 12:20:54 +0400216func (s structSchema) Meta() map[string]string {
217 return map[string]string{}
218}
219
gio44f621b2024-04-29 09:44:38 +0400220func NewCueSchema(name string, v cue.Value) (Schema, error) {
221 nameAttr := v.Attribute("name")
222 if nameAttr.Err() == nil {
223 name = nameAttr.Contents()
224 }
gioefa0ed42024-06-13 12:31:43 +0400225 role := ""
226 roleAttr := v.Attribute("role")
227 if roleAttr.Err() == nil {
228 role = strings.ToLower(roleAttr.Contents())
229 }
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400230 switch v.IncompleteKind() {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400231 case cue.StringKind:
gio36b23b32024-08-25 12:20:54 +0400232 if role == "vpnauthkey" {
233 meta := map[string]string{}
gio7fbd4ad2024-08-27 10:06:39 +0400234 usernameFieldAttr := v.Attribute("usernameField")
235 if usernameFieldAttr.Err() == nil {
236 meta["usernameField"] = strings.ToLower(usernameFieldAttr.Contents())
237 }
238 usernameAttr := v.Attribute("username")
239 if usernameAttr.Err() == nil {
240 meta["username"] = strings.ToLower(usernameAttr.Contents())
241 }
242 if len(meta) != 1 {
243 return nil, fmt.Errorf("invalid vpn auth key field meta: %+v", meta)
244 }
gio29f6b872024-09-08 16:14:58 +0400245 enabledFieldAttr := v.Attribute("enabledField")
246 if enabledFieldAttr.Err() == nil {
247 meta["enabledField"] = strings.ToLower(enabledFieldAttr.Contents())
248 }
gio36b23b32024-08-25 12:20:54 +0400249 return basicSchema{name, KindVPNAuthKey, true, meta}, nil
250 } else {
251 return basicSchema{name, KindString, false, nil}, nil
252 }
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400253 case cue.BoolKind:
gio36b23b32024-08-25 12:20:54 +0400254 return basicSchema{name, KindBoolean, false, nil}, nil
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +0400255 case cue.NumberKind:
gio36b23b32024-08-25 12:20:54 +0400256 return basicSchema{name, KindNumber, false, nil}, nil
Giorgi Lekveishvilib59b7c22024-04-03 22:17:50 +0400257 case cue.IntKind:
gioefa0ed42024-06-13 12:31:43 +0400258 if role == "port" {
gio36b23b32024-08-25 12:20:54 +0400259 return basicSchema{name, KindPort, true, nil}, nil
gioefa0ed42024-06-13 12:31:43 +0400260 } else {
gio36b23b32024-08-25 12:20:54 +0400261 return basicSchema{name, KindInt, false, nil}, nil
gioefa0ed42024-06-13 12:31:43 +0400262 }
gioe72b54f2024-04-22 10:44:41 +0400263 case cue.ListKind:
gio4ece99c2024-07-18 11:05:50 +0400264 if isMultiNetwork(v) {
gio36b23b32024-08-25 12:20:54 +0400265 return basicSchema{name, KindMultiNetwork, false, nil}, nil
gio4ece99c2024-07-18 11:05:50 +0400266 }
gio36b23b32024-08-25 12:20:54 +0400267 return basicSchema{name, KindArrayString, false, nil}, nil
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400268 case cue.StructKind:
269 if isNetwork(v) {
gio36b23b32024-08-25 12:20:54 +0400270 return basicSchema{name, KindNetwork, false, nil}, nil
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400271 } else if isAuth(v) {
gio36b23b32024-08-25 12:20:54 +0400272 return basicSchema{name, KindAuth, false, nil}, nil
Giorgi Lekveishvilib6a58062024-04-02 16:49:19 +0400273 } else if isSSHKey(v) {
gio36b23b32024-08-25 12:20:54 +0400274 return basicSchema{name, KindSSHKey, true, nil}, nil
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400275 }
gio44f621b2024-04-29 09:44:38 +0400276 s := structSchema{name, make([]Field, 0), false}
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400277 f, err := v.Fields(cue.Schema())
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400278 if err != nil {
279 return nil, err
280 }
281 for f.Next() {
gio44f621b2024-04-29 09:44:38 +0400282 scm, err := NewCueSchema(f.Selector().String(), f.Value())
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400283 if err != nil {
284 return nil, err
285 }
gio44f621b2024-04-29 09:44:38 +0400286 s.fields = append(s.fields, Field{f.Selector().String(), scm})
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400287 }
288 return s, nil
289 default:
290 return nil, fmt.Errorf("SHOULD NOT REACH!")
291 }
292}