Commit graph

3079 commits

Author SHA1 Message Date
joeduffy 01658d04bb Begin merging MuPackage/MuIL into the compiler
This is the first change of many to merge the MuPack/MuIL formats
into the heart of the "compiler".

In fact, the entire meaning of the compiler has changed, from
something that took metadata and produced CloudFormation, into
something that takes MuPack/MuIL as input, and produces a MuGL
graph as output.  Although this process is distinctly different,
there are several aspects we can reuse, like workspace management,
dependency resolution, and some amount of name binding and symbol
resolution, just as a few examples.

An overview of the compilation process is available as a comment
inside of the compiler.Compile function, although it is currently
unimplemented.

The relationship between Workspace and Compiler has been semi-
inverted, such that all Compiler instances require a Workspace
object.  This is more natural anyway and moves some of the detection
logic "outside" of the Compiler.  Similarly, Options has moved to
a top-level package, so that Workspace and Compiler may share
access to it without causing package import cycles.

Finally, all that templating crap is gone.  This alone is cause
for mass celebration!
2017-01-17 17:04:15 -08:00
joeduffy bbb60799f8 Add a Require family of functions to pkg/util/contract 2017-01-17 15:58:11 -08:00
joeduffy 0260aae0d4 Add the start of a pkg/graph package
This change introduces a pkg/graph package, which is, for now, very
barebones.  This is where the MuGL data types and functions will go.
2017-01-17 15:57:24 -08:00
joeduffy bc376f8f8d Move pkg/pack/symbols to pkg/symbols 2017-01-17 15:06:53 -08:00
joeduffy 7ea5331f7f Merge pkg/pack/encoding into pkg/encoding 2017-01-17 14:58:45 -08:00
joeduffy 6769107c66 Track module imports
This change tracks the set of imported modules in the ast.Module
structure.  Although we can in principle gather up all imports simply
by looking through the fully qualified names, that's slightly hokey;
and furthermore, to properly initialize all modules, we need to know
in which order to do it (in case there are dependencies).  I briefly
considered leaving it up to MetaMu compilers to inject the module
initialization calls explicitly -- for infinite flexibility and perhaps
greater compatibility with the source languages -- however, I'd much
prefer that all Mu code use a consistent module initialization story.
Therefore, MetaMus declare the module imports, in order, and we will
evaluate the initializers accordingly.
2017-01-17 09:50:32 -08:00
joeduffy 2849a3e64b Print descriptions as header comments 2017-01-16 14:45:32 -08:00
joeduffy 2adb334e6a Fix a bogus decoding case
The ObjectLiteral case should obviously call the decodeObjectLiteral
function, and not decodeArrayLiteral.
2017-01-16 12:27:30 -08:00
joeduffy 57d5538ec1 Fix IfStatement json metadata
The node name is "condition", not "expression".
2017-01-16 12:27:04 -08:00
joeduffy a2d847f1ef Use stable map enumeration
This change uses stable map enumeration so that output doesn't
change randomly based on hashing.
2017-01-16 12:02:33 -08:00
joeduffy 2ee3671c36 Progress on the mu describe command
This change makes considerable progress on the `mu describe` command;
the only thing remaining to be implemented now is full IL printing.  It
now prints the full package/module structure.

For example, to print the set of exports from our scenarios/point test:

    $ mujs tools/mujs/tests/output/scenarios/point/ | mu describe - -e
    package "scenarios/point" {
	    dependencies []
	    module "index" {
		    class "Point" [public] {
			    method "add": (other: any): any
			    property "x" [public, readonly]: number
			    property "y" [public, readonly]: number
			    method ".ctor": (x: number, y: number): any
		    }
	    }
    }

This is just a pretty-printed, but is coming in handy with debugging.
2017-01-16 11:47:21 -08:00
joeduffy 3b0184cec3 Add a missing JSON annotation
The NewExpression AST node type was missing a JSON annotation on
its Type field, leading to decoding errors.

Now, with this, the full suite of MuJS test cases can be unmarshaled
into fully populated MuPack and MuIL structures.
2017-01-16 10:04:25 -08:00
joeduffy 1948380eb2 Add custom decoders to eliminate boilerplate
This change overhauls the approach to custom decoding.  Instead of decoding
the parts of the struct that are "trivial" in one pass, and then patching up
the structure afterwards with custom decoding, the decoder itself understands
the notion of custom decoder functions.

First, the general purpose logic has moved out of pkg/pack/encoding and into
a new package, pkg/util/mapper.  Most functions are now members of a new top-
level type, Mapper, which may be initialized with custom decoders.  This
is a map from target type to a function that can decode objects into it.

Second, the AST-specific decoding logic is rewritten to use it.  All AST nodes
are now supported, including definitions, statements, and expressions.  The
overall approach here is to simply define a custom decoder for any interface
type that will occur in a node field position.  The mapper, upon encountering
such a type, will consult the custom decoder map; if a decoder is found, it
will be used, otherwise an error results.  This decoder then needs to switch
on the type discriminated kind field that is present in the metadata, creating
a concrete struct of the right type, and then converting it to the desired
interface type.  Note that, subtly, interface types used only for "marker"
purposes don't require any custom decoding, because they do not appear in
field positions and therefore won't be encountered during the decoding process.
2017-01-16 09:41:26 -08:00
joeduffy 9939024ccd Begin decoding statements 2017-01-16 07:47:41 -08:00
joeduffy 3ef55ac6b3 Add AST type assertions and kind constants 2017-01-16 07:47:12 -08:00
joeduffy 99f60bf5c8 Clean up some decoding logic
This change splits up the decoding logic into multiple files, to
mirror the AST package structure that the functions correspond to.

Additionally, there is now less "loose" reflection and dynamic lookup
code scattered throughout; it is now consolidated into the decoder,
with a set of "generic" functions like `fieldObject`, `asString`, etc.
2017-01-16 06:46:56 -08:00
joeduffy fee6d94e1f Implement class member decoding
This change implements custom class member decoding.  As with module methods,
the function body AST nodes remain nil, as custom AST decoding isn't yet done.
2017-01-16 06:10:24 -08:00
joeduffy 14c040bc7f Implement custom decoding of ModuleMembers
This change begins to implement some of the AST custom decoding, beneath
the Package's Module map.  In particular, we now unmarshal "one level"
beyond this, populating each Module's ModuleMember map.  This includes
Classes, Exports, ModuleProperties, and ModuleMethods.  The Class AST's
Members have been marked "custom", in addition to Block's Statements,
because they required kind-directed decoding.  But Exports and
ModuleProperties can be decoded entirely using the tag-directed decoding
scheme.  Up next, custom decoding of ClassMembers.  At that point, all
definition-level decoding will be done, leaving MuIL's ASTs.
2017-01-15 14:57:42 -08:00
joeduffy 5f33292496 Move assertion/failure functions
This change just moves the assertion/failure functions from the pkg/util
package to pkg/util/contract, so things read a bit nicer (i.e.,
`contract.Assert(x)` versus `util.Assert(x)`).
2017-01-15 14:26:48 -08:00
joeduffy f0b9f157de Add tests for decoding more complex nested structures
This adds deeper nesting into our tests, plus a few more examples of
pointer-based data structures inside of those deeply nested structures.
2017-01-15 13:50:00 -08:00
joeduffy 1ad99bcea1 Fix a few AST marshaling things
This fixes a few things so that MuPackages now unmarshal:

* Mark Module.Members as requiring "custom" decoding.  This is required
  because that's the first point in the tree that leverages polymorphism.
  Everything "above" this unmarshals just fine (e.g., package+modules).

* As such, stop marking Package.Modules as "custom".

* Add the Kind field to the Node.  Although we won't use this for type
  discrimination in the same way, since Go gives us RTTI on the structs,
  it is required for unmarshaling (to avoid "unrecognized fields" errors)
  and it's probably handy to have around for logging, messages, etc.

* Mark Position.Line and Column with "json" annotations so that they
  unmarshal correctly.
