blob: 8b49905ded966b8bd2ccdd827e5daaccb2479027 [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
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040025)
26
gio44f621b2024-04-29 09:44:38 +040027type Field struct {
28 Name string
29 Schema Schema
30}
31
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040032type Schema interface {
gio44f621b2024-04-29 09:44:38 +040033 Name() string
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040034 Kind() Kind
gio44f621b2024-04-29 09:44:38 +040035 Fields() []Field
36 Advanced() bool
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040037}
38
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +040039var AuthSchema Schema = structSchema{
gio44f621b2024-04-29 09:44:38 +040040 name: "Auth",
41 fields: []Field{
42 Field{"enabled", basicSchema{"Enabled", KindBoolean, false}},
43 Field{"groups", basicSchema{"Groups", KindString, false}},
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +040044 },
gio44f621b2024-04-29 09:44:38 +040045 advanced: false,
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +040046}
47
Giorgi Lekveishvilib6a58062024-04-02 16:49:19 +040048var SSHKeySchema Schema = structSchema{
gio44f621b2024-04-29 09:44:38 +040049 name: "SSH Key",
50 fields: []Field{
51 Field{"public", basicSchema{"Public Key", KindString, false}},
52 Field{"private", basicSchema{"Private Key", KindString, false}},
Giorgi Lekveishvilib6a58062024-04-02 16:49:19 +040053 },
gio44f621b2024-04-29 09:44:38 +040054 advanced: true,
Giorgi Lekveishvilib6a58062024-04-02 16:49:19 +040055}
56
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040057const networkSchema = `
58#Network: {
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +040059 name: string
60 ingressClass: string
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +040061 certificateIssuer: string | *""
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +040062 domain: string
Giorgi Lekveishvilib59b7c22024-04-03 22:17:50 +040063 allocatePortAddr: string
gioefa0ed42024-06-13 12:31:43 +040064 reservePortAddr: string
giocdfa3722024-06-13 20:10:14 +040065 deallocatePortAddr: string
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040066}
67
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +040068value: { %s }
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040069`
70
71func isNetwork(v cue.Value) bool {
72 if v.Value().Kind() != cue.StructKind {
73 return false
74 }
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +040075 s := fmt.Sprintf(networkSchema, fmt.Sprintf("%#v", v))
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040076 c := cuecontext.New()
77 u := c.CompileString(s)
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +040078 network := u.LookupPath(cue.ParsePath("#Network"))
79 vv := u.LookupPath(cue.ParsePath("value"))
80 if err := network.Subsume(vv); err == nil {
81 return true
82 }
83 return false
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040084}
85
gio4ece99c2024-07-18 11:05:50 +040086const multiNetworkSchema = `
87#Network: {
88 name: string
89 ingressClass: string
90 certificateIssuer: string | *""
91 domain: string
92 allocatePortAddr: string
93 reservePortAddr: string
94 deallocatePortAddr: string
95}
96
97#Networks: [...#Network]
98
99value: %s
100`
101
102func isMultiNetwork(v cue.Value) bool {
103 if v.Value().IncompleteKind() != cue.ListKind {
104 return false
105 }
106 s := fmt.Sprintf(multiNetworkSchema, fmt.Sprintf("%#v", v))
107 c := cuecontext.New()
108 u := c.CompileString(s)
109 networks := u.LookupPath(cue.ParsePath("#Networks"))
110 vv := u.LookupPath(cue.ParsePath("value"))
111 if err := networks.Subsume(vv); err == nil {
112 return true
113 }
114 return false
115}
116
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400117const authSchema = `
118#Auth: {
119 enabled: bool | false
120 groups: string | *""
121}
122
123value: { %s }
124`
125
126func isAuth(v cue.Value) bool {
127 if v.Value().Kind() != cue.StructKind {
128 return false
129 }
130 s := fmt.Sprintf(authSchema, fmt.Sprintf("%#v", v))
131 c := cuecontext.New()
132 u := c.CompileString(s)
133 auth := u.LookupPath(cue.ParsePath("#Auth"))
134 vv := u.LookupPath(cue.ParsePath("value"))
135 if err := auth.Subsume(vv); err == nil {
136 return true
137 }
138 return false
139}
140
Giorgi Lekveishvilib6a58062024-04-02 16:49:19 +0400141const sshKeySchema = `
142#SSHKey: {
143 public: string
144 private: string
145}
146
147value: { %s }
148`
149
150func isSSHKey(v cue.Value) bool {
151 if v.Value().Kind() != cue.StructKind {
152 return false
153 }
154 s := fmt.Sprintf(sshKeySchema, fmt.Sprintf("%#v", v))
155 c := cuecontext.New()
156 u := c.CompileString(s)
157 sshKey := u.LookupPath(cue.ParsePath("#SSHKey"))
158 vv := u.LookupPath(cue.ParsePath("value"))
159 if err := sshKey.Subsume(vv); err == nil {
160 return true
161 }
162 return false
163}
164
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400165type basicSchema struct {
gio44f621b2024-04-29 09:44:38 +0400166 name string
167 kind Kind
168 advanced bool
169}
170
171func (s basicSchema) Name() string {
172 return s.name
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400173}
174
175func (s basicSchema) Kind() Kind {
176 return s.kind
177}
178
gio44f621b2024-04-29 09:44:38 +0400179func (s basicSchema) Fields() []Field {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400180 return nil
181}
182
gio44f621b2024-04-29 09:44:38 +0400183func (s basicSchema) Advanced() bool {
184 return s.advanced
185}
186
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400187type structSchema struct {
gio44f621b2024-04-29 09:44:38 +0400188 name string
189 fields []Field
190 advanced bool
191}
192
193func (s structSchema) Name() string {
194 return s.name
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400195}
196
197func (s structSchema) Kind() Kind {
198 return KindStruct
199}
200
gio44f621b2024-04-29 09:44:38 +0400201func (s structSchema) Fields() []Field {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400202 return s.fields
203}
204
gio44f621b2024-04-29 09:44:38 +0400205func (s structSchema) Advanced() bool {
206 return s.advanced
207}
208
209func NewCueSchema(name string, v cue.Value) (Schema, error) {
210 nameAttr := v.Attribute("name")
211 if nameAttr.Err() == nil {
212 name = nameAttr.Contents()
213 }
gioefa0ed42024-06-13 12:31:43 +0400214 role := ""
215 roleAttr := v.Attribute("role")
216 if roleAttr.Err() == nil {
217 role = strings.ToLower(roleAttr.Contents())
218 }
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400219 switch v.IncompleteKind() {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400220 case cue.StringKind:
gio44f621b2024-04-29 09:44:38 +0400221 return basicSchema{name, KindString, false}, nil
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400222 case cue.BoolKind:
gio44f621b2024-04-29 09:44:38 +0400223 return basicSchema{name, KindBoolean, false}, nil
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +0400224 case cue.NumberKind:
gio44f621b2024-04-29 09:44:38 +0400225 return basicSchema{name, KindNumber, false}, nil
Giorgi Lekveishvilib59b7c22024-04-03 22:17:50 +0400226 case cue.IntKind:
gioefa0ed42024-06-13 12:31:43 +0400227 if role == "port" {
228 return basicSchema{name, KindPort, true}, nil
229 } else {
230 return basicSchema{name, KindInt, false}, nil
231 }
gioe72b54f2024-04-22 10:44:41 +0400232 case cue.ListKind:
gio4ece99c2024-07-18 11:05:50 +0400233 if isMultiNetwork(v) {
234 return basicSchema{name, KindMultiNetwork, false}, nil
235 }
gio44f621b2024-04-29 09:44:38 +0400236 return basicSchema{name, KindArrayString, false}, nil
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400237 case cue.StructKind:
238 if isNetwork(v) {
gio44f621b2024-04-29 09:44:38 +0400239 return basicSchema{name, KindNetwork, false}, nil
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400240 } else if isAuth(v) {
gio44f621b2024-04-29 09:44:38 +0400241 return basicSchema{name, KindAuth, false}, nil
Giorgi Lekveishvilib6a58062024-04-02 16:49:19 +0400242 } else if isSSHKey(v) {
gio44f621b2024-04-29 09:44:38 +0400243 return basicSchema{name, KindSSHKey, true}, nil
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400244 }
gio44f621b2024-04-29 09:44:38 +0400245 s := structSchema{name, make([]Field, 0), false}
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400246 f, err := v.Fields(cue.Schema())
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400247 if err != nil {
248 return nil, err
249 }
250 for f.Next() {
gio44f621b2024-04-29 09:44:38 +0400251 scm, err := NewCueSchema(f.Selector().String(), f.Value())
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400252 if err != nil {
253 return nil, err
254 }
gio44f621b2024-04-29 09:44:38 +0400255 s.fields = append(s.fields, Field{f.Selector().String(), scm})
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400256 }
257 return s, nil
258 default:
259 return nil, fmt.Errorf("SHOULD NOT REACH!")
260 }
261}