FlockData#

pydantic model mobu.models.flock.FlockData#

Information about a running flock.

Parameters:

data (Any)

Show JSON schema
{
   "title": "FlockData",
   "description": "Information about a running flock.",
   "type": "object",
   "properties": {
      "name": {
         "examples": [
            "autostart"
         ],
         "title": "Name of the flock",
         "type": "string"
      },
      "config": {
         "$ref": "#/$defs/FlockConfig",
         "title": "Configuration for the flock"
      },
      "monkeys": {
         "items": {
            "$ref": "#/$defs/MonkeyData"
         },
         "title": "Monkeys of the flock",
         "type": "array"
      }
   },
   "$defs": {
      "AuthenticatedUser": {
         "description": "Represents an authenticated user with a token.",
         "properties": {
            "username": {
               "description": "Must start with 'bot-mobu'",
               "examples": [
                  "bot-mobu-testuser"
               ],
               "pattern": "^bot-mobu",
               "title": "Username",
               "type": "string"
            },
            "uidnumber": {
               "anyOf": [
                  {
                     "type": "integer"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": null,
               "description": "If omitted, Gafaelfawr will assign a UID. (Gafaelfawr UID assignment requires Firestore be configured.)",
               "examples": [
                  60001
               ],
               "title": "Numeric UID"
            },
            "gidnumber": {
               "anyOf": [
                  {
                     "type": "integer"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": null,
               "description": "If omitted but a UID was specified, use a GID equal to the UID. If both are omitted, Gafaelfawr will assign a UID and GID. (Gafaelfawr UID and GID assignment requires Firestore and synthetic user private groups to be configured.)",
               "examples": [
                  60001
               ],
               "title": "Primary GID"
            },
            "groups": {
               "default": [],
               "description": "Groups of which the user is a member",
               "items": {
                  "$ref": "#/$defs/Group"
               },
               "title": "Groups",
               "type": "array"
            },
            "scopes": {
               "examples": [
                  [
                     "exec:notebook",
                     "read:tap"
                  ]
               ],
               "items": {
                  "type": "string"
               },
               "title": "Token scopes",
               "type": "array"
            },
            "token": {
               "examples": [
                  "gt-1PhgAeB-9Fsa-N1NhuTu_w.oRvMvAQp1bWfx8KCJKNohg"
               ],
               "title": "Authentication token for user",
               "type": "string"
            }
         },
         "required": [
            "username",
            "scopes",
            "token"
         ],
         "title": "AuthenticatedUser",
         "type": "object"
      },
      "BusinessData": {
         "additionalProperties": false,
         "description": "Status of a running business.\n\nEach type of business with additional data should create a new type\ninheriting from this type and adding that information.",
         "properties": {
            "name": {
               "examples": [
                  "Business"
               ],
               "title": "Type of business",
               "type": "string"
            },
            "failure_count": {
               "examples": [
                  0
               ],
               "title": "Number of failures",
               "type": "integer"
            },
            "success_count": {
               "examples": [
                  25
               ],
               "title": "Number of successes",
               "type": "integer"
            },
            "refreshing": {
               "title": "If the business is currently in the process of refreshing",
               "type": "boolean"
            }
         },
         "required": [
            "name",
            "failure_count",
            "success_count",
            "refreshing"
         ],
         "title": "BusinessData",
         "type": "object"
      },
      "BusinessOptions": {
         "additionalProperties": false,
         "description": "Options for monkey business.",
         "properties": {
            "error_idle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1M",
               "examples": [
                  600
               ],
               "title": "How long to wait after an error before restarting"
            },
            "idle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1M",
               "description": "After each loop executing monkey business, the monkey will pause for this long",
               "examples": [
                  60
               ],
               "title": "How long to wait between business executions"
            },
            "log_level": {
               "$ref": "#/$defs/LogLevel",
               "default": "INFO",
               "title": "Log level for this monkey business"
            }
         },
         "title": "BusinessOptions",
         "type": "object"
      },
      "CollectionRule": {
         "description": "A set of patterns to filter the list of notebooks to run in a repo.",
         "properties": {
            "type": {
               "description": "intersect_union_of will evaluate the intersection of the current collection in the rule evaluation chain with union of all of the patterns in this rule. 'exclude_union_of' will subtract the union of the notebooks found by any pattern in this rule from the current current collection in the rule chain.",
               "enum": [
                  "intersect_union_of",
                  "exclude_union_of"
               ],
               "title": "Collection rule type",
               "type": "string"
            },
            "patterns": {
               "description": "A set of Python pathlib glob patterns: https://docs.python.org/3/library/pathlib.html#pattern-language This rule will gather all of the notebooks matched by any pattern in this list to either intersect or exclude.",
               "items": {
                  "type": "string"
               },
               "title": "patterns",
               "type": "array",
               "uniqueItems": true
            }
         },
         "required": [
            "type",
            "patterns"
         ],
         "title": "CollectionRule",
         "type": "object"
      },
      "EmptyLoopConfig": {
         "additionalProperties": false,
         "description": "Configuration specialization for EmptyLoop.\n\nThis business class does nothing, successfully. It is used primarily for\ntesting mobu.",
         "properties": {
            "type": {
               "const": "EmptyLoop",
               "title": "Type of business to run",
               "type": "string"
            },
            "options": {
               "$ref": "#/$defs/BusinessOptions",
               "title": "Options for the monkey business"
            },
            "restart": {
               "default": false,
               "examples": [
                  true
               ],
               "title": "Restart business after failure",
               "type": "boolean"
            }
         },
         "required": [
            "type"
         ],
         "title": "EmptyLoopConfig",
         "type": "object"
      },
      "FlockConfig": {
         "description": "Configuration for a flock of monkeys.\n\nA flock must all share the same business and options, but may contain any\nnumber of individual monkeys, which will all run as different users.",
         "properties": {
            "name": {
               "examples": [
                  "autostart"
               ],
               "title": "Name of the flock",
               "type": "string"
            },
            "count": {
               "description": "The total number of monkeys to run, split as evenly as possible among all replicas of this Mobu StatefulSet. If this value is 100, and there are 4 replicas, then each replica will run 25 monkeys.",
               "examples": [
                  100
               ],
               "title": "How many monkeys to run",
               "type": "integer"
            },
            "start_batch_size": {
               "anyOf": [
                  {
                     "type": "integer"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": null,
               "description": "The number of monkeys to start in each batch. If not provided, all monkeys will be started at the same time. This is the total number of monkeys to start concurrently across all replicas, so if there are multiple replicas, each replica will only start a fraction of this batch.",
               "title": "Start batch size"
            },
            "start_batch_wait": {
               "anyOf": [
                  {
                     "$ref": "#/$defs/HumanTimedelta"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": null,
               "description": "The amount of time to wait before starting each batch of monkeys. Must be provided if start_batch_size is provided.",
               "title": "Start batch wait"
            },
            "users": {
               "anyOf": [
                  {
                     "items": {
                        "$ref": "#/$defs/User"
                     },
                     "type": "array"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": null,
               "description": "Run as the specific list of users. If specified, the length of the list must equal the count of monkeys to run. Specify either this or user_spec but not both.",
               "title": "Explicit list of users to run as"
            },
            "user_spec": {
               "anyOf": [
                  {
                     "$ref": "#/$defs/UserSpec"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": null,
               "description": "Specify either this or users but not both",
               "title": "Specification to generate users"
            },
            "scopes": {
               "description": "Must include all scopes required to run the business",
               "examples": [
                  [
                     "exec:notebook",
                     "read:tap"
                  ]
               ],
               "items": {
                  "type": "string"
               },
               "title": "Token scopes",
               "type": "array"
            },
            "business": {
               "discriminator": {
                  "mapping": {
                     "EmptyLoop": "#/$defs/EmptyLoopConfig",
                     "GitLFS": "#/$defs/GitLFSConfig",
                     "NotebookRunnerCounting": "#/$defs/NotebookRunnerCountingConfig",
                     "NotebookRunnerInfinite": "#/$defs/NotebookRunnerInfiniteConfig",
                     "NotebookRunnerList": "#/$defs/NotebookRunnerListConfig",
                     "NubladoPythonLoop": "#/$defs/NubladoPythonLoopConfig",
                     "SIAQuerySetRunner": "#/$defs/SIAQuerySetRunnerConfig",
                     "TAPQueryRunner": "#/$defs/TAPQueryRunnerConfig",
                     "TAPQuerySetRunner": "#/$defs/TAPQuerySetRunnerConfig"
                  },
                  "propertyName": "type"
               },
               "oneOf": [
                  {
                     "$ref": "#/$defs/TAPQueryRunnerConfig"
                  },
                  {
                     "$ref": "#/$defs/GitLFSConfig"
                  },
                  {
                     "$ref": "#/$defs/NotebookRunnerCountingConfig"
                  },
                  {
                     "$ref": "#/$defs/NotebookRunnerListConfig"
                  },
                  {
                     "$ref": "#/$defs/NotebookRunnerInfiniteConfig"
                  },
                  {
                     "$ref": "#/$defs/NubladoPythonLoopConfig"
                  },
                  {
                     "$ref": "#/$defs/TAPQuerySetRunnerConfig"
                  },
                  {
                     "$ref": "#/$defs/SIAQuerySetRunnerConfig"
                  },
                  {
                     "$ref": "#/$defs/EmptyLoopConfig"
                  }
               ],
               "title": "Business to run"
            }
         },
         "required": [
            "name",
            "count",
            "scopes",
            "business"
         ],
         "title": "FlockConfig",
         "type": "object"
      },
      "GitLFSBusinessOptions": {
         "additionalProperties": false,
         "description": "Options for business that runs git LFS operations.",
         "properties": {
            "error_idle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1M",
               "examples": [
                  600
               ],
               "title": "How long to wait after an error before restarting"
            },
            "idle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1M",
               "description": "After each loop executing monkey business, the monkey will pause for this long",
               "examples": [
                  60
               ],
               "title": "How long to wait between business executions"
            },
            "log_level": {
               "$ref": "#/$defs/LogLevel",
               "default": "INFO",
               "title": "Log level for this monkey business"
            },
            "lfs_read_url": {
               "title": "LFS read URL for Git-LFS enabled repo",
               "type": "string"
            },
            "lfs_write_url": {
               "title": "LFS write URL for Git-LFS enabled repo",
               "type": "string"
            }
         },
         "required": [
            "lfs_read_url",
            "lfs_write_url"
         ],
         "title": "GitLFSBusinessOptions",
         "type": "object"
      },
      "GitLFSConfig": {
         "additionalProperties": false,
         "description": "Configuration specialization for GitLFS.",
         "properties": {
            "type": {
               "const": "GitLFS",
               "title": "Type of business to run",
               "type": "string"
            },
            "options": {
               "$ref": "#/$defs/GitLFSBusinessOptions",
               "title": "Options for the GitLFS Business"
            },
            "restart": {
               "default": false,
               "examples": [
                  true
               ],
               "title": "Restart business after failure",
               "type": "boolean"
            }
         },
         "required": [
            "type",
            "options"
         ],
         "title": "GitLFSConfig",
         "type": "object"
      },
      "Group": {
         "description": "Configuration for the group memberships of a user.",
         "properties": {
            "name": {
               "examples": [
                  "g_special_users"
               ],
               "minLength": 1,
               "pattern": "^g_",
               "title": "Name of the group",
               "type": "string"
            },
            "id": {
               "examples": [
                  123181
               ],
               "title": "Numeric GID of the group",
               "type": "integer"
            }
         },
         "required": [
            "name",
            "id"
         ],
         "title": "Group",
         "type": "object"
      },
      "HumanTimedelta": {
         "format": "duration",
         "type": "string"
      },
      "LogLevel": {
         "description": "Python logging level.\n\nAny case variation is accepted when converting a string to an enum value\nvia the class constructor.",
         "enum": [
            "DEBUG",
            "INFO",
            "WARNING",
            "ERROR",
            "CRITICAL"
         ],
         "title": "LogLevel",
         "type": "string"
      },
      "MonkeyData": {
         "description": "Data for a running monkey.",
         "properties": {
            "name": {
               "title": "Name of the monkey",
               "type": "string"
            },
            "state": {
               "$ref": "#/$defs/MonkeyState",
               "examples": [
                  "RUNNING"
               ],
               "title": "State of monkey"
            },
            "user": {
               "$ref": "#/$defs/AuthenticatedUser",
               "title": "User as which the monkey is running"
            },
            "business": {
               "anyOf": [
                  {
                     "$ref": "#/$defs/TAPBusinessData"
                  },
                  {
                     "$ref": "#/$defs/SIABusinessData"
                  },
                  {
                     "$ref": "#/$defs/NotebookRunnerData"
                  },
                  {
                     "$ref": "#/$defs/NubladoBusinessData"
                  },
                  {
                     "$ref": "#/$defs/BusinessData"
                  }
               ],
               "title": "Business execution data"
            }
         },
         "required": [
            "name",
            "state",
            "user",
            "business"
         ],
         "title": "MonkeyData",
         "type": "object"
      },
      "MonkeyState": {
         "description": "State of a running monkey.",
         "enum": [
            "IDLE",
            "RUNNING",
            "STOPPING",
            "FINISHED",
            "ERROR"
         ],
         "title": "MonkeyState",
         "type": "string"
      },
      "NotebookRunnerCountingConfig": {
         "additionalProperties": false,
         "description": "Configuration specialization for NotebookRunnerCounting.",
         "properties": {
            "type": {
               "const": "NotebookRunnerCounting",
               "title": "Type of business to run",
               "type": "string"
            },
            "options": {
               "$ref": "#/$defs/NotebookRunnerCountingOptions",
               "title": "Options for the monkey business"
            },
            "restart": {
               "default": false,
               "examples": [
                  true
               ],
               "title": "Restart business after failure",
               "type": "boolean"
            }
         },
         "required": [
            "type"
         ],
         "title": "NotebookRunnerCountingConfig",
         "type": "object"
      },
      "NotebookRunnerCountingOptions": {
         "additionalProperties": false,
         "description": "Options to specify a fixed number of notebooks to run per session.",
         "properties": {
            "exclude_dirs": {
               "default": [],
               "description": "DEPRECATED: use collection_rules instead. If both exclude_dirs and collection_rules are set, exclude_dirs will be added as exclude rules to the collection_rules. These directories are relative to the repo root. Any notebooks in child directories of these directories will also be excluded. Only used by the NotebookRunner businesses.",
               "examples": [
                  "some-dir",
                  "some-dir/some-other-dir"
               ],
               "items": {
                  "format": "path",
                  "type": "string"
               },
               "title": "Any notebooks in these directories will not be run",
               "type": "array",
               "uniqueItems": true
            },
            "collection_rules": {
               "default": [],
               "description": "A set of rules describing which notebooks in a repo to run. Only used by NotebookRunner businesses.",
               "items": {
                  "$ref": "#/$defs/CollectionRule"
               },
               "title": "Collection rules",
               "type": "array"
            },
            "error_idle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1M",
               "examples": [
                  600
               ],
               "title": "How long to wait after an error before restarting"
            },
            "idle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1M",
               "description": "After each loop executing monkey business, the monkey will pause for this long",
               "examples": [
                  60
               ],
               "title": "How long to wait between business executions"
            },
            "log_level": {
               "$ref": "#/$defs/LogLevel",
               "default": "INFO",
               "title": "Log level for this monkey business"
            },
            "delete_lab": {
               "default": true,
               "description": "By default, the lab is deleted and recreated after each iteration of monkey business. Set this to false to keep the same lab.",
               "examples": [
                  true
               ],
               "title": "Whether to delete the lab between iterations",
               "type": "boolean"
            },
            "delete_timeout": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1M",
               "examples": [
                  60
               ],
               "title": "Timeout for deleting a lab"
            },
            "execution_idle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1S",
               "description": "Used by NubladoPythonLoop and NotebookRunner",
               "examples": [
                  1
               ],
               "title": "How long to wait between cell executions"
            },
            "get_node": {
               "default": true,
               "description": "Used by NubladoPythonLoop and its subclasses. Requires the lab have lsst.rsp pre-installed.",
               "title": "Whether to get the node name for error reporting",
               "type": "boolean"
            },
            "image": {
               "anyOf": [
                  {
                     "$ref": "#/$defs/NubladoImageByClass"
                  },
                  {
                     "$ref": "#/$defs/NubladoImageByReference"
                  },
                  {
                     "$ref": "#/$defs/NubladoImageByTag"
                  }
               ],
               "title": "Nublado lab image to use"
            },
            "jitter": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT0S",
               "description": "If set to a non-zero value, pause for a random interval between 0 and that interval before logging in to JupyterHub, and between each iteration of the core execution loop. Use this when running lots of monkeys for load testing to spread their execution sequence out more realistically and avoid a thundering herd problem.",
               "examples": [
                  60
               ],
               "title": "Maximum random time to pause"
            },
            "jupyter_timeout": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1M",
               "description": "Used as the connect, read, and write timeout for talking to either JupyterHub or Jupyter lab.",
               "title": "HTTP client timeout for Jupyter requests"
            },
            "max_websocket_message_size": {
               "anyOf": [
                  {
                     "type": "integer"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": 10485760,
               "description": "This has to be large enough to hold HTML and image output from executing notebook cells, even though we discard that data. Set to ``null`` for no limit.",
               "title": "Maximum length of WebSocket message (in bytes)"
            },
            "spawn_settle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT10S",
               "description": "Wait this long after triggering a lab spawn before starting to poll its progress. KubeSpawner 1.1.0 has a bug where progress queries prior to starting the spawn will fail with an exception that closes the progress EventStream.",
               "examples": [
                  10
               ],
               "title": "How long to wait before polling spawn progress"
            },
            "spawn_timeout": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT10M10S",
               "examples": [
                  610
               ],
               "title": "Timeout for spawning a lab"
            },
            "url_prefix": {
               "default": "/nb/",
               "title": "URL prefix for JupyterHub",
               "type": "string"
            },
            "working_directory": {
               "anyOf": [
                  {
                     "type": "string"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": null,
               "examples": [
                  "notebooks/tutorial-notebooks"
               ],
               "title": "Working directory when running code"
            },
            "repo_ref": {
               "default": "prod",
               "description": "Only used by the NotebookRunner",
               "examples": [
                  "main",
                  "03cd564dd2025bf17054d9ebfeeb5c5a266e3484"
               ],
               "title": "Git ref of notebook repository to execute",
               "type": "string"
            },
            "repo_url": {
               "default": "https://github.com/lsst-sqre/notebook-demo.git",
               "description": "Only used by the NotebookRunner",
               "title": "Git URL of notebook repository to execute",
               "type": "string"
            },
            "notebook_idle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT0S",
               "description": "Used by NotebookRunner businesses",
               "examples": [
                  "30s"
               ],
               "title": "How long to wait between notebook executions"
            },
            "max_executions": {
               "default": 25,
               "description": " NotebookRunnerCounting goes through the directory of notebooks one-by-one, running the entirety of each one and starting again at the beginning of the list when it runs out, until it has executed a total of `max_executions` notebooks. It then closes the session (and optionally deletes and recreates the lab, controlled by `delete_lab`), and then picks up where it left off.",
               "examples": [
                  25
               ],
               "minimum": 1,
               "title": "How much to execute in a given lab and session",
               "type": "integer"
            }
         },
         "title": "NotebookRunnerCountingOptions",
         "type": "object"
      },
      "NotebookRunnerData": {
         "additionalProperties": false,
         "description": "Status of a running NotebookRunner business.",
         "properties": {
            "name": {
               "examples": [
                  "Business"
               ],
               "title": "Type of business",
               "type": "string"
            },
            "failure_count": {
               "examples": [
                  0
               ],
               "title": "Number of failures",
               "type": "integer"
            },
            "success_count": {
               "examples": [
                  25
               ],
               "title": "Number of successes",
               "type": "integer"
            },
            "refreshing": {
               "title": "If the business is currently in the process of refreshing",
               "type": "boolean"
            },
            "image": {
               "anyOf": [
                  {
                     "$ref": "#/$defs/RunningImage"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": null,
               "description": "Will only be present when there is an active Jupyter lab",
               "title": "Jupyter lab image information"
            },
            "notebook": {
               "anyOf": [
                  {
                     "type": "string"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": null,
               "description": "Will not be present if no notebook is being executed",
               "examples": [
                  "cluster.ipynb"
               ],
               "title": "Name of the currently running notebook"
            },
            "running_code": {
               "anyOf": [
                  {
                     "type": "string"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": null,
               "description": "Will not be present if no code is being executed",
               "examples": [
                  "import json\nprint(json.dumps({\"foo\": \"bar\"})\n"
               ],
               "title": "Currently running code"
            }
         },
         "required": [
            "name",
            "failure_count",
            "success_count",
            "refreshing"
         ],
         "title": "NotebookRunnerData",
         "type": "object"
      },
      "NotebookRunnerInfiniteConfig": {
         "additionalProperties": false,
         "description": "Configuration specialization for NotebookRunner.",
         "properties": {
            "type": {
               "const": "NotebookRunnerInfinite",
               "title": "Type of business to run",
               "type": "string"
            },
            "options": {
               "$ref": "#/$defs/NotebookRunnerOptions",
               "title": "Options for the monkey business"
            },
            "restart": {
               "default": false,
               "examples": [
                  true
               ],
               "title": "Restart business after failure",
               "type": "boolean"
            }
         },
         "required": [
            "type"
         ],
         "title": "NotebookRunnerInfiniteConfig",
         "type": "object"
      },
      "NotebookRunnerListConfig": {
         "additionalProperties": false,
         "description": "Configuration specialization for NotebookRunnerList.",
         "properties": {
            "type": {
               "const": "NotebookRunnerList",
               "title": "Type of business to run",
               "type": "string"
            },
            "options": {
               "$ref": "#/$defs/NotebookRunnerOptions",
               "title": "Options for the monkey business"
            },
            "restart": {
               "default": false,
               "examples": [
                  true
               ],
               "title": "Restart business after failure",
               "type": "boolean"
            }
         },
         "required": [
            "type"
         ],
         "title": "NotebookRunnerListConfig",
         "type": "object"
      },
      "NotebookRunnerOptions": {
         "additionalProperties": false,
         "description": "Options for all types NotebookRunner monkey business.",
         "properties": {
            "exclude_dirs": {
               "default": [],
               "description": "DEPRECATED: use collection_rules instead. If both exclude_dirs and collection_rules are set, exclude_dirs will be added as exclude rules to the collection_rules. These directories are relative to the repo root. Any notebooks in child directories of these directories will also be excluded. Only used by the NotebookRunner businesses.",
               "examples": [
                  "some-dir",
                  "some-dir/some-other-dir"
               ],
               "items": {
                  "format": "path",
                  "type": "string"
               },
               "title": "Any notebooks in these directories will not be run",
               "type": "array",
               "uniqueItems": true
            },
            "collection_rules": {
               "default": [],
               "description": "A set of rules describing which notebooks in a repo to run. Only used by NotebookRunner businesses.",
               "items": {
                  "$ref": "#/$defs/CollectionRule"
               },
               "title": "Collection rules",
               "type": "array"
            },
            "error_idle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1M",
               "examples": [
                  600
               ],
               "title": "How long to wait after an error before restarting"
            },
            "idle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1M",
               "description": "After each loop executing monkey business, the monkey will pause for this long",
               "examples": [
                  60
               ],
               "title": "How long to wait between business executions"
            },
            "log_level": {
               "$ref": "#/$defs/LogLevel",
               "default": "INFO",
               "title": "Log level for this monkey business"
            },
            "delete_lab": {
               "default": true,
               "description": "By default, the lab is deleted and recreated after each iteration of monkey business. Set this to false to keep the same lab.",
               "examples": [
                  true
               ],
               "title": "Whether to delete the lab between iterations",
               "type": "boolean"
            },
            "delete_timeout": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1M",
               "examples": [
                  60
               ],
               "title": "Timeout for deleting a lab"
            },
            "execution_idle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1S",
               "description": "Used by NubladoPythonLoop and NotebookRunner",
               "examples": [
                  1
               ],
               "title": "How long to wait between cell executions"
            },
            "get_node": {
               "default": true,
               "description": "Used by NubladoPythonLoop and its subclasses. Requires the lab have lsst.rsp pre-installed.",
               "title": "Whether to get the node name for error reporting",
               "type": "boolean"
            },
            "image": {
               "anyOf": [
                  {
                     "$ref": "#/$defs/NubladoImageByClass"
                  },
                  {
                     "$ref": "#/$defs/NubladoImageByReference"
                  },
                  {
                     "$ref": "#/$defs/NubladoImageByTag"
                  }
               ],
               "title": "Nublado lab image to use"
            },
            "jitter": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT0S",
               "description": "If set to a non-zero value, pause for a random interval between 0 and that interval before logging in to JupyterHub, and between each iteration of the core execution loop. Use this when running lots of monkeys for load testing to spread their execution sequence out more realistically and avoid a thundering herd problem.",
               "examples": [
                  60
               ],
               "title": "Maximum random time to pause"
            },
            "jupyter_timeout": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1M",
               "description": "Used as the connect, read, and write timeout for talking to either JupyterHub or Jupyter lab.",
               "title": "HTTP client timeout for Jupyter requests"
            },
            "max_websocket_message_size": {
               "anyOf": [
                  {
                     "type": "integer"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": 10485760,
               "description": "This has to be large enough to hold HTML and image output from executing notebook cells, even though we discard that data. Set to ``null`` for no limit.",
               "title": "Maximum length of WebSocket message (in bytes)"
            },
            "spawn_settle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT10S",
               "description": "Wait this long after triggering a lab spawn before starting to poll its progress. KubeSpawner 1.1.0 has a bug where progress queries prior to starting the spawn will fail with an exception that closes the progress EventStream.",
               "examples": [
                  10
               ],
               "title": "How long to wait before polling spawn progress"
            },
            "spawn_timeout": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT10M10S",
               "examples": [
                  610
               ],
               "title": "Timeout for spawning a lab"
            },
            "url_prefix": {
               "default": "/nb/",
               "title": "URL prefix for JupyterHub",
               "type": "string"
            },
            "working_directory": {
               "anyOf": [
                  {
                     "type": "string"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": null,
               "examples": [
                  "notebooks/tutorial-notebooks"
               ],
               "title": "Working directory when running code"
            },
            "repo_ref": {
               "default": "prod",
               "description": "Only used by the NotebookRunner",
               "examples": [
                  "main",
                  "03cd564dd2025bf17054d9ebfeeb5c5a266e3484"
               ],
               "title": "Git ref of notebook repository to execute",
               "type": "string"
            },
            "repo_url": {
               "default": "https://github.com/lsst-sqre/notebook-demo.git",
               "description": "Only used by the NotebookRunner",
               "title": "Git URL of notebook repository to execute",
               "type": "string"
            },
            "notebook_idle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT0S",
               "description": "Used by NotebookRunner businesses",
               "examples": [
                  "30s"
               ],
               "title": "How long to wait between notebook executions"
            }
         },
         "title": "NotebookRunnerOptions",
         "type": "object"
      },
      "NubladoBusinessData": {
         "additionalProperties": false,
         "description": "Status of a running Nublado business.",
         "properties": {
            "name": {
               "examples": [
                  "Business"
               ],
               "title": "Type of business",
               "type": "string"
            },
            "failure_count": {
               "examples": [
                  0
               ],
               "title": "Number of failures",
               "type": "integer"
            },
            "success_count": {
               "examples": [
                  25
               ],
               "title": "Number of successes",
               "type": "integer"
            },
            "refreshing": {
               "title": "If the business is currently in the process of refreshing",
               "type": "boolean"
            },
            "image": {
               "anyOf": [
                  {
                     "$ref": "#/$defs/RunningImage"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": null,
               "description": "Will only be present when there is an active Jupyter lab",
               "title": "Jupyter lab image information"
            }
         },
         "required": [
            "name",
            "failure_count",
            "success_count",
            "refreshing"
         ],
         "title": "NubladoBusinessData",
         "type": "object"
      },
      "NubladoImageByClass": {
         "description": "Spawn the recommended image.",
         "properties": {
            "image_class": {
               "default": "recommended",
               "enum": [
                  "recommended",
                  "latest-release",
                  "latest-weekly",
                  "latest-daily"
               ],
               "title": "Class of image to spawn",
               "type": "string"
            },
            "size": {
               "$ref": "#/$defs/NubladoImageSize",
               "default": "Large",
               "description": "Must be one of the sizes understood by Nublado.",
               "title": "Size of image to spawn"
            },
            "description": {
               "default": "",
               "title": "Human-readable image description",
               "type": "string"
            },
            "debug": {
               "default": false,
               "title": "Whether to enable lab debugging",
               "type": "boolean"
            }
         },
         "title": "NubladoImageByClass",
         "type": "object"
      },
      "NubladoImageByReference": {
         "description": "Spawn an image by full Docker reference.",
         "properties": {
            "image_class": {
               "const": "by-reference",
               "default": "by-reference",
               "title": "Class of image to spawn",
               "type": "string"
            },
            "size": {
               "$ref": "#/$defs/NubladoImageSize",
               "default": "Large",
               "description": "Must be one of the sizes understood by Nublado.",
               "title": "Size of image to spawn"
            },
            "description": {
               "default": "",
               "title": "Human-readable image description",
               "type": "string"
            },
            "debug": {
               "default": false,
               "title": "Whether to enable lab debugging",
               "type": "boolean"
            },
            "reference": {
               "title": "Docker reference of lab image to spawn",
               "type": "string"
            }
         },
         "required": [
            "reference"
         ],
         "title": "NubladoImageByReference",
         "type": "object"
      },
      "NubladoImageByTag": {
         "description": "Spawn an image by image tag.",
         "properties": {
            "image_class": {
               "const": "by-tag",
               "default": "by-tag",
               "title": "Class of image to spawn",
               "type": "string"
            },
            "size": {
               "$ref": "#/$defs/NubladoImageSize",
               "default": "Large",
               "description": "Must be one of the sizes understood by Nublado.",
               "title": "Size of image to spawn"
            },
            "description": {
               "default": "",
               "title": "Human-readable image description",
               "type": "string"
            },
            "debug": {
               "default": false,
               "title": "Whether to enable lab debugging",
               "type": "boolean"
            },
            "tag": {
               "title": "Tag of image to spawn",
               "type": "string"
            }
         },
         "required": [
            "tag"
         ],
         "title": "NubladoImageByTag",
         "type": "object"
      },
      "NubladoImageSize": {
         "description": "Acceptable sizes of images to spawn.",
         "enum": [
            "Fine",
            "Diminutive",
            "Tiny",
            "Small",
            "Medium",
            "Large",
            "Huge",
            "Gargantuan",
            "Colossal"
         ],
         "title": "NubladoImageSize",
         "type": "string"
      },
      "NubladoPythonLoopConfig": {
         "additionalProperties": false,
         "description": "Configuration specialization for NubladoPythonLoop.",
         "properties": {
            "type": {
               "const": "NubladoPythonLoop",
               "title": "Type of business to run",
               "type": "string"
            },
            "options": {
               "$ref": "#/$defs/NubladoPythonLoopOptions",
               "title": "Options for the monkey business"
            },
            "restart": {
               "default": false,
               "examples": [
                  true
               ],
               "title": "Restart business after failure",
               "type": "boolean"
            }
         },
         "required": [
            "type"
         ],
         "title": "NubladoPythonLoopConfig",
         "type": "object"
      },
      "NubladoPythonLoopOptions": {
         "additionalProperties": false,
         "description": "Options for NubladoPythonLoop monkey business.",
         "properties": {
            "error_idle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1M",
               "examples": [
                  600
               ],
               "title": "How long to wait after an error before restarting"
            },
            "idle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1M",
               "description": "After each loop executing monkey business, the monkey will pause for this long",
               "examples": [
                  60
               ],
               "title": "How long to wait between business executions"
            },
            "log_level": {
               "$ref": "#/$defs/LogLevel",
               "default": "INFO",
               "title": "Log level for this monkey business"
            },
            "delete_lab": {
               "default": true,
               "description": "By default, the lab is deleted and recreated after each iteration of monkey business. Set this to false to keep the same lab.",
               "examples": [
                  true
               ],
               "title": "Whether to delete the lab between iterations",
               "type": "boolean"
            },
            "delete_timeout": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1M",
               "examples": [
                  60
               ],
               "title": "Timeout for deleting a lab"
            },
            "execution_idle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1S",
               "description": "Used by NubladoPythonLoop and NotebookRunner",
               "examples": [
                  1
               ],
               "title": "How long to wait between cell executions"
            },
            "get_node": {
               "default": true,
               "description": "Used by NubladoPythonLoop and its subclasses. Requires the lab have lsst.rsp pre-installed.",
               "title": "Whether to get the node name for error reporting",
               "type": "boolean"
            },
            "image": {
               "anyOf": [
                  {
                     "$ref": "#/$defs/NubladoImageByClass"
                  },
                  {
                     "$ref": "#/$defs/NubladoImageByReference"
                  },
                  {
                     "$ref": "#/$defs/NubladoImageByTag"
                  }
               ],
               "title": "Nublado lab image to use"
            },
            "jitter": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT0S",
               "description": "If set to a non-zero value, pause for a random interval between 0 and that interval before logging in to JupyterHub, and between each iteration of the core execution loop. Use this when running lots of monkeys for load testing to spread their execution sequence out more realistically and avoid a thundering herd problem.",
               "examples": [
                  60
               ],
               "title": "Maximum random time to pause"
            },
            "jupyter_timeout": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1M",
               "description": "Used as the connect, read, and write timeout for talking to either JupyterHub or Jupyter lab.",
               "title": "HTTP client timeout for Jupyter requests"
            },
            "max_websocket_message_size": {
               "anyOf": [
                  {
                     "type": "integer"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": 10485760,
               "description": "This has to be large enough to hold HTML and image output from executing notebook cells, even though we discard that data. Set to ``null`` for no limit.",
               "title": "Maximum length of WebSocket message (in bytes)"
            },
            "spawn_settle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT10S",
               "description": "Wait this long after triggering a lab spawn before starting to poll its progress. KubeSpawner 1.1.0 has a bug where progress queries prior to starting the spawn will fail with an exception that closes the progress EventStream.",
               "examples": [
                  10
               ],
               "title": "How long to wait before polling spawn progress"
            },
            "spawn_timeout": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT10M10S",
               "examples": [
                  610
               ],
               "title": "Timeout for spawning a lab"
            },
            "url_prefix": {
               "default": "/nb/",
               "title": "URL prefix for JupyterHub",
               "type": "string"
            },
            "working_directory": {
               "anyOf": [
                  {
                     "type": "string"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": null,
               "examples": [
                  "notebooks/tutorial-notebooks"
               ],
               "title": "Working directory when running code"
            },
            "code": {
               "default": "print(2+2, end=\"\")",
               "examples": [
                  "print(2+2, end=\"\")"
               ],
               "title": "Python code to execute",
               "type": "string"
            },
            "max_executions": {
               "default": 25,
               "description": "The number of code snippets to execute before restarting the lab.",
               "examples": [
                  25
               ],
               "minimum": 1,
               "title": "How much to execute in a given lab and session",
               "type": "integer"
            }
         },
         "title": "NubladoPythonLoopOptions",
         "type": "object"
      },
      "RunningImage": {
         "description": "Information about the running Jupyter lab image.",
         "properties": {
            "reference": {
               "anyOf": [
                  {
                     "type": "string"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": null,
               "title": "Docker reference for the image"
            },
            "description": {
               "anyOf": [
                  {
                     "type": "string"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": null,
               "title": "Human-readable description of the image"
            }
         },
         "title": "RunningImage",
         "type": "object"
      },
      "SIABusinessData": {
         "additionalProperties": false,
         "description": "Status of a running SIA business.",
         "properties": {
            "name": {
               "examples": [
                  "Business"
               ],
               "title": "Type of business",
               "type": "string"
            },
            "failure_count": {
               "examples": [
                  0
               ],
               "title": "Number of failures",
               "type": "integer"
            },
            "success_count": {
               "examples": [
                  25
               ],
               "title": "Number of successes",
               "type": "integer"
            },
            "refreshing": {
               "title": "If the business is currently in the process of refreshing",
               "type": "boolean"
            },
            "running_query": {
               "anyOf": [
                  {
                     "$ref": "#/$defs/SIAQuery"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": null,
               "description": "Will not be present if no query is being executed",
               "title": "Currently running query"
            }
         },
         "required": [
            "name",
            "failure_count",
            "success_count",
            "refreshing"
         ],
         "title": "SIABusinessData",
         "type": "object"
      },
      "SIAQuery": {
         "description": "The parameters of an SIA (v2) query.",
         "properties": {
            "ra": {
               "title": "Ra",
               "type": "number"
            },
            "dec": {
               "title": "Dec",
               "type": "number"
            },
            "radius": {
               "title": "Radius",
               "type": "number"
            },
            "time": {
               "items": {
                  "type": "number"
               },
               "title": "Time",
               "type": "array"
            }
         },
         "required": [
            "ra",
            "dec",
            "radius",
            "time"
         ],
         "title": "SIAQuery",
         "type": "object"
      },
      "SIAQuerySetRunnerConfig": {
         "additionalProperties": false,
         "description": "Configuration specialization for SIAQuerySetRunner.",
         "properties": {
            "type": {
               "const": "SIAQuerySetRunner",
               "title": "Type of business to run",
               "type": "string"
            },
            "options": {
               "$ref": "#/$defs/SIAQuerySetRunnerOptions",
               "title": "Options for the monkey business"
            },
            "restart": {
               "default": false,
               "examples": [
                  true
               ],
               "title": "Restart business after failure",
               "type": "boolean"
            }
         },
         "required": [
            "type"
         ],
         "title": "SIAQuerySetRunnerConfig",
         "type": "object"
      },
      "SIAQuerySetRunnerOptions": {
         "additionalProperties": false,
         "description": "Options for SIAQuerySetRunner monkey business.",
         "properties": {
            "error_idle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1M",
               "examples": [
                  600
               ],
               "title": "How long to wait after an error before restarting"
            },
            "idle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1M",
               "description": "After each loop executing monkey business, the monkey will pause for this long",
               "examples": [
                  60
               ],
               "title": "How long to wait between business executions"
            },
            "log_level": {
               "$ref": "#/$defs/LogLevel",
               "default": "INFO",
               "title": "Log level for this monkey business"
            },
            "query_set": {
               "default": "dp02",
               "examples": [
                  "dp02"
               ],
               "title": "Which query template set to use for a SIAQuerySetRunner",
               "type": "string"
            }
         },
         "title": "SIAQuerySetRunnerOptions",
         "type": "object"
      },
      "TAPBusinessData": {
         "additionalProperties": false,
         "description": "Status of a running TAPQueryRunner business.",
         "properties": {
            "name": {
               "examples": [
                  "Business"
               ],
               "title": "Type of business",
               "type": "string"
            },
            "failure_count": {
               "examples": [
                  0
               ],
               "title": "Number of failures",
               "type": "integer"
            },
            "success_count": {
               "examples": [
                  25
               ],
               "title": "Number of successes",
               "type": "integer"
            },
            "refreshing": {
               "title": "If the business is currently in the process of refreshing",
               "type": "boolean"
            },
            "running_query": {
               "anyOf": [
                  {
                     "type": "string"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": null,
               "description": "Will not be present if no query is being executed",
               "title": "Currently running query"
            }
         },
         "required": [
            "name",
            "failure_count",
            "success_count",
            "refreshing"
         ],
         "title": "TAPBusinessData",
         "type": "object"
      },
      "TAPQueryRunnerConfig": {
         "additionalProperties": false,
         "description": "Configuration specialization for TAPQueryRunner.",
         "properties": {
            "type": {
               "const": "TAPQueryRunner",
               "title": "Type of business to run",
               "type": "string"
            },
            "options": {
               "$ref": "#/$defs/TAPQueryRunnerOptions",
               "title": "Options for the monkey business"
            },
            "restart": {
               "default": false,
               "examples": [
                  true
               ],
               "title": "Restart business after failure",
               "type": "boolean"
            }
         },
         "required": [
            "type",
            "options"
         ],
         "title": "TAPQueryRunnerConfig",
         "type": "object"
      },
      "TAPQueryRunnerOptions": {
         "additionalProperties": false,
         "description": "Options for TAPQueryRunner monkey business.",
         "properties": {
            "error_idle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1M",
               "examples": [
                  600
               ],
               "title": "How long to wait after an error before restarting"
            },
            "idle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1M",
               "description": "After each loop executing monkey business, the monkey will pause for this long",
               "examples": [
                  60
               ],
               "title": "How long to wait between business executions"
            },
            "log_level": {
               "$ref": "#/$defs/LogLevel",
               "default": "INFO",
               "title": "Log level for this monkey business"
            },
            "sync": {
               "default": true,
               "description": "By default, queries to TAP are run via the sync endpoint. Set this to false to run as an async query.",
               "examples": [
                  true
               ],
               "title": "Whether to run TAP queries as sync or async",
               "type": "boolean"
            },
            "queries": {
               "description": "List of queries to be run",
               "examples": [
                  [
                     "SELECT TOP 10 * FROM TAP_SCHEMA.schemas",
                     "SELECT TOP 10 * FROM MYDB.MyTable"
                  ]
               ],
               "items": {
                  "type": "string"
               },
               "title": "TAP queries",
               "type": "array"
            }
         },
         "required": [
            "queries"
         ],
         "title": "TAPQueryRunnerOptions",
         "type": "object"
      },
      "TAPQuerySetRunnerConfig": {
         "additionalProperties": false,
         "description": "Configuration specialization for TAPQuerySetRunner.",
         "properties": {
            "type": {
               "const": "TAPQuerySetRunner",
               "title": "Type of business to run",
               "type": "string"
            },
            "options": {
               "$ref": "#/$defs/TAPQuerySetRunnerOptions",
               "title": "Options for the monkey business"
            },
            "restart": {
               "default": false,
               "examples": [
                  true
               ],
               "title": "Restart business after failure",
               "type": "boolean"
            }
         },
         "required": [
            "type"
         ],
         "title": "TAPQuerySetRunnerConfig",
         "type": "object"
      },
      "TAPQuerySetRunnerOptions": {
         "additionalProperties": false,
         "description": "Options for TAPQueryRunner monkey business.",
         "properties": {
            "error_idle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1M",
               "examples": [
                  600
               ],
               "title": "How long to wait after an error before restarting"
            },
            "idle_time": {
               "$ref": "#/$defs/HumanTimedelta",
               "default": "PT1M",
               "description": "After each loop executing monkey business, the monkey will pause for this long",
               "examples": [
                  60
               ],
               "title": "How long to wait between business executions"
            },
            "log_level": {
               "$ref": "#/$defs/LogLevel",
               "default": "INFO",
               "title": "Log level for this monkey business"
            },
            "sync": {
               "default": true,
               "description": "By default, queries to TAP are run via the sync endpoint. Set this to false to run as an async query.",
               "examples": [
                  true
               ],
               "title": "Whether to run TAP queries as sync or async",
               "type": "boolean"
            },
            "query_set": {
               "default": "dp0.1",
               "examples": [
                  "dp0.2"
               ],
               "title": "Which query template set to use for a TapQueryRunner",
               "type": "string"
            }
         },
         "title": "TAPQuerySetRunnerOptions",
         "type": "object"
      },
      "User": {
         "description": "Configuration for the user whose credentials the monkey will use.",
         "properties": {
            "username": {
               "description": "Must start with 'bot-mobu'",
               "examples": [
                  "bot-mobu-testuser"
               ],
               "pattern": "^bot-mobu",
               "title": "Username",
               "type": "string"
            },
            "uidnumber": {
               "anyOf": [
                  {
                     "type": "integer"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": null,
               "description": "If omitted, Gafaelfawr will assign a UID. (Gafaelfawr UID assignment requires Firestore be configured.)",
               "examples": [
                  60001
               ],
               "title": "Numeric UID"
            },
            "gidnumber": {
               "anyOf": [
                  {
                     "type": "integer"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": null,
               "description": "If omitted but a UID was specified, use a GID equal to the UID. If both are omitted, Gafaelfawr will assign a UID and GID. (Gafaelfawr UID and GID assignment requires Firestore and synthetic user private groups to be configured.)",
               "examples": [
                  60001
               ],
               "title": "Primary GID"
            },
            "groups": {
               "default": [],
               "description": "Groups of which the user is a member",
               "items": {
                  "$ref": "#/$defs/Group"
               },
               "title": "Groups",
               "type": "array"
            }
         },
         "required": [
            "username"
         ],
         "title": "User",
         "type": "object"
      },
      "UserSpec": {
         "description": "Configuration to generate a set of users.",
         "properties": {
            "username_prefix": {
               "description": "Each user will be formed by appending a number to this",
               "examples": [
                  "bot-mobu-lsptestuser"
               ],
               "title": "Prefix for usernames",
               "type": "string"
            },
            "uid_start": {
               "anyOf": [
                  {
                     "type": "integer"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": null,
               "description": "Users will be given consecutive UIDs starting with this. If omitted, Gafaelfawr will assign UIDs. (Gafaelfawr UID assignment requires Firestore be configured.)",
               "examples": [
                  60000
               ],
               "title": "Starting UID"
            },
            "gid_start": {
               "anyOf": [
                  {
                     "type": "integer"
                  },
                  {
                     "type": "null"
                  }
               ],
               "default": null,
               "description": "Users will be given consecutive primary GIDs starting with this. If omitted but UIDs were given, the GIDs will be equal to the UIDs. If both are omitted, Gafaelfawr will assign UIDs and GIDs (which requires Firestore and synthetic user private groups to be configured).",
               "examples": [
                  60000
               ],
               "title": "Starting GID"
            },
            "groups": {
               "default": [],
               "description": "Groups of which each user is a member",
               "items": {
                  "$ref": "#/$defs/Group"
               },
               "title": "Groups",
               "type": "array"
            }
         },
         "required": [
            "username_prefix"
         ],
         "title": "UserSpec",
         "type": "object"
      }
   },
   "required": [
      "name",
      "config",
      "monkeys"
   ]
}

Fields:
field config: FlockConfig [Required]#
field monkeys: list[MonkeyData] [Required]#
field name: str [Required]#