Take Capact for a spin!
Tags: capact, cloudnative, kubernetes, tutorial
Published: 2022-02-07 13:59:23

Our first blog post focused strongly on **_why_**—why we created Capact, what's the general concept behind it. Now it's time for showing **_how_**—how you can use it.

Although, there are many ways to [get started with Capact](https://capact.io/docs/getting-started), today we will focus on your perspective as a Capact User.

The recommended way to try out Capact quickly is to set up a local environment.

![install](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ubo9wgwd6cj3h4n87154.png)

Detailed instructions are already nicely described in our [local installation](https://capact.io/docs/installation/local) tutorial. Once you have Capact up and running, we can start!

### What you will learn

> **NOTE:** For this blog post, we will use Capact CLI. However, the described scenario can also be executed with our [brand-new UI](https://capact.io/docs/dashboard-ui/overview)! 😎

In 5 minutes, by running examples from this blog post, you will learn about the following Capact concepts:

- [Interface](https://capact.io/docs/terminology#interface)—provides an option to abstract Implementations.
- [Implementation](https://capact.io/docs/terminology#implementation)—defines the actual workflow that is executed.
- [Attribute](https://capact.io/docs/terminology#attribute)—provides an option to _tag_ manifests. Today we will use it to select a specific Implementation.
- [Type](https://capact.io/docs/terminology#type) and [TypeInstance](https://capact.io/docs/terminology#typeinstance)—provide a combined feature, where Type holds the schema and TypeInstance the _instance_ for the Type.
- [Action](https://capact.io/docs/terminology#action)—triggers Engine to render and run a given Interface.
- [Policy](https://capact.io/docs/feature/policies/overview)—impacts the render process to select desired Implementations.

Creating new workflows is not the concern of this article, and it will be covered in future blog posts.

Here we only briefly describe each part, but I encourage you to take a deeper look at our [Terminology](https://capact.io/docs/terminology) section later.

### Prerequisites

* [Capact CLI](https://capact.io/docs/cli/getting-started#install) at least v0.6.0
* [Capact cluster](https://capact.io/docs/installation/) at least v0.6.0
* **(Optional)** [`kubectl`](https://kubernetes.iohttps://capact.io/docs/tasks/tools/install-kubectl/)
* [`jq`](https://stedolan.github.io/jq/download/)

### 📜 Scenario

The key concepts were described in the [Introducing Capact](https://capact.io/blog/introducing-capact#capact-concept-in-one-minute) blog post. There, we used Mattermost as an example. It was a real life scenario but at the same time more resource and time-consuming.

For the purpose of this blog post, we created an extremely simple Hello World example.

![greet-manifest](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/459y50wle6x1looj31hw.png)

There are two Implementations for the `cap.interface.capactio.examples.say` Interface. Depending on the Policy, a different Implementation is selected. By default, Engine selects the Implementation which is first on the list (alphabetical order) and doesn't have any requirements regarding executions, such as requiring AWS credentials.

## 🎬 Camera, Lights, Action!

Action is the entry point for executing any Interface. It allows you to define which Interface should be executed and with which parameters. This is later consumed by Capact Engine and the appropriate workflow is rendered. Later, you can review it and approve it for the execution.

![capact-simplified-arch](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cfdy6a0d77d0t49y7a19.png)

### Steps

1. [Make sure that you are logged into local cluster](https://capact.io/docs/cli/getting-started#first-use).

2. List available Interfaces:
    ```bash
    capact hub interfaces get
    ```

    We are interested in this part:
    ```text
                               PATH          LATEST REVISION                  IMPLEMENTATIONS
    ---------------------------------------+-----------------+-----------------------------------------------
    # ... trimmed ...
    ---------------------------------------+-----------------+-----------------------------------------------
    cap.interface.capactio.examples.greet        0.1.0             cap.implementation.capactio.examples.greet
    ---------------------------------------+-----------------+-----------------------------------------------
    cap.interface.capactio.examples.say          0.1.0             cap.implementation.capactio.examples.hello
                                                                   cap.implementation.capactio.examples.ricky
    ```

3. Create a new Action:

     >💡 A Policy is not needed as we use the default behavior here.

    ```bash
    capact act create --name hello cap.interface.capactio.examples.greet
    ```

2. Wait for the Action to have the `READY_TO_RUN` status:
	 ```bash
	 capact act get hello
	 ```

3. When the status is `READY_TO_RUN`, run the Action:
	 ```bash
	 capact act run hello
	 ```

4. Watch the progress:
	 ```bash
	 capact act watch hello
	 ```

5. **(Optional)** Once the Action is finished, copy the name of the Pod for the `print` workflow step (column `PODNAME`). To read its logs, run:
    ```bash
    kubectl logs {PODNAME} main
    ```
    Example output:

    ```bash
     ____________________________
    < message: Hello from Capact >
     ----------------------------
        \
         \
          \
                        ##        .
                  ## ## ##       ==
               ## ## ## ##      ===
           /""""""""""""""""___/ ===
      ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ /  ===- ~~~
           \______ o          __/
            \    \        __/
              \____\______/
    ```

6. Get the Action output (TypeInstance). Copy the `id` field value:
	 ```bash
	 capact act get hello -ojson | jq '.Actions[0].output.typeInstances'
	 ```
 
	 Example output:
	 ```bash
   [
     {
       "id": "08fcaa07-7846-47af-b6a7-7c3818c69656",
       "typeRef": {
         "path": "cap.type.capactio.examples.message",
         "revision": "0.1.0"
       }
     }
   ]
	 ```

7. Get the TypeInstance `value.message` field value:
	 ```bash
	 capact ti get {ID} -ojson | jq -r '.[0].latestResourceVersion.spec.value.message'
	 ```

8. Delete the Action:
	 ```bash
	 capact act delete hello
	 ```

What if you wanted to output a different message, that reminds you about a masterpiece song you can listen to endlessly? With Capact that's really easy ✨

To choose a different Implementation for the `say` Interface, we need to prepare a dedicated Policy. The power of it is that you don't have to change the main workflow. You treat the step as a building block and just swap it for another existing one.

![Here we go again GIF](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6gsp3de7nhb7w4om3vft.gif)

To test it, create the Action again, and pass the Action Policy.

First, save the Policy to the `/tmp/policy.yaml` file:

```yaml
cat <<EOF > /tmp/policy.yaml
rules:
  - interface:
      path: cap.interface.capactio.examples.say
    oneOf:
    - implementationConstraints:
        attributes:
          - path: cap.attribute.capactio.examples.be-positive
            revision: 0.1.0
EOF
```

Next, create the Action with the Policy:

```bash
capact act create --name hello cap.interface.capactio.examples.greet --action-policy-from-file /tmp/policy.yaml
```

Now, you can repeat steps starting from 4th point—so, once again, wait for the Action to have the `READY_TO_RUN` status and run it!

In the optional step no. 7, if you read the Pod logs, the output will be:
```bash
 __________________________________
< message: Never gonna give you up >
 ----------------------------------
    \
     \
      \
                    ##        .
              ## ## ##       ==
           ## ## ## ##      ===
       /""""""""""""""""___/ ===
  ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ /  ===- ~~~
       \______ o          __/
        \    \        __/
          \____\______/
```
Note also that the produced TypeInstance from no. 9 is valid against the same JSON Schema but the **message** was changed.

## What is so special about it?

Our example was simplified to a minimum, but you could see that you can:

- chain different steps together,
- have a unified way of execution,
- built-in Type validation based on JSON Schema,
- and more importantly, write once and swap implementation details with ease.

Capact is also great if you want to chain different tools together and keep the same entry point (UX) but that requires more complex examples. For now, we recommend you to take a look at our other examples described in the [**How can I get involved and learn more?**](#how-can-i-get-involved-and-learn-more) section.

## Behind the CLI

As you saw, you can use CLI to browse Hub manifests, create a specific Action for a given Interface and retrieve the output. CLI communicates with our Gateway via GraphQL calls, same as the UI (Capact Dashboard). You can read more in the [E2E Architecture](https://capact.io/docs/architecture/e2e-architecture) document.

However, a more important question is, how did it happen?

```yaml
1  spec:
2    # ... trimmed ...
3    imports:
4      - interfaceGroupPath: cap.interface.capactio.examples
5        alias: examples
6        methods:
7          - name: say
8  
9    # ... trimmed ...
10   steps:
11     - - name: get-message
12         capact-action: examples.say # dynamic step, rendered by Engine based on the Policy.
13     - - name: print
14         template: print
15         arguments:
16           artifacts:
17             - name: message
18             from: "{{steps.get-message.outputs.artifacts.message}}"
19     # ... trimmed ...
20     - name: print
21       inputs:
22         artifacts:
23           - name: message
24           path: /tmp/message.yaml
25       container:
26         image: docker/whalesay:latest
27         command: [ sh, -c ]
28         args: [ "cowsay < /tmp/message.yaml" ]
```

The snippet above shows the most important parts of the Implementation manifest. In lines **4-7**, we declare the import, which is later used in steps. In line **12**, we use **capact-action** to make a dynamic step with a given Interface.

>💡 Instead of providing the full Interface path, we use **alias** assigned to the imported Interface.

As a result, our Engine and, in particular, Argo Renderer has an option to put there an Implementation that fulfills the specified Interface. Thanks to that, this step is swappable based on the defined Policy.

## How can I get involved and learn more?

The [Getting Started](https://capact.io/docs/getting-started) goes into more detail about how to start with Capact and use its more-advanced features. There are also [Rocket.Chat](https://capact.io/docs/example/rocketchat-installation) and [Mattermost](https://capact.io/docs/example/mattermost-installation) tutorials that we keep up-to-date for you to walk you through using Capact in real cases.

We appreciate any input you have about your experience with Capact!

There are plenty of options to contact us:
- [GitHub issues](https://github.com/capactio/capact/issues)
- [Slack](/community/slack)
- Email us at [contact@capact.io](mailto:contact@capact.io)

Thank you for taking the time to learn about Capact 🙌