openapi: 3.1.0
info:
  title: "Dovecot Pro: Palomar Cluster Controller"
  description: "Dovecot Pro: Palomar Cluster Controller (REST API)"
  contact:
    email: info@dovecotpro.com
  license:
    name: Dovecot Pro
    url: http://www.open-xchange.com/products/dovecot-pro/
  version: 1.0.0
servers:
  - url: http://localhost
    description: Example Server
tags:
  - name: Status
    description: Controller Status
  - name: Sites
    description: Site Operations
  - name: Backends
    description: Backend Operations
  - name: Tasks
    description: TODO
  - name: Features
    description: Feature Management
paths:
  /v1/ping:
    get:
      operationId: getPing
      tags:
        - Status
      summary: Ping server.
      description: Returns pong. Can be used for liveliness check.
      responses:
        '200':
          description: Successful ping.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ping:
                    type: string
                    example: pong
                    enum:
                      - pong
  /v1/sites:
    get:
      operationId: getSites
      tags:
        - Sites
      summary: Query list of sites.
      description: Return list of sites.
      responses:
        '200':
          description: List of sites.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/SiteObject'
    put:
      operationId: putSites
      tags:
        - Sites
      summary: Create a site.
      description: Create a new site.
      requestBody:
        $ref: '#/components/requestBodies/SiteObjectBody'
      responses:
        '200':
          description: Site was successfully added.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SiteObject'
        '400':
          description: Invalid site data.
        '409':
          description: Site already exists.
  /v1/sites/{site_id}:
    parameters:
      - $ref: '#/components/parameters/SiteUUID'
    get:
      operationId: getSitesBySiteId
      tags:
        - Sites
      summary: Retrieve information on single site.
      description: Retrieve information on single site.
      responses:
        '200':
          description: Site information.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SiteObject'
    delete:
      operationId: deleteSiteBySiteId
      tags:
        - Sites
      summary: Delete a site.
      description: >
        Deletes a site. Site backends, groups etc. must be deleted first.
      responses:
        '204':
          description: Site was successfully deleted.
        '404':
          description: Site not found.
        '409':
          description: Site is still referenced.
    patch:
      operationId: updateSiteBySiteId
      tags:
        - Sites
      summary: Update site information.
      description: Update site information. All data, except site ID, can be
        changed.
      requestBody:
        $ref: '#/components/requestBodies/SiteObjectBody'
      responses:
        '200':
          description: Site information updated.
        '400':
          description: Invalid site values provided.
        '404':
          description: Site not found.
        '409':
          description: Name is already taken.
  /v1/sites/{site_id}/backends:
    parameters:
      - $ref: '#/components/parameters/SiteUUID'
    get:
      operationId: getBackendsBySiteId
      tags:
        - Sites
      summary: List site backends.
      description: Returns list of all backends for a site.
      responses:
        '200':
          description: Backend list.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/BackendObject'
    put:
      operationId: addBackend
      tags:
        - Backends
      summary: Add a backend.
      description: |
        Add a new backend to a site.

        Site status will default to `standby`.
      requestBody:
        $ref: '#/components/requestBodies/BackendObjectBody'
      responses:
        '200':
          description: Success.
        '400':
          description: Invalid backend data.
        '404':
          description: Site not found.
        '409':
          description: Backend with same name exists.
  /v1/sites/{site_id}/backends/{backend_id}:
    parameters:
      - $ref: '#/components/parameters/BackendUUID'
      - $ref: '#/components/parameters/SiteUUID'
    get:
      operationId: getBackend
      tags:
        - Backends
      summary: Get backend information.
      description: Retrieve information on a backend.
      responses:
        '200':
          $ref: '#/components/schemas/BackendObject'
    patch:
      operationId: updateBackend
      tags:
        - Backends
      summary: Update backend information.
      description: Update information for a backend.
      requestBody:
        $ref: '#/components/requestBodies/BackendObjectBody'
      responses:
        '200':
          description: Success.
        '400':
          description: Invalid site data.
        '404':
          description: Backend not found.
        '409':
          description: Backend name already used.
    delete:
      operationId: deleteBackend
      tags:
        - Backends
      summary: Delete a backend.
      description: Delete a backend. Backend must by empty.
      responses:
        '204':
          description: Success.
        '404':
          description: Backend not found.
        '409':
          description: Backend still referenced.
  /v1/sites/{site_id}/backends/{backend_id}/groups:
    parameters:
      - $ref: '#/components/parameters/BackendUUID'
      - $ref: '#/components/parameters/SiteUUID'
    get:
      operationId: getGroupsByBackend
      tags:
        - Backends
      summary: Get groups on a backend.
      description: Retrieve information on groups assigned to a backend.
      responses:
        '200':
          description: Group list.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/GroupObject'
  /v1/sites/{site_id}/backends/{backend_id}/groups/{group_id}:
    parameters:
      - $ref: '#/components/parameters/BackendUUID'
      - $ref: '#/components/parameters/GroupID'
      - $ref: '#/components/parameters/SiteUUID'
    get:
      operationId: getGroupByBackend
      tags:
        - Backends
      summary: Get group information.
      description: Retrieve information on a single group on a backend.
      responses:
        '200':
          description: Group information.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GroupObject'
  /v1/sites/{site_id}/backends/{backend_id}/stats:
    parameters:
      - $ref: '#/components/parameters/BackendUUID'
      - $ref: '#/components/parameters/SiteUUID'
    get:
      operationId: getBackendStats
      tags:
        - Backends
      summary: Get backend stats.
      description: Retrieve backend stats.
      responses:
        '200':
          description: Statistics list.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/StatsObject'
  /v1/sites/{site_id}/desired_backends:
    parameters:
      - $ref: '#/components/parameters/SiteUUID'
    get:
      operationId: getDesiredBackends
      tags:
        - Sites
      summary: Returns number of backends desired for this site.
      description: Returns number of backends desired for this site. This is
        calculated by chosen autoscaler. This can be used for automatic
        backend up/downscaling.
      responses:
        '200':
          description: Number of backends desired for the site.
          content:
            text/plain:
              schema:
                type: integer
                example: 6
  /v1/sites/{site_id}/groups:
    parameters:
      - $ref: '#/components/parameters/SiteUUID'
    get:
      operationId: getGroupsBySite
      tags:
        - Sites
      summary: List all groups for a site.
      description: List all groups for a site.
      responses:
        '200':
          description: Group list.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/GroupObject'
  /v1/sites/{site_id}/groups/{group_id}:
    parameters:
      - $ref: '#/components/parameters/GroupID'
      - $ref: '#/components/parameters/SiteUUID'
    get:
      operationId: getGroupBySite
      tags:
        - Sites
      summary: Get group information.
      description: Retrieve information on a specific group in a site.
      responses:
        '200':
          description: Group information.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GroupObject'
  /v1/sites/{site_id}/rebalance:
    parameters:
      - $ref: '#/components/parameters/SiteUUID'
    post:
      operationId: rebalanceSite
      tags:
        - Sites
      summary: Rebalance a site.
      description: Executes site rebalancing. Should not be normally
        used, as the controller will automatically perform this
        periodically. Site ID must match with controller configured
        site. Returns a task ID for tracing.
      responses:
        '200':
          description: Successful rebalance.
          content:
            application/json:
              schema:
                type: object
                properties:
                  task_id:
                    description: Task ID
                    type: string
                    example: 4b7057db-6a6d-4c2d-8cf9-7f47d79f5671
  /v1/tasks:
    get:
      operationId: getTasks
      tags:
        - Tasks
      summary: Retrieve all tasks.
      description: Retrieve all tasks.
      responses:
        '200':
          description: Task list.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TaskObject'
  /v1/tasks/{task_id}:
    parameters:
      - $ref: '#/components/parameters/TaskUUID'
    get:
      operationId: getTasksByTaskId
      tags:
        - Tasks
      summary: Get a single task.
      description: Retrieve information on a single task.
      responses:
        '200':
          description: Task information.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskObject'
  /v1/features/site/<uuid:site_id>:
    parameters:
      - $ref: '#/components/parameters/SiteUUID'
    get:
      operationId: getSiteFeatureSettings
      tags:
        - Features
      summary: Get site feature settings.
      description: Get list of feature objects with their status for the site.
      responses:
        '200':
          description: Site feature overview information.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/FeatureObject'
    post:
      operationId: createSiteFeatureSetting
      tags:
        - Features
      summary: Create site feature setting
      description: |
        Create record for feature status if not already created.
      requestBody:
        $ref: '#/components/requestBodies/FeatureCreateUpdateBody'
      responses:
        '201':
          description: Feature setting created for the site.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/FeatureObject'
        '400':
          description: invalid request (setting name or value not set).
        '409':
          description: feature setting already exists.
        '425':
          description: settings table is not present in the database.
    put:
      operationId: updateSiteFeatureSetting
      tags:
        - Features
      summary: Update site feature setting
      description: |
        Change the value of a feature setting if created.
      requestBody:
        $ref: '#/components/requestBodies/FeatureCreateUpdateBody'
      responses:
        '200':
          description: Setting value updated successfully.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/FeatureObject'
        '400':
          description: Invalid request (setting name or value not set).
        '404':
          description: Feature setting with constraints described in request body not found.
        '425':
          description: Settings table is not present in the database.
    delete:
      operationId: deleteSiteFeatureSetting
      tags:
        - Features
      summary: Delete site feature setting
      description: Delete feature setting for the site.
      requestBody:
        $ref: '#/components/requestBodies/FeatureDeleteBody'
      responses:
        '200':
          description: Setting value deleted successfully.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/FeatureObject'
        '404':
          description: Feature setting with constraints described in request body not found.
        '425':
          description: Settings table is not present in the database.
