"star-cm" ⭐
- A rudimentary configuration management language that utilizes Starlark instead of Ruby, json, or yaml.
- Why Starlark? Starlark provides variables, functions, loops, and lots more "for free" inside of the configuration files!
- Starcm is not intended to be a full replacement for tools like Chef or Ansible, but starcm can be used to bootstrap these tools and many others through features like
exec()
, for calling binaries,template()
for rendering templated files,load_dynamic()
for chaining Starcm files dynamically, and much more!
Starcm is intended to become a viable alternative for tools like macadmins/installapplications, facebookincubator/go2chef, and google/glazier.
In order to test out the Starcm examples described this repository, the Starcm authors recommend that you install Bazelisk and alias it to bazel
.
You should be able to use
go run
directly but the documented examples usebazel
only.
Let's look at a simple starcm file that calls out to the echo
binary using a Starcm function called exec
. This is similar to Chef's exec
resource.
starcm/examples/echo/echo.star
Lines 1 to 7 in 2911aea
When we run this, we see the string we passed to args
get printed out:
$ bazel run :starcm -- --root_file examples/echo/echo.star --timestamps=false
hello from echo.star!
This is a very trivial example of what Starcm can do. Let's make it a bit more complicated...
For instance, Starcm's exec
can also handle non-zero exit codes.
See examples/exec/exit_codes/unexpected.star.
starcm/examples/exec/exit_codes/unexpected.star
Lines 1 to 8 in 2911aea
$ bazel run :starcm -- --root_file examples/exec/exit_codes/unexpected.star --timestamps=false
we expect to exit 2
result(changed = True, diff = "", error = "exit status 2", name = "explicitly exit 2", output = "we expect to exit 2\n", success = False)
exec
exited with a non-zero exit code thus result
indicates things were not successful (result(..., success=False)
).
This is because the default expected_error_code
, if not specified, is 0
.
💡 What is
result()
?result()
is a struct that is returned by most Starcm functions to signal whether a function achieved the expected result. Later we will see how Starcm code can consume theresult
struct to make conditional decisions.
If we set expected_exit_code
to 2
then this succeeds!
starcm/examples/exec/exit_codes/expected.star
Lines 1 to 9 in 2911aea
$ bazel run :starcm -- --root_file examples/exec/exit_codes/expected.star --timestamps=false
we expect to exit 2
result(changed = True, diff = "", error = "exit status 2", name = "explicitly exit 2", output = "we expect to exit 2\n", success = True)
Another thing Starcm can do is render template files via template
. This is similar to the template
resource in Chef.
As an example let's take a look at examples/templates/simple/template.star.
starcm/examples/templates/simple/template.star
Lines 1 to 11 in 2911aea
The template that is referenced in template.star
is examples/templates/simple/hello_world.tpl:
$ bazel run :starcm -- --root_file examples/templates/simple/template.star --timestamps=false -v 2
INFO: [LoadFromFile]: loading file "examples/templates/simple/template.star"
INFO: [hello world template]: hello_world.tpl before rendering: Hello {{ name | capitalize }}, you are {{ age }} years old.
INFO: [hello world template]: data: map[age:42 name:world]
Hello World, you are 42 years old.
All Starcm functions share some common functionality.
All Starcm functions return a result
struct.
In Go this represented as such:
type Result struct {
Name *string
Output *string
Error error
Success bool
Changed bool
Diff *string
Comment string
}
If printed out or inspected directly in Starlark, a result
may look something like this:
result(
changed = True,
diff = "",
error = "exit status 2",
name = "explicitly exit 2",
output = "we expect to exit 2\n",
success = True
)
Starlark, and by extension starcm, supports if
statements. Take examples/if_statements/if_statements.star for example. If the exec()
succeeds, we print party!
.
starcm/examples/if_statements/if_statements.star
Lines 1 to 11 in 2911aea
$ bazel run :starcm -- --root_file examples/if_statements/if_statements.star --timestamps=false
party!
We can also implement this same conditional behavior with a starcm-specific construct called only_if
. This feature is not built into native Starlark.
See examples/only_if/only_if.star.
starcm/examples/only_if/only_if.star
Lines 1 to 22 in 2911aea
In this example
if not(a.success):
write(
name = "print_not_success_#1",
str = "a.success: %s #1" % (a.success),
)
is essentially equivalent to
write(
name = "print_not_success_#2",
str = "a.success: %s #2" % (a.success),
only_if = a.success == False
)
with one key difference: only_if
produces a log message indicating that write(name=print_not_success, ...)
was skipped due to the only_if
condition being false. This is can be useful for debugging.
$ bazel run :starcm -- --root_file examples/only_if/only_if.star --timestamps=false -v 2
INFO: [LoadFromFile]: loading file "examples/only_if/only_if.star"
we expect to exit 2
INFO: [explicitly exit 2]: expectedExitCode: 2
INFO: [explicitly exit 2]: actualExitCode: 2
INFO: [print_not_success_#2]: skipping write(name="print_not_success_#2") because only_if was false
Notice that there is no log message regarding
print_not_success_#1
. Normalif
statements are not executed at all if the condition is false, whereasonly_if
logs that something was skipped.
See the examples folder for more examples of what starcm can do. There's lots it can do such as downloading files (with hash checking), dynamically loading additional .star
files, rendering templates, and combining all the cabilities via macros, thanks to Starlark.
$ scrut test --work-directory . README.md