From 013868fdb0a49fa73e0466cbf2ce2af843b0137a Mon Sep 17 00:00:00 2001 From: Bill Maxwell Date: Fri, 6 Oct 2023 16:33:59 -0700 Subject: [PATCH] Initial pass at paring down runtime docs. Signed-off-by: Bill Maxwell --- .../02-admin/02-volumeclasses.md | 0 .../02-admin/03-computeclasses.md | 0 .../02-admin/80-alpha-image-allow-rules.md | 165 +++++++ docs/docs/38-authoring/00-overview.md | 435 ------------------ docs/docs/38-authoring/01-best-practices.md | 33 -- docs/docs/38-authoring/02-structure.md | 86 ---- docs/docs/38-authoring/03-containers.md | 384 ---------------- docs/docs/38-authoring/04-volumes.md | 202 -------- docs/docs/38-authoring/05-secrets.md | 291 ------------ docs/docs/38-authoring/06-jobs.md | 73 --- .../docs/38-authoring/07-args-and-profiles.md | 188 -------- docs/docs/38-authoring/08-localdata.md | 34 -- docs/docs/38-authoring/09-permissions.md | 86 ---- docs/docs/38-authoring/20-labels.md | 78 ---- docs/docs/38-authoring/21-services.md | 31 -- docs/docs/38-authoring/30-advanced.md | 167 ------- docs/docs/38-authoring/31-nested-acorns.md | 62 --- docs/docs/38-authoring/_category_.yaml | 1 - docs/docusaurus.config.js | 10 +- docs/sidebars.js | 61 +-- 20 files changed, 175 insertions(+), 2212 deletions(-) rename docs/docs/{100-reference => }/02-admin/02-volumeclasses.md (100%) rename docs/docs/{100-reference => }/02-admin/03-computeclasses.md (100%) create mode 100644 docs/docs/02-admin/80-alpha-image-allow-rules.md delete mode 100644 docs/docs/38-authoring/00-overview.md delete mode 100644 docs/docs/38-authoring/01-best-practices.md delete mode 100644 docs/docs/38-authoring/02-structure.md delete mode 100644 docs/docs/38-authoring/03-containers.md delete mode 100644 docs/docs/38-authoring/04-volumes.md delete mode 100644 docs/docs/38-authoring/05-secrets.md delete mode 100644 docs/docs/38-authoring/06-jobs.md delete mode 100644 docs/docs/38-authoring/07-args-and-profiles.md delete mode 100644 docs/docs/38-authoring/08-localdata.md delete mode 100644 docs/docs/38-authoring/09-permissions.md delete mode 100644 docs/docs/38-authoring/20-labels.md delete mode 100644 docs/docs/38-authoring/21-services.md delete mode 100644 docs/docs/38-authoring/30-advanced.md delete mode 100644 docs/docs/38-authoring/31-nested-acorns.md delete mode 100644 docs/docs/38-authoring/_category_.yaml diff --git a/docs/docs/100-reference/02-admin/02-volumeclasses.md b/docs/docs/02-admin/02-volumeclasses.md similarity index 100% rename from docs/docs/100-reference/02-admin/02-volumeclasses.md rename to docs/docs/02-admin/02-volumeclasses.md diff --git a/docs/docs/100-reference/02-admin/03-computeclasses.md b/docs/docs/02-admin/03-computeclasses.md similarity index 100% rename from docs/docs/100-reference/02-admin/03-computeclasses.md rename to docs/docs/02-admin/03-computeclasses.md diff --git a/docs/docs/02-admin/80-alpha-image-allow-rules.md b/docs/docs/02-admin/80-alpha-image-allow-rules.md new file mode 100644 index 000000000..9c0a04f0d --- /dev/null +++ b/docs/docs/02-admin/80-alpha-image-allow-rules.md @@ -0,0 +1,165 @@ +--- +title: ImageAllowRules [Alpha] +--- + +ImageAllowRules (IARs) are an alpha-feature of Acorn, currently hidden behind a feature flag. +To enable them in your Acorn installation, use `acorn install --features image-allow-rules=true`. + +:::caution +Please read this page to completion before enabling IARs, as they can be quite disruptive. +::: + + +## How ImageAllowRules work + +The principle behind IARs is to make your cluster more secure. +If you enable this feature, you won't be able to deploy any Acorn image anymore without allowing it. +To do that, you have to create an `ImageAllowRule` resource. +If the app image is allowed by a single IAR in your project, it's good to run. + +:::note +Removing an IAR won't stop your running app, but will update the `image-allowed` status condition on the app. +::: + +## What makes up an ImageAllowRule + +Currently, IARs have two parts: + +1. The `images` scope (required) denotes which images the rule applies to. It uses the same syntax as the auto-upgrade pattern. Examples below. +2. The `signatures` rules (optional) define a set of image signatures and annotations on those signatures to make sure that an image was actually approved by someone or something, e.g. by your QA team. We're using [sigstore/cosign](https://docs.sigstore.dev/cosign/installation/) for everything related to signatures. + +## Example + +```yaml +apiVersion: api.acorn.io/v1 +kind: ImageAllowRule +metadata: + name: example-iar + namespace: acorn # your project name +images: + - ghcr.io/** # ** matches everything, * matches a single path item, # matches a number +signatures: + rules: + - signedBy: + anyOf: # one match is good enough + - | + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEo9QMl0ilxrBNFqOpifkhmKVZ14D8 + cUSzwOtALU9owM2ZRzE55OP4je2y9sTVvlNr59eZQ/Q4gsxHfo4EETEuog== + -----END PUBLIC KEY----- + allOf: [] # all signatures required + annotations: # those annotations have to be present on all signatures + match: # simple key-value pairs + qa: approved + expressions: # just like Kubernetes label selectors + - key: tests + operator: In # In, NotIn, Exists, DoesNotExist + values: + - passed + - ok +``` + +## About Signatures + +To sign an image, you can use [sigstore/cosign](https://docs.sigstore.dev/cosign/installation/) via the CLI. +You can download or build an Acorn image, sign it with cosign, annotate the signature (optional), then upload it to an OCI registry. +Afterwards, when you try to run that image in a protected cluster where the image is in scope of an IAR, Acorn will first ensure that: +- it matches the provided public keys +- matching signatures also have the required annotations + +If one or both of these conditions aren't met, Acorn will refuse to run the image. + +### Walkthrough + +Here's a full walkthrough to use Acorn with the ImageAllowRules feature in a fresh installation and with cosign signatures. +Please note that the exact output may be different for you, especially depending on the version of cosign you use. + +```bash +# 1. Create a fresh cluster and install Acorn - doesn't have to be k3d, you may also update your existing installation +$ k3d cluster create acorn +... + +$ acorn install --features image-allow-rules=true +... + +# 2. Pull some Acorn image and push it to another registry that you have push access to (Alternatively, build it from an Acornfile) +$ acorn pull ghcr.io/acorn-io/hello-world:latest +$ acorn tag ghcr.io/acorn-io/hello-world:latest my.registry.local/acorn/hello-world:latest +$ acorn push my.registry.local/acorn/hello-world:latest + +# 2.1 Faster using crane: +$ crane copy ghcr.io/acorn-io/hello-world:latest my.registry.local/acorn/hello-world:latest +... + +# 3. Get the digest +$ crane digest my.registry.local/acorn/hello-world:latest +sha256:1a6c64d2ccd0bb035f9c8196d3bfe72a7fdbddc4530dfcb3ab2a0ab8afb57eeb + +# 4. Generate a keypair if you don't have one already +$ cosign generate-key-pair +Enter password for private key: +Enter password for private key again: +WARNING: File cosign.key already exists. Overwrite? +Are you sure you would like to continue? [y/N] y +Private key written to cosign.key +Public key written to cosign.pub + +# 5. Sign the image with the newly generated key and an annotation that says `tag=notok` +$ cosign sign --key cosign.key -a tag=notok my.registry.local/acorn/hello-world@sha256:1a6c64d2ccd0bb035f9c8196d3bfe72a7fdbddc4530dfcb3ab2a0ab8afb57eeb +Enter password for private key: + + Note that there may be personally identifiable information associated with this signed artifact. + This may include the email address associated with the account with which you authenticate. + This information will be used for signing this artifact and will be stored in public transparency logs and cannot be removed later. + +By typing 'y', you attest that you grant (or have permission to grant) and agree to have this information stored permanently in transparency logs. +Are you sure you would like to continue? [y/N] y +tlog entry created with index: 15549911 +Pushing signature to: my.registry.local/acorn/hello-world + +# 6. Deploy a cluster-level image allow rule that will deny this image +$ cat << EOF | kubectl apply -f - +pipe heredoc> apiVersion: api.acorn.io/v1 +kind: ImageAllowRule +metadata: + name: testrule + namespace: acorn +images: + - my.registry.local/** +signatures: + rules: + - signedBy: + anyOf: + - | + -----BEGIN PUBLIC KEY----- + !!! Put your Public Key here !!! + -----END PUBLIC KEY----- + annotations: + match: + tag: ok +EOF +imageallowrule.api.acorn.io/testrule configured + +# 7. Try to run the image -> it should fail (because the annotation is wrong) + +$ acorn run my.registry.local/acorn/hello-world:latest + • WARNING: This application would like to use the image 'my.registry.local/acorn/hello-world:latest'. + This image is not trusted by any image allow rules in this project. + This could be VERY DANGEROUS to the cluster if you do not trust this + application. If you are unsure say no. + +? Do you want to allow this app to use this (POTENTIALLY DANGEROUS) image? [Use arrows to move, type to filter] +> NO + yes (this tag only) + repository (all images in this repository) + registry (all images in this registry) + all (all images out there) + +# Here, as an admin, you get to choose to have Acorn automatically generate an IAR for you to allow this image (without signatures) +# Repeating Step 5 with `-a tag=notok` and then continuing with steps 6 and 7, should make it work +... +``` + +## No need for YAML + +As you have seen in the last section, Acorn also prompts admins to allow an image that is not yet allowed to run. That's quite basic and will create an ImageAllowRule with only the `images` scope populated, no signatures required. diff --git a/docs/docs/38-authoring/00-overview.md b/docs/docs/38-authoring/00-overview.md deleted file mode 100644 index 249233fe8..000000000 --- a/docs/docs/38-authoring/00-overview.md +++ /dev/null @@ -1,435 +0,0 @@ ---- -title: Overview ---- - -The primary goal of the Acornfile is to quickly and easily describe how to build, develop, and run containerized applications. - -The syntax is very similar to JSON and YAML that you're probably already familiar with from other tools. - -The resulting artifact defined by the Acornfile and produced during `acorn build` is a complete package of your application. It includes all the container images, secrets, data, nested Acorns, etc. in a single OCI image that can be distributed through a registry. - -## The primary building blocks - -### Objects - -In the Acornfile file the primary building block is an object. The generic syntax for any object is: - -```acorn -key: { - // ... fields ... -} -``` - -They start with a name `key` and wrap a collection of fields and values in `{}`. A more Acorn specific example is: - -```acorn -containers: { - "my-app": { - // ... - } -} -``` - -In the above example, there is an object called `containers`, which contains another object called `my-app`. Keys do not need to be quoted, unless they contain a `-`. - -For convenience, you can collapse objects which have only one field to a single `:` line and omit the braces. For example these: - -```acorn -containers: foo: image: "nginx" - -containers: bar: build: { - context: "." - target: "static" -} -``` - -are equivalent to: - -```acorn -containers: { - foo: { - image: "nginx" - } -} - -containers: { - bar: { - build: { - context: "." - target: "static" - } - } -} -``` - -### Lists - -The other main building block is a list. - -```acorn -containers: { - myapp: { - // ... - ports: [ - "80/http", - "8080/http", - ] - } -} -``` - -Lists are surrounded by `[]`. Items are separated by a comma. The last item can include an optional trailing comma. - -## Fields - -A `field` is a label and a value, we have seen multiple examples of `fields` in the previous examples. Here we will dive deeper. - -### Field names - -In an Acornfile fields can be strings with [a-zA-Z0-9_] without being wrapped in double quotes. You can use `-`, `/`, and `.` if you use double quotes around the field name. - -```acorn -// Valid field names -aLongField: "" -"/a/file/path": "" -"my-application-container": "" -``` - -### Assigning field values - -Variables allow you to store values and later reference them elsewhere in the Acornfile. The syntax for defining a variable is shown below. Values can be a string, number, boolean, list, object, or null. - -```acorn -localData: { - myString: "" - myInteger: 5 - myBool: true - myObject: {} - myList: [] -} -``` - -### Strings - -Strings can be a single line or multiline. A single line string is surrounded by `"` quotes. - -Multiline strings are enclosed in triple quotes `"""`. The opening `"""` must be followed by a newline. The closing `"""` must also be on it's own line. The whitespace directly preceding the closing quotes must match the preceding whitespace on all other lines and is not included in the value. This allows you to indent the text to match current level without the indenting becoming part of the actual value. - -```acorn -singleLine: "Hi!" -multiLine: """ - Hello - World! - """ -# multiLine is equivalent to "Hello \nWorld!" -``` - -### Numbers - -Numbers are integers by default unless they contain a decimal. - -```acorn -int: 4 -float: 4.2 -``` - -### Boolean - -Booleans are `true` or `false`. - -### Null - -Null is `null`. - -### Comments - -You can add comments to document your Acornfile. Comments start with `//` and continue for the rest of the line - -```acorn -// This is a comment -some: "value" // This is a comment about this line -``` - -### Accessing fields - -To reference a variable elsewhere in the file, you separate the key fields of the path by a `.`. - -Given these variables: - -```acorn -localData: { - myVariable: "" - myInteger: 5 - myBool: true - myObject: { - aKey: "value" - } - "my-app": { - // ... - } -} - -// Can Be accessed like - -v: localData.myVariable -i: localData.myInteger -b: localData.myBool -s: localData.myObject -s0: localData.myObject.aKey -s1: localData.myObject["aKey"] -a: localData."my-app" -``` - -### Scopes - -Fields that reference another field will look for a value starting at the nearest enclosing scope and working upwards until reaching the top-level. - -```acorn -port: 3307 -containers: app: { - ports: localData.port // Evaluates to 3306 -} -data: port // Evaluates to 3307 -localData: { - port: 3306 - exposedServicePort: port // Evaluates to 3306 -} -``` - -In the above example, `containers.app.ports` would be `3306` along with `localData.exposedServicePort`. - -#### Aliases - -Because of scoping in the previous example, it would not be possible in the above example to set any value under localData to a value of `port`(3307). One way to address this is to use the `let` operator to alias the port variable. - -```acorn -let topLevelPort = port -port: 3307 -containers: app: { - ports: localData.port // Evaluates to 3306 -} -data: port // Evaluates to 3307 -localData: { - port: 3306 - exposedServicePort: topLevelPort // Evaluates to 3307 -} -``` - -In the example the top level `port` variable is aliased to `topLevelPort` and assigned to `localData.exposedServicePort`. - -### String interpolation - -Variables can be inserted into a string by wrapping their name with `\()`. For example: - -```acorn -args: { - userAdjective: "cool" -} - -localData: { - config: { - key: "This is something \(args.userAdjective)" - } -} - -# localData.config.key is "This is something cool" -``` - -Interpolation can also be used for field names: - -```acorn -localData: { - index: 3 -} - -containers: { - "my-app-\(localData.index)": { - // ... - } -} - -# A container named "my-app-3" is being defined -``` - -### Assigning a variable to another field - -Assigning a variable to a field uses the variable accessor. - -```acorn -localData: { - myTokenLength: 64 -} - -secrets: { - "my-secret": { - type: "token" - params: { - length: localData.myTokenLength // length is now 64 - } - } -} -``` - -### Basic Operators - -All the basic math and comparison operators you'd find in a typical programming language are supported: - -| Operator | Symbol | Example | Result | -| :--------|:--------|:--------|:-------| -| Addition | `+` | `1 + 1` | `2` | -| Subtraction | `-` | `4 - 1` | `3` | -| Multiplication | `*` | `4 * 2` | `8` | -| Division | `/` | `5 / 2` | `2.5`| -| Greater than | `>` | `2 > 1` | `true` | -| Greater than or equal | `>=` | `2 >= 2` | `true` | -| Less than | `<` | `1 < 1` | `false` | -| Less than or equal | `<=` | `1 <= 1` | `true` | -| Equals | `==` | `1 == 2` | `false` | -| Does not equal | `!=` | `1 != 2` | `true` | -| Not | `!` | `!true` | `false` | -| Or | \|\| | true \|\| false | `true` | -| And | `&&` | `true && false` | `false` | - -`-` can also be used to negate a value: - -```acorn -a: 42 -b: -a // -42 -``` - -Operations can be grouped with parenthesis and combined with `&&` and `||`: - -```acorn -a: 5 -b: a/(1 + 1) // 2.5 -c: (2+2)*4/8 // 2 -d: -c * 10 // -20 -e: b > c // true -f: e && b > 5 // false -``` - -### String Operators - -Strings can be concatenated, repeated, and compared: - -| Operator | Symbol | Example | Result | -|:---------|:-------|:--------|:-------| -| Concatenate | `+` | `"hello " + "world"` | `"hello world"` | -| Repeat | `*` | `"hi"*5` | `"hihihihihi"` | -| Greater than | `>` | `"hi" > "bye"` | `true` | -| Greater than or equal | `>=` | `"foo" >= "bar"` | `true` | -| Less than | `<` | `"foo" < "foo` | `false` | -| Less than or equal | `<=` | `"foo" <= "foo"` | `true` | -| Equals | `==` | `"foo" == "bar"` | `false` | -| Does not equal | `!=` | `"foo" != "bar` | `true` | -| Matches regular expression | `=~` | `"hi bob" =~ "^h"` | `true` | -| Does not match regular expression | `!~` | `"hi bob" !~ "^h"` | `false` | - -### Regular Expressions - -Regular expression syntax is the one accepted by RE2 outlined here [https://github.com/google/re2/wiki/Syntax](https://github.com/google/re2/wiki/Syntax). One exception is `\C` is not accepted. - -## Conditionals - -### If statements - -Support for standard `if` statements is available in an Acornfile. If conditions evaluate to a boolean, and apply their body if the condition is true. - -```acorn -localData: { - scale: 1 -} - -if localData > 1 { - // ... Do something -} -``` - -`if` statements can be added at any level or nested within each other, but there is no `else` in this format. - -### If-else expressions - -Ternary or "if-else" expressions are available through a built-in function which takes 3 arguments: - -```std.ifelse(condition, value-if-true, value-if-false)``` - -The following example will publish either port 3000 or 80 depending on `args.dev`: - -```acorn -containers: { - app: { - ports: publish: std.ifelse(args.dev, "3000/http", "80/http") - } -} -``` - -See the [function library](../reference/functions#ifelse) for more information. - -## For loops - -The Acornfile syntax provides a for loop construct to iterate through objects and lists. - -```acorn -for i in std.range(0, 10) { - containers: { - "my-instance-\(i)": { - // ... - } - } -} -``` - -### Object field comprehensions - -```acorn -localData:{ - dataVols: { - dbData: "/var/lib/mysql" - backups: "/backups" - } -} - -for k, v in localData.dataVols { - volumes: { - "\(k)": {} - } - containers: { - // ... - dirs: { - "\(v)": "volumes://\(k)" - } - // ... - } -} -``` - -### List comprehensions - -Acornfile - -```acorn -localData: { - list: ["one", "two", "three"] -} - -localData: { - multiLineContent: std.join([for i in localData.list {"Item: \(i)"}], "\n") -} -``` - -Renders to: - -```acorn -localData: { - list: ["one", "two", "three"] - multiLineContent: """ - Item: one - Item: two - Item: three - """ -} -``` - -## Function Library - -Acorn includes a built-in library of functions to perform common operations. See the [function library](../reference/functions) for more information. diff --git a/docs/docs/38-authoring/01-best-practices.md b/docs/docs/38-authoring/01-best-practices.md deleted file mode 100644 index 99fc132e3..000000000 --- a/docs/docs/38-authoring/01-best-practices.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: Best Practices ---- - -When authoring an Acorn it is best to offer a set of defaults so the user can simply `acorn run [IMAGE]` and be able to try out the Acorn without diving into its documentation first. - -### User arguments - -Provide the user with `profiles` for common runtime configurations, e.g. for production and testing. Bake in best practices where possible. - -Avoid passing sensitive information through the Acorn arguments. Instead define secrets that are auto-generated or bound at runtime with user-defined values. - -### Avoid string interpolation for configuration files - -It is best to keep configuration to a minimum within the Acornfile. Users should be able to pass in additional configuration and have that merged with the App's internal values. - -### Secrets - -Allow secrets to be auto-generated by default and kept within the Acorn where possible. - -### Images - -When adding Acorn to a project, let Acorn handle the building of the Docker image. - -When using existing images, prefer official DockerHub library images if available. - -Use specific versions and tags of images, at least to the minor version in SemVer. Acorn will package all images at build time and reference them by their SHA, ensuring that the application will always pull the same image. For maintainability and ease of troubleshooting, using a specific version is preferred. - -## Upgrades - -When dealing with stateful applications, use a unique container per instance. Do **not** use scale for stateful applications. This ensures each instance has a unique and stable FQDN. Scaling up and down is always deterministic. - -Each application container should use `dependsOn` for the instance before it. This will ensure that only one application container is taken down at a time. diff --git a/docs/docs/38-authoring/02-structure.md b/docs/docs/38-authoring/02-structure.md deleted file mode 100644 index c0ddfb879..000000000 --- a/docs/docs/38-authoring/02-structure.md +++ /dev/null @@ -1,86 +0,0 @@ ---- -title: Structure ---- - -## Acornfile - -### Key ordering - -An Acornfile has predefined top level structs, and it is recommended to define them in the following order: - -```acorn -name: "// Friendly name of the application" -description: "// A brief description of the application" -readme: "// This is a more descriptive version of the application. Can be pulled in from a file" -info: "// rendered text that describes what the user should do after deployment" -icon: "// a location to an image for the application" -args: { // defines arguments the consumer can provide } -profiles: { // defines a set of default arguments for different deployment types } -services: { // defines services that the application depends on } -containers: { // defines the containers to run the application } -volumes: { // defines persistent storage volumes for the containers to consume } -jobs: { // defines tasks to run on changes or via cron } -acorns: { // other Acorn applications that need to be deployed with your app (databases, etc.) } -secrets: { // defines secret bits of data that are automatically generated or passed by the user } -localData: { // default data and configuration variables } -``` - -At a minimum, the Acornfile needs to specify at least one container to run. - -```acorn -containers: { - nginx: { - image: "nginx" - } -} -``` - -### Defining conditional blocks - -When defining components that are conditionally deployed, use the same ordering as the top level keys within the block. - -For example: - -```acorn -args: enableRedis: false - -containers: { - nginx: { - image: "nginx" - env: { - DB_PASS: "secrets://db-pass/password" - DB_USER: "secrets://db-pass/username" - } - } -} - -if args.enableRedis { - containers: nginx: env: { - REDIS_HOST: "redis" - REDIS_PASS: "secrets://redis-password/token" - } - - containers: redis: { - image: "redis" - env: REDIS_PASSWORD: "secrets://redis-password/token" - } - - secrets: "redis-password": { - type: "token" - } -} - -secrets: "db-pass": type: "basic" -``` - -The above shows how to use an `if` block to add and configure a new container in the Acorn. - -### User defined key requirements - -Second-level keys defined by the user underneath the `containers`, `volumes`, `secrets`, and `jobs` blocks must: - -* Contain only lowercase alphanumeric characters, `-` or `.` -* Start with an alphanumeric character -* End with an alphanumeric character - -Keys defined in `args`, `profiles`, and `localData` should use camelCase. diff --git a/docs/docs/38-authoring/03-containers.md b/docs/docs/38-authoring/03-containers.md deleted file mode 100644 index 9cde11c11..000000000 --- a/docs/docs/38-authoring/03-containers.md +++ /dev/null @@ -1,384 +0,0 @@ ---- -title: Containers ---- - -## Defining a container - -The container section in the Acornfile defines everything about the individual components needed to run your app. Each container definition will define a service in your Acorn application. A basic example of a container definition that exposes a network endpoint looks like the following: - -```acorn -containers:{ - "my-webapp": { - image: "nginx" - ports: publish: "80/http" - } -} -``` - -## Defining a container built from a Dockerfile - -### Standard build options - -Acorn provides a mechanism to build your own containers for your application. If you have an existing project that already defines a Dockerfile, you can build it using Acorn. - -```acorn -containers: { - "my-app": { - build: { - context: "." - } - } -} -``` - -Now when `acorn build .` or `acorn run -i .` is run, the `my-app` container will be built and packaged as a part of the Acorn. It will look for a Dockerfile in the `./` directory. - -### Customized build behavior - -Acorn provides options to customize the building of OCI images. If the Dockerfile is not in the root directory of the `context` you can specify the location using the `dockerfile` parameter. If the image is a multi-stage build, the desired target can be specified. See [args and profiles](07-args-and-profiles.md) to see how to customize these values at build and runtime. - -```acorn -containers: { - "my-app": { - build: { - context: "." - dockerfile: "./pkg/Dockerfile.prod" - target: "dev" - } - } -} -``` - -You can also specify a `buildArgs` object as part of the build section to pass arguments to the build. - -```acorn -containers: { - app: { - build: { - // ... - buildArgs: { - param: "value" - // ... - } - } - } -} -``` - -## Network ports - -### Basic definition - -Most containers can be interacted with over the network. In order for containers to be reached by other containers and applications, the network ports must be defined as part of the container. - -A port definition is: - -```shell -[NAME]:[/TYPE] -``` - -Where: - -* `NAME`: is an optional name for the port, like `db` or `metrics`. -* `PORT`: is the required numeric value of the port. -* `TYPE`: is an optional value of `UDP`, `TCP`, or `HTTP`. - -An example of a named port: - -```acorn -containers: { - db: { - image: "mysql" - ports: expose: "mysql:3306/tcp" - } -} -``` - -When the user runs the built Acorn image, they can reference this port by name `--expose sql:3306:mysql:3306`. - -### Scopes - -As an author, there are three scopes used to define the port's **default** access behavior: `internal`, `expose`, and `publish`. These settings can be changed at runtime by the operator. - -| Scope | Accessibility | -| ------|-----------------------------------------------------| -| `internal`| Containers defined within the Acorn image (default) | -| `expose` | Containers running across the cluster | -| `publish` | Accessible outside the cluster by consumers | - -```acorn -containers: { - "my-webapp": { - image: "nginx" - ports: [ - "5000/http", - ] - } -} -``` - -The above example defines an HTTP port `5000` accessible to other containers defined in this Acorn image. - -The next example shows the `expose` and `publish` settings, used to define ports that are meant to be accessed outside of the Acorn app or published outside the cluster. If an Acorn image is going to provide a network service to multiple apps, something like a database, you will likely want to expose the port so it can be linked to other Acorn apps. - -```acorn -containers: { - "my-webapp": { - image: "nginx" - ports: { - expose: "5001/http" - publish: "8080/http" - } - } - database: { - image: "mysql" - ports: "3306/tcp" - } -} -``` - -This Acornfile defines two containers, one called `my-webapp` and the other `database`. The `my-webapp` container is exposing port 8080 outside of the cluster and port 5001 to apps running on the cluster. When launching this Acorn the port can be published outside the cluster and accessed by linked Acorns. - -In the database container, we are using the `ports` parameter because only the my-webapp container will communicate with the database. - -## Environment variables - -Containers often use environment variables as a way to configure common settings or pass in secrets. Acorn supports both methods in the container definition. - -```acorn -containers: { - db: { - image: "mysql" - // ... - env: { - "MYSQL_ROOT_PASSWORD": "secret://root-pass/token" - "DATABASE_NAME": "test-app" - "USER_SET_VALUE": args.userValue - } - } -} -``` - -The above example has a `db` container with the `MYSQL_ROOT_PASSWORD` variable set by a [secret](38-authoring/05-secrets.md) in the Acornfile. The `DATABASE_NAME` is set to a static value, and the `USER_SET_VALUE` is defined by a user [arg](38-authoring/07-args-and-profiles.md). When launched the container can access these environment variables as needed. - -## Files - -Files are defined in a container where the key is the location inside the container, and the value is the contents of the file. - -```acorn -containers: { - web: { - image: "nginx" - // ... - files: { - "/etc/htpasswd": "secret://htpasswd-file/content" - "/usr/share/nginx/html/index.html": "