components:
  parameters:
    BackendUUID:
      in: path
      name: backend_id
      schema:
        type: string
        format: uuid
        example: eb851641-3791-4d39-82ab-efd2e905333b
      required: true
      description: Backend UUID.
    GroupID:
      in: path
      name: group_id
      schema:
        type: string
        example: user-group-0
      required: true
      description: Group ID.
    SiteUUID:
      in: path
      name: site_id
      schema:
        type: string
        format: uuid
        example: 13d14ee9-37f8-43db-9efb-bfcaa578bc7f
      required: true
      description: Site ID.
    TaskUUID:
      in: path
      name: task_id
      schema:
        type: string
        format: uuid
        example: 4b7057db-6a6d-4c2d-8cf9-7f47d79f5671
      required: true
      description: Task ID.
  requestBodies:
    BackendObjectBody:
      description: Backend definition.
      required: true
      content:
        application/json:
          schema:
            type: object
            properties:
              host:
                description: Host name or IP address.
                type: string
                example: backend.example.com
              status:
                $ref: '#/components/schemas/BackendStatus'
                default: online
    SiteObjectBody:
      description: Site definition.
      required: true
      content:
        application/json:
          schema:
            type: object
            properties:
              name:
                description: Site name.
                type: string
                example: test
              status:
                $ref: '#/components/schemas/SiteStatus'
                default: standby
    FeatureCreateUpdateBody:
      description: Feature definition.
      content:
        application/json:
          schema:
            type: object
            properties:
              name:
                $ref: '#/components/schemas/FeatureName'
              value:
                $ref: '#/components/schemas/FeatureValue'
              backend_id:
                $ref: '#/components/schemas/FeatureBackend'
    FeatureDeleteBody:
      description: Feature definition.
      content:
        application/json:
          schema:
            type: object
            properties:
              name:
                $ref: '#/components/schemas/FeatureName'
              backend_id:
                $ref: '#/components/schemas/FeatureBackend'
  schemas:
    BackendObject:
      type: object
      description: >
        Backend represents a backend object in GeoDB. Each backend belongs
        to a site.
      properties:
        id:
          description: Backend ID.
          type: string
          format: uuid
          example: 849d17b7-4a60-480c-a924-a001f72ad6bf
        site_id:
          description: Site ID.
          type: string
          format: uuid
          example: 13d14ee9-37f8-43db-9efb-bfcaa578bc7f
        host:
          description: Host name or IP address.
          type: string
          example: backend.example.com
        load_factor:
          description: Host load factor.
          type: integer
          example: 100
        last_moved_to:
          description: Last time a group was moved here.
          type: string
          format: date-time
          nullable: true
        last_moved_from:
          description: Last time a group was moved from here.
          type: string
          format: date-time
          nullable: true
        status:
          $ref: '#/components/schemas/BackendStatus'
        status_reason:
          description: The reason why backend is not online.
          type: string
          nullable: true
        assigned_groups:
          description: Estimated number of groups assigned to this backend.
          type: integer
          example: 10
    BackendStatus:
      description: |
        Backend status.

        * `online`: backend is reachable and accepts connections
        * `offline`: backend is offline and does not accept connections
        * `standby`: backend is online but does not accept connections
      type: string
      enum:
        - online
        - offline
        - standby
    FeatureName:
      description: |
        Site feature name.

        * `backend_health_auto_handling`: automatically take backends down or bring them back up based on health data
        * `backend_load_balancing`: automatically move groups between backends to distribute load
        * `metrics_export`: collect dovecot statistics and export them to Prometheus
      enum:
        - backend_health_auto_handling
        - backend_load_balancing
        - metrics_export
    FeatureBackend:
      type: string
      description: |
        Optional backend ID this feature setting applies to. If set
        to 0, means all backends belonging to site (default,
        equivalent to not including this value).
      enum:
        - UUID
        - 0
    FeatureValue:
      description: |
        Site feature setting status.
      enum:
        - yes
        - true
        - dryrun
        - no
        - false
    GroupObject:
      type: object
      description: Represents a user group in GeoDB.
      properties:
        site_id:
          description: Site ID
          type: string
          format: uuid
          example: 13d14ee9-37f8-43db-9efb-bfcaa578bc7f
        backend_id:
          description: Backend ID
          type: string
          format: uuid
          example: 849d17b7-4a60-480c-a924-a001f72ad6bf
        alt_backend_id:
          description: Alternative backend ID (if current is unreachable)
          type: string
          format: uuid
          example: d77e8c21-0474-417c-8858-0137a3b7f9cc
        sticky_users:
          description: If true, users will not be moved from this group.
          type: boolean
          example: false
        sticky_backend:
          description: If true, this group will not be moved from a backend
            during load balancing.
          type: boolean
          example: true
        moving:
          description: |
            Whether this group is being moved to another backend.

            Either `start` or null.
          type: string
          example: start
          nullable: true
        users:
          description: Estimate of number of users in this group.
          type: integer
          example: 1
    SiteObject:
      type: object
      description: Site object that describes a data center in GeoDB. It
        consists of backends, groups and users.
      properties:
        id:
          type: string
          format: uuid
          description: Site ID.
          example: a4e3c214-eec8-4029-8efd-9843d25c60dc
        name:
          type: string
          description: Site name.
          example: test
        tag:
          type: string
          nullable: true
          description: Tag for site.
          example: foo
        status:
          $ref: '#/components/schemas/SiteStatus'
    SiteStatus:
      type: string
      description: |
        Site status.

        * `online`: site is reachable and accepts connections
        * `offline`: site is unreachable
        * `standby`: site is reachable but does not accept connections
      enum:
        - online
        - offline
        - standby
    StatsObject:
      type: object
      description: Represents a single statistic collected from backend.
      properties:
        key:
          description: Name of statistic
          type: string
          example: uptime
        value:
          description: Value of statistic
          type: number
          format: float
          example: 10.0
        type:
          description: |
            Statistic type.

            * `counter`: monotonically increasing value
            * `gauge`: current level for statistic
          type: string
          enum:
            - counter
            - gauge
    TaskObject:
      type: object
      description: Task information.
      properties:
        uuid:
          description: Task ID.
          type: string
          format: uuid
          example: 4b7057db-6a6d-4c2d-8cf9-7f47d79f5671
        name:
          description: Task name.
          type: string
          example: rebalance_sites
        state:
          description: ???
          type: string
          example: SUCCESS
          enum:
            - SUCCESS
        received:
          description: ???
          type: number
          example: 1670491278.2488532
        sent:
          description: ???
          type: number
          nullable: true
        started:
          description: ???
          type: number
          example: 1670491278.2505705
        rejected:
          description: ???
          type: number
          nullable: true
        succeeded:
          description: ???
          type: number
          example: 1670491278.777315
        failed:
          description: ???
          type: number
          nullable: true
        retried:
          description: ???
          type: number
          nullable: true
        revoked:
          description: ???
          type: number
          nullable: true
        args:
          description: ???
          type: string
          example: ()
        kwargs:
          description: ???
          type: string
          example: "{}"
        eta:
          description: ???
          type: string
          nullable: true
        expires:
          description: ???
          type: string
          nullable: true
        retries:
          description: ???
          type: integer
          example: 0
        worker:
          description: ???
          type: string
          example: celery@dev04
        result:
          description: ???
          type: string
          example: None
        exception:
          description: ???
          type: string
          nullable: true
        timestamp:
          description: ???
          type: number
          example: 1670491278.777315
        runtime:
          description: ???
          type: number
          example: 0.5256424229592085
        traceback:
          description: ???
          type: string
          nullable: true
        exchange:
          description: ???
          type: string
          nullable: true
        routing_key:
          description: ???
          type: string
          nullable: true
        clock:
          description: ???
          type: integer
          example: 2371574
        client:
          description: ???
          type: string
          nullable: true
        root:
          description: ??? ID
          type: string
          format: uuid
          example: 4b7057db-6a6d-4c2d-8cf9-7f47d79f5671
        root_id:
          description: ??? ID
          type: string
          format: uuid
          example: 4b7057db-6a6d-4c2d-8cf9-7f47d79f5671
        parent:
          description: ???
          type: string
          nullable: true
        parent_id:
          description: ???
          type: string
          nullable: true
        children:
          description: ???
          type: array
          items:
            description: ???
            type: string
            example: 0c71a067-6683-48df-b13e-832e78e53fa1
    FeatureObject:
      type: object
      description: |
        Represents state of a controller feature within the site or globally
        across all sites.
      properties:
        name:
          description: Name of the feature
          type: string
        site_id:
          description: Site ID this feature is applicable to
          type: string
          format: uuid
        backend_id:
          description: Optional Backend ID (0 means all backends)
          type: string
          format: uuid
        section:
          description: Set to `controller` for all controller features
          type: string
        title:
          description: Title version of the feature name
          type: string
        enabled:
          description: |
            Value flag if feature is active (can be `yes`, `true`,
            `dryrun` ,`no`, `false`)
          type: string
        enabled_verbose:
          description: Human readable active status
          type: string
        exists_in_db:
          description: |
            A flag describing if the feature is explicitly set in the
            database rather than inferred based on presence of the table
          type: string
