{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://docs.omniarc.eu/schema/arc.yaml.json",
  "title": "arc.yaml",
  "description": "What OmniArc needs to know about a compose project that compose itself cannot say.",
  "type": "object",
  "additionalProperties": false,
  "required": ["arc", "edge", "services"],
  "properties": {
    "arc": {
      "description": "The name of the arc this file belongs to.",
      "type": "string",
      "pattern": "^[a-z]([a-z0-9-]{0,18}[a-z0-9])?$"
    },
    "compose": {
      "description": "The compose files to use, in order.",
      "type": ["array", "null"],
      "items": { "type": "string" }
    },
    "profiles": {
      "description": "The compose profiles to turn on.",
      "type": ["array", "null"],
      "items": { "type": "string" }
    },
    "edge": {
      "description": "Which service answers HTTP for the arc, or none if nothing should.",
      "oneOf": [
        { "const": "none" },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["service", "port"],
          "properties": {
            "service": {
              "description": "The compose service that answers HTTP.",
              "type": "string",
              "pattern": "^[a-z]([a-z0-9-]{0,38}[a-z0-9])?$"
            },
            "port": {
              "description": "The container port the service listens on.",
              "type": "integer",
              "minimum": 1,
              "maximum": 65535
            }
          }
        }
      ]
    },
    "recordsize": {
      "description": "The block size the arc's storage uses.",
      "type": ["string", "null"],
      "pattern": "^(0*(4|8|16|32|64|128|256|512|1024)KiB|0*1MiB)$"
    },
    "resources": {
      "description": "How much CPU, memory and disk the arc needs.",
      "type": ["object", "null"],
      "additionalProperties": false,
      "properties": {
        "cpu": {
          "description": "How much CPU the arc needs.",
          "type": ["string", "number", "null"],
          "exclusiveMinimum": 0,
          "pattern": "^\\s*([+]?([0-9]+[.]?[0-9]*|[.][0-9]+)([eE][+-]?[0-9]+)?|[+]?[0-9]+\\s*m)\\s*$"
        },
        "memory": {
          "description": "How much memory the arc needs.",
          "type": ["string", "null"],
          "pattern": "^\\s*[+]?[0-9]+\\s*(Mi|Gi)\\s*$"
        },
        "disk": {
          "description": "How much disk the arc needs.",
          "type": ["string", "null"],
          "pattern": "^\\s*[+]?[0-9]+\\s*(Mi|Gi)\\s*$"
        }
      }
    },
    "ready_timeout_secs": {
      "description": "How many seconds OmniArc waits for the services to become ready.",
      "type": "integer",
      "minimum": 1,
      "maximum": 3600,
      "default": 300
    },
    "services": {
      "description": "One entry for each service in your compose project.",
      "type": ["object", "null"],
      "propertyNames": { "pattern": "^[a-z]([a-z0-9-]{0,38}[a-z0-9])?$" },
      "additionalProperties": {
        "description": "What OmniArc needs to know about one compose service.",
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "kind": {
            "description": "What kind of service this is, which decides how its storage is tuned.",
            "enum": ["database", "cache", "document", "object", "forge", "web-service", "frontend", null]
          },
          "deploy": {
            "description": "Whether OmniArc runs this service at all.",
            "type": "boolean",
            "default": true
          },
          "branch": {
            "description": "Whether a branch of the arc gets a copy of this service's data.",
            "type": "boolean",
            "default": true
          },
          "contains_pii": {
            "description": "Whether this service holds personal data.",
            "type": ["boolean", "null"]
          },
          "anonymize": {
            "description": "How to remove personal data from this service's database when a branch is made.",
            "type": ["object", "null"],
            "additionalProperties": false,
            "required": ["engine", "database", "user", "rules"],
            "properties": {
              "engine": {
                "description": "The database engine of the service.",
                "type": "string"
              },
              "database": {
                "description": "The name of the database to anonymize.",
                "type": "string",
                "pattern": "^[A-Za-z_][A-Za-z0-9_$-]{0,62}$"
              },
              "user": {
                "description": "The database role OmniArc connects as.",
                "type": "string",
                "pattern": "^[A-Za-z_][A-Za-z0-9_$-]{0,62}$"
              },
              "rules": {
                "description": "The columns to change and what to change them to.",
                "type": "array",
                "minItems": 1,
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": ["table", "column", "transform"],
                  "properties": {
                    "table": {
                      "description": "The table to change.",
                      "type": "string",
                      "pattern": "^[A-Za-z_][A-Za-z0-9_]*([.][A-Za-z_][A-Za-z0-9_]*)?$"
                    },
                    "column": {
                      "description": "The column to change.",
                      "type": "string",
                      "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
                    },
                    "transform": {
                      "description": "What to do to the values in the column.",
                      "enum": ["redact", "hash", "null", "fake-email", "fake-name", "fake-phone"]
                    }
                  }
                }
              }
            }
          },
          "quiesce": {
            "description": "A command that makes the service safe to snapshot.",
            "type": ["object", "null"],
            "additionalProperties": false,
            "required": ["exec"],
            "properties": {
              "exec": {
                "description": "The command and its arguments, one list item for each.",
                "type": "array",
                "minItems": 1,
                "items": { "type": "string", "minLength": 1 }
              },
              "timeout_secs": {
                "description": "How many seconds the command may run before the branch fails.",
                "type": "integer",
                "minimum": 1,
                "default": 30
              }
            }
          }
        },
        "allOf": [
          {
            "if": { "required": ["deploy"], "properties": { "deploy": { "const": false } } },
            "then": {
              "properties": {
                "contains_pii": { "type": "null" },
                "anonymize": { "type": "null" },
                "quiesce": { "type": "null" }
              }
            },
            "else": {
              "required": ["contains_pii"],
              "properties": { "contains_pii": { "type": "boolean" } },
              "if": { "required": ["contains_pii"], "properties": { "contains_pii": { "const": true } } },
              "then": {
                "if": { "required": ["branch"], "properties": { "branch": { "const": false } } },
                "else": { "required": ["anonymize"], "properties": { "anonymize": { "type": "object" } } }
              }
            }
          }
        ]
      }
    }
  }
}
