Commit graph

3024 commits

Author SHA1 Message Date
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