Commit graph

6540 commits

Author SHA1 Message Date
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 62d1f5c4c1 Add a note about MUROOT in the dependency probe sequence
We will use the $MUROOT envvar to determine where Mu has been installed,
which will by default be /usr/local/mu.  From there, we can access the
predefined library of stacks (underneath $MUROOT/bin/stacks).
2016-11-21 08:39:28 -08:00
joeduffy 58fa832b98 Describe more about "capabilities" as properties 2016-11-21 08:16:19 -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 536065bd57 Add a mu get command
This adds a `mu get` command for downloading Mu Stacks.  It isn't really
implemented yet, but puts a stake in the ground on how we intend this to work.
2016-11-19 16:44:25 -08:00
joeduffy b31c4467ac Move glogging into Mu command startup/teardown
This change moves glogging into the Mu command, so that `mu --help`
actually shows the right thing.  (Sadly, glog hijacks the command help.)
It also adds an option --logtostderr that redirects glog output to the
console.
2016-11-19 16:42:27 -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 efe69a2e2c Explicitly declare the demo's dependencies 2016-11-18 16:52:51 -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 f8a84c4fa3 Set target and architecture from the command line 2016-11-17 12:29:10 -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 74ebf36dd0 Update conversion modules to the latest 2016-11-17 05:26:32 -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