Skip to content

Commit

Permalink
docs: add subdirectories (actions and Go code) documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
pandatix committed Nov 1, 2024
1 parent 12d4b15 commit 7ef7208
Show file tree
Hide file tree
Showing 10 changed files with 374 additions and 7 deletions.
146 changes: 140 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,148 @@
<div align="center">
<h1>Romeo</h1>
<p>O Romeo, Romeo, whatfore art coverages Romeo?</p>
<a href="https://pkg.go.dev/github.com/ctfer-io/romeo"><img src="https://shields.io/badge/-reference-blue?logo=go&style=for-the-badge" alt="reference"></a>
<a href=""><img src="https://img.shields.io/github/license/ctfer-io/romeo?style=for-the-badge" alt="License"></a>
<a href="https://coveralls.io/github/ctfer-io/romeo?branch=main"><img src="https://img.shields.io/coverallsCoverage/github/ctfer-io/romeo?style=for-the-badge" alt="Coverage Status"></a>
<br>
<a href="https://github.com/ctfer-io/romeo/actions/workflows/codeql-analysis.yaml"><img src="https://img.shields.io/github/actions/workflow/status/ctfer-io/romeo/codeql-analysis.yaml?style=for-the-badge&label=CodeQL" alt="CodeQL"></a>
<a href="https://securityscorecards.dev/viewer/?uri=github.com/ctfer-io/romeo"><img src="https://img.shields.io/ossf-scorecard/github.com/ctfer-io/romeo?label=openssf%20scorecard&style=for-the-badge" alt="OpenSSF Scoreboard"></a>
<img src="https://img.shields.io/badge/slsa-level%203-green?style=for-the-badge" alt="SLSA Level 3">
</div>

The repository is structured as:
- [webserver](webserver/) contains the Romeo Go webserver made for distant coverages fetch
- [deploy](deploy/) contains the Go Pulumi deployment code for Romeo, in a Kubernetes environment
- [action](action/) contains the TypeStript Pulumi bridge for GitHub Actions
- [install](install/) contains the Kubernetes environment to deploy Romeo on Demand
## How it works

