blob: a0acfc345cb0130a4a24db97307a8f3e38b3b265 [file] [log] [blame]
package installer
import (
"fmt"
"cuelang.org/go/cue"
"cuelang.org/go/cue/cuecontext"
)
type Kind int
const (
KindBoolean Kind = 0
KindInt = 7
KindString = 1
KindStruct = 2
KindNetwork = 3
KindAuth = 5
KindSSHKey = 6
KindNumber = 4
KindArrayString = 8
)
type Field struct {
Name string
Schema Schema
}
type Schema interface {
Name() string
Kind() Kind
Fields() []Field
Advanced() bool
}
var AuthSchema Schema = structSchema{
name: "Auth",
fields: []Field{
Field{"enabled", basicSchema{"Enabled", KindBoolean, false}},
Field{"groups", basicSchema{"Groups", KindString, false}},
},
advanced: false,
}
var SSHKeySchema Schema = structSchema{
name: "SSH Key",
fields: []Field{
Field{"public", basicSchema{"Public Key", KindString, false}},
Field{"private", basicSchema{"Private Key", KindString, false}},
},
advanced: true,
}
const networkSchema = `
#Network: {
name: string
ingressClass: string
certificateIssuer: string | *""
domain: string
allocatePortAddr: string
}
value: { %s }
`
func isNetwork(v cue.Value) bool {
if v.Value().Kind() != cue.StructKind {
return false
}
s := fmt.Sprintf(networkSchema, fmt.Sprintf("%#v", v))
c := cuecontext.New()
u := c.CompileString(s)
network := u.LookupPath(cue.ParsePath("#Network"))
vv := u.LookupPath(cue.ParsePath("value"))
if err := network.Subsume(vv); err == nil {
return true
}
return false
}
const authSchema = `
#Auth: {
enabled: bool | false
groups: string | *""
}
value: { %s }
`
func isAuth(v cue.Value) bool {
if v.Value().Kind() != cue.StructKind {
return false
}
s := fmt.Sprintf(authSchema, fmt.Sprintf("%#v", v))
c := cuecontext.New()
u := c.CompileString(s)
auth := u.LookupPath(cue.ParsePath("#Auth"))
vv := u.LookupPath(cue.ParsePath("value"))
if err := auth.Subsume(vv); err == nil {
return true
}
return false
}
const sshKeySchema = `
#SSHKey: {
public: string
private: string
}
value: { %s }
`
func isSSHKey(v cue.Value) bool {
if v.Value().Kind() != cue.StructKind {
return false
}
s := fmt.Sprintf(sshKeySchema, fmt.Sprintf("%#v", v))
c := cuecontext.New()
u := c.CompileString(s)
sshKey := u.LookupPath(cue.ParsePath("#SSHKey"))
vv := u.LookupPath(cue.ParsePath("value"))
if err := sshKey.Subsume(vv); err == nil {
return true
}
return false
}
type basicSchema struct {
name string
kind Kind
advanced bool
}
func (s basicSchema) Name() string {
return s.name
}
func (s basicSchema) Kind() Kind {
return s.kind
}
func (s basicSchema) Fields() []Field {
return nil
}
func (s basicSchema) Advanced() bool {
return s.advanced
}
type structSchema struct {
name string
fields []Field
advanced bool
}
func (s structSchema) Name() string {
return s.name
}
func (s structSchema) Kind() Kind {
return KindStruct
}
func (s structSchema) Fields() []Field {
return s.fields
}
func (s structSchema) Advanced() bool {
return s.advanced
}
func NewCueSchema(name string, v cue.Value) (Schema, error) {
nameAttr := v.Attribute("name")
if nameAttr.Err() == nil {
name = nameAttr.Contents()
}
switch v.IncompleteKind() {
case cue.StringKind:
return basicSchema{name, KindString, false}, nil
case cue.BoolKind:
return basicSchema{name, KindBoolean, false}, nil
case cue.NumberKind:
return basicSchema{name, KindNumber, false}, nil
case cue.IntKind:
return basicSchema{name, KindInt, false}, nil
case cue.ListKind:
return basicSchema{name, KindArrayString, false}, nil
case cue.StructKind:
if isNetwork(v) {
return basicSchema{name, KindNetwork, false}, nil
} else if isAuth(v) {
return basicSchema{name, KindAuth, false}, nil
} else if isSSHKey(v) {
return basicSchema{name, KindSSHKey, true}, nil
}
s := structSchema{name, make([]Field, 0), false}
f, err := v.Fields(cue.Schema())
if err != nil {
return nil, err
}
for f.Next() {
scm, err := NewCueSchema(f.Selector().String(), f.Value())
if err != nil {
return nil, err
}
s.fields = append(s.fields, Field{f.Selector().String(), scm})
}
return s, nil
default:
return nil, fmt.Errorf("SHOULD NOT REACH!")
}
}