score-spec / docs

Docs for Score Specification.
https://docs.score.dev/docs/
Apache License 2.0
6 stars 12 forks source link

"How to write a new Score implementation?" doc #122

Open mathieu-benoit opened 5 days ago

mathieu-benoit commented 5 days ago

We need to have a dedicated "How to write a new Score implementation?" doc entry.

Here is rough idea/draft:

---
title: "Score Implementation Guidelines"
linkTitle: "Score Implementation Guidelines"
weight: 10
description: >
  Guidelines for how a Score implementation should operate.
---

## Score Implementation Guidelines

A "Score implementation" is a process or system that can be used to transform an input Score definition that follows the Score schema into a real set of resources or workloads in a target runtime.

Examples:

- `score-compose` converts workloads into a Docker Compose project.
- `score-helm` injects workloads into a Helm chart to insert Kubernetes manifests into a target cluster.
- Humanitec's Platform Orchestrator can convert Score workloads into deployed modules and provides extensive support for resource types and classes.
- Future implementations may target other platforms like cloud container runtimes, serverless functions, or other workload implementations.

## CLI implementations

### SHOULD support an `init` subcommand

The init command should be run in a directory in order to initialize local state needed for a Score project.

- Validate that the system state will support the Score implementation. Throw an error if required binaries or configuration is not available or not valid.
- Create a skeleton `score.yaml` file if one does not already exist.
- Create a `.score-<impl>` directory in the current directory if one does not exist yet. This may be skipped if the Score implementation never stores project-local state. This may also be a symlink to a directory elsewhere on the system.

The `init` command can support other optional flags as needed.

### MUST support a `generate` subcommand

The `generate` command should be run after `init` and should transform the Score workloads into a format that can be deployed in an implementation specific way.

For example, `score-compose` generates Docker compose manifests; `score-helm` generates a Helm values file to combine with a chart; other Score implementations may generate terraform, cloud formation, or other infrastructure manifests for deployment.

#### `generate` SHOULD support multiple score files

When technically possible, the implementation should support multiple score files. For example: `generate (options..) some-score.yaml score*.yaml`.

The workloads and resources from multiple files should be combined so that workloads from different score files remain independed but resources and references may be shared between them.

#### `generate` MUST NOT deploy workloads

The `generate` command generates the final manifests that can be deployed in an implementation specific way. It must not actually perform the deployment.

#### `generate` MAY prepare or provision resources if not supported at deploy time

While the `generate` command should not deploy the workloads, it may take steps to prepare, validate, or event provision resources required by the workloads.

This should only be done if the deployment step cannot provision the resources.

For example, `score-compose` may use the generate command to generate initial configuration files, secrets, volumes, or other configuration required by the services generated.

#### `generate` SHOULD use the local directory created by `init` for storage

The local project directory (`.score-<impl>`) should be used for project specific storage. This may be used for preparing manifests, tracking resource state, or creating mount points for files and volumes used by either resources or workloads used in the deployment.

#### `generate` SHOULD support override flags to override elements of the provided Score files

It's common that a specific score implementation may not support a feature or requirement used by the given Score file. This can usually be fixed by allowing users to provide some overrides that are merged into the Score files before manifests are generated. This allows values to be changed or overridden.

For example, `generate -f score.yaml -o score.compose-overrides.yaml` might be used to remove or modify unsupported resources.

### SHOULD deprecate the `run` command

Implementations that currently support a `run` command, should mark it deprecated and direct users towards the `init` + `generate` flow.

`run` was deprecated becaue it's meaning is not clear, and it's implementation had wildly different affects in different Score implementations. It may be kept for backwards compatibility, but documentation and tutorials should point users towards the generate flow.

## Validation

### MUST validate against the Score specification

Implementations MUST strictly validate the input Score definition against the schema and throw errors for any unexpected fields, structure, or incorrect typing.

### MUST reject Score definitions that depend on unsupported features

Not all Score implementations can realisticaly support all features of the Score specification. In some cases, these features will change the functionality of the workload negatively and should be rejected.

For example, if a Score implementation doesn't support mounted files, but the workload depends on a mounted file for runtime configuration, it cannot ignore this and should throw an error.

### SHOULD warn on Score definitions that use unsupported features

Related to the previous point, many aspects of the Score definition are not strictly required for the workload to function.

For example, a workload may not be affected if an implementation does not support liveness or readiness probes. Or an implementation may not support cpu reservations and may convert these into limits or ignore them completely.

These warnings should be presented to the user at generate or deploy time through log messages or other appropriate warnings in the user interface.

### MUST reject Score definitions that use unsupported resource types or classes

If a resource type is used that the Score implementation does not support, it cannot be ignored and must be rejected with an appropriate error.

Similarly with classes or more complex resource provisioning implementations, the resource must be rejected if the resource cannot be provisioned.

## Core features

### MUST support container image, command, args, and environment variables

These are the core features of the container element. The command and args should be interpreted like the `Entrypoint` and `Cmd` properties of the OCI specification respectively: `command` indicates the default argv to execute while `args` provides additional arguments that are appended to `command` or the default `Entrypoint` of the container.

Environment variables must be supported.

### MUST support multiple containers per Workload

The implementation must support provisioning multiple containers for the workload. This is a common part of the Score specification that users expect to be transferable between implementations.

### SHOULD place containers of the same Workload in the same network namespace or host if possible

When possible, the containers of a Workload should be placed in the same network namespace so that they can access network sockets over the local loopback interface and cannot listen on the same ports.

If this is not possible, a warning should be thrown, and users should be guided towards alternative network connectivity options if they require inter-container connectivity.

For similar reasons, if containers depend on shared volumes or files that will not be backed by the same storage, warnings should be thrown.

### MUST support service ports

If the definition includes one or more Services, these should be used to expose ports from the network of the Workloads or any replicas of the same Workloads.

A Service should be interpreted like a networkload load balancer or proxy that can route traffic on an incoming port to the target port of the Workload replicas.

Service ports on the same Workload may not overlap, but may overlap with other Workloads (for example, each Workload might have a port 80 service). If this not supported, warnings should be raised.

Workloads may reference and connect to service ports of other Workloads.