{"id":19736842,"url":"https://github.com/travis-ci/job-board","last_synced_at":"2025-04-30T04:32:23.328Z","repository":{"id":2515202,"uuid":"41551067","full_name":"travis-ci/job-board","owner":"travis-ci","description":"have a job? need a job? no jobs? all the jobs! ","archived":false,"fork":false,"pushed_at":"2024-10-30T11:10:28.000Z","size":427,"stargazers_count":9,"open_issues_count":16,"forks_count":9,"subscribers_count":18,"default_branch":"master","last_synced_at":"2024-10-30T12:20:35.695Z","etag":null,"topics":["cats","space-invaders"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/travis-ci.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-08-28T14:14:58.000Z","updated_at":"2024-10-30T11:09:38.000Z","dependencies_parsed_at":"2024-08-29T12:08:13.084Z","dependency_job_id":null,"html_url":"https://github.com/travis-ci/job-board","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/travis-ci%2Fjob-board","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/travis-ci%2Fjob-board/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/travis-ci%2Fjob-board/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/travis-ci%2Fjob-board/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/travis-ci","download_url":"https://codeload.github.com/travis-ci/job-board/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224197929,"owners_count":17271999,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cats","space-invaders"],"created_at":"2024-11-12T01:08:42.309Z","updated_at":"2024-11-12T01:08:42.996Z","avatar_url":"https://github.com/travis-ci.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `job-board`\n\n[![Build Status](https://travis-ci.org/travis-ci/job-board.svg?branch=master\u0026cachebust=1)](https://travis-ci.org/travis-ci/job-board)\n[![Code Climate](https://codeclimate.com/github/travis-ci/job-board/badges/gpa.svg?cachebust=1)](https://codeclimate.com/github/travis-ci/job-board)\n[![Test Coverage](https://codeclimate.com/github/travis-ci/job-board/badges/coverage.svg?cachebust=1)](https://codeclimate.com/github/travis-ci/job-board/coverage)\n\nJob placement for everyone!\n\n**job-board** Is intended to be responsible for job delivery to\n[worker](https://github.com/travis-ci/worker) over HTTP as a replacement for\nRabbitMQ/AMQP.  A separate image-specific API is also provided, which has been\nhistorically used as a test bed for per-job HTTP querying across various worker\ninfrastructures.\n\nFor a detailed explanation of the APIs provided by **job-board**, and the\nbehaviors expected from consumers, please see the [API section](#api) below.\n\n## Status\n\nActively running in production, albeit with mixed levels of API adoption across\ninfrastructures.\n\n## How does it fit into the rest of the system\n\n* **Deployment**: Heroku\n* **scheduler** ([github](https://github.com/travis-ci/travis-scheduler)):\n  scheduler sends job payloads to job-board over HTTP.\n* **worker** ([github](https://github.com/travis-ci/worker)): worker repeatedly\n  sends \"heartbeat\" requests to job-board via HTTP for job delivery and job\nclaim renewal.  Once worker internally begins processing a job, an additional\nHTTP request fetches the full job payload from job-board.  Some of the backend\nproviders in worker may be configured to select images via HTTP queries to\njob-board, although this API is slated for eventual removal as the job delivery\nAPI provides the same data in the job payload.\n* **gcloud-cleanup** ([github](https://github.com/travis-ci/gcloud-cleanup)):\n  gcloud-cleanup queries job-board over HTTP, and deletes images from GCE that\nare no longer registered with job-board.\n\n## API\n\n### Job Delivery API\n\n#### Unique source identifier (`${UNIQUE_ID}`)\n\nThe `${UNIQUE_ID}` string used in the `From:` header is intended to be a unique\nidentifier for purposes of a given Worker Processor communicating with Job\nBoard.  The scope of uniqueness we need is limited to \"one Worker Processor\",\nwhich is used on the Job Board side as a way to track job IDs claimed by Worker\nProcessors.\n\n```\n${UUID}@${PID}.${HOSTNAME}\n```\n\n#### Delivery workflow\n\nEach Worker\n[Processor](https://github.com/travis-ci/worker/blob/9aed935dc3e67df7d4793560d08fc5947982e249/processor.go)\nhas an HTTP Job Queue that is responsible for repeatedly placing `POST /jobs/pop`\nrequests to Job Board for purposes of fetching newly available job ids.\n\n#### `POST /jobs/pop{?queue}`\n\nAs shown below, the `queue` query param is required.\n\n##### Requests\n\nIf the Worker Processor is \"waiting\" or \"new\", such as when first initializing:\n\n```\nPOST /jobs/pop?queue=flah\nContent-Type: application/json\nTravis-Site: ${SITE}\nAuthorization: basic ${BASE64_BASIC_AUTH}\nFrom: ${UNIQUE_ID}\n\n{\n  \"job_id\": \"${JOB_ID}\"\n}\n```\n\n##### Responses\n\nIf there is a job ID available:\n\n```\nHTTP/1.1 200 OK\nContent-Type: application/json\n\n{\n  \"job_id\": \"${JOB_ID}\",\n  \"@queue\": \"${QUEUE}\"\n}\n```\n\nIf the queue param is omitted or empty:\n\n```\nHTTP/1.1 400 Bad Request\nContent-Type: application/json\n\n{\n  \"@type\": \"error\",\n  \"error\": \"missing queue param\"\n}\n```\n\nIf no job IDs are available for claim:\n\n```\nHTTP/1.1 204 No Content\nContent-Type: application/json\n```\n\nIf the `Authorization` header is missing, `401`.\n\nIf the `Authorization` header is invalid, `403`.\n\n#### `POST /jobs/add`\n\nThis resource is intended to be used by\n[scheduler](https://github.com/travis-ci/travis-scheduler) to add to the pool\nof jobs available for delivery.  It is the rough equivalent of scheduler\n\"enqueueing\" a job to RabbitMQ.  The reason why this isn't a `PUT` is because\nthe scheduler representation of a job has significantly less information in it\nthan the representation returned by `GET /jobs/{job_id}`.\n\n##### Request\n\n```\nAuthorization: basic ${BASE64_BASIC_AUTH}\nTravis-Site: ${SITE}\nContent-Type: application/json\n\n{\n  \"@type\": \"job\",\n  \"id\": \"${JOB_ID}\"\n  \"config\": {\n    // … other job representation bits\n  }\n}\n```\n\n##### Responses\n\nIf request is valid and the job does not already exist:\n\n```\nHTTP/1.1 201 Created\nContent-Length: 0\n```\n\nIf the request is valid but the job already exists, the request acts as an\nupdate, and will alter all persisted fields except for the job id:\n\n```\nHTTP/1.1 204 No Content\n```\n\nIf the request is invalid:\n\n```\nHTTP/1.1 400 Bad Request\nContent-Type: application/json\n\n{\n  \"@type\": \"error\",\n  \"error\": \"${ERROR_MESSAGE}\"\n}\n```\n\nIf the `Travis-Site` header is missing, `412`.\n\nIf the `Authorization` header is missing, `401`.\n\nIf the `Authorization` header is invalid, `403`.\n\n#### `POST /jobs/{job_id}/claim`\n\n_TODO_\n\n##### Request\n\n_TODO_\n\n##### Responses\n\n_TODO_\n\n#### `GET /jobs/{job_id}`\n\nThis resource is intended to be used by a given Worker Processor after the\nabove \"heartbeat\" request has succeeded as a way to retrieve the ful job\nrepresentation as needed by Worker, which includes the job script and URI\ntemplates used for communicating with the Job State and Log Parts APIs.\n\n##### Request\n\n```\nTravis-Site: ${SITE}\nTravis-Infrastructure: ${INFRASTRUCTURE}\nAuthorization: basic ${BASE64_BASIC_AUTH}\nFrom: ${UNIQUE_ID}\n```\n\n##### Responses\n\nIf the request and auth are valid:\n\n```\nHTTP/1.1 200 OK\nContent-Type: application/json\n\n{\n  \"@type\": \"job\",\n  \"id\": \"${JOB_ID}\",\n  \"data\": {\n    \"queue\": \"${QUEUE}\",\n    \"config\": {\n      \"os\": \"linux\",\n      \"dist\": \"trusty\",\n      // ... other config bits\n    },\n  },\n  // … other job representation bits\n  \"job_script\": {\n    \"name\": \"main\",\n    \"encoding\": \"base64\",\n    \"content\": \"MjBiZGQzOTk0Mjc...(base64-encoded build.sh)\"\n  },\n  \"job_state_url\": \"${JOB_STATE_URL}\",\n  \"log_parts_url\": \"${LOG_PARTS_URL}\",\n  \"jwt\": \"${JOB_SPECIFIC_JWT}\",\n  \"image_name\": \"${IMAGE_NAME}\"\n}\n```\n\nIf the `Travis-Site` header is missing, `412`.\n\nIf the `Authorization` header is missing, `401`.\n\nIf the `Authorization` header is invalid, `403`.\n\n#### `DELETE /jobs/{job_id}`\n\nThis resource is intended to be used by Worker as a way to mark the job as\n\"completed\" with Job Board.  This is the rough equivalent of what Worker does\nwhen communicating via AMQP by sending an \"ACK\" for the job message.  The\nAuthorization should use the `JWT` that was issued by Job Board specifically\nfor this job.\n\n##### Request\n\n```\nTravis-Site: ${SITE}\nAuthorization: Bearer ${JWT}\nFrom: ${UNIQUE_ID}\n```\n\n##### Responses\n\nIf the request and auth are valid:\n\n```\nHTTP/1.1 204 No Content\n```\n\nIf the `Travis-Site` header is missing, `412`.\n\nIf the `Authorization` header is missing, `401`.\n\nIf the `Authorization` header is invalid, `403`.\n\n### Image API\n\nOne of the things that's queryable in job-board is image names on various\ninfrastructures.\n\n#### `POST /images{?infra,tags,name,is_default}`\n\nCreates an image record.\n\n##### Request\n\n```\nPOST /images?infra=gce\u0026tags=org:true\u0026name=floo-flah-trusty-1438203722\u0026limit=1\nAuthorization: basic ${BASE64_BASIC_AUTH}\n```\n\n##### Responses\n\nIf the request and auth are valid:\n\n```\nHTTP/1.1 201 Created\nContent-Type: application/json\n\n{\n  \"data\": [\n    {\n      \"id\": \"${IMAGE_ID}\"\n      // ... other stuff\n    }\n  ],\n}\n```\n\nIf the request is invalid, `400`.\n\nIf the `Authorization` header is missing, `401`.\n\nIf the `Authorization` header is invalid, `403`.\n\n\n#### `PUT /images{?infra,tags,name,is_default}`\n\nUpdates an image record.\n\n##### Request\n\n```\nPUT /images?infra=gce\u0026tags=production:true,org:true\u0026name=floo-flah-trusty-1438203722\nAuthorization: basic ${BASE64_BASIC_AUTH}\n```\n\n##### Responses\n\nIf the request and auth are valid:\n\n```\nHTTP/1.1 200 OK\nContent-Type: application/json\n\n{\n  \"data\": [\n    {\n      \"id\": \"${IMAGE_ID}\"\n      // ... other stuff\n    }\n  ],\n}\n```\n\nIf the request is invalid, `400`.\n\nIf the `Authorization` header is missing, `401`.\n\nIf the `Authorization` header is invalid, `403`.\n\n#### `GET /images{?infra,tags,name,is_default,limit}`\n\nQuery for an image record.\n\n##### Request\n\n```\nGET /images?infra=gce\u0026tags=production:true,org:true\nAuthorization: basic ${BASE64_BASIC_AUTH}\n```\n\n##### Responses\n\nIf the request and auth are valid:\n\n```\nHTTP/1.1 200 OK\nContent-Type: application/json\n\n{\n  \"data\": [\n    {\n      \"id\": \"${IMAGE_ID}\"\n      // ... other stuff\n    }\n  ],\n}\n```\n\nIf the request is invalid, `400`.\n\nIf the `Authorization` header is missing, `401`.\n\nIf the `Authorization` header is invalid, `403`.\n\n#### `POST /images/search`\n\nPerform the equivalent of multiple `GET\n/images{?infra,tags,name,is_default,limit}` requests within one request via\nnewline-delimited `application/x-www-form-urlencoded` queries.\n\n##### Request\n\n```\nContent-Type: application/x-www-form-urlencoded; boundary=NL\nAuthorization: basic ${BASE64_BASIC_AUTH}\n\ninfra=gce\u0026tags=os:linux,group:stable,language_ruby:true\u0026is_default=false\u0026fields[images]=name\u0026limit=1\ninfra=gce\u0026tags=group:stable,language_ruby:true\u0026is_default=false\u0026fields[images]=name\u0026limit=1\ninfra=gce\u0026tags=language_ruby:true\u0026is_default=false\u0026fields[images]=name\u0026limit=1\ninfra=gce\u0026tags=os:linux\u0026is_default=true\u0026fields[images]=name\u0026limit=1\n```\n\n##### Responses\n\nIf the request and auth are valid, noting that the above example includes\n`fields[images]=name`, which has the potential to reduce the response body size\nsubstantially.\n\n```\nHTTP/1.1 200 OK\nContent-Type: application/json\n\n{\n  \"data\": [\n    {\n      \"name\": \"floo-flah-trusty-1438203722\"\n    }\n  ]\n  \"meta\": {\n    \"limit\": 1,\n    \"matching_query\": {\n      \"infra\": \"gce\",\n      \"tags\": {\n        \"group\": \"stable\",\n        \"language_ruby\": \"true\",\n      },\n      \"is_default\": false,\n      \"limit\": 1\n    }\n  }\n}\n```\n\nIf the request is invalid, `400`.\n\nIf the `Authorization` header is missing, `401`.\n\nIf the `Authorization` header is invalid, `403`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftravis-ci%2Fjob-board","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftravis-ci%2Fjob-board","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftravis-ci%2Fjob-board/lists"}