The code section

The code section puts together the rest of a component’s specification to instruct how to actually run the code for an instance of the component.

The code section is essentially, a set of specifications for how to run a container. A component’s code may consist of more than one image, each of which should be ran within its own container.

Containers within the code section are organized as a dictionary of #Container structures:

code: [nm=string]: #Container & {name: nm}

Where each field in code is the name of the container to be created when an instance of the component is run, and the #Container structure must contain itself a name field whose value is that of the key leading to that structure,

#Container: {
	name:  string
	size: #ContainerSize
	image: #Image
	entrypoint?: [...string]
	cmd?: [...string]
	user?: {
		userid:  uint16
		groupid: uint16
	}
	mapping: #Mapping
}
#Image: {
	hub:  #Hub | * {name: "registry.hub.docker.com", secret: ""}
	tag:  string
	digest?: string
}
#Hub: {
	name:   string
	secret: string
}

Note that, as we mentioned earlier, code for a component must be encapsulated by docker images, which are referenced within the #Container structure.

We can use a tag to refer to an image, and, currently, providing the digest is optional. This will change in future revisions, as Kumori Platform enphasizes reproducibility of results, and referring to the code by tag is very weak, as such tags provide no guarantee of immutability of the code behind the tag, besides being fully dependent on the hub from which the image is retrieved.
A hub to retrieve an image must be provided (there is a suitable default, though). In the future, this may be optional, relying on registered hubs, or default hubs. The Hub in itself is not a defining characteristic of the image itself, nor of the component.

The secret referred to within a hub description must be registered in the platform. As other resources referenced from within the code section, what actually gets placed in the image secret is the path into the resource configuration contianing that secret.

Size

As before the size of a component instance has a global part and a per-container part.

Mappings

An important part of the code section is the mapping subsection. Its definition is here:

#Mapping: {
	filesystem: [...#FileMap | #FolderMap]
	env: #EnvMap
}
#EnvMap: {
	[string]: {[#DataResourceKey]: string} |  {parameter: string} | {value: string}
}
#FileMap: {
	path:   string
	mode:   uint16 | *0o644
	data:   {[#DataResourceKey]: string} | {parameter: string} | {value: string}
	format: *"text" | "json" | "yaml"
}
#FolderMap: {
	path: string
	{{tree: [...#FileMap | #FolderMap]} | volume: string}
}

From the definition we see that there are filesystem and environment mappings. The environment mapping is organized as a dictionary of environment variables, whose values will be made available to the container on execution.

In the specification, values for environment variables can be provided in several ways:

  • As the data value of resources made available through the configuration, where the string provided must correspond to a path in the resource part of the config leading to a resource of the kind specified by the #DataResourceKey provided

  • With an arbitrary string value (value: string) which may come from the configuration through standard CUE references.

  • As a reference to a parameter (parameter: string), where the string must be a path into the parameter section of the config. The value injected will be the value of the parameter as a string.

Values for environment variable mappings must resolve to string

Filesystem mappings

Filesystem mappings are organizad as an array of mappings. Each element of the array may map a file or, potentially, a tree of folders and files.

File Maps

Mapping a file is simple: its path within the container must be specified. In addition, we can provide a mode for the file (useful to inject executable scripts when adapting third party images).

In addition to the path, we need to provide the data. Data for a file map can come from the same sources as data for an environment variable (see above).

In addition, a format field completes the specification for a file map. By default it assumes data value should be interpreted as a raw string and be left as is within the resulting text file, without marshalling it.

It is an error to specify a `text`format for a non-string data value.

If format is json, or yaml, then data is marshalled to either of those two formats, and the result of that is placed within the file.

Folder maps

Folder maps seem a bit more complex, but they are also quite simple to understand.

A folder map, like a file map, must provide a path. The simplest folder map then, interprets that path as the path of a folder where to mount either a Volatile or a Persistent Volume. In such a case, the string provided for the volume is actually a path into the resource section of the config. This approach allows us to share the same volume accross multiple containers.

If, however, a tree field is specified, then the path provided is still treated as a folder path, however no mounting is produced on that path, serving only to recursively define further mappings withint the folder (either file or folder maps).

As mentioned, volumes can be shared among the containers of a component.
When references to the config are used outside a value: ` field, the strings must match a path within the respective part of the config (either `parameter or resource). The values to be inserted into the file/env variable will be provided by the platform at deployment.