2017-01-14 11:22:54 -08:00
joeduffy bd32867e81 Adjust pointers
This change adjusts pointers correctly when unmarshaling into target
pointer types.  This handles arrays and maps of pointer elements, in
addition to consolidating existing logic for marshaling into a
destination top-level pointer as well.
2017-01-14 11:21:27 -08:00
joeduffy d1188ed8c8 Add recursive struct unmarshaling 2017-01-14 10:06:55 -08:00
joeduffy c0c75d0f08 Split decoder logic out from MuPack-specific decode functions 2017-01-14 09:50:25 -08:00
joeduffy ab2d0ae6cb Implement tag-directed decoding
This change eliminates boilerplate decoding logic in all the different
data structures, and instead uses a new tag-directed decoding scheme.
This works a lot like the JSON deserializers, in that it recognizes the
`json:"name"` tags, except that we permit annotation of fields that
require custom deserialization, as `json:"name,custom"`.  The existing
`json:"name,omitempty"` tag is recognized for optional fields.
2017-01-14 09:42:05 -08:00
joeduffy 120f139812 Decode dependencies metadata 2017-01-14 07:58:21 -08:00
joeduffy d334ea322b Add custom decoding for MuPack metadata
This adds basic custom decoding for the MuPack metadata section of
the incoming JSON/YAML.  Because of the type discriminated union nature
of the incoming payload, we cannot rely on the simple built-in JSON/YAML
unmarshaling behavior.  Note that for the metadata section -- what is
in this checkin -- we could have, but the IL AST nodes are problematic.
(To know what kind of structure to creat requires inspecting the "kind"
field of the IL.)  We will use a reflection-driven walk of the target
structure plus a weakly typed deserialized map[string]interface{}, as
is fairly customary in Go for scenarios like this (though good libaries
seem to be lacking in this area...).
2017-01-14 07:40:13 -08:00
joeduffy e7dbfa59c3 Create MuPack and MuIL packages in our Go toolset
This change carries over all of the metadata shapes in the MuPack
and MuIL file formats to our Go toolset.  This includes creating a
proper discriminated AST type tree along with correct annotations
so that the metadata will serialize and deserialize correctly.
2017-01-13 14:32:10 -08:00
joeduffy d22e6b44b5 Add orElse/orEmpty template functions
This change adds the ability to substitute a default value if one is
missing from a map (orElse), and/or to substitute an empty string (orEmpty).
2016-12-09 17:22:09 -08:00
joeduffy db80229899 Fix a few type binding mistakes
* Persue the default/optional checking if a property value == nil.

* Use the Interface() function to convert a reflect.Type to its underlying
  interface{} value.  This is required for typechecking to check out.

* Also, unrelated to the above, change type assertions to use nil rather than
  allocating real objects.  Although minimal, this incurs less GC pressure.
2016-12-09 13:12:57 -08:00
joeduffy b408c3ce2a Pass compiler options to template evaluation
In some cases, we want to specialize template generation based on
the options passed to the compiler.  This change flows them through
so that they can be accessed as

        {{if .Options.SomeSetting}}
        ...
        {{end}}
2016-12-09 12:42:28 -08:00
joeduffy 3c5ca84d89 Switch back to the official YAML repo
Sam merged the pull request, so we can go back to the official repo.
This closes https://github.com/marapongo/mu/issues/28.
2016-12-09 11:59:05 -08:00
joeduffy e137837455 Revert back to T[], instead of []T, for array syntax
This change reverts the syntax for arrays back to T[] from []T.  The main
reason is that YAML doesn't permit unquoted strings beginning with [], meaning
any array type needs to be quoted as in "[]T", which is annoying compared to all
other primitive types which don't require quotes.  And, anyway, this syntax is
more familiar too.

I've also added a number of tests.
2016-12-07 13:24:05 -08:00
joeduffy 6a8df6126a Reject Refs with missing names 2016-12-07 13:07:54 -08:00
joeduffy eb6ef5a1b8 Fix control paths within bindValue
Any of the bindXValue routines can fail if there was no way to convert
the interface{} to an ast.Literal.  In such a case, we need to issue an
error about the wrong type being passed.  Unfortunately, in the most
recent set of changes, we began simply returning nils without issuing
the error.  This change fixes that.
2016-12-07 12:40:26 -08:00
joeduffy 9e005fb6d8 Rename Schemas to Types
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.
2016-12-06 20:56:47 -08:00
joeduffy 86219e781b Custom types, round 2
This checkin continues progress on marapongo/mu#9.  It's still not
complete, however we're getting there.  In particular, this includes:

* Rename of ComplexLiteral to SchemaLiteral, as it is used exclusively
  for schematized types.  Also includes a set of changes associated
  with this, like deep value conversion to `map[string]interface{}`.

* Binding of schema types included within a Stack.  This allows names in
  type references to be bound to those schema types during typechecking.
  This also includes binding schema properties, reusing all the existing
  property binding logic for stacks.  In this way, properties between
  stacks and custom schema types are one and the same, which is nice.

* Enforcement for custom schema constraints; this includes Pattern,
  MaxLength, MinLength, Maximum, and Minimum, as per the JSON Schema
  specification.
2016-12-06 20:51:05 -08:00
joeduffy 713fe29fef Custom types, round 1
This change overhauls the core of how types are used by the entire
compiler.  In particular, we now have an ast.Type, and have begun
using its use where appropriate.  An ast.Type is a union representing
precisely one of the possible sources of types in the system:

* Primitive type: any, bool, number, string, or service.

* Stack type: a resolved reference to an actual concrete stack.

* Schema type: a resolved reference to an actual concrete schema.

* Unresolved reference: a textual reference that hasn't yet been
  resolved to a concrete artifact.

* Uninstantiated reference: a reference that has been resolved to
  an uninstantiated stack, but hasn't been bound to a concrete
  result yet.  Right now, this can point to a stack, however
  eventually we would imagine this supporting inter-stack schema
  references also.

* Decorated type: either an array or a map; in the array case, there
  is a single inner element type; in the map case, there are two,
  the keys and values; in all cases, the type recurses to any of the
  possibilities listed here.

All of the relevant AST nodes have been overhauled accordingly.

