Skip to content

Commit

Permalink
Add "puccini-tosca validate" command
Browse files Browse the repository at this point in the history
Also add --timeout to all commands
tliron committed Sep 10, 2024
1 parent ea44905 commit 35a7849
Showing 23 changed files with 283 additions and 135 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -90,7 +90,7 @@ individual files as well as packaged in
[CSAR files](https://docs.oasis-open.org/tosca/TOSCA-Simple-Profile-YAML/v1.3/TOSCA-Simple-Profile-YAML-v1.3.html#_Toc302251718).

Puccini also comes with a simple CSAR creation tool that can be used independently of the
other tools, [`puccini-csar`](puccini-csar/).
other tools, [`puccini-csar`](executables/puccini-csar/).

### Design Principles

258 changes: 154 additions & 104 deletions TUTORIAL.md
Original file line number Diff line number Diff line change
@@ -10,28 +10,27 @@ The distribution comes with three executables:
* [`puccini-csar`](executables/puccini-csar/): packs TOSCA sources and artifacts into a CSAR


Basic Usage
-----------
Files, Archives, and URLs
-------------------------

Let's start by compiling a self-contained local file:
Let's start by compiling a TOSCA service template in a self-contained local file:

puccini-tosca compile examples/1.3/descriptions.yaml

What if the file imports other files? TOSCA `imports` can refer to either absolute
URLs or relative URLs ([RFC 1808](https://tools.ietf.org/html/rfc1808)). Note that
if the URL scheme is not provided it defaults to "file:", so that it can be treated
as a platform-independent file system path. Also note that relative URLs support
Unix-like `.` and `..` components, allowing you to refer to resources upwards in the
directory tree.

Let's compile a local example for OpenStack that uses imports (relative URLs):
Now let's compile a local example for OpenStack that uses imports (relative URLs):

puccini-tosca compile examples/openstack/hello-world.yaml

Note that you can also use relative URLs in TOSCA artifacts (their `file` keyword).
TOSCA `imports` can refer to either absolute URLs or relative URLs
([RFC 1808](https://tools.ietf.org/html/rfc1808)). Note that if the URL scheme is not
provided it defaults to "file:", so that it can be treated as a platform-independent
file system path. Also note that relative URLs support Unix-like `.` and `..` components,
allowing you to refer to resources upwards in the directory tree.

Puccini also supports relative URLs in TOSCA artifacts (their `file` keyword).

Puccini can also compile directly from a URL. Let's use the same OpenStack example as
above:
We can also compile directly from a URL. Let's use the same OpenStack example as
above, but point to it on the web:

puccini-tosca compile https://raw.githubusercontent.com/tliron/puccini/main/examples/openstack/hello-world.yaml

@@ -49,8 +48,8 @@ Puccini can also compile YAML from stdin:

cat examples/1.3/descriptions.yaml | puccini-tosca compile

Be aware that a stdin source does not have a path and thus cannot support relative
URLs.
Just be aware that a stdin source does not have a path and thus cannot support relative
URL imports. Absolute URLs will work.

For the above examples we referred to a single, root YAML file. However, Puccini can also
compile from a CSAR package and, again, the CSAR can be a local file or at a URL. Let's create
@@ -59,8 +58,7 @@ a local CSAR and then compile it:
puccini-csar create openstack.tar.gz examples/openstack
puccini-tosca compile openstack.tar.gz

The `puccini-csar` tool will archive the entire directory and automatically create a
"TOSCA-Metadata" section for us, resulting in a compliant CSAR file.
(More on the `puccini-csar` tool below.)

For TOSCA files within the CSAR the relative URLs refer to the internal structure of the
archive. Note that Puccini does *not* unpack the archive into individual files, but rather
@@ -69,102 +67,57 @@ in our TOSCA imports, the same exact OpenStack example works whether it's access
at a URL, or from within a CSAR, and even a CSAR at a URL.


Controlling the Output
----------------------

The default output format is YAML but other formats are supported: JSON (and
[ARD](https://github.com/tliron/kutil/tree/master/ard/)-compatible extended JSON), XML,
CBOR, and MessagePack. Here's ARD-compatible XJSON:

puccini-tosca compile examples/1.3/descriptions.yaml --format=xjson

By default the output is nicely indented and and colorized for human readability. You can
turn off prettification if you're interested in the most compact output:

puccini-tosca compile examples/1.3/descriptions.yaml --pretty=false

Note that colorization will *always* be disabled in contexts that do not support it. In
other words it will likely only appear in stdout for terminal emulators that support ANSI
color codes. However, you can also specifically turn off colorization:

puccini-tosca compile examples/1.3/descriptions.yaml --colorize=false

By default the output is sent to stdout but you can also send it to a file (without
colorization):

puccini-tosca compile examples/1.3/descriptions.yaml --output=clout.yaml

Of course if running in a shell you can also redirect stdout to a file (again, without
colorization):

puccini-tosca compile examples/1.3/descriptions.yaml > clout.yaml

You can increase the verbosity of logging using `-v` or even `-vv`:

puccini-tosca compile examples/1.3/descriptions.yaml -vv

By default all the log messages go to stderr but we can send them to a file:

puccini-tosca compile examples/1.3/descriptions.yaml -vv --log=puccini.log
cat puccini.log

If you only want to see the logs and not the Clout output:

puccini-tosca compile examples/1.3/descriptions.yaml -vv > /dev/null

To suppress all output (if you're only interested in the return error code):

puccini-tosca compile examples/1.3/descriptions.yaml --quiet

Also note that there is a `puccini-tosca parse` command that provides a lot
of internal diagnostic information about the language parser. It's generally
useful for Puccini developers rather than Puccini users, so it is out of scope
for this quickstart guide. See [here](executables/puccini-tosca/) for more
information.


More on Compilation
-------------------
Problems
--------

Let's try to compile a TOSCA service template that requires inputs:

puccini-tosca compile examples/1.3/inputs-and-outputs.yaml

You'll see that Puccini reported a "problem" regarding the unassigned input. Any and all
compilation errors, whether they are syntactical, grammatical, or topological, are
gathered and organized by file, row, and column. Indeed, Puccini's strict and detailed
problem reporting is one of its most powerful features.
gathered and organized by file, row, and column.

By default problems are reported in a human-readable format. However, like the Clout
output, problems can be formatted for easier consumption by other tools:
Puccini's strict and detailed problem reporting is one of its most powerful features.

puccini-tosca compile examples/1.3/inputs-and-outputs.yaml --problems-format=json

Let's set that missing input:
Inputs
------

Let's set that missing input that caused us a problem above:

puccini-tosca compile examples/1.3/inputs-and-outputs.yaml --input=ram=1gib

In this case the input is a string (actually a TOSCA `scalar-unit.size`), but note that
the the input format is YAML, which is also JSON-compatible, so that complex input
values can be provided, e.g. `--input=myinput={key1:value1,key2:value2}`. Also Note that
you can use the `--input` flag more than once to provide multiple inputs.
the the input format is actually YAML, so that complex input values can be provided,
e.g. `--input=myinput={key1:value1,key2:value2}`.

Inputs can also be loaded from a file (locally or at a URL) as straightforward YAML:
(Does that look like JSON to you and not YAML? Actually, JSON is compatible with YAML
so a YAML parser can parse JSON.)

Also note that you can use the `--input` flag more than once to provide multiple inputs.

All inputs can also be loaded from a file (locally or at a URL) as straightforward YAML
using `--inputs`:

echo 'ram: 1 gib' > inputs.yaml
puccini-tosca compile examples/1.3/inputs-and-outputs.yaml --inputs=inputs.yaml


Topology Resolution
-------------------

By default the compiler will "resolve" the topology, meaning that it will atempt to satisfy
all node template requirements and create relationships, thus completing the graph. However,
sometimes it may be useful to disable the resolution phase in order to avoid excessive problem
reports:
reports while designing:

puccini-tosca compile examples/1.3/requirements-and-capabilities.yaml --resolve=false

When you turn off the resolution phase you will indeed see no relationships in the Clout
(you'll see that the `edgesOut` for all vertexes is an empty list).

Read more about how Puccini implements resolution
Read more about how Puccini implements topology resolution
[here](assets/tosca/profiles/common/1.0/js/RESOLUTION.md).


@@ -191,10 +144,7 @@ convenience we can use the `--coerce` flag to coerce the values during compilati

You'll see that all properties now have their actual values rather than call stubs.

(In a real-world orchestration scenario we would want to coerce a Clout later as its
attribute values change. We'll discuss that below.)

Puccini handles TOSCA constraints in exactly the same way, the reason being that,
Puccini handles TOSCA data constraints during coercion, too. The reason is that,
like functions, constraints would have to be applied to data that might not be
available during compilation, e.g. constraints associated with an attribute or its
data type.
@@ -217,10 +167,43 @@ Now, let's compile this same file without coercion (the default behavior):

The problem was not reported this time.

**IMPORTANT! The implication is that by default you will not see constraint-related
problems reported during compilation, even for values that are known! Thus it's common
to use the `--coerce` flag with `puccini-tosca compile` when your goal is to validate
the TOSCA.**
You might be wondering why `compile` doesn't coerce values by default. The reason is
that in real world orchestration environments you would normally want an *un*-coerced
Clout, because you would actually be evaluating values at runtime, on demand, as they
may very well depend on runtime attributes derived from the deployment environment.
Thus every time you coerce the Clout you would get different values. None of these
values exist when you just compile TOSCA to Clout.

Similarly, even though `--resolve` is true by default, you may prefer to use an
*un*-resolved Clout, so that the topology can be resolved later, in the deployment
environment. In that case just set `--resolve=false`.

Not that by contrast, the `puccini-tosca validate` command (see below) *does* enable
coercion by default. That is the most significant difference between the two commands.


Validation
----------

If you don't need the Clout output and only need to know whether the TOSCA is valid or
if there are problems, then you can use the `puccini-tosca validate` command:

puccini-tosca validate examples/1.3/descriptions.yaml

`puccini-tosca validate` accepts most of the flags supported by `compile`, e.g. you can
provide example inputs via `--input`.

One important difference is that `puccini-tosca validate` automatically sets
`--coerce=true`, unlike `puccini-tosca compile` in which it defaults to false. You can
of course turn this off explicitly for `validate` if necessary. Just note that you are
coercing based on default values for attributes.

If you don't even need the problem report and only need the exit code (0 when valid, 1
when invalid) then add the `--quiet` flag. This can be useful in conditional scripts:

if puccini-tosca validate examples/1.3/descriptions.yaml --quiet; then
deploy examples/1.3/descriptions.yaml
fi


Scriptlets
@@ -305,16 +288,20 @@ For examples of how to create your own custom functions, constraints, and other
scriptlets for TOSCA, see [here](examples/javascript/).


More on CSARs
-------------
Creating CSARs
--------------

The [`tosca-csar`](executables/puccini-csar/) tool can create compliant CSAR files
by archiving a directory and its subdirectories and automatically creating a
"TOSCA-Metadata" section.

The [`tosca-csar`](executables/puccini-csar/) tool supports tarball CSARs (`.tar.gz`
or `.tar`) as well as zip (`.zip` or the `.csar` alias). Note that tarballs have the
advantage that they can be streamed (e.g. from a HTTP URL) whereas using the zip
format would require `puccini-tosca` to first download the entire archive to the
system's temporary directory. Both will work, but tarballs are far more efficient.
It supports tarball CSARs (`.tar.gz` or `.tgz` or `.tar`) as well as zip (`.zip`
or the `.csar` alias). Note that tarballs have the advantage that they can be streamed
(e.g. from a HTTP URL) whereas using the zip format would require `puccini-tosca` to
first download the entire archive to the system's temporary directory. Both will work,
but tarballs are more efficient for network access.

Try zip via the `.csar` alias:
Let's try zip via the `.csar` alias:

puccini-csar create openstack.csar examples/openstack
puccini-tosca compile openstack.csar
@@ -345,13 +332,76 @@ Example:

puccini-tosca compile "tar:$PWD/cloud.tar.gz\!main.yaml"

(We are using a backslash to escape the "!" for bash.)
(Since we need an absolute path, we are using "$PWD" to get the current working
directory. Also note that we are using a backslash to escape the "!" for bash.)

Also useful is the `meta` command to validate and extract the CSAR metadata:
Also useful is the `meta` command to validate and extract metadata from existing
CSAR files:

puccini-csar meta cloud.tar.gz


Controlling the Output
----------------------

The default output format is YAML but other formats are supported: JSON (and
[ARD](https://github.com/tliron/kutil/tree/master/ard/)-compatible extended JSON), XML,
CBOR, and MessagePack. Here's ARD-compatible XJSON:

puccini-tosca compile examples/1.3/descriptions.yaml --format=xjson

By default the output is nicely indented and and colorized for human readability. You can
turn off prettification if you want the most compact output:

puccini-tosca compile examples/1.3/descriptions.yaml --pretty=false

Note that colorization will *always* be disabled in contexts that do not support it. In
other words it will likely only appear in stdout for terminal emulators that support ANSI
color codes. However, you can also specifically turn off colorization:

puccini-tosca compile examples/1.3/descriptions.yaml --colorize=false

By default the output is sent to stdout but you can also send it to a file (without
colorization):

puccini-tosca compile examples/1.3/descriptions.yaml --output=clout.yaml

Of course if running in a shell you can also redirect stdout to a file (again, without
colorization):

puccini-tosca compile examples/1.3/descriptions.yaml > clout.yaml

By default problems are reported in a human-readable format. However, like the Clout
output, problems can be formatted for easier consumption by other tools:

puccini-tosca compile examples/1.3/inputs-and-outputs.yaml --problems-format=json


Debugging
---------

You can increase the verbosity of logging using `-v` or even `-vv`:

puccini-tosca compile examples/1.3/descriptions.yaml -vv

By default all the log messages go to stderr but you can send them to a file
instead:

puccini-tosca compile examples/1.3/descriptions.yaml -vv --log=puccini.log
cat puccini.log

If you only want to see the logs and not the Clout output, send stdout to
null:

puccini-tosca compile examples/1.3/descriptions.yaml -vv > /dev/null

Also note that there is a `puccini-tosca parse` command that provides a lot
of internal diagnostic information about the language parser. It's generally
useful for Puccini developers rather than Puccini users, so it is out of scope
for this quickstart guide. See [here](executables/puccini-tosca/) for more
information.


Next Steps
----------

2 changes: 2 additions & 0 deletions executables/puccini-clout/commands/root.go
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@ var (
strict bool
pretty bool
base64 bool
timeout float64
cpuProfilePath string
)

@@ -30,6 +31,7 @@ func init() {
rootCommand.PersistentFlags().BoolVarP(&strict, "strict", "y", false, "strict output (for \"yaml\" format only)")
rootCommand.PersistentFlags().BoolVarP(&pretty, "pretty", "p", true, "prettify output")
rootCommand.PersistentFlags().BoolVarP(&base64, "base64", "", false, "output base64 (for \"cbor\", \"messagepack\" formats)")
rootCommand.PersistentFlags().Float64Var(&timeout, "timeout", 30.0, "timeout in seconds")
rootCommand.PersistentFlags().StringVarP(&cpuProfilePath, "cpu-profile", "", "", "CPU profile file path")
}

7 changes: 5 additions & 2 deletions executables/puccini-clout/commands/scriptlet-exec.go
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ package commands

import (
contextpkg "context"
"time"

"github.com/spf13/cobra"
"github.com/tliron/exturl"
@@ -33,8 +34,10 @@ var execCommand = &cobra.Command{
}

urlContext := exturl.NewContext()
defer urlContext.Release()
context := contextpkg.TODO()
util.OnExitError(urlContext.Release)

context, cancel := contextpkg.WithTimeout(contextpkg.Background(), time.Duration(timeout*float64(time.Second)))
util.OnExit(cancel)

clout := LoadClout(context, url, urlContext)

8 changes: 6 additions & 2 deletions executables/puccini-clout/commands/scriptlet-get.go
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ package commands

import (
contextpkg "context"
"time"

"github.com/spf13/cobra"
"github.com/tliron/exturl"
@@ -29,9 +30,12 @@ var getCommand = &cobra.Command{
}

urlContext := exturl.NewContext()
defer urlContext.Release()
util.OnExitError(urlContext.Release)

clout := LoadClout(contextpkg.TODO(), url, urlContext)
context, cancel := contextpkg.WithTimeout(contextpkg.Background(), time.Duration(timeout*float64(time.Second)))
util.OnExit(cancel)

clout := LoadClout(context, url, urlContext)

scriptlet, err := js.GetScriptlet(scriptletName, clout)
util.FailOnError(err)
8 changes: 6 additions & 2 deletions executables/puccini-clout/commands/scriptlet-list.go
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ package commands
import (
contextpkg "context"
"strings"
"time"

"github.com/spf13/cobra"
"github.com/tliron/exturl"
@@ -29,9 +30,12 @@ var listCommand = &cobra.Command{
}

urlContext := exturl.NewContext()
defer urlContext.Release()
util.OnExitError(urlContext.Release)

clout := LoadClout(contextpkg.TODO(), url, urlContext)
context, cancel := contextpkg.WithTimeout(contextpkg.Background(), time.Duration(timeout*float64(time.Second)))
util.OnExit(cancel)

clout := LoadClout(context, url, urlContext)

List(clout)
},
7 changes: 5 additions & 2 deletions executables/puccini-clout/commands/scriptlet-put.go
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ package commands

import (
contextpkg "context"
"time"

"github.com/spf13/cobra"
"github.com/tliron/exturl"
@@ -29,8 +30,10 @@ var putCommand = &cobra.Command{
}

urlContext := exturl.NewContext()
defer urlContext.Release()
context := contextpkg.TODO()
util.OnExitError(urlContext.Release)

context, cancel := contextpkg.WithTimeout(contextpkg.Background(), time.Duration(timeout*float64(time.Second)))
util.OnExit(cancel)

clout := LoadClout(context, url, urlContext)

Binary file modified executables/puccini-clout/default.pgo
Binary file not shown.
5 changes: 4 additions & 1 deletion executables/puccini-csar/commands/meta.go
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ package commands

import (
contextpkg "context"
"time"

"github.com/spf13/cobra"
"github.com/tliron/exturl"
@@ -48,7 +49,9 @@ func Meta(url string) {

urlContext := exturl.NewContext()
util.OnExitError(urlContext.Release)
context := contextpkg.TODO()

context, cancel := contextpkg.WithTimeout(contextpkg.Background(), time.Duration(timeout*float64(time.Second)))
util.OnExit(cancel)

var csarUrl exturl.URL
csarUrl, err = urlContext.NewValidAnyOrFileURL(context, url, Bases(urlContext))
2 changes: 2 additions & 0 deletions executables/puccini-csar/commands/root.go
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@ import (
var (
logTo string
verbose int
timeout float64
cpuProfilePath string
)

@@ -18,6 +19,7 @@ func init() {
rootCommand.PersistentFlags().StringVarP(&logTo, "log", "l", "", "log to file (defaults to stderr)")
rootCommand.PersistentFlags().CountVarP(&verbose, "verbose", "v", "add a log verbosity level (can be used twice)")
rootCommand.PersistentFlags().BoolVarP(&commonlog.Trace, "trace", "", false, "add stack trace to log messages")
rootCommand.PersistentFlags().Float64Var(&timeout, "timeout", 30.0, "timeout in seconds")
rootCommand.PersistentFlags().StringVarP(&cpuProfilePath, "cpu-profile", "", "", "CPU profile file path")
}

Binary file modified executables/puccini-csar/default.pgo
Binary file not shown.
14 changes: 14 additions & 0 deletions executables/puccini-tosca/README.md
Original file line number Diff line number Diff line change
@@ -40,6 +40,20 @@ The list of supported quirks is maintained [here](../tosca/parsing/QUIRKS.md).
See the [tutorial](../TUTORIAL.md) for more detail.


`validate`
----------

Equivalent to `compile` but without Clout output and with `--coerce=true`. Will print "valid" to stderr
if valid, otherwise will print the problems. Use `--quiet` to suppress all output.

Note that both `validate` and `compile` set the exit code: 0 for valid, 1 if there are problems, which
is useful in scripts:

if puccini-tosca validate service.yaml --quiet; then
deploy service.yaml
fi


`parse`
-------

21 changes: 14 additions & 7 deletions executables/puccini-tosca/commands/compile.go
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ package commands

import (
contextpkg "context"
"time"

"github.com/spf13/cobra"
"github.com/tliron/exturl"
@@ -12,11 +13,12 @@ import (
)

var (
output string
resolve bool
coerce bool
exec string
arguments map[string]string
enableOutput bool
output string
resolve bool
coerce bool
exec string
arguments map[string]string
)

func init() {
@@ -47,8 +49,13 @@ var compileCommand = &cobra.Command{
url = args[0]
}

enableOutput = true
dumpPhases = nil
Compile(contextpkg.TODO(), url)

context, cancel := contextpkg.WithTimeout(contextpkg.Background(), time.Duration(timeout*float64(time.Second)))
util.OnExit(cancel)

Compile(context, url)
},
}

@@ -87,7 +94,7 @@ func Compile(context contextpkg.Context, url string) {
if exec != "" {
err = Exec(context, exec, arguments, clout, urlContext)
util.FailOnError(err)
} else if !terminal.Quiet || (output != "") {
} else if enableOutput && (!terminal.Quiet || (output != "")) {
err = Transcriber().Write(clout)
util.FailOnError(err)
}
6 changes: 5 additions & 1 deletion executables/puccini-tosca/commands/parse.go
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ package commands
import (
contextpkg "context"
"sort"
"time"

"github.com/spf13/cobra"
"github.com/tliron/commonlog"
@@ -63,7 +64,10 @@ var parseCommand = &cobra.Command{
dumpPhases = nil
}

Parse(contextpkg.TODO(), url)
context, cancel := contextpkg.WithTimeout(contextpkg.Background(), time.Duration(timeout*float64(time.Second)))
util.OnExit(cancel)

Parse(context, url)
},
}

2 changes: 2 additions & 0 deletions executables/puccini-tosca/commands/root.go
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@ var (
strict bool
pretty bool
base64 bool
timeout float64
cpuProfilePath string
)

@@ -28,6 +29,7 @@ func init() {
rootCommand.PersistentFlags().BoolVarP(&strict, "strict", "y", false, "strict output (for \"yaml\" format only)")
rootCommand.PersistentFlags().BoolVarP(&pretty, "pretty", "p", true, "prettify output")
rootCommand.PersistentFlags().BoolVarP(&base64, "base64", "", false, "output base64 (for \"cbor\", \"messagepack\" formats)")
rootCommand.PersistentFlags().Float64Var(&timeout, "timeout", 30.0, "timeout in seconds")
rootCommand.PersistentFlags().StringVarP(&cpuProfilePath, "cpu-profile", "", "", "CPU profile file path")
}

48 changes: 48 additions & 0 deletions executables/puccini-tosca/commands/validate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package commands

import (
contextpkg "context"
"time"

"github.com/spf13/cobra"
"github.com/tliron/kutil/terminal"
"github.com/tliron/kutil/util"
)

func init() {
rootCommand.AddCommand(validateCommand)
validateCommand.Flags().StringSliceVarP(&importPaths, "path", "b", nil, "specify an import path or base URL")
validateCommand.Flags().StringVarP(&template, "template", "t", "", "select service template in CSAR (leave empty for root, or use \"all\", path, or integer index)")
validateCommand.Flags().StringToStringVarP(&inputs, "input", "i", nil, "specify input (format is name=value)")
validateCommand.Flags().StringVarP(&inputsUrl, "inputs", "n", "", "load inputs from a PATH or URL to YAML content")
validateCommand.Flags().StringVarP(&problemsFormat, "problems-format", "m", "", "problems format (\"yaml\", \"json\", \"xjson\", \"xml\", \"cbor\", \"messagepack\", or \"go\")")
validateCommand.Flags().StringSliceVarP(&quirks, "quirk", "x", nil, "parser quirk")
validateCommand.Flags().StringToStringVarP(&urlMappings, "map-url", "u", nil, "map a URL (format is from=to)")

validateCommand.Flags().BoolVarP(&resolve, "resolve", "r", true, "resolves the topology (attempts to satisfy all requirements with capabilities)")
validateCommand.Flags().BoolVarP(&coerce, "coerce", "c", true, "coerces all values (calls functions and applies constraints)")
}

var validateCommand = &cobra.Command{
Use: "validate [[TOSCA PATH or URL]]",
Short: "Validate TOSCA",
Long: `Validates TOSCA service templates. Equivalent to "compile" without the Clout output.`,
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
var url string
if len(args) == 1 {
url = args[0]
}

dumpPhases = nil

context, cancel := contextpkg.WithTimeout(contextpkg.Background(), time.Duration(timeout*float64(time.Second)))
util.OnExit(cancel)

Compile(context, url)

if !terminal.Quiet {
terminal.Eprintln("valid")
}
},
}
Binary file modified executables/puccini-tosca/default.pgo
Binary file not shown.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ module github.com/tliron/puccini
go 1.23

require (
github.com/dop251/goja v0.0.0-20240806095544-3491d4a58fbe
github.com/dop251/goja v0.0.0-20240828124009-016eb7256539
github.com/fxamacker/cbor/v2 v2.7.0
github.com/klauspost/compress v1.17.9
github.com/klauspost/pgzip v1.2.6
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
@@ -37,8 +39,8 @@ github.com/docker/docker v24.0.0+incompatible h1:z4bf8HvONXX9Tde5lGBMQ7yCJgNahmJ
github.com/docker/docker v24.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
github.com/dop251/goja v0.0.0-20240806095544-3491d4a58fbe h1:jwFJkgsdelB87ohlXaAGSd05Cb5ALDFa9iW9IGRHcRM=
github.com/dop251/goja v0.0.0-20240806095544-3491d4a58fbe/go.mod h1:DF+w/nLMIkvRpyhd/0K+Okbh3fVZBtXLwRtS/ccAa5w=
github.com/dop251/goja v0.0.0-20240828124009-016eb7256539 h1:YIxvsQAoCLGScK2c9ag+4sFCgiQFpMzywJG6dQZFu9k=
github.com/dop251/goja v0.0.0-20240828124009-016eb7256539/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
Binary file modified library/default.pgo
Binary file not shown.
8 changes: 4 additions & 4 deletions scripts/generate-pgo
Original file line number Diff line number Diff line change
@@ -6,10 +6,10 @@ HERE=$(dirname "$(readlink --canonicalize "$BASH_SOURCE")")

EXECUTABLES=$ROOT/executables

rm --force "$EXECUTABLES/puccini-tosca/default.pgo"
rm --force "$EXECUTABLES/puccini-clout/default.pgo"
rm --force "$EXECUTABLES/puccini-csar/default.pgo"
rm --force "$ROOT/library/default.pgo"
rm --force "$EXECUTABLES/puccini-tosca"/*.pgo
rm --force "$EXECUTABLES/puccini-clout"/*.pgo
rm --force "$EXECUTABLES/puccini-csar"/*.pgo
rm --force "$ROOT/library"/*.pgo

"$HERE/build"

8 changes: 4 additions & 4 deletions tosca/parsing/QUIRKS.md
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ Puccini TOSCA Quirks
====================

These are activated via the `--quirk/-x` switch for
[**puccini-tosca**](../../puccini-tosca/):
[**puccini-tosca**](../../executables/puccini-tosca/):

* **imports.implicit.disable**: In TOSCA 1.0-1.3 the Simple Profile is implicitly imported by
default. This quirk will disable implicit imports.
@@ -36,10 +36,10 @@ These are activated via the `--quirk/-x` switch for

* **namespace.normative.shortcuts.disable**: In TOSCA 1.0-1.3 all the normative types have long
names, such as "tosca.nodes.Compute", prefixed names ("tosca:Compute"), and also short names
("Compute"). Those short names are annoying because it means you can't use those names for your
own types. This quirk disables the short names (the prefixed names remain).
("Compute"). Those short names might be annoying because it means you can't use those names for
your own types. This quirk disables the short names (the prefixed names remain).

* **substitution_mappings.requirements.list**: According to the examples in the TOSCA 1.0-1.3 specs,
* **substitution_mappings.requirements.list**: According to the examples in the TOSCA 1.0-2.0 specs,
the `requirements` key under `substitution_mappings` is syntactically a map. However, this syntax
is inconsistent because it doesn't match the syntax in node templates, which is a sequenced list.
(In node types, too, it is a sequenced list, although grammatically it works like a map.) This
4 changes: 2 additions & 2 deletions tosca/parsing/quirks.go
Original file line number Diff line number Diff line change
@@ -50,8 +50,8 @@ const (

// In TOSCA 1.0-1.3 all the normative types have long
// names, such as "tosca.nodes.Compute", prefixed names ("tosca:Compute"), and also short names
// ("Compute"). Those short names are annoying because it means you can't use those names for your
// own types. This quirk disables the short names (the prefixed names remain).
// ("Compute"). Those short names might be annoying because it means you can't use those names for
// your own types. This quirk disables the short names (the prefixed names remain).
QuirkNamespaceNormativeShortcutsDisable Quirk = "namespace.normative.shortcuts.disable"

// According to the examples in the TOSCA 1.0-1.3 specs,

0 comments on commit 35a7849

Please sign in to comment.