Control Plane
Structure overview
The control plane is a Go service that leverages protocol buffers and gRPC for its API, ent as ORM, wire for dependency injection and the kratos framework for additional utilities such middlewares, configuration management or error handling.
It's implemented following(ish) Hexagonal architecture with the following top to down layers.
- API definition layer
./api/
. proto definitions and generated code for the external gRPC API
- Server layer
./internal/server
. Definition and registration of the HTTP and gRPC servers and middlewares.
- Service layer
./internal/service
. Implementation of the protocol buffer services.
- Business layer
./internal/biz
. Implementation of use-cases referenced by the service layer and definition of the data repository abstractions.
- Data layer
./internal/data
. Implementation of data repositories interfaces defined in the business layer.
System Dependencies
The control plane has 4 main dependencies
- OpenID Connect (OIDC) provider. Chainloop authentication backend is delegated to a OIDC provider (i.e Google, GitHub or Auth0) for single sign on.
- The persistance layer requires a PostgreSQL database.
- Sensitive information provided by the user such as OCI registry credentials is sent to a secret storage backend. Currently we support both Hashicorp Vault and AWS Secret Manager.
- In addition to those third party dependencies, the control plane also has a dependency on Chainloop own Artifact CAS. It is used to upload the received attestation to the end-user storage backend.
NOTE: The control plane does not store attestation or artifact data, these get forwarded to the user storage backend through the Artifact CAS.
Runbook
We use make
for most development tasks. Run make -C app/controlplane
to see a list of the available tasks.
Run the project in development
Refer to development guide for more information but in a nutshell.
# Run external dependencies (Dex for OIDC, bitnami/postgreSQL container for persistence and Vault for secret management)
docker compose -f devel/docker-compose.yml up
# Run the control plane
make -C app/controlplane run
Next, follow the steps that can be found here to configure the CLI
Run tests
We've implemented two kinds of tests, unit tests (make run test-unit
) and integration tests. The latter launch a containerized database and hence they are expensive to run.
To run all the tests make test
Build binary
make build
Generate API code from protocol buffer defintions (*.proto)
We use buf.io to lint and generate proto files. Make sure you install buf first.
Once done, generating the API code is as easy as executing
make api
Update Application data model
We use ent as database Object Relational Mapping (ORM).
The way a change in the data model will works is
- Add a new/update an existing entity via a schema update. Schemas can be found at
internal/data/ent/schema
- Generate the code changes associated with that schema change.
make generate
- Restarting the control plane will cause the schema change to be automatically migrated.
Update configuration schema
The service runtime configuration is implemented by using kratos built-in config module.
Meaning that the configuration schema is defined at internal/conf/conf.proto
.
To regenerate it run:
make config
Update dependency injection
In order to enforce inversion of control and prevent import cycles we use wire for dependency injection.
Wire has a fairly steep learning curve, so we recommend taking a look at their tutorial. In practice, in this project you will find a couple of wire_gen.go files (i.e cmd/wire_gen.go
) and different provider defined.
If you need to re-generate the injection code after a change just run make generate
Contribution guidelines
Please make sure to review the Contribution guidelines and feel free to reach out if you have any questions!
Welcome!