{"id":17316668,"url":"https://github.com/mlowicki/rhythm","last_synced_at":"2025-03-22T19:33:00.728Z","repository":{"id":57480977,"uuid":"143887242","full_name":"mlowicki/rhythm","owner":"mlowicki","description":"Time-based job scheduler for Apache Mesos","archived":false,"fork":false,"pushed_at":"2019-07-31T22:28:37.000Z","size":610,"stargazers_count":29,"open_issues_count":5,"forks_count":2,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-03-18T14:05:09.314Z","etag":null,"topics":["cron","docker","gitlab","golang","hashicorp-vault","mesos","scheduler","sentry"],"latest_commit_sha":null,"homepage":null,"language":"Go","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/mlowicki.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}},"created_at":"2018-08-07T14:44:31.000Z","updated_at":"2023-03-17T14:40:15.000Z","dependencies_parsed_at":"2022-09-26T17:41:26.223Z","dependency_job_id":null,"html_url":"https://github.com/mlowicki/rhythm","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlowicki%2Frhythm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlowicki%2Frhythm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlowicki%2Frhythm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlowicki%2Frhythm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mlowicki","download_url":"https://codeload.github.com/mlowicki/rhythm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245013709,"owners_count":20547175,"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":["cron","docker","gitlab","golang","hashicorp-vault","mesos","scheduler","sentry"],"created_at":"2024-10-15T13:13:43.732Z","updated_at":"2025-03-22T19:32:59.626Z","avatar_url":"https://github.com/mlowicki.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Rhythm [![Go Report Card](https://goreportcard.com/badge/github.com/mlowicki/rhythm)](https://goreportcard.com/report/github.com/mlowicki/rhythm) [![Travis CI](https://travis-ci.org/mlowicki/rhythm.svg?branch=master)](https://travis-ci.org/mlowicki/rhythm)\n\n## Features\n\n* Support for [Docker](https://mesos.apache.org/documentation/latest/docker-containerizer/) and [Mesos](https://mesos.apache.org/documentation/latest/mesos-containerizer/) Containerizers \n* Integration with [HashiCorp Vault](https://www.vaultproject.io/) for secrets management\n* Access control list (ACL) backed by [GitLab](https://gitlab.com/) or LDAP\n* [Cron syntax](http://www.nncron.ru/help/EN/working/cron-format.htm)\n* Integration with [Sentry](https://sentry.io/) for error tracking\n* Command-line client ([Documentation](#command-line-client))\n* Support for Linux and OSX\n\n## Life cycle of a Job\n\n* job - periodic action to perform (e.g. database backup).\n* task - single run (instance) of job. If task fails and job allows for retries then new task will be launched.\n\nJob is always in one of the following states:\n* idle - either job hasn't been scheduled yet or its last run (task) was successful.\n* staging - task (job's run) has been scheduled to run in response to received offer. Job moves to this state in parallel with sending to Mesos request to run task.\n* starting - executor has learned about the task but has not yet started to run it.\n* running - task (job's run) begun running successfully.\n* failed - last job's task aborted with error. Failed job can be retried up-to `maxretries` times.\n\n![job life cycle](docs/job_life_cycle.png)\n\n## Running the server\n\nRhythm server can be started with:\n```\nrhythm server -config=/etc/rhythm/config.json\n```\n\nServer is configured using file in JSON format. By default `config.json` from the current directory is used but it can overwritten using `-config` parameter. List of available configuration options is available [here](docs/server_config.md).\n\nDocumentation for server API is available [here](https://mlowicki.github.io/rhythm/api).\n\n## Command-line client\nRhythm binary besides running in server mode provides also CLI tool. To see the list of available commands use `-help`. There is also [interactive client](#client) with auto-completion.\n\nAddress of Rhythm server is set either via `-addr` flag or `RHYTHM_ADDR` environment variable. If both are set then flag takes precedence.\n\nAuthentication method is set either via `-auth` flag or `RHYTHM_AUTH` environment variable. If both are set then flag takes precedence. Possible values are `gitlab` or `ldap`. No method set means no authentication.\n\nTo not enter GitLab access token every time, use [update-token](#update-token) command.\n\n### health\nProvides basic information about state of the server.\n\nExamples:\n\n```\n$ rhythm health -addr https://example.com\nLeader: true\nVersion: 0.5\nServerTime: Mon Nov 12 20:15:49 CET 2018\n```\n\n```\n$ RHYTHM_ADDR=https://example.com rhythm health\nLeader: true\nVersion: 0.5\nServerTime: Mon Nov 12 23:53:11 CET 2018\n```\n\n### read-job\nShows configuration and state of job with the given fully-qualified ID.\n\nExample:\n```\n$ rhythm read-job -addr https://example.com group/project/id\nState: Idle\n    Last start: Mon Nov 12 20:17:22 CET 2018\nScheduler: Cron\n    Rule: */1 * * * *\n    Next start: Mon Nov 12 20:18:00 CET 2018\nContainer: Docker\n    Image: alpine:3.8\n    Force pull image: false\nCmd: /bin/sh -c 'echo $FOO'\nEnvironment:\n    FOO: foo\nUser: someone\nResources:\n    Memory: 1024.0 MB\n    Disk: 0.0 MB\n    CPUs: 1.0\n```\n\n### read-tasks\nShows tasks (runs) of job with the given fully-qualified ID.\n\nExample:\n```\n$ rhythm read-tasks -addr https://example.com group/project/id\nStatus:         SUCCESS\nStart:          Wed Nov 14 23:38:51 CET 2018\nEnd:            Wed Nov 14 23:38:51 CET 2018\nTask ID:        group:project:id:7eb8d4fa-f133-4880-9840-f8f62d3d06b2\nExecutor ID:    group:project:id:7eb8d4fa-f133-4880-9840-f8f62d3d06b2\nAgent ID:       18fd8e84-9213-4f51-9343-bae8b9c517fe-S0\nFramework ID:   18fd8e84-9213-4f51-9343-bae8b9c517fe-0000\nExecutor URL:   https://example.com:5050/#/agents/18fd8e84-9213-4f51-9343-bae8b9c517fe-S0/frameworks/18fd8e84-9213-4f51-9343-bae8b9c517fe-0000/executors/group:project:id:7eb8d4fa-f133-4880-9840-f8f62d3d06b2\n\nStatus:         FAIL\nStart:          Wed Nov 14 23:41:06 CET 2018\nEnd:            Wed Nov 14 23:41:06 CET 2018\nMessage:        Reading secret failed: Get https://example.com/v1/secret/rhythm/group/project/foo: dial tcp [::1]:8200: connect: connection refused\nReason:         Failed to create task\nSource:         Scheduler\n\nStatus:         FAIL\nStart:          Wed Nov 14 23:43:06 CET 2018\nEnd:            Wed Nov 14 23:43:06 CET 2018\nMessage:        Reading secret failed: Get https://example.com/v1/secret/rhythm/group/project/foo: dial tcp [::1]:8200: connect: connection refused\nReason:         Failed to create task\nSource:         Scheduler\n\nStatus:         FAIL\nStart:          Wed Nov 14 23:51:04 CET 2018\nEnd:            Wed Nov 14 23:51:04 CET 2018\nTask ID:        group:project:id:504d8c5c-f4d1-41f6-8797-c608bee7195b\nExecutor ID:    group:project:id:504d8c5c-f4d1-41f6-8797-c608bee7195b\nAgent ID:       18fd8e84-9213-4f51-9343-bae8b9c517fe-S0\nFramework ID:   18fd8e84-9213-4f51-9343-bae8b9c517fe-0000\nExecutor URL:   https://example.com:5050/#/agents/18fd8e84-9213-4f51-9343-bae8b9c517fe-S0/frameworks/18fd8e84-9213-4f51-9343-bae8b9c517fe-0000/executors/group:project:id:504d8c5c-f4d1-41f6-8797-c608bee7195b\nMessage:        Container exited with status 127\nReason:         REASON_COMMAND_EXECUTOR_FAILED\nSource:         SOURCE_EXECUTOR\n```\n\n### delete-job\nRemove job with the given fully-qualified ID.\n\nExample:\n```\n$ rhythm delete-job -addr https://example.com group/project/id\n```\n\n### create-job\nAdd new job specified by config file.\n\nExample:\n```\n$ rhythm create-job --addr=https://example.com echo.json\n```\n\necho.json:\n```javascript\n{\n    \"id\": \"id\",\n    \"group\": \"group\",\n    \"project\": \"project\",\n    \"cpus\": 1,\n    \"mem\": 1024,\n    \"cmd\": \"echo $FOO\",\n    \"user\": \"someone\",\n    \"env\": {\n        \"FOO\": \"bar\"\n    },\n    \"schedule\": {\n        \"cron\": \"*/1 * * * *\"\n    },\n    \"container\": {\n        \"docker\": {\n            \"image\": \"alpine:3.8\"\n        }\n    }\n}\n```\n\n### update-job\nModify job with config file containing job's parameters to change.\nCan we launched with either one arguments or two. If one argument is set then it must be path to job config file which contains also job's group, project and ID. If two arguments are set then first one must be fully-qualified ID (e.g. \"group/project/id\") and second one path to job config file.\nOnly parameters form config file will be changed - absent parameters wont' be modified.\n\nExamples:\n```\n$ rhythm update-job --addr=https://example.com group/project/id diff.json\n```\n\ndiff.json:\n```javascript\n{\n    \"user\": \"root\"\n}\n```\n\n```\n$ rhythm update-job --addr=https://example.com diff.json\n```\n\ndiff.json:\n```javascript\n{\n    \"group\": \"group\",\n    \"project\": \"project\",\n    \"id\": \"id\",\n    \"user\": \"root\"\n}\n```\n\n### run-job\nSchedule job with the given fully-qualified ID for immediate run.\nIf job is already queued (scheduled but not launched yet) then command will be no-op.\n\nExample:\n```\n$ rhythm run-job -addr https://example.com group/project/id\n```\n\n### find-jobs\nShow IDs of jobs matching FILTER.\n\nFILTER can be one of:\n* GROUP to return all jobs from group\n* GROUP/PROJECT to return all jobs from project\n* no set to return all jobs across all groups and projects\n\nExamples:\n```\n$ rhythm find-jobs --addr=https://example.com\ngroup:project:id Idle\ngroup:project:id2 Idle\ngroup:project2:id Running\ngroup2:project:id Failed\n```\n\n```\n$ rhythm find-jobs --addr=https://example.com group\ngroup:project:id Idle\ngroup:project:id2 Running\ngroup:project2:id Failed\n```\n\n```\n$ rhythm find-jobs --addr=https://example.com group/project\ngroup:project:id Idle\ngroup:project:id2 Idle\n```\n\n## update-token\nUpdate (or set) authz token. Used to save token so subsequent commands requiring authorization won't require to enter token every time.\nBy default it stores token on disk in the `~/.rhythm-token` file but it can be changed via the use of [token helper](./token_helper.md).\n\nExample:\n```\n$ rhythm update-token\nToken:\n```\n\n## client\nStart interactive client. To see list of top-level commands press tab or space key.\n\nExamples:\n```\n$ rhythm client\n```\n\n![](docs/client_demo.gif)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmlowicki%2Frhythm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmlowicki%2Frhythm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmlowicki%2Frhythm/lists"}