9e005fb6d8
This change renames Schemas to Types on Stack. More interestingly, it renames the JSON/YAML property used to specify them, from "schemas:" to "types:"; I feel like this reads more naturally, especially as a sister to the existing "services:" section.
292 lines
13 KiB
Go
292 lines
13 KiB
Go
// Copyright 2016 Marapongo, Inc. All rights reserved.
|
|
|
|
// This package contains the core Mu abstract syntax tree types.
|
|
//
|
|
// N.B. for the time being, we are leveraging the same set of types for parse trees and abstract syntax trees. The
|
|
// reason is that minimal "extra" information is necessary between front- and back-end parts of the compiler, and so
|
|
// reusing the trees leads to less duplication in types and faster runtime performance. As the compiler matures in
|
|
// functionality, we may want to revisit this. The "back-end-only" parts of the data structures are easily identified
|
|
// because their fields do not map to any serializable fields (i.e., `json:"-"`).
|
|
//
|
|
// Another controversial decision is to mutate nodes in place, rather than taking the performance hit of immutability.
|
|
// This can certainly be tricky to deal with, however, it is simpler and we can revisit it down the road if needed.
|
|
// Of course, during lowering, sometimes nodes will be transformed to new types entirely, allocating entirely anew.
|
|
package ast
|
|
|
|
import (
|
|
"github.com/marapongo/mu/pkg/diag"
|
|
)
|
|
|
|
// Name is an identifier. Names may be optionally fully qualified, using the delimiter `/`, or simple. Each element
|
|
// conforms to the regex [A-Za-z_][A-Za-z0-9_]*. For example, `marapongo/mu/stack`.
|
|
type Name string
|
|
|
|
// Ref is a dependency reference. It is "name-like", in that it contains a Name embedded inside of it, but also carries
|
|
// a URL-like structure. A Ref starts with an optional "protocol" (like https://, git://, etc), followed by an optional
|
|
// "base" part (like hub.mu.com/, github.com/, etc), followed by the "name" part (which is just a Name), followed by
|
|
// an optional "@" and version number (where version may be "latest", a semantic version range, or a Git SHA hash).
|
|
type Ref string
|
|
|
|
// Version represents a precise version number. It may be either a Git SHA hash or a semantic version (not a range).
|
|
type Version string
|
|
|
|
// VersionSpec represents a specification of a version that is bound to a precise number through a separate process.
|
|
// It may take the form of a Version (see above), a semantic version range, or the string "latest", to indicate that the
|
|
// latest available sources are to be used at compile-time.
|
|
type VersionSpec string
|
|
|
|
// Node is the base of all abstract syntax tree types.
|
|
type Node struct {
|
|
}
|
|
|
|
func (node *Node) Where() (*diag.Document, *diag.Location) {
|
|
// TODO[marapongo/mu#14]: implement diag.Diagable on all AST nodes.
|
|
return nil, nil
|
|
}
|
|
|
|
// Workspace defines settings shared amongst many related Stacks.
|
|
type Workspace struct {
|
|
Node
|
|
|
|
Namespace string `json:"namespace,omitempty"` // an optional namespace for this project space.
|
|
Clusters Clusters `json:"clusters,omitempty"` // an optional set of predefined target clusters.
|
|
Dependencies Dependencies `json:"dependencies,omitempty"`
|
|
|
|
Doc *diag.Document `json:"-"` // the document from which this came.
|
|
}
|
|
|
|
func (w *Workspace) Where() (*diag.Document, *diag.Location) {
|
|
return w.Doc, nil
|
|
}
|
|
|
|
// Clusters is a map of target names to metadata about those targets.
|
|
type Clusters map[string]*Cluster
|
|
|
|
// Cluster describes a predefined cloud runtime target, including its OS and Scheduler combination.
|
|
type Cluster struct {
|
|
Node
|
|
|
|
Default bool `json:"default,omitempty"` // a single target can carry default settings.
|
|
Description string `json:"description,omitempty"` // a human-friendly description of this target.
|
|
Cloud string `json:"cloud,omitempty"` // the cloud target.
|
|
Scheduler string `json:"scheduler,omitempty"` // the cloud scheduler target.
|
|
Settings PropertyBag `json:"settings,omitempty"` // any options passed to the cloud provider.
|
|
|
|
Name string `json:"-"` // name is decorated post-parsing, since it is contextual.
|
|
}
|
|
|
|
// Dependencies maps dependency refs to the semantic version the consumer depends on.
|
|
type Dependencies map[Ref]*Dependency
|
|
|
|
// Dependency is metadata describing a dependency target (for now, just its target version).
|
|
type Dependency VersionSpec
|
|
|
|
// Stack represents a collection of private and public cloud resources, a method for constructing them, and optional
|
|
// dependencies on other Stacks (by name).
|
|
type Stack struct {
|
|
Node
|
|
|
|
Name Name `json:"name,omitempty"` // a friendly name for this node.
|
|
Version Version `json:"version,omitempty"` // a specific version number.
|
|
Description string `json:"description,omitempty"` // an optional friendly description.
|
|
Author string `json:"author,omitempty"` // an optional author.
|
|
Website string `json:"website,omitempty"` // an optional website for additional info.
|
|
License string `json:"license,omitempty"` // an optional license governing legal uses of this package.
|
|
|
|
Base Ref `json:"base,omitempty"` // an optional base Stack type.
|
|
BoundBase *Stack `json:"-"` // base, optionally bound during analysis.
|
|
Abstract bool `json:"abstract,omitempty"` // true if this stack is "abstract".
|
|
Intrinsic bool `json:"intrinsic,omitempty"` // true if this stack is an "intrinsic" type.
|
|
Properties Properties `json:"properties,omitempty"`
|
|
PropertyValues PropertyBag `json:"-"` // the properties used to construct this stack.
|
|
BoundPropertyValues LiteralPropertyBag `json:"-"` // bound properties used to construct this stack.
|
|
Types Schemas `json:"types,omitempty"` // an optional types section with custom schemas.
|
|
Services Services `json:"services,omitempty"`
|
|
|
|
Doc *diag.Document `json:"-"` // the document from which this came.
|
|
|
|
// TODO[marapongo/mu#8]: permit Stacks to declare exported APIs.
|
|
}
|
|
|
|
func (stack *Stack) Where() (*diag.Document, *diag.Location) {
|
|
return stack.Doc, nil
|
|
}
|
|
|
|
// UninstStack represents a dependency that hasn't yet been instantiated. This is like an uninstantiated generic type
|
|
// in classical programming languages, except that in our case we use template expansion on the document itself.
|
|
// TODO(joe): eventually this ought to also encompass cross-stack schema references.
|
|
type UninstStack struct {
|
|
Node
|
|
Ref Ref `json:"-"`
|
|
Doc *diag.Document `json:"-"`
|
|
}
|
|
|
|
// DependendyRefs is simply a map of dependency reference to the associated uninstantiated Stack for that dependency.
|
|
type DependencyRefs map[Ref]*UninstStack
|
|
|
|
// Propertys maps property names to metadata about those propertys.
|
|
type Properties map[string]*Property
|
|
|
|
// Property describes the requirements of arguments used when constructing Stacks, etc.
|
|
type Property struct {
|
|
Node
|
|
|
|
Type Ref `json:"type,omitempty"` // the type of the property; required.
|
|
BoundType *Type `json:"-"` // if the property is a stack type, it will be bound.
|
|
Description string `json:"description,omitempty"` // an optional friendly description of the property.
|
|
Default interface{} `json:"default,omitempty"` // an optional default value if the caller elides one.
|
|
Optional bool `json:"optional,omitempty"` // true if may be omitted (inferred if a default value).
|
|
Readonly bool `json:"readonly,omitempty"` // true if this property is readonly.
|
|
Perturbs bool `json:"perturbs,omitempty"` // true if changing this property is perturbing.
|
|
|
|
Name string `json:"-"` // name is decorated post-parsing, since it is contextual.
|
|
}
|
|
|
|
// Schemas is a list of public and private service references, keyed by name.
|
|
type Schemas struct {
|
|
Public SchemaMap `json:"-"`
|
|
Private SchemaMap `json:"-"`
|
|
}
|
|
|
|
// SchemaMap is a map of schema names to metadata about those schemas.
|
|
type SchemaMap map[Name]*Schema
|
|
|
|
// Schema represents a complex schema type that extends Mu's type system and can be used by name.
|
|
// TODO[marapongo/mu#9]: support the full set of JSON schema operators (like allOf, anyOf, etc.); to see the full list,
|
|
// refer to the spec: http://json-schema.org/latest/json-schema-validation.html.
|
|
// TODO[marapongo/mu#9]: we deviate from the spec in a few areas; e.g., we default to required and support optional. We
|
|
// should do an audit of all such places and decide whether it's worth deviating. If yes, we must clearly document.
|
|
type Schema struct {
|
|
Node
|
|
|
|
Base Ref `json:"base,omitempty"` // the base type from which this derives.
|
|
BoundBase *Type `json:"-"` // base, optionally bound during analysis.
|
|
Properties Properties `json:"properties,omitempty"` // all of the custom properties for object types.
|
|
|
|
// constraints for string types:
|
|
Pattern *string `json:"pattern,omitempty"` // an optional regex pattern for string types.
|
|
MaxLength *float64 `json:"maxLength,omitempty"` // an optional max string length (in characters).
|
|
MinLength *float64 `json:"minLength,omitempty"` // an optional min string length (in characters).
|
|
|
|
// constraints for numeric types:
|
|
Maximum *float64 `json:"maximum,omitempty"` // an optional max value for numeric types.
|
|
Minimum *float64 `json:"minimum,omitempty"` // an optional min value for numeric types.
|
|
|
|
// constraints for strings *and* number types:
|
|
Enum []interface{} `json:"enum,omitempty"` // an optional enum of legal values.
|
|
|
|
Name Name `json:"-"` // a friendly name; decorated post-parsing, since it is contextual.
|
|
Public bool `json:"-"` // true if this schema type is publicly exposed; also decorated post-parsing.
|
|
}
|
|
|
|
// Services is a list of public and private service references, keyed by name.
|
|
type Services struct {
|
|
// These fields are expanded after parsing:
|
|
Public ServiceMap `json:"-"`
|
|
Private ServiceMap `json:"-"`
|
|
|
|
// These fields are "untyped" due to limitations in the JSON parser. Namely, Go's parser will ignore
|
|
// properties in the payload that it doesn't recognize as mapping to a field. That's not what we want, especially
|
|
// for services since they are highly extensible and the contents will differ per-type. Therefore, we will first
|
|
// map the services into a weakly typed map, and later on during compilation, expand them to the below fields.
|
|
// TODO[marapongo/mu#4]: support for `json:",inline"` or the equivalent so we can eliminate these fields.
|
|
PublicUntyped UntypedServiceMap `json:"public,omitempty"`
|
|
PrivateUntyped UntypedServiceMap `json:"private,omitempty"`
|
|
}
|
|
|
|
// ServiceMap is a map of service names to metadata about those services.
|
|
type ServiceMap map[Name]*Service
|
|
|
|
// UntypedServiceMap is a map of service names to untyped, bags of parsed properties for those services.
|
|
type UntypedServiceMap map[Name]PropertyBag
|
|
|
|
// Service is a directive for instantiating another Stack, including its name, arguments, etc.
|
|
type Service struct {
|
|
Node
|
|
|
|
Type Ref `json:"type,omitempty"` // an explicit type; if missing, the name is used.
|
|
BoundType *Stack `json:"-"` // services are bound to stacks during semantic analysis.
|
|
Properties PropertyBag `json:"-"` // all of the custom properties (minus what's above).
|
|
BoundProperties LiteralPropertyBag `json:"-"` // the bound properties, expanded and typed correctly.
|
|
|
|
Name Name `json:"-"` // a friendly name; decorated post-parsing, since it is contextual.
|
|
Public bool `json:"-"` // true if this service is publicly exposed; also decorated post-parsing.
|
|
}
|
|
|
|
// PropertyBag is simply a map of string property names to untyped data values.
|
|
type PropertyBag map[string]interface{}
|
|
|
|
// LiteralPropertyBag is simply a map of string property names to literal typed AST nodes.
|
|
type LiteralPropertyBag map[string]Literal
|
|
|
|
// ServiceRef is an intra- or inter-stack reference to a specific service.
|
|
type ServiceRef struct {
|
|
Name Name // the name used to resolve the capability.
|
|
Selector Name // the "selector" used if the target service exports multiple endpoints.
|
|
Service *Service // the service that this capability reference names.
|
|
Selected *Service // the selected service resolved during binding.
|
|
}
|
|
|
|
// Literal represents a strongly typed AST value.
|
|
type Literal interface {
|
|
Node() *Node
|
|
Type() *Type
|
|
}
|
|
|
|
// AnyLiteral is an AST node containing a literal value of "any" type (`interface{}`).
|
|
type AnyLiteral interface {
|
|
Literal
|
|
Any() interface{}
|
|
}
|
|
|
|
// BoolLiteral is an AST node containing a literal boolean (`bool`).
|
|
type BoolLiteral interface {
|
|
Literal
|
|
Bool() bool
|
|
}
|
|
|
|
// NumberLiteral is an AST node containing a literal number (`float64`).
|
|
type NumberLiteral interface {
|
|
Literal
|
|
Number() float64
|
|
}
|
|
|
|
// StringLiteral is an AST node containing a literal string (`string`).
|
|
type StringLiteral interface {
|
|
Literal
|
|
String() string
|
|
}
|
|
|
|
// ServiceLiteral is an AST node containing a literal capability reference.
|
|
type ServiceLiteral interface {
|
|
Literal
|
|
Service() *ServiceRef
|
|
}
|
|
|
|
// ArrayLiteral is an AST node containing an array of other literals (`[]x`).
|
|
type ArrayLiteral interface {
|
|
Literal
|
|
ElemType() *Type
|
|
Array() []Literal
|
|
}
|
|
|
|
// MapLiteral is an AST node containing a map from literals of a certain type to other literals.
|
|
type MapLiteral interface {
|
|
Literal
|
|
KeyType() *Type
|
|
ValueType() *Type
|
|
Keys() []Literal
|
|
Values() []Literal
|
|
}
|
|
|
|
// SchemaLiteral is an AST node containing a literal value that is strongly typed, but too complex to represent
|
|
// structually in Go's type system. For these types, we resort to dynamic manipulation of the contents.
|
|
type SchemaLiteral interface {
|
|
Literal
|
|
Properties() LiteralPropertyBag
|
|
}
|
|
|
|
// TODO[marapongo/mu#9]: extensible schema support.
|
|
// TODO[marapongo/mu#17]: identity (users, roles, groups).
|
|
// TODO[marapongo/mu#16]: configuration and secret support.
|