Hello!

" - } - // ... - } -} -``` - -In the above, the file `/etc/htpasswd` will contain the contents from a secret that contains the appropriate values for an htpasswd file. The `/usr/share/nginx/html/index.html` will contain static content from the string. - -## Directories - -Directories are defined in their own block and are used to mount volumes, files from a secret, or -pull in content at build time. The block has keys that are path names inside the container and values are the source. - -```acorn -containers: { - web: { - image: "nginx" - // ... - dirs:{ - "/init-scripts": "./scripts" - "/home/.ssh/": "secret://ssh-keys" - "/data": "volume://data-vol" - } - // ... - } -} -``` - -In the above file, when building the Acorn the `/init-scripts` directory will be populated with the contents of the local `./scripts` directory. Files are copied in with the same permissions. - -The `/home/.ssh/` directory will have files named after the secrets keys and content. The `/data` directory will have the volume `data-vol` mounted. - -## Probes - -Applications running for a long time sometimes fail in strange ways, or take time to startup before they are ready to receive traffic. To ensure the container is running and ready to receive traffic there are probes. - -There are three types of probes: `readiness`, `liveness`, and `startup`. Probes are defined per container in a list. You can define one of each type per container. If the probes fail, the container is restarted. - -Each probe type has the following parameters that can be optionally set. - -| Parameter | Default | Description | -| ----------|---------|-------------| -| `initialDelaySeconds`| 0 | Number of seconds to wait after the container is started before probes are initiated. | -| `periodSeconds` | 10 | Number of seconds between probe attempts. | -| `timeoutSeconds` | 1 | Number of seconds before the probe times out. | -| `successThreshold` | 1 | Number of consecutive successful probes before considering the container healthy. | -| `failureThreshold` | 3 | Number of consecutive failed probes before considering the container unhealthy. | - -There are three types of checks that can be used to check the health of the container. A script can be executed inside the container, an HTTP endpoint can be checked, or a TCP endpoint can be checked. Each of the probe types can use one of any of these check types. - -### Liveness probes - -The liveness probe detects that the container is up and considered running. The example here defines an HTTP type of check, though TCP and exec types could also be used. - -```acorn -containers: { - web: { - image: "nginx" - // ... - probes: [ - { - type: "liveness" - http: { - url: "http://localhost/healthz" - headers: { - Accept: "application/json" - } - } - }, - ] - } -} -``` - -Headers are an optional field on the HTTP health probe type. - -### Readiness probes - -A readiness probe means the container is ready to receive traffic. Sometimes when a database server starts it needs to have data loaded. During this data loading process, it should not be contacted by the application in case it has incomplete data. You can use the readinessprobe probe to prevent other containers from accessing this container before it is ready. - -This example will use an exec check, but HTTP and TCP checks could also be used. - -*Note: Readiness probes do not wait for the liveness probe to succeed first. If you need the readiness probe to wait you should use the `initialDelaySeconds` parameter to delay. Alternatively, the startup probe can also be used.* - -```acorn -containers: { - db: { - image: "mysql" - env: { - MYSQL_ROOT_PASSWORD: "secret://db-root-pass/token" - // ... - } - // ... - probes: [ - { - type: "readiness" - initialDelaySeconds: 10 - exec: command: [ - "mysqladmin", - "status", - "-uroot", - "-p${MYSQL_ROOT_PASSWORD}", - ] - }, - ] - } -} -``` - -### Startup probes - -Startup probes exist to give slow starting applications time to load data and/or configuration before starting the liveness and readiness probes. The startup probe should use the same command, HTTP, or TCP check as the liveness probe with enough time to cover the worst case startup scenario. The time is calculated by `failureThreshold * periodSeconds`. - -```acorn -containers: { - web: { - image: "nginx" - // ... - probes: [ - { - type: "liveness" - tcp:{ - url: tcp://localhost:80 - } - }, - { - type: "startup" - failureThreshold: 10 - periodSeconds: 6 - tcp:{ - url: tcp://localhost:80 - } - } - ] - } -} -``` - -In the above example the `web` container would have 60 seconds (10 tries * 6 seconds) to startup before being restarted. - -## Defining sidecar containers - -Sometimes a container needs some setup before it runs, or has additional services running alongside it. For these scenarios, the `sidecar` can be defined as part of the container. - -```acorn -containers: { - frontend: { - image: "nginx" - // ... - sidecars: { - "git-clone": { - image: "my-org/git-cloner" - init: true - } - "metrics-collector": { - image: "my-org/metrics-collector" - ports: "5000/http" - } - } - } -} -``` - -In the above file, we have two sidecars defined. One is `git-clone` which is defined as an `init` container. The init container starts up before the primary container. Each init container should run a single task, and must complete successfully before additional init and application containers are started. - -The second sidecar above is a service that runs alongside the primary frontend container and in this case provides a metrics endpoint. You can define as many sidecar containers as you need to run and support your application. - -## Memory - -There are times that you want to define the amount of memory that an Acorn will use for resource management purposes. This can be defined by the `memory` property and is settable for all `workloads` (`containers` and `jobs`). Check out the [memory reference documentation](100-reference/06-compute-resources.md#memory) for more information on ways to set memory. - -```acorn -containers: { - nginx: { - image: "nginx" - ports: publish: "80/http" - files: { - "/usr/share/nginx/html/index.html": "