Romeo creates ephemeral environments on a Kubernetes cluster to measure Go binaries coverage.
This work based on [this blog post](https://go.dev/blog/integration-test-coverage), so require **Go >= 1.20**.

<div align="center">
<img src="res/workflow.excalidraw.png" alt="The Romeo workflow">
<img src="res/workflow.excalidraw.png" alt="The Romeo workflow" height="600px">
</div>

The repository is structured as follows:
1. [Webserver](webserver) is a Go server exposing an API that remotly executes the Go coverage merge.
2. The resulting coverage data are later fetched by the [download Action](download).
3. To deploy this you firstly instanciate a [deployment](deployment).
4. To avoid passing a privileged account you can restrein the RBAC accesses with a pre-deployment [install](install).

## Usage

The recommended process is to run both [install](install) and [deployment](deployment) in a workflow.
This provides good isolation thus ensure actual Go coverages.

It is acceptable, mostly for performance reasons, to pre-[install](install) Romeo RBAC resources thus only running a [deployment] per workflow.
Refer to their own documentation to implement this in your process.

Configure secrets and inputs accordingly to each action/step documentation.

```yaml
name: Run Go tests

on: [push]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'

- uses: pulumi/actions@v6

- name: Cache Go modules
uses: actions/cache@v4
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
# ... Run your Go unit tests ...

# Login local by default, you can login somewhere else. Adapt to your needs.
- name: Pulumi login
run: |
pulumi login --local
- name: Romeo install
id: install
uses: ctfer-io/romeo/install@v1
with:
kubeconfig: ${{ secrets.KUBECONFIG }}
api-server: ${{ secrets.API_SERVER }}

- name: Romeo environment
id: env
uses: ctfer-io/romeo/environment@v1
with:
kubeconfig: ${{ steps.install.outputs.kubeconfig }}
namespace: ${{ steps.install.outputs.namespace }}

- name: Run functional tests
run: |
go test ./... -run=^Test_F_ -json | tee -a gotest.json
env:
# Use a ServiceAccount with enough privileges to deploy the resources you require.
# If not possible, you can use an administration account.
KUBECONFIG: ${{ secrets.KUBECONFIG }}
CLAIM_NAME: ${{ steps.env.outputs.claim-name }}
NAMESPACE: ${{ steps.env.outputs.namespace }}
# Put additional configuration if necessary...

- name: Download coverages
id: download
uses: ctfer-io/romeo/download@v1
with:
server: ${{ secrets.SERVER_BASE }}:${{ steps.env.outputs.port }}

- name: Merge coverages
run: |
go tool covdata textfmt -i="${{ steps.download.outputs.directory }}" -o cov.out
- name: Upload coverage to Coveralls
uses: shogo82148/actions-goveralls@v1
with:
path-to-profile: cov.out
```
## Security
### Signature and Attestations
For deployment purposes (and especially in the deployment case of Kubernetes), you may want to ensure the integrity of what you run.
The release assets are SLSA 3 and can be verified using [slsa-verifier](https://github.com/slsa-framework/slsa-verifier) using the following.
```bash
slsa-verifier verify-artifact "<path/to/release_artifact>" \
--provenance-path "<path/to/release_intoto_attestation>" \
--source-uri "github.com/ctfer-io/romeo" \
--source-tag "<tag>"
```

The Docker image is SLSA 3 and can be verified using [slsa-verifier](https://github.com/slsa-framework/slsa-verifier) using the following.

```bash
slsa-verifier slsa-verifier verify-image "ctferio/romeo:<tag>@sha256:<digest>" \
--source-uri "github.com/ctfer-io/romeo" \
--source-tag "<tag>"
```

Alternatives exist, like [Kyverno](https://kyverno.io/) for a Kubernetes-based deployment.

### SBOMs

A SBOM for the whole repository is generated on each release and can be found in the assets of it.
They are signed as SLSA 3 assets. Refer to [Signature and Attestations](#signature-and-attestations) to verify their integrity.

A SBOM is generated for the Docker image in its manifest, and can be inspected using the following.

```bash
docker buildx imagetools inspect "ctferio/romeo:<tag>" \
--format "{{ json .SBOM.SPDX }}"
47 changes: 47 additions & 0 deletions download/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Romeo download

Download a [Romeo environment](../environment)'s coverages from an Action.

It reaches the [Romeo webserver](../webserver) to archive and ship the coverages it reads from the shared volume of the Romeo environment, given the following architecture.

<div align="center">
<img src="architecture.excalidraw.png" alt="Romeo download coverages" width="600px">
</div>

## Usage

### GitHub Actions

To download coverages from a Romeo environment, use `ctfer-io/romeo/download`.

```yaml
- name: Download coverages
id: download
uses: ctfer-io/romeo/download@v1
with:
server: ${{ secrets.SERVER_BASE }}:${{ steps.env.outputs.port }}
```
#### Inputs
| Name | Type | Default | Description |
|---|---|---|---|
| `server` | String | | Server URL to reach out the Romeo environment. |
| `directory` | String | `coverout` | Directory to export the coverages data. |

#### Outputs

| Name | Type | Description |
|---|---|---|
| `directory` | String | Directory the coverages data were exported to. |

### Manually

The download action is a thin helper around the [Romeo webserver](../webserver) API: you can totally do it by hand.
You need to reach the API at `/coverout` (`GET` method), then decode base 64 and unzip the result to the filesystem.

```bash
# From the Romeo environment directory
curl "$SERVER_BASE:$(pulumi stack output -j | jq -r '.port')/coverout" | jq -r '.merged' | base64 -d > cov.zip
unzip cov.zip && rm cov.zip
```
Binary file added download/architecture.excalidraw.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
66 changes: 66 additions & 0 deletions environment/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Romeo environment

Deploy a Romeo environment from an Action, or manually.

It deploys Kubernetes resources required to extract the Go coverages of binaries under tests/IVV, given the following architecture.

<div align="center">
<img src="architecture.excalidraw.png" alt="Romeo environment Kubernetes architecture" width="600px">
</div>

## Usage

### GitHub Actions

To deploy a Romeo environment from an Action, use `ctfer-io/romeo/environment`.
It will create the Kubernetes resources. We recommend you deploy a [Romeo install](../install) per workflow run.

```yaml
- name: Romeo environment
id: env
uses: ctfer-io/romeo/environment@v1
with:
kubeconfig: ${{ steps.install.outputs.kubeconfig }}
namespace: ${{ steps.install.outputs.namespace }}
```
Once your tests ran, you can [download the coverages](../download).
At the end of the Action, it will delete the deployed resources.
#### Inputs
| Name | Type | Default | Description |
|---|---|---|---|
| `kubeconfig` | String | | **Required.** The kubeconfig to use for deploying a Romeo environment. |
| `namespace` | String | | The namespace in which to deploy, in case the kubeconfig has access to many. |
| `tag` | String | `latest` | **Required.** The [Romeo webserver docker tag](https://hub.docker.com/r/ctferio/romeo/tags) to use. |
| `claim-name` | String | | If specified, turns on Romeo's coverage export in the given PersistenVolumeClaim name. This should only be used by CTFer.io to test Romeo itself. |

#### Outputs

| Name | Type | Description |
|---|---|---|
| `port` | String | The port to reach out the Romeo webserver. |
| `claim-name` | String | The PersistentVolumeClaim name for binaries to mount in order to write coverage data. |
| `namespace` | String | The namespace in which Romeo has been deployed. Reuse it to target the PersistentVolumeClaim corresponding to the claim-name. |

### Manually

You may want to deploy the "Romeo environment" to test things manually, or from a non-supported CI system (e.g. GitLab, Drone, Travis).

It has the advantage of not requiring an extensive install of Romeo. We still recommend you **run one Romeo environment per workflow run** to ensure proper isolation between multiple runs thus avoid falsing your coverage measurements.

```bash
# Get in deploy directory
cd deploy
# Create stack and configure
export PULUMI_CONFIG_PASSPHRASE="some-secret"
pulumi stack init --secrets-provider passphrase --stack dev
pulumi config set --secret kubeconfig "$(cat ~/.kube/config)"
pulumi config set namespace ""
# Deploy
pulumi up -y
```
Binary file added environment/architecture.excalidraw.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
69 changes: 69 additions & 0 deletions install/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Romeo Install

Install Romeo from an Action, or manually.

It deploys RBAC resources given the following architecture, according to the needs of a [Romeo environment](../environment).

<div align="center">
<img src="architecture.excalidraw.png" alt="Romeo install Kubernetes architecture" width="600px">
</div>

## Usage

### GitHub Actions

To deploy a Romeo install from an Action, use `ctfer-io/romeo/install`.
It will create the RBAC resources required by [Romeo environment](../environment) to deploy.

```yaml
- name: Romeo install
id: install
uses: ctfer-io/romeo/install@v1
with:
kubeconfig: ${{ secrets.KUBECONFIG }}
api-server: ${{ secrets.API_SERVER }}
```
At the end of the Action, it will delete the deployed resources.
#### Inputs
| Name | Type | Default | Description |
|---|---|---|---|
| `kubeconfig` | String | | **Required.** The kubeconfig to use for installing Romeo and generating its own kubeconfig (with restreined privileges). |
| `namespace` | String | | The namespace to install Romeo into. May be randomly generated, as long as it fits Kubernetes naming specification. If not specified, will be randomly generated. |
| `api-server` | String | | **Required.** The Kubernetes api-server URL to pipe into the generated kubeconfig. Example: `https://cp.my-k8s.lan:6443`. |

#### Outputs

| Name | Type | Description |
|---|---|---|
| `kubeconfig` | String | The kubeconfig to use for deploying a Romeo environment. |
| `namespace` | String | The namespace in which the install has took place. Pass it to Romeo's environment step and tests/IVV ones to know where to deploy too. |

---

### Manually

You may want to deploy the "Romeo install" once for your whole cluster, so _manually_.

It has the advantage of making it only once, then share its instance between all repositories/CI under tests/IVV.
Nevertheless, sharing is not always caring: collisions can happen, and we encourage you to deploy it once per CI run, or once at least once per repository. This enables you to update seemlessly between repositories rather than having to sync multiple people.

```bash
# Get in deploy directory
cd deploy
# Create stack and configure
export PULUMI_CONFIG_PASSPHRASE="some-secret"
pulumi stack init --secrets-provider passphrase --stack dev
pulumi config set --secret kubeconfig "$(cat ~/.kube/config)"
pulumi config set namespace ""
pulumi config set api-server "https://cp.my-k8s.lan:6443"
# Deploy
pulumi up -y
# Fetch ServiceAccount kubeconfig from outputs
pulumi stack output --show-secrets -j | jq -r '.kubeconfig' > kubeconfig
```
2 changes: 1 addition & 1 deletion install/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ inputs:
description: 'The kubeconfig to use for installing Romeo and generating its own kubeconfig (with restreined privileges)'
required: true
namespace:
description: 'The namespace to install Romeo into. May be randomly generated, as long as it fits Kubernetes naming specification.'
description: 'The namespace to install Romeo into. May be randomly generated, as long as it fits Kubernetes naming specification. If not specified, will be randomly generated.'
api-server:
description: 'The Kubernetes api-server URL to pipe into the generated kubeconfig. Example: "https://cp.my-k8s.lan:6443"'
required: true
Expand Down
Binary file added install/architecture.excalidraw.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified res/workflow.excalidraw.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 7ef7208

Please sign in to comment.