In addition to this, we now have an ast.Schema type.  It is loosely
modeled on JSON Schema in its capabilities (http://json-schema.org/).
Although we parse and perform some visitation and binding of these,
there are mostly placeholders left in the code for the interesting
aspects, such as registering symbols, resolving dependencies, and
typechecking usage of schema types.

This is part of the ongoing work behind marapongo/mu#9.
2016-12-06 14:49:47 -08:00
joeduffy a28f02ce68 Use intrinsics in place of predefineds
This change leverages intrinsics in place of the predefined types.
It remains to be seen if we can reach 100% on this, however I am hopeful.
It's also nice that the system will be built "out of itself" with this
approach; in other words, each of the types is simply a Mufile that can
use conditional targeting as appropriate for the given cloud providers.
If we find that this isn't enough, we can always bring back the concept.
2016-12-05 16:13:49 -08:00
joeduffy ae9ffd0a0c Prohibit instantiation of abstract stacks 2016-12-05 15:53:36 -08:00
joeduffy de58637162 Use the long-hand CF Ref syntax
This change stops using the short-hand "!Ref" YAML syntax.  The Golang
marshaler encodes it with quotes and, apparently, has no way to suppress
this behavior; this isn't surprising, since the YAML parser we're using
admits it doesn't support this aspect of the YAML spec fully.  But that's
okay, the long-hand syntax works just fine, and has the added benefit
that we don't need to special case the logig for JSON versus YAML.
2016-12-05 15:20:11 -08:00
joeduffy 069342fef6 Issue an error for missing property names 2016-12-05 15:18:04 -08:00
joeduffy a82a49291b Simplify mu/x/cf property mapping
I think things have gotten a little out of hand with the way mu/x/cf
auto-maps properties.  In the beginning, it looked like everything could
be trivially auto-mapped, and I wanted to avoid the verbosity of mapping
each property by hand (since you can easily fat finger a name, mess up
capitalization, forget one, etc).  But then we began mapping service
references using proper CloudFormation !Refs, which meant suppressing
some of the auto-mappings, etc., etc.  This led to properties, extraProperties,
skipProperties, renamedProperties, and so on... Pretty confusing IMHO.

I just took a step back and decided to eliminate auto-mapping.  Instead,
you get two options: properties just lists a set of property name mappings,
and extraProperties lets you do template magic to map thing instead if you
wish to take matters into your own hands.  The result isn't too verbose
and has a lot less magic going on so it's easier to understand.
2016-12-05 15:04:33 -08:00
joeduffy 73a3699ea0 Add a renamedProperties section to aws/x/cf
This enables properties to be mapped to arbitrary names, as is needed
to translate strongly typed capability references into string CF IDs.
2016-12-05 14:25:23 -08:00
joeduffy 5b791aab77 Introduce intrinsic types
This change eliminates the special type mu/extension in favor of extensible
intrinsic types.  This subsumes the previous functionality while also fixing
a number of warts with the old model.

In particular, the old mu/extension approach deferred property binding until
very late in the compiler.  In fact, too late.  The backend provider for an
extension simply received an untyped bag of stuff, which it then had to
deal with.  Unfortunately, some operations in the binder are inaccessible
at this point because doing so would cause a cycle.  Furthermore, some
pertinent information is gone at this point, like the scopes and symtables.

The canonical example where we need this is binding services names to the
services themselves; e.g., the AWS CloudFormation "DependsOn" property should
resolve to the actual service names, not the string values.  In the limit,
this requires full binding information.

There were a few solutions I considered, including ones that've required
less code motion, however this one feels the most elegant.

Now we permit types to be marked as "intrinsic."  Binding to these names
is done exactly as ordinary name binding, unlike the special mu/extension
provider name.  In fact, just about everything except code-generation for
these types is the same as ordinary types.  This is perfect for the use case
at hand, which is binding properties.

After this change, for example, "DependsOn" is expanded to real service
names precisely as we need.

As part of this change, I added support for three new basic schema types:

* ast.StringList ("string[]"): a list of strings.
* ast.StringMap ("map[string]any"): a map of strings to anys.
* ast.ServiceList ("service[]"): a list of service references.

Obviously we need to revisit this and add a more complete set.  This work
is already tracked by marapongo/mu#9.

At the end of the day, it's likely I will replace all hard-coded predefined
types with intrinsic types, for similar reasons to the above.
2016-12-05 13:46:18 -08:00
joeduffy 06625e9541 Dig through to the "real" AWS resources for !Refs 2016-12-05 10:46:18 -08:00
joeduffy a6986be090 Auto-map from the stack's bound properties 2016-12-05 10:30:38 -08:00
joeduffy db4a87d7c4 Don't rebind properties
There are two cases to consider in the case of a stack's bound properties.
First, it was a stack we didn't need to construct.  This is the case for
built-in primitives.  In that case, we must rebind each service uniquely.
Second, it was an imported stack, which by definition we had to construct.
In this case, we don't need to rebind the properties -- we already did so
-- and can just reuse them in the service's own bound properties.

I am pondering a better way of representing this and will probably do this
soon.  The concept of unconstructed vs. constructed types should be unified
between the built-in types and imported ones.  (Kind of like generics.)
But, I still need a little bit more time prototyping before I make up my
mind what a better way to represent all of this might look like.
2016-12-05 10:25:56 -08:00
joeduffy 504659c38b Remember stack property values (including bounds)
We need to access the bound property values for a given stack, especially
during code-generation.  This information was present for services before,
however not for stacks constructed via other means (e.g., the top-most one).
This change adds a PropertyValues bag plus a corresponding BoundPropertyValues
to the ast.Stack type.
2016-12-05 10:13:57 -08:00
joeduffy 94cd53f2dd Fix a reference to Props that should be BoundProps 2016-12-03 15:23:28 -08:00
joeduffy f726c21402 Remember parent documents during template expansion
During subtypeOf checking, we need to walk the chain of documents from
which a stack came.  This is because, due to template expansion, we'll
end up with a different document for instantiated types than uninstantiated
ones.  This change keeps track of the parent and walks it appropriately.
2016-12-03 15:19:45 -08:00
joeduffy 17f53f419f Don't force selection
The prior change wasn't quite right vis-a-vis service selection.  By default,
we actually want to pick the service itself that was named by a capability reference.
We only want to pick one of its public exported services as the selected one when
a selector is given.  For convenience, we still have "<service>:." to pick the sole
public service, however, no longer is a selector absolutely required.
2016-12-03 14:46:19 -08:00
joeduffy 412a54e5a7 Switch to joeduffy/yaml
See https://github.com/marapongo/mu/issues/28 for details.
2016-12-03 13:18:08 -08:00
joeduffy e3a2002155 Support binding to arbitrary service types
This implements support for arbitrary service types on properties,
not just the weakly typed "service".  For example, in the AWS stacks,
the aws/ec2/route type requires a routeTable, among other things:

        name: aws/ec2/route
        properties:
                routeTable:
                        type: aws/ec2/routeTable

This not only binds the definition of such properties, but also the
callsites of those creating stacks and supplying values for them.
This includes checking for concrete, instantiated, and even base
types, so that, for instance, if a custom stack derived from
aws/ec2/routeTable using the base property, in the above example
it could be supplied as a legal value for the routeTable property.
2016-12-03 13:00:08 -08:00
joeduffy 9181a69033 Store deps to be bound in an array
The previous code stored dependencies in a map.  This caused non-determinism
in the order in which the resulting dependencies would be bound.  Instead of
doing that, this change tracks them in an array, simply using a map to avoid
binding duplicate dependencies.
2016-12-03 11:30:15 -08:00
joeduffy d63a09ea2f Bind properties that refer to types
A stack property can refer to other stack types.  For example:

        properties:
                gateway:
                        type: aws/ec2/internetGateway
                        ...

In such cases, we need to validate the property during binding,
in addition to binding it to an actual type so that we can later
validate callers who are constructing instances of this stack
and providing property values that we must typecheck.

Note that this binding is subtly different than existing stack
type binding.  All the name validation, resolution, and so forth
are the same.  However, notice that in this case we are not actually
supplying any property setters.  That is, internetGateway is not
an "expanded" type, in that we have not processed any of its templates.
An analogy might help: this is sort of akin referring to an
uninstantiated generic type in a traditional programming language,
versus its instantiated form.  In this case, certain properties aren't
available to us, however we can still use it for type identity, etc.
2016-12-03 11:14:06 -08:00
joeduffy fc5814d5bc Eliminate property enumeration nonderminism 2016-12-03 10:05:59 -08:00
joeduffy 962aa9cb51 Clean up an assertion message 2016-12-02 17:07:15 -08:00
joeduffy 28c6f1575f Move return into switch statement 2016-12-02 17:02:50 -08:00
joeduffy 0644ea0ce5 Transform literals during code-gen
This change properly transforms literal AST nodes during code-gen.
This includes emitting CloudFormation !Refs where appropriate, for
intra-stack references (capability types).
2016-12-02 15:00:44 -08:00
joeduffy 4cf6be0f07 Add some property binding tests
This change adds a handful of property binding tests.

It also fixes:

* AsName should assert IsName.

* Enumerate properties stably, so that it is deterministic.

* Do not issue errors about unrecognized properties for the special
  `mu/extension` type.  It's entire purpose in life is to offer an
  entirely custom set of properties, which the provider is meant to
  validate.

* Default to an empty map if properties are missing.

* Add a "/" to the end of the namespace from the workspace, if present.

And rearranges some code:

* Rename the LiteralX types to XLiteral; e.g., StringLiteral instead of
  LiteralString.  I kept typing XLiteral erroneously.

* Eliminate the Mu prefix on all of the predefined type and service
  functions and types.  It's superfluous and reads nicer this way.

* Swap the order of "expected" vs. "got" in the error message about
  incorrect property types.  It used to say "got %v, expected %v"; I
  personally find that it is more helpful if it says "expected %v,
  got %v".  YMMV.
2016-12-02 14:33:22 -08:00
joeduffy b3e1eab6d8 Allow workspaces to have namespaces
This change permits a workspace to specify a namespace, which is just a name
part that is trimmed off the front of directories when probing for inter-
workspace dependencies.  For example, if our namespace is aws/, normally we'd
need to organize our namespace into directories like:

        <root>
        |       aws/
        |       |       dynamodb/
        |       |       ec2/
        |       |       s3/
        ... and so on ...

If we instead specify a namespace

        namespace: aws

Then we can instead organize our project workspace as follows:

        <root>
        |       dynamodb/
        |       ec2/
        |       s3/
        ... and so on ...
2016-12-02 14:06:39 -08:00
joeduffy 370b0a1406 Implement property binding and typechecking
This is an initial pass at property binding.  For all stack instantiations,
we must verify that the set of properties supplied are correct.  We also must
remember the bound property information so that code-generation has all of
the information it needs to generate correct code (including capability refs).

This entails:

* Ensuring required properties are provided.

* Expanding missing properties that have Default values.

* Type-checking that supplied properties are of the right type.

* Expanding property values into AST literal nodes.

To do this requires a third AST pass in the semantic analysis part of the
compiler.  In the 1st pass, dependencies aren't even known yet; in the 2nd
pass, dependencies have not yet been bound; therefore, we need a 3rd pass,
which can depend on the full binding information for the transitive closure
of AST nodes and dependencies to have been populated with types.

There are a few loose ends in here:

* We don't yet validate top-level stack properties.

* We don't yet validate top-level stack base type properties.

* We don't yet support complex schema property types.

* We don't yet support even "simple" complex property types, like `[ string ]`.

* We don't yet support strongly typed capability property types (just `service`).

That said, I am going to turn to writing a few tests for the basic cases, and then
resume to finishing this afterwards (tracked by marapongo/mu#25).
2016-12-02 13:23:18 -08:00
joeduffy 8bddd4097e Reverse the order of stack/service in CF resource IDs
This order reads a bit nicer; for example

        SelfAwsEc2Route

instead of

        AwsEc2RouteSelf

for a service named Self containing an AwsEc2Route stack.
2016-12-01 16:29:44 -08:00
joeduffy 5976abc9d8 Properly convert interface{} to []string
The prior code could miss arrays of strings during conversion because
the arrays created by the various marshalers are weakly typed.  In other
words, even though they contain strings, the array type is []interface{}.
This change introduces the encoding.ArrayOfStrings function to perform
this conversion, first by checking for []string and returning that directly
where possible, and second, if that fails, checking each element and copying.
2016-12-01 16:20:09 -08:00
joeduffy 40eeab646f Track dependency instantiations during binding phase 2 2016-12-01 16:09:12 -08:00
joeduffy 737efdac1b Fully bind transitive dependencies
This changes the way binding dependencies works slightly, to ensure that
the full transitive closure of dependencies is bound appropriately before
hitting code-generation.  Namely, now binder.PrepareStack returns a list
of unresolved dependency Refs; the compiler is responsible for turning this
into a map from Ref to the loaded diag.Document, before calling BindStack;
then, BindStack instantiates these as necessary (template expansion, etc),
returning an array of unbound *ast.Stacks that the compiler must then bind.
2016-12-01 15:39:58 -08:00
joeduffy 11f7f4963f Go back to glog.Fail
Well, it turns out glog.Fail is slightly better than panic, because it explicitly
dumps the stacks of *all* goroutines.  This is especially good in logging scenarios.
It's really annoying that glog suppresses printing the stack trace (see here
https://github.com/golang/glog/blob/master/glog.go#L719), however this is better.
2016-12-01 11:50:19 -08:00
joeduffy 3a7fa9a983 Use panic for fail-fast
This change switches away from using glog.Fatalf, and instead uses panic,
should a fail-fast arise (due to a call to util.Fail or a failed assertion
by way of util.Assert).  This leads to a better debugging experience no
matter what flags have been passed to glog.  For example, glog.Fatal* seems
to suppress printing stack traces when --logtostderr is supplied.
2016-12-01 11:43:41 -08:00
joeduffy 334615c6b1 Move parse-tree analysis into the Parse* functions
This change moves parse-tree analysis into the Parse* functions, so that
any callers doing parsing don't need to do this as a multi-step activity.
(We had neglected to do the parse-tree analysis phase during dependency
resolution, for example, meaning services were left untyped.)
2016-12-01 11:39:03 -08:00
joeduffy f5c8c926dc Eliminate recursive dependency analysis 2016-12-01 11:14:05 -08:00
joeduffy e9ca1bf0c0 Raise template loglevel from V(5) to V(7) 2016-12-01 11:13:39 -08:00
joeduffy 9f2b737715 Clean up workspace file naming
This change makes workspace file naming a little more consistent with respect
to Mufile naming.  Instead of having a .mu/ directory, under which a workspace.yaml
and/or a stacks directory might exist, we now have a Muspace.yaml (or .json) file,
and a .Mudeps/ directory.  This has nicer symmetric with respect to Mu.yaml files.
2016-11-29 20:07:27 -08:00
joeduffy c9f7d44a30 Fix a type cast problem in aws/cf provider
...and also add better diagnostics to the associated error messages, so
that the "expected" type is printed in addition to the "got" type.
2016-11-29 15:33:43 -08:00
joeduffy cb9c152104 Permit passing stack properties via the CLI
If compiling a stack that accepts properties directly, we need a way to
pass arguments to that stack at the command line.  This change permits this
using the ordinary "--" style delimiter; for example:

        $ mu build -- --name=Foo

This is super basic and doesn't handle all the edge cases, but is sufficient
for testing and prototyping purposes.
2016-11-29 15:27:02 -08:00
joeduffy bccda1ee29 Nit-rename a variable 2016-11-29 14:32:02 -08:00
joeduffy 787ece1058 Log stack properties (under v=7, extreme verbosity) 2016-11-29 14:31:31 -08:00
joeduffy 2238a95502 Add the notion of "perturbing" properties
This change introduces the notion of "perturbing" properties.  Changing
one of these impacts the live service, possibly leading to downtime.  As
such, we will likely encourage blue/green deployments of them just to be
safe.  Note that this is really just a placeholder so I can keep track of
metadata as we go, since AWS CF has a similar notion to this.

I'm not in love with the name.  I considered `interrupts`, however,
I must admit I liked that `readonly` and `perturbs` are symmetric in
the number of characters (meaning stuff lines up nicely...)
2016-11-29 14:29:34 -08:00
joeduffy c1b5239667 Detect target cloud earlier on
This change detects the target cloud earlier on in the compilation process.
Prior to this change, we didn't know this information until the backend code-generation.
Clearly we need to know this at least by then, however, templates can specialize on this
information, so we actually need it sooner.  This change moves it into the frontend part.

Note that to support this we now eliminate the ability to specify target clusters in
the Mufile alone.  That "feels" right to me anyway, since Mufiles are supposed to be
agnostic to their deployment environment, other than template specialization.  Instead,
this information can come from the CLI and/or the workspace settings file.
2016-11-29 13:42:39 -08:00
joeduffy 89d9269377 Bind dependencies that have no Stacks
In some cases, a dependency will resolve to a diag.Document, rather than
a fully instantiated ast.Stack (in fact, that is the common case).  The
binder needs to detect and tolerate this situation.
2016-11-29 13:04:36 -08:00
joeduffy 9326607c46 Add the notion of readonly properties
This change adds the notion of readonly properties to stacks.  Although these
*can* be "changed", doing so implies recreation of the resources all over again.
As a result, all dependents must be recreated, in a cascading manner.
2016-11-29 12:36:02 -08:00
joeduffy f9a1cf400e Add DependsOn support in CF template generation 2016-11-29 12:23:46 -08:00
joeduffy fb1a6ec4d2 Add a "require" template function
The "require" template function simply checks a condition and, if it
is false, issues an error and quits template processing immediately.
This is useful for concisely encoding validation logic.
2016-11-29 12:12:53 -08:00
joeduffy 6d3296b65f Permit explicit listing of properties to map
In some cases, we actually want to suppress auto-mapping for all or
most of the properties.  In those cases, it's easier to specify those
that we *do* want rather than the ones we *do not* want.  Now with
properties, skipProperties, and extraProperties, we have all the
necessary flexibility to control auto-mapping for CF templates.
2016-11-29 12:04:05 -08:00
joeduffy 4b58c862a4 Add a "has" template function
The new "has" function lets templates conveniently check the existence of
keys in property bag-like maps.  For example:

        {{if has .Properties "something"}}
                ...
        {{end}}
2016-11-29 11:43:20 -08:00
joeduffy 9a962b2014 Add skipProperties and extraProperties to aws/cf extension provider
Most properties in CF templates are auto-mapped by the aws/cf extension
provider.  However, sometimes we want to inject extra properties that are
outside of that auto-mapping (like our convenient shortcut for supplying
name to mean adding a "Name=v" tag).  And sometimes we want to skip auto-
mapping for certain properties (like our capability-based approach to
passing service references, versus string IDs).
2016-11-29 11:31:58 -08:00
joeduffy 6f572a6a5b Add an initial whack at a cluster Mufile
This change adds a super simple initial whack at a basic cluster topology
comprised of VPC, subnet, internet gateway, attachments, and route tables.
This is actually written in Mu itself, and I am committing this early, since
there are quite a few features required before we can actually make progress
getting this up and running.
2016-11-28 16:18:38 -08:00
joeduffy 9acfd18dc2 Add the target architecture to the rendering context
This allows templates to switch on the target cloud and do the right thing.
2016-11-28 15:15:49 -08:00
joeduffy 5233b2bcbe Add a "panic" template function
This introduces a "panic" template function, so that templates may abandon
evaluation if something unexpected occurs.  It accepts a string, indicating
the error, and optional arguments, if the string is to be formatted.

For example:

        {{if eq .Target "aws"}}
                ...
        {{else}}
                {{panic "Unrecognized cloud target: %v" .Target}}
        {{end}}
2016-11-28 14:56:43 -08:00
joeduffy 63ddbc6e7a Add an "include" template function
This lets YAML files include others, often conditionally, based on things
like the cloud target.  For example, I am currently using this to define the
overall cluster stack by doing things like:

        name: mu/cluster
        services:
        {{if eq .Target "aws"}}
                {{include "Mu-aws.yaml" | indent 4}}
        {{else}}
                ...
        {{end}}
2016-11-28 14:54:41 -08:00
joeduffy 1302fc8a47 Add rudimentary template expansion
This change performs template expansion both for root stack documents in
addition to the transitive closure of dependencies.  There are many ongoing
design and implementation questions about how this should actually work;
please see marapongo/mu#7 for a discussion of them.
2016-11-25 12:58:29 -08:00
joeduffy c1c43c7907 Use /usr/local/mu as the default install path 2016-11-23 14:18:18 -08:00
joeduffy 0d6208bb00 Simplify cf/template extension provider
For now, we can simply auto-map the Mu properties to CF properties,
eliminating the need to manually map them in the templates.  Eventually
we'll want more sophistication here to control various aspects of the CF
templates, but this eliminates a lot of tedious manual work in the meantime.
2016-11-23 14:16:35 -08:00
joeduffy 925ee92c60 Annotate a bunch of TODOs with work item numbers 2016-11-23 12:30:02 -08:00
joeduffy d26c1e395a Implement diag.Diagable on ast.Workspace and ast.Stack
The only two AST nodes that track any semblance of location right now
are ast.Workspace and ast.Stack.  This is simply because, using the standard
JSON and YAML parsers, we aren't given any information about the resulting
unmarshaled node locations.  To fix that, we'll need to crack open the parsers
and get our hands dirty.  In the meantime, we can crudely implement diag.Diagable
on ast.Workspace and ast.Stack, however, to simply return their diag.Documents.
2016-11-23 07:54:40 -08:00
joeduffy 2b385b2e20 Prepare for better diagnostics
This change adds a new Diagable interface from which you can obtain
a diagnostic's location information (Document and Location).  A new
At function replaces WithDocument, et al., and will be used soon to
permit all arbitrary AST nodes to report back their position.
2016-11-23 07:44:03 -08:00
joeduffy e02921dc35 Finish dependency and type binding
This change completes the implementation of dependency and type binding.
The top-level change here is that, during the first semantic analysis AST walk,
we gather up all unknown dependencies.  Then the compiler resolves them, caching
the lookups to ensure that we don't load the same stack twice.  Finally, during
the second and final semantic analysis AST walk, we populate the bound nodes
by looking up what the compiler resolved for us.
2016-11-23 07:26:45 -08:00
joeduffy c478ffbe40 Preserve blank ""s in RefParts
This changes the logic when parsing RefParts so that we can actually
round-trip them faithfully when required.  To do this, by default we
preserve blank ""s in the RefPart fields that are legally omitted from
the Ref string.  Then, there is a Defaults() method that can populate
the missing fields with the defaults if desired.
2016-11-22 17:24:49 -08:00
joeduffy 12eb91bcbb Fix a typo that crept in 2016-11-22 17:07:10 -08:00
joeduffy c84512510a Implement dependency versions
This change implements dependency versions, including semantic analysis, per the
checkin 83030685c3.

There's quite a bit in here but at a top-level this parses and validates dependency
references of the form

        [[proto://]base.url]namespace/.../name[@version]

and verifies that the components are correct, as well as binding them to symbols.

These references can appear in two places at the moment:

* Service types.
* Cluster dependencies.

As part of this change, a number of supporting changes have been made:

* Parse Workspaces using a full-blown parser, parser analysis, and semantic analysis.
  This allows us to share logic around the validation of common AST types.  This also
  moves some of the logic around loading workspace.yaml files back to the parser, where
  it can be unified with the way we load Mu.yaml files.

* New ast.Version and ast.VersionSpec types.  The former represents a precise version
  -- either a specific semantic version or a short or long Git SHA hash -- and the
  latter represents a range -- either a Version, "latest", or a semantic range.

* New ast.Ref and ast.RefParts types.  The former is an unparsed string that is
  thought to contain a Ref, while the latter is a validated Ref that has been parsed
  into its components (Proto, Base, Name, and Version).

* Added some type assertions to ensure certain structs implement certain interfaces,
  to speed up finding errors.  (And remove the coercions that zero-fill vtbl slots.)

* Be consistent about prefixing error types with Error or Warning.

* Organize the core compiler driver's logic into three methods, FE, sema, and BE.

* A bunch of tests for some of the above ...  more to come in an upcoming change.
2016-11-22 16:58:23 -08:00
joeduffy 6f99088e2b Add scaffolding for the AWS ECS scheduler backend
Right now, the AWS ECS scheduler simply passes through to the underlying
AWS cloud provider.  However, now we have the necessary hooks to start
incrementally recognizing stack types and emitting specialized code for
them (e.g., starting with mu/container).
2016-11-22 12:37:14 -08:00
joeduffy 5a8069e2fe Fix a silly message 2016-11-22 11:25:51 -08:00
joeduffy 1c5afa2b63 Add code-generation for Stack references
This change adds code-generation for Stack references other than the built-in types.
This permits you to bind to a dependency and have it flow all the way through to the
code-generation phases.  It still most likely bottoms out on something that fails,
however for pure AWS resources like aws/s3/bucket everything now works.
2016-11-22 11:13:16 -08:00
joeduffy 5f3af891f7 Support Workspaces
This change adds support for Workspaces, a convenient way of sharing settings
among many Stacks, like default cluster targets, configuration settings, and the
like, which are not meant to be distributed as part of the Stack itself.

The following things are included in this checkin:

* At workspace initialization time, detect and parse the .mu/workspace.yaml
  file.  This is pretty rudimentary right now and contains just the default
  cluster targets.  The results are stored in a new ast.Workspace type.

* Rename "target" to "cluster".  This impacts many things, including ast.Target
  being changed to ast.Cluster, and all related fields, the command line --target
  being changed to --cluster, various internal helper functions, and so on.  This
  helps to reinforce the desired mental model.

* Eliminate the ast.Metadata type.  Instead, the metadata moves directly onto
  the Stack.  This reflects the decision to make Stacks "the thing" that is
  distributed, versioned, and is the granularity of dependency.

* During cluster targeting, add the workspace settings into the probing logic.
  We still search in the same order: CLI > Stack > Workspace.
2016-11-22 10:41:07 -08:00
joeduffy eb12b7a197 Use precompiled regexps for AWS cloud provider 2016-11-22 09:45:42 -08:00
joeduffy 0f666688bd Add a diag.Sink.Success helper function
This new API cleans up callsites so that they can say

        if d.Success() {
        }

rather than

        if d.Errors() == 0 {
        }
2016-11-22 09:40:09 -08:00
joeduffy 9120a12134 Initialize the dependency map during compiler creation 2016-11-22 09:33:18 -08:00
joeduffy 4df1ec8c35 Support baseless and baseful package paths
This changes the probing logic for dependency resolution.  The old logic was
inconsistent between the various roots.  The new approach simply prefers locations
with a base URL component -- since they are more specific -- but will allow for
locations missing a base URL component.  This is convenient for developers managing
a workspace where needing to specify the base URL in the path is annoying and
slightly too "opinionated" for my taste (especially for migrating existing services).
2016-11-22 09:28:25 -08:00
joeduffy d79ec0d0c5 Add some handy logging 2016-11-22 09:20:23 -08:00
joeduffy dc9f7412b0 Add one more name test just for fun 2016-11-21 12:09:51 -08:00
joeduffy 52faa94a80 Add some AST names tests
Also add some convenience helper methods to deal with names, including
IsName and AsName, which validate that names obey the intended regular expressions.
2016-11-21 12:06:32 -08:00
joeduffy d100f77b9c Implement dependency resolution
This change includes logic to resolve dependencies declared by stacks.  The design
is described in https://github.com/marapongo/mu/blob/master/docs/deps.md.

In summary, each stack may declare dependencies, which are name/semver pairs.  A
new structure has been introduced, ast.Ref, to distinguish between ast.Names and
dependency names.  An ast.Ref includes a protocol, base part, and a name part (the
latter being an ast.Name); for example, in "https://hub.mu.com/mu/container/",
"https://" is the protocol, "hub.mu.com/" is the base, and "mu/container" is the
name.  This is used to resolve URL-like names to package manager-like artifacts.

The dependency resolution phase happens after parsing, but before semantic analysis.
This is because dependencies are "source-like" in that we must load and parse all
dependency metadata files.  We stick the full transitive closure of dependencies
into a map attached to the compiler to avoid loading dependencies multiple times.
Note that, although dependencies prohibit cycles, this forms a DAG, meaning multiple
inbound edges to a single stack may come from multiple places.

From there, we rely on ordinary visitation to deal with dependencies further.
This includes inserting symbol entries into the symbol table, mapping names to the
loaded stacks, during the first phase of binding so that they may be found
subsequently when typechecking during the second phase and beyond.
2016-11-21 11:19:25 -08:00
joeduffy 3e766c34c6 Create a Workspace abstraction
This change introduces a Workspace interface that can be used as a first
class object.  We will embellish this as we start binding to dependencies,
which requires us to search multiple paths.  This change also introduces a
workspace.InstallRoot() function to fetch the Mu install path.
2016-11-21 09:23:39 -08:00
joeduffy 9c1b72596c Write up a bit about Workspaces and Dependencies 2016-11-20 09:22:29 -08:00
joeduffy 47f7b0e609 Rearrange workspace logic
This change moves the workspace and Mufile detection logic out of the compiler
package and into the workspace one.

This also sketches out the overall workspace structure.  A workspace is "delimited"
by the presence of a .mu/ directory anywhere in the parent ancestry.  Inside of that
directory we have an optional .mu/clusters.yaml (or .json) file containing cluster
settings shared among the whole workspace.  We also have an optional .mu/stacks/
directory that contains dependencies used during package management.

The notion of a "global" workspace will also be present, which is essentially just
a .mu/ directory in your home, ~/.mu/, that has an equivalent structure, but can be
shared among all workspaces on the same machine.
2016-11-20 08:20:19 -08:00
joeduffy 5e51b04d7f Add a BoundDependency type
This change prepares to bind dependencies during semantic analysis
by introducing a new BoundDependency type and initializing a map of them.
2016-11-20 07:28:58 -08:00
joeduffy c20c151edf Use assertions in more places
This change mostly replaces explicit if/then/glog.Fatalf calls with
util.Assert calls.  In addition, it adds a companion util.Fail family
of methods that does the same thing as a failed assertion, except that
it is unconditional.
2016-11-19 16:13:13 -08:00
joeduffy 81ff753afa Modify a test to hit the "first character" logic 2016-11-19 15:54:11 -08:00
joeduffy 395133ebc5 Add tests for AWS name prettification
And fix a few issues with the existing function.
2016-11-19 15:53:09 -08:00
joeduffy 29eb6c84cf Don't capnext if the first rune is unprintable
If the first rune is unprintable, then we don't want to go ahead and force
capitalization on the next character.  (Unlike any other non-first rune,
where of course we do.)  In the case of a first rune, we want to let the
current default based on the pascal parameter take charge.
2016-11-19 13:40:42 -08:00
joeduffy 2e6c0e4343 Prettify AWS resource names
This uses normal AWS resource naming conventions during stack template
creation.  Part of this is just a "best practice" thing, however, part of it
is also that we generate illegal names if Mu stacks have illegal characters
like /, -, and so on.
2016-11-19 13:36:21 -08:00
joeduffy f8ef7a8ddd Generate valid CF service names (and do some more validation) 2016-11-19 13:16:37 -08:00
joeduffy 02a39173d9 Add references to marapongo/mu#4, harden JSON/YAML unmarshaling 2016-11-19 12:31:00 -08:00
joeduffy 14bce9804f Move aws/cf spec underneath a "resource" property
This changes the layout of the aws/cf extension to specify the CloudFormation
resource underneath a "resource" property.
2016-11-19 12:17:54 -08:00
joeduffy 509a695f7c Capitalize AWS CF template properties
AWS uses capitalized property names for its markup, so we should
be looking for "Type" and "Properties", not "type" and "properties"
when validating that a aws/cf is formatted correctly.
2016-11-19 11:33:32 -08:00
joeduffy a28ad489e4 Fail gracefully when a aws/cf template is incorrect
This change fails gracefully, and avoids inserting nil into the resource map,
should an error occur.
2016-11-19 11:32:40 -08:00
joeduffy ac5df8ba40 Supply diag.Sink at backend construction time
This change eliminates the diag.Sink field, Diag, on the Compiland struct.
Instead, we should provide it at backend provider construction time.  This
is consistent with how other phases of the compiler work and also ensures
the backends can properly implement the core.Phase interface.
2016-11-19 11:28:40 -08:00
joeduffy a23d151610 Fix a type assertion (should be string, not ast.Name) 2016-11-19 11:22:58 -08:00
joeduffy c2b5480d95 Fix a couple missed "parameter"s in the property rename 2016-11-19 11:18:42 -08:00
joeduffy a31e59fa3a Implement the aws/cf extension provider
This change implements the aws/cf extension provider, so that AWS resources
may be described and encapsulated inside of other stacks.  Each aws/cf instantiation
requires just two fields -- type and properties -- corresponding to the equivalent
AWS resource object.  The result is simply plugged in as an AWS resource, after
Mu templates have been expanded, permitting stack properties, etc. to be used.
2016-11-19 11:13:15 -08:00
joeduffy ed0710dd0b Rename parameters to properties
The more I live with the current system, the more I prefer "properties" to
"parameters" for stacks and services.  Although it is true that these things
are essentially construction-time arguments, they manifest more like properties
in the way they are used; in fact, if you think of the world in terms of primary
constructors, the distinction is pretty subtle anyway.

For example, when creating a new service, we say the following:

        services:
                private:
                        some/service:
                                a: 0
                                b: true
                                c: foo

This looks like a, b, and c are properties of the type some/service.  If, on
the other hand, we kept calling these parameters, then you'd arguably prefer to
see the following:

        services:
                private:
                        some/service:
                                arguments:
                                        a: 0
                                        b: true
                                        c: foo

This is a more imperative than declarative view of the world, which I dislike
(especially because it is more verbose).

Time will tell whether this is the right decision or not ...
2016-11-19 10:34:51 -08:00
joeduffy 2e11d9a1e9 Add a strongly typed Service for mu/extension 2016-11-19 10:22:56 -08:00
joeduffy ffad5f5d30 Add *Kind enums for Metadata.Kind and Parameter.Type 2016-11-19 10:22:16 -08:00
joeduffy 652305686d Add some simple assertion functions
This introduces three assertion functions:

* util.Assert: simply asserts a boolean condition, ripping the process using
  glog should it fail, with a stock message.

* util.AssertM: asserts a boolean condition, also using glog if it fails,
  but it comes with a message to help with debugging too.

* util.AssertMF: the same, except it formats the message with arguments.
2016-11-19 10:17:44 -08:00
joeduffy d9631f6e75 Retain unrecognized service properties
During unmarshaling, the default behavior of the stock Golang JSON marshaler,
and consequently the YAML one we used which mimics its behavior, is to toss away
unrecognized properties.  This isn't what we want for two reasons:

First, we want to issue errors/warnings on unrecognized fields to aid in diagnostics;
we will set aside some extensible section for 3rd parties to use.  This is not
addressed in this change, however.

Second, and more pertinent, is that we need to retain unrecognized fields for certain
types like services, which are extensible by default.

Until golang/go#6213 is addressed -- imminent, it seems -- we will have to do a
somewhat hacky workaround to this problem.  This change contains what I consider to
be the "least bad" in that we won't introduce a lot of performance overhead, and
just have to deal with the slight annoyance of the ast.Services node type containing
both Public/Private *and* PublicUntyped/PrivateUntyped fields alongside one another.
The marshaler dumps property bags into the *Untyped fields, and the parsetree analyzer
expands them out into a structured ast.Service type.  Subsequent passes can then
ignore the *Untyped fields altogether.

Note that this would cause some marshaling funkiness if we ever wanted to remarshal
the mutated ASTs back into JSON/YAML.  Since we don't do that right now, however, I've
not made any attempt to keep the two pairs in synch.  Post-parsetree analyzer, we
literally just forget about the *Untyped guys.
2016-11-19 09:01:23 -08:00
joeduffy 1f2ef35552 Store BoundType information on Service AST nodes
This change rearranges the last checkin a little bit.  Rather than storing
shadow BoundPublic/BoundPrivate maps, we will store the *ast.Stack directly on
the ast.Service node itself.  This helps with context-free manipulation (e.g.,
you don't need access to the parent map just to interact with the node), and
simplifies the backend code quite a bit (again, less context to pass).
2016-11-18 18:20:19 -08:00
joeduffy be4f3c6df9 Sketch out the service compilation for the AWS backend
This is another change of mostly placeholders.

In general, there will be three kinds of types handled by code-generation:

* Mu primitives will be expanded into AWS goo in a very specialized way, to
  accomplish the desired Mu semantics for those abstractions.

* AWS-specific extension types (mu/extension) will be recognized, so that we
  can create special AWS resources like S3 buckets, DynamoDB tables, etc.

* Anything else is interpreted as a reference to another stack that will be
  instantiated at deployment time (basically through template expansion).

This change does rearrange two noteworthy things in the core compiler, however:
first, it creates a place for bound nodes in the public and private service
references, so that the backend can access the raw stack types behind them; and
second, it moves the predefined types underneath their own package to avoid cycles.
2016-11-18 18:12:26 -08:00
joeduffy b57e4c4414 Add Stack subclassing
This change introduces the notion of "Stack subclassing" in two ways:

1. A Stack may declare that it subclasses another one using the base property:

        name: mystack
        base: other/stack
        .. as before ..

2. A Stack may declare that it is abstract; in other words, that it is meant
   solely for subclassing, and cannot be compiled and deployed independently:

        name: mystack
        abstract: true
        .. as before ..

   Note that non-abstract Stacks are required to declare at least one Service,
   whether that is public, private, or both.
2016-11-18 17:30:32 -08:00
joeduffy 60ce2d03c3 Add a true error for CloudFormation template marshaling failures 2016-11-18 17:15:00 -08:00
joeduffy 6dfc528ad1 Create a new core.Compiland type
This change rejiggers a few things so that we can more clearly introduce
a boundary between front- and back-end compiler phases, including sharing more,
like a diagnostics sink.  Future extensions will include backend code-generation
options.
2016-11-18 17:08:44 -08:00
joeduffy 0b34f256f0 Sketch out more AWS backend code-generator bits and pieces
This change includes a few steps towards AWS backend code-generation:

* Add a BoundDependencies property to ast.Stack to remember the *ast.Stack
  objects bound during Stack binding.

* Make a few CloudFormation properties optional (cfOutput Export/Condition).

* Rename clouds.ArchMap, clouds.ArchNames, schedulers.ArchMap, and
  schedulers.ArchNames to clouds.Values, clouds.Names, schedulers.Values,
  and schedulers.Names, respectively.  This reads much nicer to my eyes.

* Create a new anonymous ast.Target for deployments if no specific target
  was specified; this is to support quick-and-easy "one off" deployments,
  as will be common when doing local development.

* Sketch out more of the AWS Cloud implementation.  We actually map the
  Mu Services into CloudFormation Resources; well, kinda sorta, since we
  don't actually have Service-specific logic in here yet, however all of
  the structure and scaffolding is now here.
2016-11-18 16:46:36 -08:00
joeduffy e56a34b4e0 Make ast.StableX routines for the various AST maps
We previously used stable enumeration of the various AST maps in the core
visitor, however we now need stable enumeration in more places (like the AWS
backend I am working on).  This change refactors this logic to expose a set
of core ast.StableX routines that stably enumerate maps, and then simply uses
them in place of the existing visitor logic.  (Missing generics right now...)
2016-11-18 15:57:07 -08:00
joeduffy 66a3fc2b07 Add AWS CloudFormation payload structs
This change simply adds the necessary AWS CloudFormation struct types to
permit marshaling and unmarshaling to/from JSON and YAML.  This will be used
by the AWS cloud provider's backend code-generation.  It adds a bit of strong
typing so that we can catch more errors at compile-time (both for our own sanity
but also to provide developers better diagnostics when compiling their stacks).
2016-11-18 15:10:09 -08:00
joeduffy 2a2c93f8ab Add a Backend interface, and dispatch to it
This change adds a Backend Phase to the compiler, implemented by each of the
cloud/scheduler implementations.  It also reorganizes some of the modules to
ensure we can do everything we need without cycles, including introducing the
mu/pkg/compiler/backends package, under which the clouds/ and schedulers/
sub-packages now reside.  The backends.New(Arch) factory function acts as the
entrypoint into the entire thing so callers can easily create new Backend instances.
2016-11-18 12:40:15 -08:00
joeduffy f5d310ae50 Rename BuildJSON/BuildYAML to just BuildFile
This change is more consistent with our approach to file extensions
elsewhere in the compiler, and prevents an explosion of APIs should we
ever want to support more.
2016-11-17 15:13:36 -08:00
joeduffy 8fa1fd9082 Add some targeting tests
This adds some tests around cloud targeting, in addition to enabling builds
to use in-memory Mufiles (mostly to make testing simpler, but this is a
generally useful capability to have when hosting the compiler API).
2016-11-17 13:08:20 -08:00
joeduffy 6fb6c2de09 Add cloud target and architecture detection
This change implements most of the cloud target and architecture detection
logic, along with associated verification and a bunch of new error messages.

There are two settings for picking a cloud destination:

* Architecture: this specifies the combination of cloud (e.g., AWS, GCP, etc)
      plus scheduler (e.g., none, Swarm, ECS, etc).

* Target: a named, preconfigured entity that includes both an Architecture and
      an assortment of extra default configuration options.

The general idea here is that you can preconfigure a set of Targets for
named environments like "prod", "stage", etc.  Those can either exist in a
single Mufile, or the Mucluster file if they are shared amongst multiple
Mufiles.  This can be specified at the command line as such:

        $ mu build --target=stage

Furthermore, a given environment may be annointed the default, so that

        $ mu build

selects that environment without needing to say so explicitly.

It is also possible to specify an architecture at the command line for
scenarios where you aren't intending to target an existing named environment.
This is good for "anonymous" testing scenarios or even just running locally:

        $ mu build --arch=aws
        $ mu build --arch=aws:ecs
        $ mu build --arch=local:kubernetes
        $ .. and so on ..

This change does little more than plumb these settings around, verify them,
etc., however it sets us up to actually start dispating to the right backend.
2016-11-17 10:30:37 -08:00
joeduffy f1627fed2b Create a new mu/pkg/compiler/core package
This change creates a new mu/pkg/compiler/core package for any fundamental
compiler types that need to be shared among the various compiler packages
(.../compiler, .../compiler/clouds/..., and .../compiler/schedulers/...).
This avoids package cycles.
2016-11-17 08:52:54 -08:00
joeduffy 8a7fbf019c Add the ability to select a cloud provider
This adds two packages:

        mu/pkg/compiler/clouds
        mu/pkg/compiler/schedulers

And introduces enums for the cloud targets we expect to support.

It also adds the ability at the command line to specify a provider;
for example:

        $ mu build --target=aws         # AWS native
        $ mu build --target=aws:ecs     # AWS ECS
        $ mu build -t=gcp:kubernetes    # Kube on GCP
2016-11-17 07:00:52 -08:00
joeduffy 54148c67e4 Bind predefined stack types
This prepopulates the symbol table with our predefined "primitive" types
like mu/container, mu/gateway, mu/func, and the like.  Also added a positive
test case to ensure the full set works; this will obviously need updating as
we embellish the predefined types with things like required parameters.
2016-11-17 06:10:23 -08:00
joeduffy 065c900c2c Add a concept of "scopes"
This change introduces a binding scope structure, enabling names to be
bound to symbols using standard nested scopes.
2016-11-17 05:36:58 -08:00
joeduffy ad8195f1b5 Fix erroneous comments (Lookup*, not Bind*) 2016-11-16 19:08:41 -08:00
joeduffy 9c0c6e6916 Add rudimentary type binding
This change adds rudimentary type binding to phase 2 of the binder.  Note that
we still don't have the notion of predefined types (for the primitives), so this
basically rejects any well-formed Mufile.  Primitives are on deck.
2016-11-16 18:55:20 -08:00
joeduffy a4fdcaab68 Swap argument order to expected: actual: are correct 2016-11-16 18:51:14 -08:00
joeduffy 8daced40a1 Delete a garbage file
This seems to be leftover from before the diag package got a Document type.
2016-11-16 18:30:50 -08:00
joeduffy 6e70d01bf5 Fix diag.Sink errors/warnings count bug
This fixes a bug in diag.Sink where we didn't properly increment the
errors and warnings counts as they were issued.  Also added a regression test.
2016-11-16 17:52:14 -08:00
joeduffy b2d529a6f8 Place stack services underneath a "services" property
Instead of:

        name: mystack
        public:
                someservice
        private:
                someotherservice

we want it to be:

        name: mystack
        services:
                public:
                        someservice
                private
                        someotherservice

I had always intended it to be this way, but coded up the ASTs wrong.
2016-11-16 17:30:03 -08:00
joeduffy 109d8c3f4f Add a way to control logging verbosity
This change adds a --verbose=x flag (-v=x shorthand) to the Mu command,
to set the glog logging level to x, for all subcommands.
2016-11-16 17:19:49 -08:00
joeduffy c080ddbf46 Eliminate pointers from the parse tree
Neither the YAML nor JSON decoders appreciate having pointers in the AST
structures.  This is unfortunate because we end up mutating them later on.
Perhaps we will need separate parse trees and ASTs after all ...
2016-11-16 17:00:58 -08:00
joeduffy 832ef1f743 Lay some groundwork for symbol binding
This change lays some groundwork that registers symbols when doing semantic
analysis of the resulting AST.  For now, that just entails detecting duplicate
services by way of symbol registration.

Note that we've also split binding into two phases to account for the fact
that intra-stack dependencies are wholly legal.
2016-11-16 13:11:58 -08:00
joeduffy 917342f76f Decorate the AST with contextual info
This change decorates the AST with some information that is only known
after parsing.  This enables subsequent logic to remain context-free.
2016-11-16 11:51:50 -08:00
joeduffy 85a828fdf9 Use stable map enumeration for determinism
Go's map iteration order is undefined.  As such, anywhere we enumerate one
we need to go out of our way to sort the keys so that iteration is stable.
Unfortunately, this isn't a single line of code, as it would seem, due to
Go's lack of generics or even a reflectionless Keys() function on maps.  To
do this more elegently, I've created a new InOrderVisitor that handles the
stable enumeration so that other Visitors can remain simple and focus just
on the logic they need to get their job done.
2016-11-16 11:45:41 -08:00
joeduffy badf57c923 Type alias Dependency to SemVer, not string 2016-11-16 11:12:42 -08:00
joeduffy 2665a1a4c4 Check dependencies for validity
This change introduces a check during parse-tree analysis that dependencies
are valid, along with some tests.  Note that this could technically happen later
during semantic analysis and I will likely move it so that we can get better
diagnostics (more errors before failing).  I've also cleaned up and unified some
of the logic by introducing the general notion of a Visitor interface, which the
parse tree analyzer, binder, and analyzers to come will all implement.
2016-11-16 11:09:45 -08:00
joeduffy e2d04f7754 Add tests for parse tree validation
This adds a few tests for parse tree validation, and further restructures
the existing test logic.  The common_test.go file now contains helper methods
common to all tests in the mu/compiler package.  I've also adopted a naming
convention for the testdata/ directory to keep some sanity; namely, each
directory uses "(good|bad)_testname[_seqnum]" as a naming scheme.
2016-11-16 10:02:34 -08:00
joeduffy 1614258437 Declare a Mumodules constant
This is a placeholder for future use; .mu_modules will be our moral
equivalent to NPM/Yarn's node_modules directory.  I've chosen a dot
since for the most part developers can ignore its existence.
2016-11-16 10:01:46 -08:00
joeduffy 5a6c581a7e Check Stack semantic version numbers for correctness
This change ensures that Stack semantic version numbers specified in Mufiles
are correct.  Note that we do not accept ranges for the version number itself,
although obviously when checking dependencies we will permit it.
2016-11-16 10:00:52 -08:00
joeduffy 2dd8665c46 Prepare for semantic analysis
This change begins to lay the groundwork for doing semantic analysis and
lowering to the cloud target's representation.  In particular:

* Split the mu/schema package.  There is now mu/ast which contains the
  core types and mu/encoding which concerns itself with JSON and YAML
  serialization.

* Notably I am *not* yet introducing a second AST form.  Instead, we will
  keep the parse tree and AST unified for the time being.  I envision very
  little difference between them -- at least for now -- and so this keeps
  things simpler, at the expense of two downsides: 1) the trees will be
  mutable (which turns out to be a good thing for performance), and 2) some
  fields will need to be ignored during de/serialization.  We can always
  revisit this later when and if the need to split them arises.

* Add a binder phase.  It is currently a no-op.
2016-11-16 09:29:44 -08:00
joeduffy 60a1f02666 Add more compiler tests
This change adds a few more compiler tests and rearranges some bits and pieces
that came up while doing so.  For example, we now issue warnings for incorrect
casing and/or extensions of the Mufile (and test these conditions).  As part of
doing that, it became clear the layering between the mu/compiler and mu/workspace
packages wasn't quite right, so some logic got moved around; additionally, the
separation of concerns between mu/workspace and mu/schema wasn't quite right, so
this has been fixed also (workspace just understands Mufile related things while
schema understands how to unmarshal the specific supported extensions).
2016-11-16 08:19:26 -08:00
joeduffy 38c73b2e6a Add a simple compiler test
This change adds a compiler test that just checks the basic "Mufile is missing"
error checking.  The test itself is mostly uninteresting; what's more interesting
is the addition of some basic helper functionality that can be used for future
compiler tests, like capturing of compiler diagnostics for comparisons.
2016-11-15 19:16:02 -08:00
joeduffy 0ab10dd207 Rename Semver to SemVer
Eric rightly pointed out in a CR that Semver is...different.  Since it
is an abbreviation of the lengthier SemanticVersion name, SemVer seems
more appropriate.  Changed.
2016-11-15 18:34:19 -08:00
joeduffy 79f5f312b8 Support .yml Mufile extensions
This change recognizes .yml in addition to the official .yaml extension,
since .yml is actually very commonly used.  In addition, while in here, I've
centralized more of the extensions logic so that it's more "data-driven"
and easier to manage down the road (one place to change rather than two).
2016-11-15 18:26:21 -08:00
joeduffy ff0059cd7b Print relative filenames in errors
Error messages could get quite lengthy as the code was written previously,
because we always used the complete absolute path for the file in question.
This change "prettifies" this to be relative to whatever contextual path
the user has chosen during compilation.  This shortens messages considerably.
2016-11-15 18:00:43 -08:00
joeduffy c527cedb03 Delete api/types (missing from last commit) 2016-11-15 17:58:48 -08:00
joeduffy 094d3a0817 Perform more real compilation activities
This change includes some progress on actual compilation (albeit with several
TODOs remaining before we can actually spit out a useful artifact).  There are
also some general cleanups sprinkled throughout.  In a nutshell:

* Add a compiler.Context object that will be available during template expansion.

* Introduce a diag.Document abstraction.  This is better than passing raw filenames
  around, and lets us embellish diagnostics as we go.  In particular, we will be
  in a better position to provide line/column error information.

* Move IO out of the Parser and into the Compiler, where it can cache and reuse
  Documents.  This will become important as we start to load up dependencies.

* Rename PosRange to Location.  This reads nicer with the new Document terminology.

* Rename the mu/api package to mu/schema.  It's likely we will need to introduce a
  true AST that is decoupled from the serialization format and contains bound nodes.
  As a result, treating the existing types as "schema" is more honest.

* Add in a big section of TODOs at the end of the compiler.Compiler.Build function.
2016-11-15 17:42:22 -08:00
joeduffy d238ed61da Make a few improvements, mostly readability
* Rename Meta to Metadata.

* Rename Target's CloudOS and CloudScheduler properties to Cloud
  and Scheduler, respectively.  Also rename Target's JSON properties
  to match (they had drifted); they are now "cloud" and "scheduler".

* Rename Diags() to Diag() on the Compiler and Parser interfaces.

* Rename defaultDiags to defaultSink, to match the interface name.

* Add a few useful logging outputs.
2016-11-15 16:30:10 -08:00
joeduffy c928a0f60b Correctly recognize file extensions 2016-11-15 16:15:58 -08:00
joeduffy e75f06bb2b Sketch a mu build command and its scaffolding
This adds a bunch of general scaffolding and the beginning of a `build` command.

The general engineering scaffolding includes:

* Glide for dependency management.
* A Makefile that runs govet and golint during builds.
* Google's Glog library for logging.
* Cobra for command line functionality.

The Mu-specific scaffolding includes some packages:

* mu/pkg/diag: A package for compiler-like diagnostics.  It's fairly barebones
  at the moment, however we can embellish this over time.
* mu/pkg/errors: A package containing Mu's predefined set of errors.
* mu/pkg/workspace: A package containing workspace-related convenience helpers.

in addition to a main entrypoint that simply wires up and invokes the CLI.  From
there, the mu/cmd package takes over, with the Cobra-defined CLI commands.

Finally, the mu/pkg/compiler package actually implements the compiler behavior.
Or, it will.  For now, it simply parses a JSON or YAML Mufile into the core
mu/pkg/api types, and prints out the result.
2016-11-15 14:30:34 -05:00
joeduffy 9c7f774fc6 Project the core Mufile types
This change adds the core Mufile types (Cluster, Stack, and friends), including
mapping them to the relevant JSON names in the serialized form.  Notably absent
are all of the Identity-related types, which will come later on.
2016-11-15 14:28:17 -05:00