1 Component definition
A component is a deployable unit that encapsulates code, configuration, resources, and service endpoints. Components can define interfaces (contracts) and provide implementations.
A component definition is split into two files:
- Interface (
*.h.kumori): Declares the component’s public contract, including services, configuration, and resources. - Implementation (
*.kumori): Provides the concrete implementation of the component, including code, scaling parameters, and resource requirements.
Both files must have the same component name and be in the same package.
1.1 Interface
The interface of a component might define channels, configuration and resources a component publicly exposes.
A component’s interface is declared following its type alias definition:
alias ComponentInterface struct {
srv? Links
config? struct open[]
resource? struct open[Resource]
}
In plain words, the previous definition accepts a component interface composed of an optional set of component Links under the srv keyword, an optional set of configuration properties that accept any definition under the config keyword and an optional set of resource declarations, whose type is restricted to Resources type.
A component interface is defined with the component keyword inside a .h.kumori file.
component NAME { ... }
Given the previous restrictions, this would be a full example of a component interface:
// <your-component>.h.kumori
package component
import "kumori"
component MyComponent {
srv {
server {
server1 "tcp"
server2 "http"
server3 {
protocol "tcp"
port 3333
}
server4 {
protocol "http"
port 3334
}
}
client {
client1 "tcp"
client2 "http"
}
duplex {
duplex1 {
protocol "tcp"
port 6666
}
}
}
config {
value: string
anotherValue: number
}
resource {
volumeOne: kumori.Volume
secret: kumori.Secret
cert: kumori.Certificate
}
}
1.1.1 Links
The Links type alias defines how a component exposes its services to the outside world through channels. A channel can be either a client, a server or a duplex channel. For a deep understanding of links, please refer to the Component Connectivity documentation.
alias Links struct {
client? struct open[Client]
server? struct open[Server]
duplex? struct open[Server]
}
alias Channel "udp" | "tcp" | "http" | "grpc"
alias Client Channel
alias Server Channel | struct { protocol channel, port number }
1.1.2 Resources
The Resource type alias defines the resources that a component can declare in its interface. A resource can be a volume, a certificate authority (CA), a certificate, a secret, a domain or a port. For a deep understanding of resources, please refer to the Resources documentation.
package kumori
library
alias Resource Volume | CA | Certificate | Secret | Domain | Port
type CA string
type Certificate string
type Secret string
type Domain string
type Port string
alias Volume Registered | InlineVolume | DeprecatedVolume
alias InlineVolume NonReplicated | Persisted | Volatile
type Registered string
type NonReplicated StorageSized
type Persisted StorageSized
type Volatile StorageSized
1.2 Implementation
The implementation of a component actually defines the component itself: with all its code (container) fields, scaling parameters, probes…
A component is defined with the component keyword inside a .kumori file.
component NAME { ... }
After the interface has been defined, a component must be declared with the same identifier as the one used in the interface. For example, if in <your-component>.h.kumori you defined one component as component MyComponent, then your <your-component>.kumori file should define the same component with MyComponent name.
A component is defined following its type alias definition:
alias component struct {
srv? Links
config? struct open[]
resource? struct open[Resource]
size struct {
bandwidth BandwidthSized
minbandwidth? BandwidthSized
mincpu CPUSized
}
code? struct open[Code]
init? []struct {
image Image
cmd? []string
entrypoint? []string
size struct {
memory RAMSized
cpu CPUSized
mincpu? CPUSized
}
env? struct open[Env]
fs? struct open[File]
}
probe? struct open[Probes]
}
As you might have noticed, at the beginning of a component implementation type definition, there are three fields exactly the same as in the ComponentInterface type alias. You could declare everything inside a Component implementation rather than having it separated in its interface since all the fields are merged while a component is being processed. However, when a component is defined with an implementation, it is mandatory to declare its interface as well (even if it is empty).
A component allows for multiple fields.
1.2.1 Init
The init field accepts an array of structs with a certain structure (which is actually the same as Code). The difference with the code field is that init containers do not accept an identifier.
Inside this field you can declare as many containers as you want. This example illustrates how a component with two init containers would look like:
//
package component
[...]
init [{
image "docker.io/..."
entrypoint ["/bin/entrypoint"]
size {
memory 100M
cpu 100m
mincpu 100m
}
fs {
"/kumori/shared" {
volume "shared"
}
"/bin/entrypoint" {
data io.Open("init_entrypoint.sh")
mode 0o755
}
}
},
{
image "docker.io/..."
size {
memory 50M
cpu 50m
}
env {
"CONFIG_PATH" "/kumori/config/config.yaml"
}
}]
[...]
1.2.2 Code
The code field defines containers for your component and accepts one or more structs with the following structure:
- The field
imagemust contain the container image to be used. The format of the image should comply with OCI Image Specification. Additionally, you can specify the image as a struct containing thehubandtagfields. Thehubfield is itself a struct that contains thenameof the image and an optionalsecretfield to reference a secret defined in the component’s resource field. Thetagfield specifies the tag of the image. - The field
cmddefines the command to be executed when the container starts. - The field
entrypointdefines the entrypoint of the container. - The field
sizedefines the resource requests for the container. Inside this field, you must define thememoryandcpufields, which are both mandatory. You can also define themincpufield, which is optional. - The field
envdefines the environment variables for the container. See Environment variables for more details. - The field
fsdefines the file mounts for the container. See File mounts for more details.
An example of a component with a code container would be: ```//
[…]
code { Redis { image { hub { name “my-custom-registry.com” secret “my-dockerhub-secret” } tag “redis:alpine” } entrypoint [“sh”] cmd [“/bin/entrypoint”]
size {
memory 1000M
cpu 250m
mincpu 100m
}
env {
ROOT_USERNAME interface.config.redisrootusername
GLOBAL_PASSWORD {
secret "redisglobalpassword"
}
}
fs {
"/data/" {
volume "data"
}
"/bin/entrypoint" {
data io.Open("entrypoint.sh")
mode 0o755
}
}
}
}
You can define multiple containers inside the `code` field by adding more structs with different identifiers.
### Environment variables
You can define environment variables for both `code` and `init` containers using the `env` field. This field accepts a struct with any number of fields, where each field name is the environment variable name and its value is the environment variable value.
Any environment variable is defined following the following type alias:
alias Env string | bool | number | struct { secret: string }
Therefore, the following environment variables would be valid examples:
[…] env { DEBUG true PORT 8080 ROOT_USERNAME interface.config.rootUser CONFIG io.Open(“my_config.cnf”)
API_KEY {
// Must be a secret defined in the component's resource field
secret: "my_secret_name"
}
}
### File mounts
You can define file mounts for both `code` and `init` containers using the `fs` field. This field accepts a struct with any number of fields, restricted to the `File` type alias defined below:
```kumori
alias File string | FSMap | struct { volume: string }
alias FSMap FSData | FSSecret | FSPort | FSDomain | FSCertificate | FSCa
alias AllowedFormat "text" | "json" | "yaml" | "flatdict"
alias FSData struct { data: string, mode?: number, format?: AllowedFormat }
alias FSSecret struct { secret: string, mode?: number, format?: AllowedFormat }
alias FSPort struct { port: string, mode?: number, format?: AllowedFormat }
alias FSDomain struct { domain: string, mode?: number, format?: AllowedFormat }
alias FSCertificate struct { certificate: string, mode?: number, format?: AllowedFormat }
alias FSCa struct { ca: string, mode?: number, format?: AllowedFormat }
This definition allows you to specify file mounts in three different ways:
- By directly specifying the content of the mount:
- By specifying the content, with the optional
modeandformatfields. The allowed keys are:data,secret,port,domain,certificateandca.
[!IMPORTANT]
datais used to specify raw data, while the other keys are used to reference resources defined in the component’sresourcefield.
fs {
"/bin/entrypoint/" {
data io.Open("entrypoint.sh")
format "text"
}
"/your/config/" {
// Must be a secret defined in the component's resource field
secret "your-secret-name"
mode 0o644
}
"/another/path" {
// Must be a port defined in the component's resource field
certificate "your-port-name"
mode 0o755
}
}[!NOTE] When specified, the
modefield accepts values in octal format. If not specified, the default mode is0o644.When specified, the
formatfield accepts one of the following values:text,json,yamlorflatdict. If not specified, the default format istext.
- By mounting a
volumedefined in the component’sresourcefield:
fs {
"/kumori/data" {
// Must be a volume defined in the component's resource field
volume "data_volume"
}
}
1.2.3 Probes
The probe field accepts a struct with any of the following fields: liveness, readiness and pmetrics. The definition of the Probes type alias guides how to declare each probe:
alias Probes struct {
liveness? LivenessProbeAttributes
readiness? ReadinessProbeAttributes
pmetrics? PrometheusMetricsProbeAttributes
}
alias LivenessProbeAttributes struct {
protocol ProbeProtocol
startupGraceWindow? StartupGraceWindow
frequency? any
timeout? number
}
alias ReadinessProbeAttributes struct {
protocol ProbeProtocol
frequency? any
timeout? number
}
alias PrometheusMetricsProbeAttributes struct {
protocol HTTPOnlyProbeProtocol
}
alias ProbeProtocol struct {
http? HTTPProbeProtocol
tcp? TCPProbeProtocol
exec? ExecProbeProtocol
}
alias HTTPOnlyProbeProtocol struct {
http HTTPProbeProtocol
}
alias HTTPProbeProtocol struct {
port number
path string
}
alias TCPProbeProtocol struct {
port number
}
alias ExecProbeProtocol struct {
path string
}
alias StartupGraceWindow struct {
unit "ms" | "attempt"
duration number
probe bool
}
Probes can only be declared for existing containers inside the code field, using their identifiers. For example, if you have a container named Redis inside the code field, you can declare probes for it as follows:
probe {
Redis {
liveness {
protocol {
tcp {
port 6379
}
}
frequency "10s"
timeout 5
}
readiness {
protocol {
tcp {
port 6379
}
}
frequency "5s"
timeout 3
}
pmetrics {
protocol {
http {
port 9121
path "/metrics"
}
}
}
}
}
The Component Examples section contains several complete component definitions showcasing different configurations and use cases.