Deliberately stateless cloud topology management and deployment tools based on TOSCA.
Want to dive in?
Check out this live demo of Puccini TOSCA running in a browser.
And then head to the quickstart guide.
Note that Puccini is intentionally not an orchestrator. This is a “BYOO” kind of establishment (“Bring Your Own Orchestrator”). If you are looking for a comprehensive TOSCA orchestrator for Kubernetes based on Puccini, check out sister project, Turandot. Also included in Puccini are examples for using Ansible and BPMN2 middleware as orchestrators.
Each tool is a self-contained executable file, allowing them to be easily distributed and embedded in toolchains, orchestration, and development environments. They are coded in 100% Go and are very portable, even available for WebAssembly (which is how the in-browser demo linked above works).
You can also embed Puccini into your program as a library. Puccini is immediately usable from Go, but can be used in many other programming languages via self-contained shared C libraries. See included wrappers and examples for Java, Python, and Ruby.
To build Puccini yourself see the build guide.
A TOSCA processor. Parses a TOSCA service template and compiles it to Clout (see below).
Why TOSCA? It’s a high-level language made for modeling and validating cloud topologies using reusable and extensible objects. It allows architects to focus on application design and requirements without being bogged down by the ever-changing specificities of the infrastructure.
Puccini can compile several popular TOSCA and TOSCA-like dialects: TOSCA 1.3, TOSCA 1.2, TOSCA 1.1, TOSCA 1.0, as well as the more limited grammars of Cloudify DSL 1.3, and OpenStack Heat HOT 2018-08-31.
Puccini is also following the progress on TOSCA 2.0.
The input files can be accessed by URL, on the local file systems or via HTTP/HTTPS, as individual files as well as packaged in CSAR files. Puccini also includes a simple CSAR creation tool, puccini-csar.
puccini-tosca comes with TOSCA profiles for the Kubernetes and OpenStack cloud infrastructures, as well as BPMN processes. Profiles include node, capability, relationship, policy, and other types. Also included are detailed examples using these profiles to get you started. These profiles would work with any TOSCA-compliant product.
puccini-tosca compile my-app.yaml --exec=kubernetes.generate | kubectl apply -f -
Et voilà, your abstract architectural design became a running deployment.
Puccini’s TOSCA parser is available as an independent Go library. Its 5 phases do normalization, validation, inheritance, and assignment of TOSCA’s many types and templates, resulting in a flat, serializable data structure that can easily be consumed by your program. Validation error messages are precise and useful. It’s a very, very fast multi-threaded parser, fast enough that it can be usefully embedded in editors and IDEs for validating TOSCA while typing.
TOSCA is a complex object-oriented language. We put considerable effort into adhering to every aspect of the grammar, especially in regards to value type checking and type inheritance contracts, which are key to delivering the object-oriented promise of extensibility while maintaining reliable base type compatibility. Unfortunately, the TOSCA specification is famously inconsistent and imprecise. For this reason, the Puccini parser also supports quirk modes that enable alternative behaviors based on differing interpretations of the spec.
The TOSCA-to-Clout compiler’s main role is to take the parsed data structure and dump it into Clout. The next step in the toolchain (which could be puccini-clout) would then connect the Clout to your orchestration systems: deploying to your platforms, on-boarding to a service catalog, etc. Thusly Clout functions as an “intermediate representation” (IR) for TOSCA.
You can graphically visualize the compiled TOSCA in a dynamic web page. A one-line example:
puccini-tosca compile examples/tosca/requirements-and-capabilities.yaml --exec=assets/tosca/profiles/common/1.0/js/visualize.js > /tmp/puccini.html && xdg-open /tmp/puccini.html
The visualization scriptlet is not embedded by default into the Clout, but can be manually added via
puccini-clout scriptlet put (see below) for added portability.
The tool can also be used to add/remove scriptlets by manipulating the metadata section in the Clout.
For convenience, execution functionality is included in puccini-tosca via the
These two commands are equivalent:
puccini-tosca compile my-app.yaml | puccini-clout scriptlet exec my.scriptlet puccini-tosca compile my-app.yaml --exec=my.scriptlet
Note that puccini-clout is not a requirement for your toolchain. You can process and consume the Clout output with your own tools.
TOSCA Functions and Constraints
puccini-clout scriptlet exec tosca.coerce my-clout.yaml
For convenience, this functionality is included in puccini-tosca via the
The following is identical to the above:
puccini-tosca compile --coerce my-clout.yaml
A useful side benefit of Puccini’s implementation is it allows you to extend TOSCA by adding your own functions/constraints. Obviously, such custom functions are not part of the TOSCA specification and may be incompatible with other TOSCA implementations.
TOSCA attributes (as opposed to properties) represent “live” data in a running deployment. And the
get_attribute allows other values to make use of this live data. The implication is that
the Clout should be updated to reflect changes to attribute. But also, attribute definitions
in TOSCA allow you to define constraints on attributes, so we must also make sure that the new
incoming data complies with them before allowing the change to occur.
Putting it all together, let’s refresh a Clout:
puccini-clout scriptlet exec kubernetes.update my-clout.yaml | puccini-clout scriptlet exec tosca.coerce > coerced-clout.yaml
TOSCA Operations, Notifications, Policy Triggers, and Workflows
WORK IN PROGRESS
In TOSCA, a workflow is grammar for describing a task graph, which is tightly coupled to the topology. It represents the “classical” orchestration paradigm, which procedurally (in serial and/or in parallel) executes individual self-contained tasks. When the workflow reaches a successful ending, there would be a new state for a service instance. Dealing with workflow failures is often painful, resulting in an indeterminate state for the service. It’s not always practical to “roll back” the task graph or even reset to the original state. Thus, this paradigm is best avoided if possible.
The building blocks of workflows are “operations” (calling a single operation is the simplest workflow). Notifications and policy triggers are a related feature, as they specify an event or condition that could launch a workflow or an individual operation.
Puccini comes with three different implementations of these features:
1) For OpenStack, Puccini can generate Ansible playbooks that rely on the Ansible OpenStack roles. Custom operation artifacts, if included, are deployed to the virtual machines and executed. Effectively, the combination of TOSCA + Ansible provides an equivalent set of features to Heat/Mistral. However, Ansible is a general-purpose orchestrator that can do a lot more than Heat. The generated playbooks comprise roles that can be imported and used in other playbooks, allowing for custom orchestration integrations. Also note that although Puccini can compile HOT directly, we recommend TOSCA because of its much richer grammar and features.
2) Puccini’s BPMN profile lets you generate BPMN2 processes from TOSCA workflows and policy triggers. These allow for tight integration with enterprise process management (called OSS/BSS in the telecommunications industry). The generated processes can also be included as sub-processes within larger business processes.
3) Kubernetes doesn’t normally require workflows: its “scheduling” paradigm is a declarative
alternative to the classical procedural orchestration paradigm. As it provides a truly
cloud-native environment, Kubernetes applications are better off orchestrating themselves, for
example by relying on operators to do the
heavy lifting. Still, it could make sense to use workflows for certain externally triggered
features. Puccini’s solution is straightforward: it can generate an Ansible playbook that deploys
TOSCA artifacts with
kubectl cp and executes them with
Introducing the cloud topology (“clou” + “t”) representation language, which is simply a representation of a generic graph database in YAML/JSON/XML.
Clout functions as the intermediary format for your deployments. As an analogy, consider a program written in the C language. First, you must compile the C source into machine code for your hardware architecture. Then, you link the compiled object, together with various libraries, into a deployable executable for a specific target platform. Clout is the compiled object in this analogy. If you only care about the final result then you won’t see the Clout at all. However, the decoupling allows for a more powerful toolchain. For example, some tools might change your Clout after the initial compilation (to scale out, to optimize, to add platform hooks, debugging features, etc.) and then you just need to “re-link” in order to update your deployment. This can happen without requiring you to update your original source design. It may also possible to “de-compile” some cloud deployments so that you can generate a Clout without any TOSCA “source code”.
Clout is essentially a big, unopinionated, implementation-specific dump of vertexes and the edges between them with un-typed, non-validated properties. Rule #1 of Clout is that everything and the kitchen sink should be in one Clout file. Really, anything goes: specifications, configurations, metadata, annotations, source code, documentation, and even text-encoded binaries. (The only exception might be that you might want to store security certificates and keys elsewhere.)
In itself Clout is an unremarkable format. Think of it as a way to gather various deployment specifications for disparate technologies in one place while allowing for the relationships (edges) between entities to be specified and annotated. That’s the topology.
Clout is not supposed to be human-readable or human-manageable. The idea is to use tools (Clout frontends and processors) to deal with its complexity. We have some great ones for you here. For example, with Puccini you can use just a little bit of TOSCA to generate a single big Clout file that describes a complex Kubernetes service mesh.
If Clout’s file size is an issue, it’s good to know that Clout is usually eminently compressible, comprising just text with quite a lot of repetition.
Orchestrators may choose to store Clout opaquely, as is, in a key-value database or filesystem. This could work well because cloud deployments change infrequently: often all that’s needed is to retrieve a Clout, parse and lookup data, and possibly update a TOSCA attribute and store it again. Iterating many Clouts in sequence this way could be done quickly enough even for large environments. Simple solutions are often best.
That said, it could also make sense to store Clout data in a graph database. This would allow for sophisticated queries, using languages such GraphQL and Gremlin, as well as localized transactional updates. This approach could be especially useful for highly composable and dynamic environments in which Clouts combine together to form larger topologies and even relate to data coming from other systems.
Graph databases are quite diverse in features and Clout is very flexible, so one schema will not fit all. Puccini instead comes with examples: see storing in Neo4j and storing in Dgraph.
Can Puccini deploy my existing TOSCA to Kubernetes or OpenStack?
If you didn’t plan it that way, then: no. Our TOSCA Kubernetes/OpenStack profiles do not make use of TOSCA’s Simple Profile or Simple Profile for NFV types (Compute, BlockStorage, VDU, etc.). Still, if you find these so-called “normative” types useful, they are included in Puccini and will be compiled into Clout. You may bring in your own orchestration to deploy them to your cloud environments. But, we encourage you to consider carefully whether this is a good idea. We think it’s a dead end.
The notion that a single set of normative types could be used for all the various cloud and container platforms out there is a pipe dream. There may be superficial similarities between them, but the devil is in the details and the amount of detail needed for integrated, scalable, cloud-native deployments keeps growing and diversifying. Thus every platform needs and deserves its own concepts, models, data points, and thus its own profile of interrelated TOSCA types. However, by bringing all these tiny-but-important implementation details into one place we can at least have a lingua franca and common toolchain for all platforms. That’s the value proposition of TOSCA and the gist of Puccini.
Go is fast becoming the language of choice for cloud-native solutions. It has the advantage of producing very deployable executables that make it easy to containerize and integrate. Go features garbage collection and easy multi-threading (via lightweight goroutines), but unlike Python, Ruby, and Perl it is a strictly typed language, which encourages good programming practices and reduces the chance for bugs.
Our chosen ECMAScript engine is goja, which is 100% Go and does not require any external dependencies.
puccini-tosca compile my-app.yaml | python myprocessor.py
Also check out yq, a great little tool for extracting YAML and even performing simple manipulations. Example:
puccini-tosca compile examples/tosca/requirements-and-capabilities.yaml | yq r - 'vertexes.(properties.name==light12)'
Can I use simple text templating instead of TOSCA functions and YAML processing?
Nothing is stopping you. You can pipe the input and output to and from the text translator of your choice at any point in the toolchain. Here’s an example using gomplate:
puccini-tosca compile my-app.yaml | gomplate | puccini-clout scriptlet exec kubernetes.generate
my-app.yaml can then include template expressions, such as:
Note the proper escaping of quotation marks to avoid invalid YAML. Also, remember that indentation in YAML is significant, so it can be tricky to insert blocks into arbitrary locations. Generally, using text templating to manipulate YAML is not a great idea for these reasons.
If you insist on text templating, a useful convention for your toolchain could be to add a file
extension to mark a file for template processing. For example,
.yaml.j2 could be recognized as
requiring Jinja2 template processing, after which the
.j2 extension would be stripped.
Can I compose a single service from several interrelated Clout files?
TOSCA has a feature called “substitution mapping”, which is useful for modeling service composition. It is, however, a design feature. The implementation is up to your orchestration toolchain. See our examples here and here.
Puccini intentionally does not support service composition. Each Clout file is its own universe. If you need to create edges between vertexes in one Clout file and vertexes in other Clout files, then it’s up to you and your tools to design and implement that integration. The solution could be very elaborate indeed: the two Clouts might represent services with very different lifecycles, that run in different clouds, that are handled by different orchestrators. And the connection might require complex networking to achieve. There’s simply no one-size-fits-all way Puccini could do it—namespaces? proxies? catalogs? repositories?—so it insists on not having an opinion.
TOSCA is so complicated! Help!
I know, right? Now imagine writing a parser for it… Not only is it a complex language, but the specification itself (as of version 1.3) has many contradictions, errors, and gaps.
Please join OASIS’s TOSCA community to help improve the language!
Meanwhile, we’ve included examples of TOSCA core grammatical features, with some running commentary. Treat them as your playground. Also, if you have 4 hours to spare, grab some snacks, get comfortable, and watch the author’s free online course for TOSCA 1.0: part 1, part 2.
(Author’s note: This is my second take at writing a TOSCA parser. The first was AriaTosca, an incubation project under the Apache Software Foundation. I am grateful to Cloudify for funding much of the AriaTosca project. Note, however, that Puccini is a fresh start initiated by myself with no commercial backing. It does not use AriaTosca code and has a radically different architecture as well as very different goals.)
Why is it called “Puccini”?
Giacomo Puccini was the composer of the Tosca opera (based on Victorien Sardou’s play, La Tosca), as well as La bohème, Madama Butterfly, and other famous works. The theme here is orchestration, orchestras, composition, and thus operas. Capiche?
How to pronounce “Puccini”?
For a demonstration of its authentic 19th-century Italian pronunciation see this clip.