My first Acorn!

" - } - memory: 512Mi - } -} -``` - -:::tip -The `memory` property can be abbreviated to `mem` in the file. -::: - -## Compute Classes - -When you want to specify what infrastructure your Acorn's workload will run on, you use Compute Classes. You can set the class of a workload by defining by the `class` property definable on all workloads (`containers` and `jobs`). Check out the [compute class documentation](100-reference/06-compute-resources.md#compute-classes) for more information. - -```acorn -containers: { - nginx: { - class: "sample-compute-class" - image: "nginx" - ports: publish: "80/http" - files: { - "/usr/share/nginx/html/index.html": "

My first Acorn!

" - } - } -} -``` - -## Additional Reading - -* [Acorn Security Considerations](60-architecture/02-security-considerations.md) -* [Acornfile reference](100-reference/03-acornfile.md) diff --git a/docs/docs/38-authoring/04-volumes.md b/docs/docs/38-authoring/04-volumes.md deleted file mode 100644 index a325c4889..000000000 --- a/docs/docs/38-authoring/04-volumes.md +++ /dev/null @@ -1,202 +0,0 @@ ---- -title: Volumes ---- - -Volumes are used to store persistent data in your applications. Volumes can be bound to containers, sidecars, and jobs whenever persistence is needed. Defining a volume in the Acornfile is done under the `volumes` key and referenced via the `volumes://` uri path when mounting. - -```acorn -containers: { - "my-app": { - image: "web" - // ... - dirs: { - "/data": "volume://my-data" - } - } -} -// ... -volumes: { - "my-data": {} -} -// ... -``` - -In the above example, there is a `my-data` volume defined and mounted into the `my-app` container at the `/data` path. The volume using the `default` volume class defined in the cluster. If the `default` volume class does not have a default size set, then the volume will be created a size of 10G. The default volume type will be created as a `ReadWriteOnce` volume and consumable by multiple containers on a single host. - -A volume has the following fields that can be customized, here is the above volume defined with all of the fields. - -```acorn -volumes: { - "my-data": { - size: 10G - class: "default" - accessModes: "readWriteOnce" - } -} -``` - -The volume class used may have restrictions on the size of volumes created or the access modes available. If your volume uses a class that is not available or uses class settings that violate its rules, then your app will build but will not run. A descriptive error will be produced to explain any failures. - -## Volumes with subpaths - -Volume subpaths allow you to utilize the underlying file structure of a volume to reference specific parts within it. This is useful for times that we want to mount specific, but not all, parts of a volume to a container. For example, say that we have a volume, named `example-volume`, with the following content. - -``` -example-volume -|- data-1 - |- nested-data-1 - |- nested-data-2 -|- data-2 -``` - -In this example, we want to create a mount for the content found in `data-1` to a container. Without subpaths, our Acornfile would look something like this. - -```acorn -containers: { - "my-app": { - image: "web" - // ... - dirs: { - "/data": "volume://example-volume" - } - } -} -``` - -This makes the content of `example-volume` available to our container. If we wanted to access `data-1`, we can now do so under the `data-1` directory. However, this also comes along with the `data-2` directory that we don't care to have. We can solve this by using subpaths. - -```acorn -containers: { - "my-app": { - image: "web" - // ... - dirs: { - "/data": "volume://example-volume?subpath=data-1" - } - } -} -``` - -As a result, the only content mounted in our container from `example-volume` is `data-1`. We can take this a step further and have another container that has a mount for the `data-2` directory without bringing along `data-1`. - -```acorn -containers: { - "my-app": { - image: "web" - // ... - dirs: { - "/data": "volume://example-volume?subpath=data-1" - } - } - "my-other-app": { - image: "web" - // ... - dirs: { - "/data": "volume://example-volume?subpath=data-2" - } - } -} -``` - -By utilizing subpaths, we now have a single volume being utilized by two containers without collisions occuring between them. If you'd like to see another example of subpaths in action you can take a look at our [Getting Started](37-getting-started.md) guide. - -## Volumes with sidecars - -Sidecars can share volumes with the primary app container or have volumes for their exclusive use. In order to share data, a volume must be created and mounted in both containers. - -```acorn -containers: { - frontend: { - image: "nginx" - dirs: { - "/var/www/html": "volume://web-content" - } - // ... - sidecars: { - image: "git-cloner" - // ... - dirs: { - "/var/www/html": "volume://web-content" - } - } - } -} -// ... -volumes: { - "web-content": {} -} -``` - -In the above example both containers will have read-write access to the data in `volume://web-content`. - -A volume can also be exclusively mounted in a sidecar container. - -## Ephemeral storage - -There are two ways to create ephemeral scratch type of storage. This type of volume is useful when you are transforming data perhaps during a restore process. - -A shorthand way to define the volume is: - - ```acorn -containers: { - frontend: { - // ... - dirs: { - "/scratch": "ephemeral://scratch-data" - } - } -} -``` - -The above is equivalent to: - -```acorn -containers: { - frontend: { - // ... - dirs: { - "/scratch": "volume://scratch-data" - } - } -} - -volumes: { - "scratch-data": { - class: "ephemeral" - } -} -``` - -The `ephemeral` class is a special case that Acorn will handle behind the scenes to create an `emptyDir` volume. - -## Volumes with jobs - -Volumes can also be mounted between app containers and job containers. - -```acorn -containers: { - db: { - // ... - dirs: { - "/var/lib/db_data": "volume://db-data" - } - // ... - } -} - -volumes: { - "db-data": {} - "backups": {} -} - -jobs: { - backups: { - // ... - dirs: { - "/backups": "volume://backups" - "/var/lib/db_data": "volume://db-data" - } - // ... - } -} -``` diff --git a/docs/docs/38-authoring/05-secrets.md b/docs/docs/38-authoring/05-secrets.md deleted file mode 100644 index 23ad8cbc5..000000000 --- a/docs/docs/38-authoring/05-secrets.md +++ /dev/null @@ -1,291 +0,0 @@ ---- -title: Secrets ---- - -Secrets are used to provide data that is considered sensitive like passwords, keys, and other sensitive information. Acorn provides multiple secret types, and makes it easy to create closed loop systems where information can be kept within the defined Acorn. - -To learn how to use secrets while deploying an Acorn image see [Args and Secrets](50-running/01-args-and-secrets.md) - -## Using secrets in an Acornfile - -Secrets must be defined in the Acornfile. The values can be generated by Acorn or provided by an existing secret in the cluster. Secrets should not be assigned values from user arguments. Secrets can be consumed in containers by referencing them by the `secret:///` URI syntax. - -It is possible to define the behavior when the secret is updated to a new value. The default is to redeploy the containers one at a time. If that operation is unsafe, or requires more user intervention, you can add the `?onchange=no-action` to the end of the secret reference URI. - -### Consume secret in an environment variable - -Some containers use environment variables for secret bits of data. The MariaDB container for instance uses an environment variable to set the root password. - -```acorn -containers: { - db: { - image: "mysql" - // ... - env: { - "MYSQL_ROOT_PASSWORD": "secret://db-root-password/token" - } - } -} -// ... -secrets: { - "db-root-password": { - type: "token" - } -} -``` - -The example shows the secret `db-root-password` being consumed as an environment variable in the db container. The secret is of type token and will automatically be generated at runtime by Acorn. The user can then use the Acorn CLI to get the credential if needed. - -### Consuming secrets in templates - -Secrets in templates are wrapped in `${}`. It is possible to pass secret configuration data to a container in a pre-rendered form. This next example shows how you can add a configuration file with sensitive data. - -```acorn -containers: { - web: { - image: "nginx" - // ... - files: { - "/etc/nginx/conf.d/website.conf": "secret://website-conf/template" - } - } -} -// ... -secrets: { - "proxy-auth": { - type: "opaque" - data: { - "basic-auth-string": "credentialsgohere" - } - } - "website-conf": { - type: "template" - data: { - template: """ - server { - listen 80; - // ... - location / { - // ... - proxy_set_header Authorization "Basic ${secret://proxy-auth/basic-auth-string}" - } - } - """ - } - } -} -``` - -The above example has a container that will use the `website-conf` secret to create a config file. Before rendering the config file, Acorn will substitute the `basic-auth-string` into the template. This technique makes it possible for the user to pass in the sensitive `basic-auth-string` at runtime by [binding a pre-existing secret](50-running/01-args-and-secrets.md#binding-a-secret-at-runtime). - -### Populating a directory with files - -You can populate a directory with sensitive data using a secret. The example below shows populating the `~/.ssh` directory with private keys from a secret. - -```acorn -containers: { - git: { - image: "my-git" - dirs: { - "/home/user/.ssh": "secret://user-provided-ssh-keys" - } - } -} -secrets: { - "user-provided-ssh-keys": { - type: "opaque" - data: { - //Example content from bound secret. - //id_rsa: "-----BEGIN OPENSSH PRIVATE KEY-----...." - //id_rsa_aws: "-----BEGIN OPENSSH PRIVATE KEY-----..." - //Do not put private keys in Acornfile - } - } -} -``` - -The above will populate the `/home/user/.ssh` directory with the content of each item in files named after the key. - -```shell -# inside the git container -ls /home/user/.ssh -id_rsa id_rsa_aws -``` - -## Types of secrets - -Acorn makes multiple types of secrets available to the Acorn author. - - 1. **Basic:** Used to generate and/or store usernames and passwords. - 1. **Template:** Used to store configuration files that contain sensitive information. - 1. **Token:** Used to generate and/or store long secret strings. - 1. **Generated:** Used to take the output of a `job` and pass along as a secret bit of info. - 1. **Opaque:** A generic secret that can store defaults in the Acorn, or is meant to be overridden by the user to pass unknown/unstructured sensitive data. - -### Basic secrets - -Basic secrets are defined in the secrets block with the type "basic". - -```acorn -// ... -secrets: { - "my-creds": { - type: "basic" // required - data: { - username: "" // optional - password: "" // optional - } - } -} -``` - -The basic secret type is used for username / password pairs. The key names must be username and password. If one or both of the fields are defined with a non-empty string, those values will be used. If the empty string, the default value, is used Acorn will generate random values for one or both. - -### Template secrets - -The template secret can be used to render and store multiple secret values into a single output. It has the following format: - -```acorn -// ... -secrets: { - "password-file": { - type: "template" // required - data: { - "password.txt": """ - password=${secret://token/token} - """ - } - } - "token": { - type: "token" - } -} -``` - -In the above example the secret renders a template secret with one key called "password.txt", consuming the token from the secret named "token." See [advanced topics](38-authoring/30-advanced.md) for other uses for the template secret type. - -### Token secrets - -Token secrets are useful for generating a password or secure string used for passwords when the user is already known or not required. - -```acorn -secrets: { - "my-token": { - type: "token" // required - params: { - length: 32 // optional - characters: "abcdedfhifj01234567890" // optional - } - data: { - token: "" // optional - } - } -} -``` - -The token secret type must be defined. The params allow customization of the generated token. By default tokens are 54 characters in length. By defining the `length` param the token can be customized to be within 0-256 characters long. The `characters` param allows the user to define the allowed character values within the token. - -The `token` field in the data object is optional and needs to be left the default empty string if Acorn should generate the token. If the `token` is defined that value will always be used. - -### Generated secrets - -Generated secrets allow storing sensitive data output from a [job](38-authoring/06-jobs.md). - -```acorn -containers: { - "frontend-proxy": { - // ... - files: { - "/etc/htpasswd": "secret://htpasswd-file/content" - } - // ... - } -} -jobs: { - "htpasswd-create": { - env: { - "USER": "secret://user-creds/username" - "PASS": "secret://user-creds/password" - } - entrypoint: "/bin/sh -c" - image: "httpd:2" - // Output of a generated secret needs to be placed in the file /run/secrets/output. - cmd: ["htpasswd -Bbc /run/secrets/output $USER $PASS"] - } -} -secrets: { - "user-creds": { - type: "basic" - } - "htpasswd-file": { - type: "generated" // required - params: { - job: "htpasswd-create" // required - format: "text" // optional - } - } -} -``` - -In the above example, there is a basic secret that Acorn will generate a username and password for. The job will run with the basic secret data passed in as environment variables. When the job runs it will generate an htpasswd file and write the content out to the required `/run/secrets/output` target. The contents of the `/run/secrets/output` file will be placed into the secret `htpasswd-file` and consumed by the `frontend-proxy` container. The key `content` must be used when referencing the value. - -The `job` parameter is always required, and is the name of the job that will generate the output. The `format` parameter is optional and defaults to text. - -### Opaque secrets - -Opaque secrets have no defined structure and can have arbitrary key value pairs. These types of secrets are best used for allowing a user to input sensitive data at runtime. In some cases an unstructured secret can be used if the user will be passing data that will be used in user defined templates. Expected keys should be predefined with reasonable defaults to provide the user some context. - -```acorn -secrets: { - "user-secret-data": { - type: "opaque" - } -} -``` - -## External secrets - -External secrets are defined in the Acornfile to specify a specific secret must be present in the cluster before the Acorn can be deployed. The definition must include the field `external` with the value of the expected name of the secret in the cluster. - -```acorn -containers: app: { - image: ubuntu - entrypoint: ["sleep"] - command: ["3600"] - env: { - USER: "secret://foo/user" - PASS: "secret://foo/pass" - } -} - -secrets: foo:{ - external: "basic-creds" -} -``` - -The above example requires a secret named `basic-creds` to be present in the cluster before the Acorn can be deployed. - -For readability and documentation purposes, the best practice to define the type and data fields that the external secret is expected to have. This is optional and the values will be ignored by Acorn. - -```acorn -containers: app: { - image: ubuntu - entrypoint: ["sleep"] - command: ["3600"] - env: { - USER: "secret://foo/user" - PASS: "secret://foo/pass" - } -} - -secrets: foo: { - external: "basic-creds" - type: "opaque" - data: { - user: "username" - pass: "password" - } -} -``` - -Looking at the above example a user knows they must create a secret named `basic-creds` with keys/values for `user` and `pass` before the Acorn can be deployed. diff --git a/docs/docs/38-authoring/06-jobs.md b/docs/docs/38-authoring/06-jobs.md deleted file mode 100644 index 70f3b1acc..000000000 --- a/docs/docs/38-authoring/06-jobs.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -title: Jobs ---- - -Jobs are containers that perform one-off or scheduled tasks to support the application. Jobs are defined in their own top-level `jobs` section of the Acornfile. A job container will continue to run until it has successfully completed all operations once. - -A Job has all the same fields as a container, with the exception of an optional `schedule` and `events` field. - -## Scheduled jobs - -Jobs that need to be run on a schedule, like a backup job, must also define the schedule field. - -```acorn -jobs: { - "db-backup": { - image: "registry.io/myorg/db-backup" - env: { - "BACKUP_USER": "secret://backup-user-creds/username" - "BACKUP_PASS": "secret://backup-user-creds/password" - } - command: ["/scripts/backup"] - schedule: "@hourly" - } -} -``` - -The `schedule` key makes this a cron based job. The `schedule` field must be a valid crontab format entry. Meaning it can use standard `* * * * *` format or @[interval] crontab shorthand. - -## Events - -Acorn supports four events that can trigger a job to run: `create`, `update`, `stop`, and `delete`. By default jobs will run on create and update. To change this behavior, use the `events` field. - -The `create` event will run the job when the app is created, or when the job is first added to the Acornfile. - -The `update` event will run the job when the app is updated or started from stop. - -The `stop` event will run the job when the app is stopped. - -The `delete` event will run the job when the app is deleted. The job will run, and must complete successfully, before the remaining containers are deleted in that Acorn app. If the job fails, the app will not be deleted. To skip the job, use the [`--ignore-cleanup`](100-reference/01-command-line/acorn_rm.md#options) flag. - -```acorn -jobs: { - "cluster-reconcile": { - image: "registry.io/myorg/cluster-manager" - env: { - "CLUSTER_PASS": "secret://cluster-auth-token/token" - } - events: ["create", "update", "stop", "delete"] - entrypoint: ["/lc.sh"] - files: { - "/lc.sh": """ - #!/bin/sh - if if [ "${ACORN_EVENT}" = "create" ]; then - echo "Create event" - exit 0 - elif [ "${ACORN_EVENT}" = "update" ]; then - echo "Update event" - exit 0 - elif [ "${ACORN_EVENT}" = "stop" ]; then - echo "Stop event" - exit 0 - elif [ "${ACORN_EVENT}" = "delete" ]; then - echo "Delete event" - exit 0 - else - echo "Unknown event" - exit 1 - fi - """ - } - } -} -``` diff --git a/docs/docs/38-authoring/07-args-and-profiles.md b/docs/docs/38-authoring/07-args-and-profiles.md deleted file mode 100644 index 83ac0d0d9..000000000 --- a/docs/docs/38-authoring/07-args-and-profiles.md +++ /dev/null @@ -1,188 +0,0 @@ ---- -title: Args and Profiles ---- - -## Args - -Args are provided to allow users to provide input at different points in the Acorn lifecycle. Args allow Acornfile authors to let the user provide data and values to best suit their needs. Args should be dynamic bits of information. If they are static, use the localData structure to store the variables. - -Args are defined in the top level `args` struct. - -### Defining default values - -Arguments to an Acorn can be standard `strings`, `ints`, `bools`, and other complex types. To define an argument, specify a name and a default value. The type will be inferred from the default value. Here are some examples: - -```acorn -args: { - myIntVar: 1 - myStringVar: "somestring" - myBoolVar: true -} -``` - -Arg names should be in camelCase, and when entered by the user they will be dash separated. - -`thisVariableName` becomes `--this-variable-name` when the user passes it on the command line. - -### Provide the user some guidance - -When defining arguments to the Acorn, it is helpful to the end user to also provide some context. When the user runs `acorn [IMAGE] --help` the output shows all available arguments and if defined provides a short help string. - -When defining args add a `// Comment` above the argument. That will be shown to the user when they do a `--help` - -```acorn -args: { - // Number of instances to run. - replicas: 1 -} -``` - -When the user passes the `--help` arg to Acorn for this image they will see - -```shell -$ acorn MYIMAGE --help -// ... ---replicas Number of instances to run. -// ... -``` - -### Complex data types - -Sometimes more complex data types are needed from the user. If the Acorn provides the minimum production ready configuration for an app, but some users might want to use more advanced features, authors can allow passing in `yaml`objects from files. - -Authors define the variable like: - -```acorn -args: { - // User configuration data for XYZ tool - userConfigData: {} -} -``` - -The user can then create a `config.yaml` file like: - -```yaml -toplevel: - config: - - key1: "value" - - key2: "valueOther" -``` - -The config file can then be passed to the Acorn using -`acorn run [IMAGE] --user-config-data @config.yaml` - -### Built-in - -To prevent the author from having to create a profile, Acorn provides the `args.dev` boolean value. It is set to `true` when running in dev mode (`acorn dev` or `acorn run -i`). Acorn authors can use this boolean with `if` statements to change dev vs. production runtime behaviors. - -```acorn -containers: { - web: { - // ... - if args.dev { - ports: publish: "1313/http" - } - if !args.dev { - ports: publish: "80/http" - } - } -} -``` - -## Profiles - -Profiles specify default arguments for different contexts like dev, test, and prod. This makes it easier for the end user to consume the Acorn application. When developing an application, often there are non-prod ports, different Dockerfile build targets, and replica counts differ from prod. Authors can define a different set of defaults for each environment. - -```acorn -args: { - // Number of instances to run - replicas: 3 -} -profiles: { - dev: { - replicas: 1 - } -} -``` - -:::note -Acorn automatically uses the `dev` profile when when running in dev mode (`acorn dev` or `acorn run -i`). -::: - -In this case when an Acorn consumer deploys the Acorn in production, 3 replicas will be deployed. When the developer working on this app runs it locally with `acorn run --profile dev .` there will only be a single replica deployed by default. - -In either case, consumers of the Acorn can pass `--replicas #` to customize the deployment. - -## Using args in the Acornfile - -### As an environment variable or input to localData - -When the value is assigned to any key in the config file, you can use '.' notation to reference the variable. - -```acorn -args: { - // URL to documentation website - docUrl: "" - - // App Config Value - configValue: "follower" -} -containers: { - web: { - // ... - env: { - "APP_DOC_URL": args.docUrl - } - } -} -localData: { - web: { - config: { - key: args.configValue - } - } -} -``` - -### In a string or template - -When using an arg in a string or template the '.' variable needs to be placed in "\()". - -```acorn -args: { - // A string arg - aStringArg: "default" -} -// ... -secrets: { - type: "template" - data: { - template: """ - a_config_line=\(args.aStringArg) - """ - } -} -``` - -### Complex data input / merging - -When allowing the user to pass complex structures to the Acorn, you can merge that with data predefined in localData. If you would like the user to be able to override the default localData copy of the config, you will need to also define it with ` | *` in the localData section of the Acornfile. - -Merging data is done with the `&` operator. - -Here is an example of allowing the user to override some defaults, but pass in additional configuration. - -```acorn -args: { - userConfig: {} -} -// ... -localData: { - appConfig: args.userConfig & { - userDefinableInt: 3 - staticConfigString: "this is static" - } -} -``` - -In the above if the user passes a config that contains a `userDefinableInt` value the user value will be used. If the user passes `staticConfigString` in their input, Acorn will error out letting the user know that value is already defined. Everything else the user passes will be added to the `appConfig` structure. diff --git a/docs/docs/38-authoring/08-localdata.md b/docs/docs/38-authoring/08-localdata.md deleted file mode 100644 index da30875ec..000000000 --- a/docs/docs/38-authoring/08-localdata.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: Local Data ---- - -The `localData` top level key is used by the Acorn author to store default values for the application. The entire object is freeform below the top level and it's up to the author to decide how it needs to be structured. Fields in this block should all be camelCased. - -```acorn -containers:{ - frontend: { - // ... - env: { - "MY_IMPORTANT_SETTING": localData.myApp.frontendConfig.key - } - // ... - } - database: { - // ... - env: { - "MY_DATABASE_NAME": localData.myApp.databaseConfig.name - } - // ... - } -} -localData: { - myApp:{ - frontendConfig: { - key: "value" - } - databaseConfig: { - name: "db-prod" - } - } -} -``` diff --git a/docs/docs/38-authoring/09-permissions.md b/docs/docs/38-authoring/09-permissions.md deleted file mode 100644 index 09f8d463f..000000000 --- a/docs/docs/38-authoring/09-permissions.md +++ /dev/null @@ -1,86 +0,0 @@ ---- -title: Permissions ---- - -When writing applications, you can run into a situation where your application needs to interact with on-cluster resources. In a typical Kubernetes environment you would need to go through a somewhat involved process to accomplish this. Luckily, `permissions` is a straight forward Acornfile definition that allows you to simplify that process. - -Let's take a look at an example. Here we have a `container`, named API, that we want to grant CRUD operations on the `FooResource` in the application's namespace. For all other namespaces, we want the `container` to only be able to retrieve `FooResources`. - -```acorn -containers:{ - api: { - // ... - permissions: { - rules: [{ - verbs: [ - "get", - "list", - "watch", - "create", - "update", - "patch", - "delete" - ] - apiGroups: [ - "api.sample.io" - ] - resources: [ - "fooresource" - ] - }] - clusterRules: [{ - verbs: [ - "get", - "list", - "watch", - ] - apiGroups: [ - "api.sample.io" - ] - resources: [ - "fooresource" - ] - }] - // ... - } -} -``` - -:::info -Standard with every [container](03-containers.md) and [job](06-jobs.md) definition, you get a `ServiceAccount` with the same name as that service. - -If you're curious, creating `permissions` in our Acornfile generates a few on-cluster resources that interact with this `ServiceAccount`, such as a: -- `Role` with the `rules` we specified -- `ClusterRole` with the `clusterRules` we specified -- `RoleBinding` with the `Role` bound to the `ServiceAccount` -- `ClusterRoleBinding` with the `ClusterRole` bound to the `ServiceAccount` -::: - -With this Acornfile, we accomplish our original goal. Breaking down the Acornfile a bit further, we get 5 keywords that are set to define permissions. Let's look at them one at a time. - -## Rules -Physically defining the permissions of your application, `rules` get converted into a `Role` that then gets attached to your application's unique `ServiceAccount`. This is only applicable for your application's unique namespace and, as a result, the permissions will not work in other namespaces. - -## ClusterRules -Similar to `rules`, `clusterRules` define permissions in the application's namespace but with the added benefit of working in other ones as well. Instead of creating a `Role` that gets attached to your application's `ServiceAccount`, you get a `ClusterRole`. If you would like to allow your application to perform the defined rules in any namespace on the cluster then `clusterRules` are the way to go. - -## Verbs -To define what actions your application can perform on a given resource, you define a `verb`. These `verbs` are words that allow you to declaratively define what actions your application can perform on given resources. - -:::info -Wondering what verbs are available? Take a look! -- get -- list -- watch -- create -- update -- patch -- delete -- deletecollection -::: - -## ApiGroups -When interacting with on-cluster resources, related resources are typically grouped by an `apiGroup`. For the context of Acorn, we need to know what `apiGroup` the resource we're granting permissions for is in. In our original example this was `api.sample.io` and others will typically be in this format. - -## Resources -Inside of `apiGroups` you'll find associated `resources`. With this field, you specify which `resources` the `rules` you are creating apply to. In our original example, this was `foo`. \ No newline at end of file diff --git a/docs/docs/38-authoring/20-labels.md b/docs/docs/38-authoring/20-labels.md deleted file mode 100644 index c6a0ca9e5..000000000 --- a/docs/docs/38-authoring/20-labels.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: Labels and Annotations ---- - -Labels and annotations are Kubernetes constructs for attaching arbitrary metadata as key-value pairs to resources. Often, they are used by third-party integrations to enhance the functionality of Kubernetes. For example, if you were using [cert-manager](https://cert-manager.io/docs/) to provision SSL certificates, you could add the `cert-manager.io/cluster-issuer` annotation to your ingress resources. - -To allow you to take advantage of such integrations, Acorn supports specifying labels and annotations in your Acornfile. These will be applied to the core Kubernetes resources created by Acorn. - -Labels and annotations can be defined as top-level elements in an Acornfile or on individual members of the following resources: -- containers -- jobs -- volumes -- secrets - -To define labels or annotations that apply to all resources created for your app, add them as top-level elements: -```acorn -labels: { - key: "value" -} -annotations: { - key: "value" -} - -containers: { - // ... -} - -volumes: { - // .. -} -``` - -To define labels or annotations that apply to a specific resource, add them to the desired resource: -```acorn -containers:{ - frontend: { - labels: { - key: "value" - } - annotations: { - key: "value" - } - } - // ... -} -``` -In the above examples, the core Kubernetes resources created for the acorn container called "frontend" will get the labels and annotations. This includes the deployment, pods, ingress, and services. - -You can also specify labels and annotations from the CLI when launching an acorn via the `run` command. See [here](50-running/20-labels.md) for more details. - -:::note - -If the Acorn installation has [disabled user label and annotation propagation](30-installation/02-options.md#ignoring-user-defined-labels-and-annotations), then, except for the metadata scope, labels and annotations will be silently ignored. - -::: - -## Metrics - -To automatically create Prometheus scrape annotations on your Acorn apps, define the metrics port and HTTP path in the Acornfile: -```acorn -containers: "mycontainer": { - ports: ["8080/http", "8081/http"] - metrics: { - port: 8081 - path: "/metrics" - } - // ... -} -``` - -This would create the following annotations on the Kubernetes Pods for the container: -```yaml -prometheus.io/scrape: "true" -prometheus.io/port: "8081" -prometheus.io/path: "/metrics" -``` - -The `path` parameter must begin with `/`, and the `port` parameter must be an integer in between 1 and 65535. diff --git a/docs/docs/38-authoring/21-services.md b/docs/docs/38-authoring/21-services.md deleted file mode 100644 index a467d2dbe..000000000 --- a/docs/docs/38-authoring/21-services.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Services ---- - -## Consuming services - -When you are authoring the Acornfile for your application you can define cloud services that will be provisioned for your application. The services will be deployed alongside your application at deploy/run time. To learn how to create your own service Acorns see [services](/100-reference/10-services.md) in the reference section. - -### Wiring services into your Acorn app - -Service attritibutes are accessed through the `@{}` syntax in the Acornfile. Here is a simple example of accessing the `address` attribute of a service named `db`. For complete service syntax see the [services](/100-reference/03-acornfile.md#services-consuming) section in the Acornfile reference. - -```acorn -// This service exposes an address and a secret -services: db: { - image: "ghcr.io/acorn-io/aws/rds-aurora-cluster:latest" -} - -containers: app: { - image: "my-app:latest" - env: { - MY_SERVICE_ADDRESS: "@{service.db.address}" - MY_SERVICE_PORT: "@{service.db.ports.3306}" - DB_USER: "@{service.db.secrets.admin.username}" - DB_PASS: "@{service.db.secrets.admin.password}" - DB_NAME: "@{service.db.data.dbName}" - } -} -``` - -In the above example the service db parameters are accessed using the `@{service.db}` syntax. Ports are referenced by the expected value, in this case `3306` for MySQL. However, the actual port number may not be `3306` as it can be dynamically assigned during the service creation. diff --git a/docs/docs/38-authoring/30-advanced.md b/docs/docs/38-authoring/30-advanced.md deleted file mode 100644 index db34d69ec..000000000 --- a/docs/docs/38-authoring/30-advanced.md +++ /dev/null @@ -1,167 +0,0 @@ ---- -title: Advanced Topics ---- - -## Scaling Applications - -### Stateless applications - -In the case of scaling stateless applications the `scale` field can be defined on the container to increase the number of containers deployed. This is where consumers of the Acorn won't need persistent data or to know which instance will be deleted on scale down operation. - -```acorn -args: { - // Number of stateless web servers to run - scale: 1 -} - -containers: { - web: { - image: "web" - scale: args.scale - } -} -``` - -### Stateful applications - -Applications that have stateful data or where an operator would care in which order the containers will be removed in a scale down event should not use the `scale` field and should instead create unique instances of the container. - -To accomplish this, users can leverage `for` loops in the Acornfile. Within the `for` loop all items unique to that instance should be defined. In most cases, this will be a container and data volumes. The loop can contain any of the top level objects if needed. - -```acorn -args: { - // Number of instances - replicas: 1 -} - -for i in std.range(0, replicas, 1) { - containers: { - "instance-\(i)": { - // ... - dirs: { - "/data": "volume://instance-data-\(i)" - } - } - } - volumes: { - "instance-data-\(i)": {} - } -} -``` - -The above example makes use of the `std.range` function used in the `for` loop. The loop variable `i` will be an integer and placed into the container and volume names. When the application is scaled up, new containers will be deployed with their own data volumes. When the application is scaled down the highest numbered replicas will be removed first. The `0` replica will always be the first replica deployed and last removed. - -When deploying stateful applications it is a reasonable assumption to bootstrap from the `0` instance and for new replicas to use that as the first point of contact to register. - -### Yaml templates for config files - -If you would like to dump a section of the localData config into YAML format, you can use the YAML encoder package. - -```acorn -args: { - // User provided yaml - userConfig: {} -} - -containers: { - frontend: { - // ... - files: { - "/my/app/config.yaml": "secret://yaml-config/template" - } - // ... - } -} - -secrets: { - "yaml-config": { - type: "template" - data: { - template: std.toYAML(localData.config) - } - } -} - -localData: { - config: std.merge({ - this: { - isGoing: { - to: "be a yaml file" - } - }}, args.userConfig) -} -``` - -In the above example the frontend config file will be rendered from user and Acorn data in YAML format. This example is using the `std.merge()` function which takes two objects and merges them where the second overwrites the first. - -### Generating files from key value pairs - -Another useful built-in for rendering key value pairs with an optional separator is the `std.join` function. - -If you need to create a file with content in this format: - -`key=value` - -```acorn -// ... -containers: { - web: { - // ... - files: { - "/etc/config_file": "secret://config/template" - } - // ... - } -} -secrets: { - "config": { - type: "template" - data: { - template: std.join([for key, value in localData.configData {"\(key)=\(value)"}], "\n") - } - } -} -localData: { - configData: { - key: "value1" - key0: "value2" - } -} -``` - -The above will output into /etc/config_file: - -```ini -key=value1 -key0=value2 -``` - -## Templates - -Templates provide a way to bulk add additional fields to objects. - -To do this, the template is declared for the top level Acorn object, and then a set of `[]` to bind to the nested objects field. - -```acorn -args: dev: false -containers: { - app: {} - db: {} -} - -// ... Other objects ... - -if !args.dev { - containers: [string]: { - probes: [ - // ... probe definitions - ] - } - - containers: [Name= =~ "db"]: { - ports: internal: "\(Name)-metrics-port:5000/http" // Metrics port - } -} -``` - -In the above example when the `args.dev` variable is not set, all containers would have [probes](38-authoring/03-containers.md#probes) assigned. In the case of the `db` container it would have a metrics port defined. The field's name is assigned to the `Name` variable if the regex matches `db`, the `Name` variable can then be referenced in the template. diff --git a/docs/docs/38-authoring/31-nested-acorns.md b/docs/docs/38-authoring/31-nested-acorns.md deleted file mode 100644 index 7b7532260..000000000 --- a/docs/docs/38-authoring/31-nested-acorns.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -title: Nested Acorns ---- - -Nested acorns allow you to describe a collection of microservices that make up a complete deployment of an Application. This is useful when describing entire deployments along with profiles for their arguments. Combined with the auto-upgrade functionality, it allows you to create a delivery pipeline in a single file that can be checked into VCS. Additionally, Acorns can also be used within an Acorn app to provide functionality to the app. The use of Acorns can be thought of as using a module or library in a programming language. - -## Using nested Acorn - -Acorns are defined under the `acorns` toplevel key in the Acornfile. - -```acorn -//... -acorns: { - "my-acorn": { - image: "ghcr.io/acorn-io/hello-world:latest" - } -} -//... -``` - -## Describe a deployment pipeline with Acorns and services - -To describe an entire deployment with Acorns you declare each Acorn and define a profile for the deployment type. You can also use `services` to provide infrastructure components to the app deployment. - -```acorn -args: { - uri: "" - tag: "" -} - -profiles: { - dev: { - uri: "index.io/my-app/dev/secret-name" - tag: "latest" - } - prod: { - uri: "index.io/my-app/prod/secret-name" - tag: "1.2.#" - } -} - -acorns: { - "my-app": { - image: "example.com/repo/org/hello-world:\(args.tag)" - secrets: ["secrets-getter.this:redis-creds"] - autoUpgrade: true - } - -} - -services: { - // A service that returns a secret from an external source under the name `this` - "secret-getter": { - image: "example.com/repo/org/secret-getter:latest" - serviceArgs: { - secretURI: args.uri - } - } -} -``` - -Now when this Acornfile is initially deployed with the `--profile` flag Acorn will deploy the app with the appropriate default values. In this case `dev` will always update whenever the latest tag moves. Production will always update on new patch releases. diff --git a/docs/docs/38-authoring/_category_.yaml b/docs/docs/38-authoring/_category_.yaml deleted file mode 100644 index fa78ce640..000000000 --- a/docs/docs/38-authoring/_category_.yaml +++ /dev/null @@ -1 +0,0 @@ -label: Authoring Acornfiles diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 3cfbe3868..90705f9cd 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -8,7 +8,7 @@ const darkCodeTheme = require('prism-react-renderer/themes/dracula'); const config = { title: 'Acorn Docs', tagline: 'Welcome to Acorn Docs', - url: 'http://docs.acorn.io', + url: 'https://docs.acorn.io', baseUrl: '/', onBrokenLinks: 'throw', trailingSlash: false, @@ -30,9 +30,9 @@ const config = { ({ docs: { versions: { - "0.7": {label: "0.7", banner: "none", path: "0.7"}, - "0.6": {label: "0.6", banner: "none", path: "0.6"}, - "0.5": {label: "0.5", banner: "none", path: "0.5"}, + "0.7": { label: "0.7", banner: "none", path: "0.7" }, + "0.6": { label: "0.6", banner: "none", path: "0.6" }, + "0.5": { label: "0.5", banner: "none", path: "0.5" }, }, routeBasePath: '/', // Serve the docs at the site's root sidebarPath: require.resolve('./sidebars.js'), @@ -103,7 +103,7 @@ const config = { prism: { theme: lightCodeTheme, darkTheme: darkCodeTheme, - additionalLanguages: ['cue','docker'], + additionalLanguages: ['cue', 'docker'], }, algolia: { appId: '7QCEFR54LA', diff --git a/docs/sidebars.js b/docs/sidebars.js index a57a0481f..45b3f14e7 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -29,57 +29,13 @@ const sidebars = { "getting-started", { "type": "category", - "label": "Authoring Acornfiles", + "label": "Administration", "items": [ - "authoring/overview", - "authoring/best-practices", - "authoring/structure", - "authoring/containers", - "authoring/volumes", - "authoring/secrets", - "authoring/jobs", - "authoring/args-and-profiles", - "authoring/localdata", - "authoring/permissions", - "authoring/labels", - "authoring/services", - "authoring/advanced", - "authoring/nested-acorns" + "admin/volumeclasses", + "admin/computeclasses", + "admin/alpha-image-allow-rules" ] }, - "publishing", - { - "type": "category", - "label": "Running Acorn Apps", - "items": [ - "running/args-and-secrets", - "running/networking", - "running/certificates", - "running/volumes", - "running/linking-acorns", - "running/labels", - "running/troubleshooting", - "running/upgrades", - "running/auto-upgrades", - "running/namespaces-and-service-accounts", - "running/compute-resources", - "running/projects", - "running/update-acorns", - "running/remove-acorns", - "running/dev", - "running/events", - "running/alpha-image-allow-rules" - ], - "collapsed": true - }, - { - "type": "category", - "label": "Integrations", - "items": [ - "integrations/github-actions" - ], - "collapsed": true - }, { "type": "category", "label": "Architecture", @@ -143,14 +99,7 @@ const sidebars = { "reference/command-line/acorn_wait" ] }, - { - "type": "category", - "label": "Administration", - "items": [ - "reference/admin/volumeclasses", - "reference/admin/computeclasses" - ] - }, + "reference/acornfile", "reference/functions", "reference/compute-resources",