score-compose

repository·main·Indexed 19 days ago

https://github.com/score-spec/score-compose

A reference implementation of the Score specification for Docker Compose. It provides a CLI to transform Score specifications (score.yaml) into runnable Docker Compose files (compose.yaml), facilitating local development by provisioning dynamic resources like databases and storage.

Tokens
15.8K
Snippets
64
Records
82
Agent score
65%

What's inside score-compose

  1. What is score-compose

    main

    score-compose is a reference implementation of the Score specification for Docker compose. It is primarily designed for local development.

    It provides a resource provisioning system that allows you to supply and customize the dynamic configuration of attached services, such as databases, queues, storage, and other network or storage APIs, by translating Score resource definitions into Docker Compose configurations.

  2. Score feature support and limitations in score-compose

    main

    score-compose supports most aspects of the Score specification, but certain features are validated without being applied because they do not map cleanly to the Docker Compose specification. These limitations do not impact workload execution.

    FeatureSupportImpact
    containers.*.resources.limits / containers.*.resources.requestsnoneLimits will be validated but ignored.
    containers.*.livenessProbe.httpGet / containers.*.readinessProbe.httpGetnoneProbes will be validated but ignored. (Only exec mode is supported by Compose)
  3. Define multiple containers in a single Score Workload

    main

    Score allows you to define multiple containers within a single Workload. When using score-compose, these containers are placed within the same network namespace (similar to Kubernetes).

    Important: Listening ports for containers within the same workload must not overlap.

    apiVersion: score.dev/v1b1
    
    metadata:
      name: hello-world
    
    containers:
      first:
        image: "nginx:latest"
        variables:
          NGINX_PORT: "8080"
      second:
        image: "nginx:latest"
        variables:
          NGINX_PORT: "8081"
  4. Share stateful resources across workloads using `id`

    main

    By default, resources in score-compose are independent. To share a single stateful resource (like a Redis instance or a database) across multiple workloads, assign the same id to the resource definition in each workload. This id must be unique to the project but identical across the workloads that need to share it.

    Use the placeholder syntax ${resources.<resource-name>.<output-key>} to access resource outputs. If an output key contains a dot, escape it with a backslash: ${resources.cache-a.some\.key}.

    apiVersion: score.dev/v1b1
    metadata:
      name: workload-one
    resources:
      cache-a:
        type: redis
        id: main-cache
    
    ---
    apiVersion: score.dev/v1b1
    metadata:
      name: workload-two
    resources:
      cache-b:
        type: redis
        id: main-cache
      cache-c:
        type: redis
  5. How patching templates work

    main

    Patching templates allow you to perform post-processing on the generated Docker Compose file. You provide one or more template files via the --patch-templates flag during score-compose init.

    Template Requirements:

    • Templates are evaluated as Golang text/templates.
    • They must output a YAML or JSON encoded array of patches.
    • Each patch object must contain:
      • op: Either set or delete.
      • patch: A dot-separated JSON path to the target field.
      • value: Required if op is set.
      • description (optional): A string to be shown in the logs.
  6. Construct an AMQP connection URI from resource outputs

    main

    When provisioning an amqp resource, score-compose provides several output fields that can be used to construct a connection string. The available outputs are:

    • host
    • port
    • vhost
    • username
    • password

    You can assemble these into a standard URI format: amqp://${resources.<resource_name>.username}:${resources.<resource_name>.password}@${resources.<resource_name>.host}:${resources.<resource_name>.port}/${resources.<resource_name>.vhost}.

    amqp://${resources.bus.username}:${resources.bus.password}@${resources.bus.host}:${resources.bus.port}/${resources.bus.vhost}
  7. Use the service-port resource to link workloads

    main

    The service-port resource type allows one workload to discover and link to another workload using its advertised service ports. In score-compose, this resource is resolved to the workload's hostname and the specific targetPort of the named service port.

    Key Behaviors:

    • Resolution: It translates a named service port into a hostname:port combination.
    • Error Handling: score-compose will throw errors if the specified workload or the named port does not exist.
    • Dependency Model: Using service-port does not create a startup dependency between workloads. score-compose assumes workloads may start or restart in any order, so services should be designed to handle cases where the dependency is not immediately available (e.g., using retry logic in shell commands).

    Example Workflow:

    1. Define a workload (Workload A) that advertises a service port.
    2. Define a second workload (Workload B) that uses a service-port resource to reference Workload A.
    3. Use the resolved resource values (e.g., ${resources.dependency.hostname}) in environment variables.
    # Workload A: Advertises a service port named 'web'
    apiVersion: score.dev/v1b1
    metadata:
      name: workload-a
    containers:
      example:
        image: nginx
    service:
      ports:
        web:
          port: 8080
          targetPort: 80
    
    ---
    
    # Workload B: Consumes the 'web' port from 'workload-a'
    apiVersion: score.dev/v1b1
    metadata:
      name: workload-b
    containers:
      example:
        image: busybox
        variables:
          DEPENDENCY_URL: "http://${resources.dependency.hostname}:${resources.dependency.port}"
    resources:
      dependency:
        type: service-port
        params:
          workload: workload-a
          port: web
  8. Understand and use `*.provisioners.yaml` files

    main

    Provisioners define how Score resources are transformed into Compose components.

    • Default Provisioners: When you run score-compose init, a zz-default.provisioners.yaml file is created in the .score-compose directory containing built-in definitions.
    • Custom Provisioners: When you run score-compose generate, all *.provisioners.yaml files in the .score-compose directory are loaded in lexicographic order. This allows you to extend or override defaults by adding your own files.
    • Matching Logic: Provisioners are matched in first-match order based on the lexicographic loading of files. Custom files are matched before zz-default.provisioners.yaml.

    Each provisioner entry contains:

    • uri: A unique identifier combining the implementation type (e.g., template:// or cmd://) and an ID.
    • type: The resource type it handles.
    • class: (Optional) The resource class.
    • id: (Optional) The resource ID.
  9. Define multiple Workloads in a single project

    main

    You can include multiple Score workloads in the same project directory. Each workload must have a unique name in its metadata.name field.

    When score-compose generates a compose file for multiple workloads:

    1. Containers from the same workload share a network namespace.
    2. Containers from different workloads run in different network namespaces.
    # Workload 1
    apiVersion: score.dev/v1b1
    metadata:
      name: hello-world
    containers:
      first:
        image: "nginx:latest"
    
    # Workload 2 (in a separate file like score2.yaml)
    ---
    apiVersion: score.dev/v1b1
    metadata:
      name: hello-world-2
    containers:
      first:
        image: "nginx:latest"
  10. Use patching templates to modify compose output

    main

    You can modify or adjust the output of the score-compose conversion process by providing patching templates at init time. These templates generate JSON patches that are applied to the generated compose file just before it is written to disk.

    Template Context

    Patching templates have access to the following data structures:

    • .Compose: The current compose specification.
    • .Workloads: A map of workload names to their corresponding Score Spec.
    • Functions: You can use any functions from the Masterminds/sprig library.

    Patch Format

    Each patch produced by a template must be a YAML/JSON blob containing:

    • op: The operation type, either set or delete.
    • path: A dot-separated JSON path. Use a backslash (\) to escape dots in keys. Use a colon (:) to escape numeric indices.
    • value: The value to set (required for set operations).
    • description: (Optional) A description of the patch.
    - op: set
      path: services.some\.thing
      value: true
      description: Set services to read only root fs
  11. Publish AMQP and management ports to localhost

    main

    To access the RabbitMQ broker and its management UI from your local machine, use the compose.score.dev/publish-port and compose.score.dev/publish-management-port annotations in your resource metadata.

    It is recommended to apply these annotations via an --overrides-file rather than modifying your primary Score file. When using these ports, the default guest/guest credentials will be available for debugging.

    resources:
      bus:
        type: amqp
        metadata:
          annotations:
            "compose.score.dev/publish-port": "5672"
            "compose.score.dev/publish-management-port": "15672"