├── .dockerignore ├── .gcloudignore ├── .gitignore ├── .rspec ├── .rubocop.yml ├── .travis.yml ├── Appraisals ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── Dockerfile ├── Dockerfile.myapp ├── Gemfile ├── Guardfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin ├── console └── setup ├── cloudbuild.yaml ├── examples ├── docker │ ├── Gemfile │ └── test_worker.rb ├── k8s │ └── service-account.yaml └── myapp │ ├── .gitignore │ ├── Gemfile │ ├── Gemfile.lock │ ├── README.md │ ├── Rakefile │ ├── app │ ├── assets │ │ ├── config │ │ │ └── manifest.js │ │ ├── images │ │ │ └── .keep │ │ ├── javascripts │ │ │ ├── application.js │ │ │ ├── cable.js │ │ │ ├── channels │ │ │ │ └── .keep │ │ │ ├── import_tasks.coffee │ │ │ └── tasks.coffee │ │ └── stylesheets │ │ │ ├── application.css │ │ │ ├── import_tasks.scss │ │ │ ├── scaffolds.scss │ │ │ └── tasks.scss │ ├── channels │ │ └── application_cable │ │ │ ├── channel.rb │ │ │ └── connection.rb │ ├── controllers │ │ ├── application_controller.rb │ │ ├── concerns │ │ │ └── .keep │ │ ├── import_tasks_controller.rb │ │ └── tasks_controller.rb │ ├── helpers │ │ ├── application_helper.rb │ │ ├── import_tasks_helper.rb │ │ └── tasks_helper.rb │ ├── jobs │ │ ├── application_job.rb │ │ ├── archive_done_tasks_job.rb │ │ ├── compute_pi_job.rb │ │ ├── import_task_job.rb │ │ └── print_message_job.rb │ ├── mailers │ │ └── application_mailer.rb │ ├── models │ │ ├── application_record.rb │ │ ├── concerns │ │ │ └── .keep │ │ └── task.rb │ └── views │ │ ├── import_tasks │ │ └── create.html.erb │ │ ├── layouts │ │ ├── application.html.erb │ │ ├── mailer.html.erb │ │ └── mailer.text.erb │ │ └── tasks │ │ ├── _form.html.erb │ │ ├── _import_form.html.erb │ │ ├── _task.json.jbuilder │ │ ├── edit.html.erb │ │ ├── index.html.erb │ │ ├── index.json.jbuilder │ │ ├── new.html.erb │ │ ├── show.html.erb │ │ └── show.json.jbuilder │ ├── bin │ ├── bundle │ ├── rails │ ├── rake │ ├── setup │ ├── spring │ ├── update │ └── yarn │ ├── config.ru │ ├── config │ ├── application.rb │ ├── boot.rb │ ├── cable.yml │ ├── credentials.yml.enc │ ├── database.yml │ ├── environment.rb │ ├── environments │ │ ├── development.rb │ │ ├── production.rb │ │ └── test.rb │ ├── initializers │ │ ├── application_controller_renderer.rb │ │ ├── assets.rb │ │ ├── backtrace_silencers.rb │ │ ├── content_security_policy.rb │ │ ├── cookies_serializer.rb │ │ ├── filter_parameter_logging.rb │ │ ├── inflections.rb │ │ ├── kube_queue.rb │ │ ├── mime_types.rb │ │ └── wrap_parameters.rb │ ├── locales │ │ └── en.yml │ ├── puma.rb │ ├── routes.rb │ ├── spring.rb │ └── storage.yml │ ├── db │ ├── migrate │ │ └── 20190813062954_create_tasks.rb │ ├── schema.rb │ └── seeds.rb │ ├── k8s │ ├── app.yaml │ └── database.yaml │ ├── lib │ ├── assets │ │ └── .keep │ └── tasks │ │ └── .keep │ ├── log │ └── .keep │ ├── package.json │ ├── public │ ├── 404.html │ ├── 422.html │ ├── 500.html │ ├── apple-touch-icon-precomposed.png │ ├── apple-touch-icon.png │ ├── favicon.ico │ └── robots.txt │ ├── storage │ └── .keep │ ├── tasks.csv │ ├── test │ ├── application_system_test_case.rb │ ├── controllers │ │ ├── .keep │ │ ├── import_tasks_controller_test.rb │ │ └── tasks_controller_test.rb │ ├── fixtures │ │ ├── .keep │ │ ├── files │ │ │ └── .keep │ │ └── tasks.yml │ ├── helpers │ │ └── .keep │ ├── integration │ │ └── .keep │ ├── mailers │ │ └── .keep │ ├── models │ │ ├── .keep │ │ └── task_test.rb │ ├── system │ │ ├── .keep │ │ └── tasks_test.rb │ └── test_helper.rb │ ├── tmp │ └── .keep │ ├── vendor │ └── .keep │ └── yarn.lock ├── exe └── kube_queue ├── gemfiles ├── .bundle │ └── config ├── rails_5.1.gemfile ├── rails_5.1.gemfile.lock ├── rails_5.2.gemfile ├── rails_5.2.gemfile.lock ├── rails_6.0.gemfile └── rails_6.0.gemfile.lock ├── kube_queue.gemspec ├── lib ├── active_job │ └── adapters │ │ └── kube_queue_adapter.rb ├── kube_queue.rb └── kube_queue │ ├── client.rb │ ├── configuration.rb │ ├── executor.rb │ ├── job_specification.rb │ ├── manifest_builder.rb │ ├── railties.rb │ ├── version.rb │ ├── worker.rb │ └── worker │ └── dsl.rb ├── spec ├── active_job │ └── adapters │ │ └── kube_queue_adapter_spec.rb ├── kube_queue │ ├── manifest_spec.rb │ └── worker_spec.rb ├── kube_queue_spec.rb ├── spec_helper.rb ├── template │ └── test.yaml └── workers.rb └── template └── job.yaml /.dockerignore: -------------------------------------------------------------------------------- 1 | secrets 2 | Gemfile.lock 3 | -------------------------------------------------------------------------------- /.gcloudignore: -------------------------------------------------------------------------------- 1 | !.git 2 | secrets 3 | examples/myapp/tmp/* 4 | examples/myapp/log/* 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /secrets 4 | /_yardoc/ 5 | /Gemfile.lock 6 | /coverage/ 7 | /doc/ 8 | /pkg/ 9 | /spec/reports/ 10 | /tmp/ 11 | 12 | # rspec failure tracking 13 | .rspec_status 14 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | --require spec_helper 4 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | require: 2 | - rubocop-performance 3 | 4 | AllCops: 5 | TargetRubyVersion: 2.5 6 | Exclude: 7 | - Gemfile 8 | - Rakefile 9 | - examples/**/* 10 | - kube_queue.gemspec 11 | - Guardfile 12 | - vendor/**/* 13 | DisplayCopNames: true 14 | 15 | Gemspec/OrderedDependencies: 16 | Enabled: false 17 | 18 | Style/NumericLiterals: 19 | Enabled: false 20 | 21 | Style/StderrPuts: 22 | Enabled: false 23 | 24 | Naming/MethodParameterName: 25 | Enabled: false 26 | 27 | Style/StringLiterals: 28 | Enabled: false 29 | 30 | Style/ExpandPathArguments: 31 | Enabled: false 32 | 33 | Style/PercentLiteralDelimiters: 34 | Enabled: false 35 | 36 | Style/FrozenStringLiteralComment: 37 | Enabled: false 38 | 39 | Style/Documentation: 40 | Enabled: false 41 | 42 | Layout/LineLength: 43 | Max: 128 44 | 45 | Style/EmptyCaseCondition: 46 | Enabled: false 47 | 48 | Layout/ArgumentAlignment: 49 | Enabled: false 50 | 51 | Metrics/MethodLength: 52 | Max: 15 53 | 54 | Metrics/AbcSize: 55 | Max: 25 56 | 57 | Style/ParallelAssignment: 58 | Enabled: false 59 | 60 | Style/Alias: 61 | EnforcedStyle: prefer_alias_method 62 | 63 | Metrics/BlockLength: 64 | Exclude: 65 | - 'spec/**/*.rb' 66 | 67 | Style/MixinUsage: 68 | Exclude: 69 | - 'spec/**/*.rb' 70 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | sudo: false 3 | cache: bundler 4 | rvm: 5 | - 2.4.10 6 | - 2.6.6 7 | - 2.7.1 8 | - ruby-head 9 | branches: 10 | only: 11 | - master 12 | before_install: 13 | - gem update --system 14 | - gem update --remote bundler 15 | install: bundle install --job=3 --retry=3 16 | script: 17 | - bundle exec rspec 18 | gemfile: 19 | - gemfiles/rails_5.1.gemfile 20 | - gemfiles/rails_5.2.gemfile 21 | - gemfiles/rails_6.0.gemfile 22 | matrix: 23 | allow_failures: 24 | - rvm: ruby-head 25 | fast_finish: true 26 | notifications: 27 | slack: 28 | secure: P996C+m/+CeH9RacgDyVWYY7jdOKR3/FA194GwXE+l5Yc51AtH0Us8EWhaizNUPmxIO9DI8wFrX+/RYTl0mV2dOAToj655yTNIQAPh+FFHbC3dpjwHejXP90cWstwdR/PBPsy71sMk476RQPl4jWzR+OOoCQdyEWPUlYnVhu+DR2Tt54ixcoZqb7Li/s5gQCY7RDXBlO/lNA4nvslHTUTtdlHYM9MnUFtvYa/aCQ16bvHk/HdzUKUiZ1mZ1q+DDc104KFwcTxRMyyP3zfL0A5fNFsUoajVkifPBOi+3rrj2dlrwQJp7bv/WdBrv8R7WXbZLOx3EOZLnFzqmy63Hb2n6rbpAtYuILdAzmL4cW9/sGZCbnCUsBmwh/xk3/wTEUdHHqgLIkjPsvD0Z/AJmSf/ub3rsBG9mIvpDSyB+ok0ymsqda2W3Fe5+8zJ7AikO8W9OwhBoGLOaTBJKxl7ujy+z6BZjt4FaOUtl1StxjVk7j6Pzc0Io8EOronoe+PMJF2AthdtSZglf0CvHaKT43nh+mKmXv8hk2o/APpuSGdH5/0HBxyiziSZZJisEBHAkk8Zyv2SDG7KWyjz3GVpRBbFeRG7zZEbZ8QJSfN2d6eqa7lYEnyyTXZk2irkCLuMS7WJgGybgGPna49sj4/9YoithMPomPIz9Zt2/3SsKX/nc= 29 | on_success: change 30 | on_failure: always 31 | -------------------------------------------------------------------------------- /Appraisals: -------------------------------------------------------------------------------- 1 | appraise "rails-5.1" do 2 | gem "activejob", "5.1.7" 3 | end 4 | 5 | appraise "rails-5.2" do 6 | gem "activejob", "5.2.3" 7 | end 8 | 9 | appraise "rails-6.0" do 10 | gem "activejob", "6.0.0.beta2" 11 | end 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 0.4.1 4 | 5 | - Fix `Worker.find` dosen't work 6 | - Add `Worker.template` accessor 7 | 8 | ## 0.4.0 9 | 10 | - Support scheduled job with Kubernetes CronJob. 11 | - Support more settings 12 | - `env_from_secret` 13 | - `env_from_configmap` 14 | - `cpu_limit`, `cpu_request`, `memory_limit`, `memory_request` 15 | 16 | ## 0.3.0 17 | 18 | - Support ActiveJob 19 | - Change command line interfaces 20 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at yuemori@aiming-inc.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby 2 | 3 | WORKDIR /app 4 | 5 | RUN apt-get update -y && apt-get install git --no-install-recommends && rm -r /var/cache/apt /var/lib/apt/lists 6 | 7 | RUN gem install bundler:2.0.2 8 | COPY examples/docker/Gemfile Gemfile 9 | COPY Gemfile kube_queue.gemspec .git /vendor/kube_queue/ 10 | COPY exe/kube_queue /vendor/kube_queue/exe/kube_queue 11 | COPY lib/kube_queue/version.rb /vendor/kube_queue/lib/kube_queue/version.rb 12 | RUN bundle install -j4 13 | 14 | COPY examples/docker/test_worker.rb . 15 | COPY . /vendor/kube_queue 16 | 17 | CMD ["bundle", "exec", "kube_queue", "TestWorker", "-r", "./test_worker.rb"] 18 | -------------------------------------------------------------------------------- /Dockerfile.myapp: -------------------------------------------------------------------------------- 1 | FROM ruby 2 | 3 | RUN apt-get update -y && apt-get install git --no-install-recommends && rm -r /var/cache/apt /var/lib/apt/lists 4 | 5 | RUN gem install bundler:2.0.2 6 | 7 | ENV KUBE_QUEUE_PATH /vendor/kube_queue 8 | ENV WORKDIR /app 9 | 10 | WORKDIR $WORKDIR 11 | 12 | COPY .git $KUBE_QUEUE_PATH/.git 13 | COPY Gemfile kube_queue.gemspec $KUBE_QUEUE_PATH/ 14 | COPY exe/kube_queue $KUBE_QUEUE_PATH/exe/kube_queue 15 | COPY lib/kube_queue/version.rb $KUBE_QUEUE_PATH/lib/kube_queue/version.rb 16 | COPY examples/myapp/Gemfile examples/myapp/Gemfile.lock $WORKDIR/ 17 | RUN bundle install -j4 18 | 19 | COPY lib $KUBE_QUEUE_PATH/lib 20 | COPY template $KUBE_QUEUE_PATH/template 21 | COPY examples/myapp/ $WORKDIR 22 | 23 | RUN bundle exec rails assets:precompile 24 | 25 | CMD ["bundle", "exec", "rails", "console"] 26 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | # Specify your gem's dependencies in kube_queue.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | guard :rspec, cmd: "bundle exec rspec" do 2 | require "guard/rspec/dsl" 3 | dsl = Guard::RSpec::Dsl.new(self) 4 | 5 | rspec = dsl.rspec 6 | watch(rspec.spec_helper) { rspec.spec_dir } 7 | watch(rspec.spec_support) { rspec.spec_dir } 8 | watch(rspec.spec_files) 9 | 10 | ruby = dsl.ruby 11 | dsl.watch_spec_files_for(ruby.lib_files) 12 | end 13 | 14 | guard :rubocop do 15 | watch(%r{.+\.rb$}) 16 | watch(%r{(?:.+/)?\.rubocop(?:_todo)?\.yml$}) { |m| File.dirname(m[0]) } 17 | end 18 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 yuemori 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KubeQueue 2 | 3 | [![Build Status](https://travis-ci.org/yuemori/kube_queue.svg?branch=master)](https://travis-ci.org/yuemori/kube_queue) 4 | 5 | ## Installation 6 | 7 | Add this line to your application's Gemfile: 8 | 9 | ```ruby 10 | gem 'kube_queue' 11 | ``` 12 | 13 | And then execute: 14 | 15 | $ bundle 16 | 17 | Or install it yourself as: 18 | 19 | $ gem install kube_queue 20 | 21 | ## Getting Started 22 | 23 | Implement worker: 24 | 25 | ```ruby 26 | class TestWorker 27 | include KubeQueue::Worker 28 | 29 | job_name 'kube-queue-test' 30 | image "my-registry/my-image" 31 | container_name 'kube-queue-test' 32 | 33 | command 'bundle', 'exec', 'kube_queue', 'TestWorker', '-r', './test_worker.rb' 34 | 35 | def perform(payload) 36 | puts payload['message'] 37 | end 38 | end 39 | ``` 40 | 41 | Setting kubernetes configuration. 42 | 43 | ```ruby 44 | KubeQueue.kubernetes_configure do |client| 45 | client.url = ENV['K8S_URL'] 46 | client.ssl_ca_file = ENV['K8S_CA_CERT_FILE'] 47 | client.auth_token = File.read(ENV['K8S_TOKEN']) 48 | end 49 | ``` 50 | 51 | and run: 52 | 53 | ```ruby 54 | TestWorker.enqueue(message: 'hello') 55 | 56 | # delay 57 | TestWorker.enqueue_at(message: 'hello', Time.now + 100) 58 | ``` 59 | 60 | ### ActiveJob Support 61 | 62 | Write to `application.rb`: 63 | 64 | ```ruby 65 | Rails.application.config.active_job.adapter = :kube_queue 66 | ``` 67 | 68 | Just put your job into `app/jobs` . Example: 69 | 70 | ```ruby 71 | # app/jobs/print_message_job.rb 72 | class PrintMessageJob < ApplicationJob 73 | include KubeQueue::Worker 74 | 75 | worker_name 'print-message-job' 76 | image "your-registry/your-image" 77 | container_name 'your-container-name' 78 | 79 | def perform(payload) 80 | logger.info payload[:message] 81 | end 82 | end 83 | ``` 84 | 85 | and run: 86 | 87 | ```ruby 88 | irb(main):001:0> job = PrintMessageJob.perform_later(message: 'hello, kubernetes!') 89 | Enqueued PrintMessageJob (Job ID: 0bf15b35-62d8-4380-9173-99839ce735ff) to KubeQueue(default) with arguments: {:message=>"hello, kubernetes!"} 90 | => #"hello, kubernetes!"}], @job_id="0bf15b35-62d8-4380-9173-99839ce735ff", @queue_name="default", @priority=nil, @executions=0> 91 | irb(main):002:0> job.status 92 | => # 93 | irb(main):003:0> job.status 94 | => #"Complete", :status=>"True", :lastProbeTime=>"2019-08-12T15:57:03Z", :lastTransitionTime=>"2019-08-12T15:57:03Z"}], startTime="2019-08-12T15:56:37Z", completionTime="2019-08-12T15:57:03Z", succeeded=1> 95 | ``` 96 | 97 | See more examples in [here](examples/myapp/app/jobs). 98 | 99 | ### Run job on locally 100 | 101 | ``` 102 | bundle exec kube_queue runner JOB_NAME [PAYLOAD] 103 | ``` 104 | 105 | See more information by `kube_queue help` or read [here](exe/kube_queue). 106 | 107 | ## Advanced Tips 108 | 109 | ### Get a job status 110 | 111 | ```ruby 112 | job = ComputePiJob.perform_later 113 | job.status 114 | ``` 115 | 116 | scheduled job dosent supported now. 117 | 118 | ### Check a generating manifest 119 | 120 | ```ruby 121 | # from class 122 | puts ComputePiJob.manifest 123 | 124 | # from instance 125 | job = ComputePiJob.perform_later 126 | puts job.manifest 127 | ``` 128 | 129 | ### Retry job 130 | 131 | Kubernetes Job has a own retry mechanism, if set backoff_limit and/or restart_policy to use it. 132 | 133 | ```ruby 134 | class ComputePiJob 135 | include KubeQueue::Worker 136 | 137 | worker_name 'pi' 138 | image 'perl' 139 | container_name 'pi' 140 | command "perl", "-Mbignum=bpi", "-wle", "print bpi(2000)" 141 | 142 | backoff_limit 10 143 | restart_policy 'Never' 144 | end 145 | ``` 146 | 147 | More information, see the official document [here](https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/#pod-backoff-failure-policy). 148 | 149 | ### Timeout 150 | 151 | Kubernetes Job has a own timeout mechanism, if set the active_deadline_seconds to use it. 152 | 153 | ```ruby 154 | class ComputePiJob 155 | include KubeQueue::Worker 156 | 157 | worker_name 'pi' 158 | image 'perl' 159 | container_name 'pi' 160 | command "perl", "-Mbignum=bpi", "-wle", "print bpi(2000)" 161 | 162 | active_deadline_seconds 300 163 | end 164 | ``` 165 | 166 | More information, see the official document [here](https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/#job-termination-and-cleanup). 167 | 168 | ### Managing container resources 169 | 170 | When you specify a Pod, you can optional specify hou much CPU and memory container needs. 171 | 172 | ```ruby 173 | class ComputePiJob 174 | include KubeQueue::Worker 175 | 176 | worker_name 'pi' 177 | image 'perl' 178 | container_name 'pi' 179 | command "perl", "-Mbignum=bpi", "-wle", "print bpi(2000)" 180 | 181 | cpu_limit '0.3' 182 | cpu_request '0.2' 183 | memory_limit '100m' 184 | memory_request '50m' 185 | end 186 | ``` 187 | 188 | More information, see the official document [here](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/). 189 | 190 | ### Use environment variable from ConfigMap/Secret 191 | 192 | ```ruby 193 | class ComputePiJob 194 | include KubeQueue::Worker 195 | 196 | worker_name 'pi' 197 | image 'perl' 198 | container_name 'pi' 199 | command "perl", "-Mbignum=bpi", "-wle", "print bpi(2000)" 200 | 201 | env_from_secret 'mysecret1', 'mysecret2' 202 | env_from_config_map 'myapp' 203 | end 204 | ``` 205 | 206 | ## Features 207 | 208 | - Add tests. 209 | - Support multiple kubernetes client configuration. 210 | - Logging informations. 211 | - Support to get CronJob status. 212 | 213 | ## Development(on GCP/GKE) 214 | 215 | setup: 216 | 217 | ``` 218 | # create service account and cluster role. 219 | kubectl apply -f examples/k8s/service-account.yaml 220 | 221 | # get ca.crt and token 222 | kubectl get secret -n kube-system kube-queue-test-token-xxx -o jsonpath="{['data']['token']}" | base64 -d > secrets/token 223 | kubectl get secret -n kube-system kube-queue-test-token-xxx -o jsonpath="{['data']['ca\.crt']}" | base64 -d > secrets/ca.crt 224 | 225 | # build image 226 | gcloud builds submit --config cloudbuild.yaml . 227 | ``` 228 | 229 | run: 230 | 231 | ``` 232 | K8S_URL=https://xx.xxx.xxx.xxx K8S_CA_CERT_FILE=$(pwd)/secrets/ca.crt K8S_TOKEN=$(pwd)/secrets/token IMAGE_NAME=gcr.io/your-project/kube-queue bin/console 233 | 234 | irb(main):001:0> TestWorker.enqueue(message: 'hello, kubernetes!') 235 | ``` 236 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rspec/core/rake_task" 3 | 4 | RSpec::Core::RakeTask.new(:spec) 5 | 6 | task :default => :spec 7 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "kube_queue" 5 | require_relative "../examples/docker/test_worker" 6 | 7 | # You can add fixtures and/or initialization code here to make experimenting 8 | # with your gem easier. You can also use a different console, if you like. 9 | 10 | # (If you use this, don't forget to add pry to your Gemfile!) 11 | # require "pry" 12 | # Pry.start 13 | 14 | require "irb" 15 | 16 | KubeQueue.kubernetes_configure do |client| 17 | client.url = ENV['K8S_URL'] 18 | client.ssl_ca_file = ENV['K8S_CA_CERT_FILE'] 19 | client.auth_token = File.read(ENV['K8S_BEARER_TOKEN_FILE']) 20 | end 21 | 22 | IRB.start(__FILE__) 23 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | images: ['gcr.io/$PROJECT_ID/kube-queue:latest', 'gcr.io/$PROJECT_ID/kube-queue-test-app:latest'] 2 | 3 | steps: 4 | - name: 'gcr.io/cloud-builders/docker' 5 | args: ['build', '-f', '/workspace/Dockerfile', '/workspace', '-t', 'gcr.io/$PROJECT_ID/kube-queue:latest'] 6 | 7 | - name: 'gcr.io/cloud-builders/docker' 8 | args: ['build', '-f', '/workspace/Dockerfile.myapp', '/workspace', '-t', 'gcr.io/$PROJECT_ID/kube-queue-test-app:latest'] 9 | waitFor: ['-'] 10 | -------------------------------------------------------------------------------- /examples/docker/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem 'kube_queue', path: '/vendor/kube_queue' 4 | -------------------------------------------------------------------------------- /examples/docker/test_worker.rb: -------------------------------------------------------------------------------- 1 | require 'kube_queue' 2 | 3 | class TestWorker 4 | include KubeQueue::Worker 5 | 6 | worker_name 'kube-queue-test' 7 | image "gcr.io/#{ENV['PROJECT_ID']}/kube-queue" 8 | container_name 'kube-queue-test' 9 | command 'bundle', 'exec', 'kube_queue', 'runner', 'TestWorker', '-r', './test_worker.rb' 10 | 11 | def perform(payload) 12 | puts payload['message'] 13 | end 14 | end 15 | 16 | # Run official example. 17 | # see: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/ 18 | class TestWorker2 19 | include KubeQueue::Worker 20 | 21 | worker_name 'pi' 22 | image 'perl' 23 | container_name 'pi' 24 | command "perl", "-Mbignum=bpi", "-wle", "print bpi(2000)" 25 | end 26 | -------------------------------------------------------------------------------- /examples/k8s/service-account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: kube-queue-test 5 | namespace: kube-system 6 | --- 7 | apiVersion: rbac.authorization.k8s.io/v1 8 | kind: ClusterRoleBinding 9 | metadata: 10 | name: kube-queue-test 11 | roleRef: 12 | apiGroup: rbac.authorization.k8s.io 13 | kind: ClusterRole 14 | name: cluster-admin 15 | subjects: 16 | - kind: ServiceAccount 17 | name: kube-queue-test 18 | namespace: kube-system 19 | -------------------------------------------------------------------------------- /examples/myapp/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-journal 13 | 14 | # Ignore all logfiles and tempfiles. 15 | /log/* 16 | /tmp/* 17 | !/log/.keep 18 | !/tmp/.keep 19 | 20 | # Ignore uploaded files in development 21 | /storage/* 22 | !/storage/.keep 23 | 24 | /node_modules 25 | /yarn-error.log 26 | 27 | /public/assets 28 | .byebug_history 29 | 30 | # Ignore master key for decrypting credentials and more. 31 | /config/master.key 32 | -------------------------------------------------------------------------------- /examples/myapp/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | gem 'kube_queue', path: ENV['KUBE_QUEUE_PATH'] || '../../' 5 | 6 | gem 'rails', '~> 5.2.3' 7 | gem 'mysql2' 8 | gem 'puma', '~> 4.3' 9 | gem 'sass-rails', '~> 5.0' 10 | gem 'uglifier', '>= 1.3.0' 11 | gem 'mini_racer', platforms: :ruby 12 | 13 | gem 'coffee-rails', '~> 4.2' 14 | gem 'turbolinks', '~> 5' 15 | gem 'jbuilder', '~> 2.5' 16 | 17 | gem 'bootsnap', '>= 1.1.0', require: false 18 | 19 | gem 'pry-rails' 20 | 21 | gem 'kaminari' 22 | 23 | group :development, :test do 24 | gem 'byebug' 25 | gem 'pry-byebug' 26 | end 27 | 28 | group :development do 29 | gem 'web-console', '>= 3.3.0' 30 | gem 'listen', '>= 3.0.5', '< 3.2' 31 | gem 'spring' 32 | gem 'spring-watcher-listen', '~> 2.0.0' 33 | end 34 | 35 | group :test do 36 | gem 'capybara', '>= 2.15' 37 | gem 'selenium-webdriver' 38 | gem 'chromedriver-helper' 39 | end 40 | -------------------------------------------------------------------------------- /examples/myapp/Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: ../.. 3 | specs: 4 | kube_queue (0.3.0) 5 | k8s-client 6 | thor 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | actioncable (5.2.3) 12 | actionpack (= 5.2.3) 13 | nio4r (~> 2.0) 14 | websocket-driver (>= 0.6.1) 15 | actionmailer (5.2.3) 16 | actionpack (= 5.2.3) 17 | actionview (= 5.2.3) 18 | activejob (= 5.2.3) 19 | mail (~> 2.5, >= 2.5.4) 20 | rails-dom-testing (~> 2.0) 21 | actionpack (5.2.3) 22 | actionview (= 5.2.3) 23 | activesupport (= 5.2.3) 24 | rack (~> 2.0) 25 | rack-test (>= 0.6.3) 26 | rails-dom-testing (~> 2.0) 27 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 28 | actionview (5.2.3) 29 | activesupport (= 5.2.3) 30 | builder (~> 3.1) 31 | erubi (~> 1.4) 32 | rails-dom-testing (~> 2.0) 33 | rails-html-sanitizer (~> 1.0, >= 1.0.3) 34 | activejob (5.2.3) 35 | activesupport (= 5.2.3) 36 | globalid (>= 0.3.6) 37 | activemodel (5.2.3) 38 | activesupport (= 5.2.3) 39 | activerecord (5.2.3) 40 | activemodel (= 5.2.3) 41 | activesupport (= 5.2.3) 42 | arel (>= 9.0) 43 | activestorage (5.2.3) 44 | actionpack (= 5.2.3) 45 | activerecord (= 5.2.3) 46 | marcel (~> 0.3.1) 47 | activesupport (5.2.3) 48 | concurrent-ruby (~> 1.0, >= 1.0.2) 49 | i18n (>= 0.7, < 2) 50 | minitest (~> 5.1) 51 | tzinfo (~> 1.1) 52 | addressable (2.6.0) 53 | public_suffix (>= 2.0.2, < 4.0) 54 | archive-zip (0.12.0) 55 | io-like (~> 0.3.0) 56 | arel (9.0.0) 57 | bindex (0.8.1) 58 | bootsnap (1.4.4) 59 | msgpack (~> 1.0) 60 | builder (3.2.3) 61 | byebug (11.0.1) 62 | capybara (3.28.0) 63 | addressable 64 | mini_mime (>= 0.1.3) 65 | nokogiri (~> 1.8) 66 | rack (>= 1.6.0) 67 | rack-test (>= 0.6.3) 68 | regexp_parser (~> 1.5) 69 | xpath (~> 3.2) 70 | childprocess (1.0.1) 71 | rake (< 13.0) 72 | chromedriver-helper (2.1.1) 73 | archive-zip (~> 0.10) 74 | nokogiri (~> 1.8) 75 | coderay (1.1.2) 76 | coffee-rails (4.2.2) 77 | coffee-script (>= 2.2.0) 78 | railties (>= 4.0.0) 79 | coffee-script (2.4.1) 80 | coffee-script-source 81 | execjs 82 | coffee-script-source (1.12.2) 83 | concurrent-ruby (1.1.5) 84 | crass (1.0.5) 85 | dry-configurable (0.9.0) 86 | concurrent-ruby (~> 1.0) 87 | dry-core (~> 0.4, >= 0.4.7) 88 | dry-container (0.7.2) 89 | concurrent-ruby (~> 1.0) 90 | dry-configurable (~> 0.1, >= 0.1.3) 91 | dry-core (0.4.9) 92 | concurrent-ruby (~> 1.0) 93 | dry-equalizer (0.3.0) 94 | dry-inflector (0.2.0) 95 | dry-logic (0.6.1) 96 | concurrent-ruby (~> 1.0) 97 | dry-core (~> 0.2) 98 | dry-equalizer (~> 0.2) 99 | dry-struct (0.5.1) 100 | dry-core (~> 0.4, >= 0.4.3) 101 | dry-equalizer (~> 0.2) 102 | dry-types (~> 0.13) 103 | ice_nine (~> 0.11) 104 | dry-types (0.13.4) 105 | concurrent-ruby (~> 1.0) 106 | dry-container (~> 0.3) 107 | dry-core (~> 0.4, >= 0.4.4) 108 | dry-equalizer (~> 0.2) 109 | dry-inflector (~> 0.1, >= 0.1.2) 110 | dry-logic (~> 0.4, >= 0.4.2) 111 | erubi (1.8.0) 112 | excon (0.71.0) 113 | execjs (2.7.0) 114 | ffi (1.11.1) 115 | globalid (0.4.2) 116 | activesupport (>= 4.2.0) 117 | hashdiff (1.0.0) 118 | i18n (1.6.0) 119 | concurrent-ruby (~> 1.0) 120 | ice_nine (0.11.2) 121 | io-like (0.3.0) 122 | jbuilder (2.9.1) 123 | activesupport (>= 4.2.0) 124 | jsonpath (0.9.9) 125 | multi_json 126 | to_regexp (~> 0.2.1) 127 | k8s-client (0.10.4) 128 | dry-struct (~> 0.5.0) 129 | dry-types (~> 0.13.0) 130 | excon (~> 0.66) 131 | hashdiff (~> 1.0.0) 132 | jsonpath (~> 0.9.5) 133 | recursive-open-struct (~> 1.1.0) 134 | yajl-ruby (~> 1.4.0) 135 | yaml-safe_load_stream (~> 0.1) 136 | kaminari (1.1.1) 137 | activesupport (>= 4.1.0) 138 | kaminari-actionview (= 1.1.1) 139 | kaminari-activerecord (= 1.1.1) 140 | kaminari-core (= 1.1.1) 141 | kaminari-actionview (1.1.1) 142 | actionview 143 | kaminari-core (= 1.1.1) 144 | kaminari-activerecord (1.1.1) 145 | activerecord 146 | kaminari-core (= 1.1.1) 147 | kaminari-core (1.1.1) 148 | libv8 (7.3.492.27.1) 149 | listen (3.1.5) 150 | rb-fsevent (~> 0.9, >= 0.9.4) 151 | rb-inotify (~> 0.9, >= 0.9.7) 152 | ruby_dep (~> 1.2) 153 | loofah (2.3.1) 154 | crass (~> 1.0.2) 155 | nokogiri (>= 1.5.9) 156 | mail (2.7.1) 157 | mini_mime (>= 0.1.1) 158 | marcel (0.3.3) 159 | mimemagic (~> 0.3.2) 160 | method_source (0.9.2) 161 | mimemagic (0.3.3) 162 | mini_mime (1.0.2) 163 | mini_portile2 (2.4.0) 164 | mini_racer (0.2.6) 165 | libv8 (>= 6.9.411) 166 | minitest (5.11.3) 167 | msgpack (1.3.1) 168 | multi_json (1.14.1) 169 | mysql2 (0.5.2) 170 | nio4r (2.4.0) 171 | nokogiri (1.10.5) 172 | mini_portile2 (~> 2.4.0) 173 | pry (0.12.2) 174 | coderay (~> 1.1.0) 175 | method_source (~> 0.9.0) 176 | pry-byebug (3.7.0) 177 | byebug (~> 11.0) 178 | pry (~> 0.10) 179 | pry-rails (0.3.9) 180 | pry (>= 0.10.4) 181 | public_suffix (3.1.1) 182 | puma (4.3.1) 183 | nio4r (~> 2.0) 184 | rack (2.0.8) 185 | rack-test (1.1.0) 186 | rack (>= 1.0, < 3) 187 | rails (5.2.3) 188 | actioncable (= 5.2.3) 189 | actionmailer (= 5.2.3) 190 | actionpack (= 5.2.3) 191 | actionview (= 5.2.3) 192 | activejob (= 5.2.3) 193 | activemodel (= 5.2.3) 194 | activerecord (= 5.2.3) 195 | activestorage (= 5.2.3) 196 | activesupport (= 5.2.3) 197 | bundler (>= 1.3.0) 198 | railties (= 5.2.3) 199 | sprockets-rails (>= 2.0.0) 200 | rails-dom-testing (2.0.3) 201 | activesupport (>= 4.2.0) 202 | nokogiri (>= 1.6) 203 | rails-html-sanitizer (1.2.0) 204 | loofah (~> 2.2, >= 2.2.2) 205 | railties (5.2.3) 206 | actionpack (= 5.2.3) 207 | activesupport (= 5.2.3) 208 | method_source 209 | rake (>= 0.8.7) 210 | thor (>= 0.19.0, < 2.0) 211 | rake (12.3.3) 212 | rb-fsevent (0.10.3) 213 | rb-inotify (0.10.0) 214 | ffi (~> 1.0) 215 | recursive-open-struct (1.1.0) 216 | regexp_parser (1.6.0) 217 | ruby_dep (1.5.0) 218 | rubyzip (1.3.0) 219 | sass (3.7.4) 220 | sass-listen (~> 4.0.0) 221 | sass-listen (4.0.0) 222 | rb-fsevent (~> 0.9, >= 0.9.4) 223 | rb-inotify (~> 0.9, >= 0.9.7) 224 | sass-rails (5.0.7) 225 | railties (>= 4.0.0, < 6) 226 | sass (~> 3.1) 227 | sprockets (>= 2.8, < 4.0) 228 | sprockets-rails (>= 2.0, < 4.0) 229 | tilt (>= 1.1, < 3) 230 | selenium-webdriver (3.142.3) 231 | childprocess (>= 0.5, < 2.0) 232 | rubyzip (~> 1.2, >= 1.2.2) 233 | spring (2.1.0) 234 | spring-watcher-listen (2.0.1) 235 | listen (>= 2.7, < 4.0) 236 | spring (>= 1.2, < 3.0) 237 | sprockets (3.7.2) 238 | concurrent-ruby (~> 1.0) 239 | rack (> 1, < 3) 240 | sprockets-rails (3.2.1) 241 | actionpack (>= 4.0) 242 | activesupport (>= 4.0) 243 | sprockets (>= 3.0.0) 244 | thor (0.20.3) 245 | thread_safe (0.3.6) 246 | tilt (2.0.9) 247 | to_regexp (0.2.1) 248 | turbolinks (5.2.0) 249 | turbolinks-source (~> 5.2) 250 | turbolinks-source (5.2.0) 251 | tzinfo (1.2.5) 252 | thread_safe (~> 0.1) 253 | uglifier (4.1.20) 254 | execjs (>= 0.3.0, < 3) 255 | web-console (3.7.0) 256 | actionview (>= 5.0) 257 | activemodel (>= 5.0) 258 | bindex (>= 0.4.0) 259 | railties (>= 5.0) 260 | websocket-driver (0.7.1) 261 | websocket-extensions (>= 0.1.0) 262 | websocket-extensions (0.1.4) 263 | xpath (3.2.0) 264 | nokogiri (~> 1.8) 265 | yajl-ruby (1.4.1) 266 | yaml-safe_load_stream (0.1.1) 267 | 268 | PLATFORMS 269 | ruby 270 | 271 | DEPENDENCIES 272 | bootsnap (>= 1.1.0) 273 | byebug 274 | capybara (>= 2.15) 275 | chromedriver-helper 276 | coffee-rails (~> 4.2) 277 | jbuilder (~> 2.5) 278 | kaminari 279 | kube_queue! 280 | listen (>= 3.0.5, < 3.2) 281 | mini_racer 282 | mysql2 283 | pry-byebug 284 | pry-rails 285 | puma (~> 4.3) 286 | rails (~> 5.2.3) 287 | sass-rails (~> 5.0) 288 | selenium-webdriver 289 | spring 290 | spring-watcher-listen (~> 2.0.0) 291 | turbolinks (~> 5) 292 | uglifier (>= 1.3.0) 293 | web-console (>= 3.3.0) 294 | 295 | BUNDLED WITH 296 | 2.0.2 297 | -------------------------------------------------------------------------------- /examples/myapp/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | This README would normally document whatever steps are necessary to get the 4 | application up and running. 5 | 6 | Things you may want to cover: 7 | 8 | * Ruby version 9 | 10 | * System dependencies 11 | 12 | * Configuration 13 | 14 | * Database creation 15 | 16 | * Database initialization 17 | 18 | * How to run the test suite 19 | 20 | * Services (job queues, cache servers, search engines, etc.) 21 | 22 | * Deployment instructions 23 | 24 | * ... 25 | -------------------------------------------------------------------------------- /examples/myapp/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /examples/myapp/app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../javascripts .js 3 | //= link_directory ../stylesheets .css 4 | -------------------------------------------------------------------------------- /examples/myapp/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuemori/kube_queue/df546ebf7a927884fc2318bdd7cf8ca36af91f72/examples/myapp/app/assets/images/.keep -------------------------------------------------------------------------------- /examples/myapp/app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's 5 | // vendor/assets/javascripts directory can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. JavaScript code in this file should be added after the last require_* statement. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require rails-ujs 14 | //= require activestorage 15 | //= require turbolinks 16 | //= require_tree . 17 | -------------------------------------------------------------------------------- /examples/myapp/app/assets/javascripts/cable.js: -------------------------------------------------------------------------------- 1 | // Action Cable provides the framework to deal with WebSockets in Rails. 2 | // You can generate new channels where WebSocket features live using the `rails generate channel` command. 3 | // 4 | //= require action_cable 5 | //= require_self 6 | //= require_tree ./channels 7 | 8 | (function() { 9 | this.App || (this.App = {}); 10 | 11 | App.cable = ActionCable.createConsumer(); 12 | 13 | }).call(this); 14 | -------------------------------------------------------------------------------- /examples/myapp/app/assets/javascripts/channels/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuemori/kube_queue/df546ebf7a927884fc2318bdd7cf8ca36af91f72/examples/myapp/app/assets/javascripts/channels/.keep -------------------------------------------------------------------------------- /examples/myapp/app/assets/javascripts/import_tasks.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /examples/myapp/app/assets/javascripts/tasks.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /examples/myapp/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's 6 | * vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /examples/myapp/app/assets/stylesheets/import_tasks.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the ImportTasks controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /examples/myapp/app/assets/stylesheets/scaffolds.scss: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #fff; 3 | color: #333; 4 | margin: 33px; 5 | font-family: verdana, arial, helvetica, sans-serif; 6 | font-size: 13px; 7 | line-height: 18px; 8 | } 9 | 10 | p, ol, ul, td { 11 | font-family: verdana, arial, helvetica, sans-serif; 12 | font-size: 13px; 13 | line-height: 18px; 14 | } 15 | 16 | pre { 17 | background-color: #eee; 18 | padding: 10px; 19 | font-size: 11px; 20 | } 21 | 22 | a { 23 | color: #000; 24 | 25 | &:visited { 26 | color: #666; 27 | } 28 | 29 | &:hover { 30 | color: #fff; 31 | background-color: #000; 32 | } 33 | } 34 | 35 | th { 36 | padding-bottom: 5px; 37 | } 38 | 39 | td { 40 | padding: 0 5px 7px; 41 | } 42 | 43 | div { 44 | &.field, &.actions { 45 | margin-bottom: 10px; 46 | } 47 | } 48 | 49 | #notice { 50 | color: green; 51 | } 52 | 53 | .field_with_errors { 54 | padding: 2px; 55 | background-color: red; 56 | display: table; 57 | } 58 | 59 | #error_explanation { 60 | width: 450px; 61 | border: 2px solid red; 62 | padding: 7px 7px 0; 63 | margin-bottom: 20px; 64 | background-color: #f0f0f0; 65 | 66 | h2 { 67 | text-align: left; 68 | font-weight: bold; 69 | padding: 5px 5px 5px 15px; 70 | font-size: 12px; 71 | margin: -7px -7px 0; 72 | background-color: #c00; 73 | color: #fff; 74 | } 75 | 76 | ul li { 77 | font-size: 12px; 78 | list-style: square; 79 | } 80 | } 81 | 82 | label { 83 | display: block; 84 | } 85 | -------------------------------------------------------------------------------- /examples/myapp/app/assets/stylesheets/tasks.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Tasks controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /examples/myapp/app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /examples/myapp/app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /examples/myapp/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | end 3 | -------------------------------------------------------------------------------- /examples/myapp/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuemori/kube_queue/df546ebf7a927884fc2318bdd7cf8ca36af91f72/examples/myapp/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /examples/myapp/app/controllers/import_tasks_controller.rb: -------------------------------------------------------------------------------- 1 | require 'csv' 2 | 3 | class ImportTasksController < ApplicationController 4 | rescue_from(StandardError) do |e| 5 | logger.error e 6 | 7 | redirect_to({ controller: :tasks, action: :index }, alert: 'Task import was failure') 8 | end 9 | 10 | def create 11 | file = params[:upload_file] 12 | 13 | csv = CSV.parse(file.read) 14 | 15 | ImportTaskJob.perform_later(csv) 16 | 17 | respond_to do |format| 18 | format.html { redirect_to({ controller: :tasks, action: :index }, notice: 'Task import was successfully started.') } 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /examples/myapp/app/controllers/tasks_controller.rb: -------------------------------------------------------------------------------- 1 | class TasksController < ApplicationController 2 | before_action :set_task, only: [:show, :edit, :update, :destroy] 3 | 4 | def index 5 | @tasks = Task.page(params[:page]) 6 | end 7 | 8 | def show 9 | end 10 | 11 | def new 12 | @task = Task.new 13 | end 14 | 15 | def edit 16 | end 17 | 18 | def create 19 | @task = Task.new(task_params) 20 | 21 | respond_to do |format| 22 | if @task.save 23 | format.html { redirect_to @task, notice: 'Task was successfully created.' } 24 | format.json { render :show, status: :created, location: @task } 25 | else 26 | format.html { render :new } 27 | format.json { render json: @task.errors, status: :unprocessable_entity } 28 | end 29 | end 30 | end 31 | 32 | def update 33 | respond_to do |format| 34 | if @task.update(task_params) 35 | format.html { redirect_to @task, notice: 'Task was successfully updated.' } 36 | format.json { render :show, status: :ok, location: @task } 37 | else 38 | format.html { render :edit } 39 | format.json { render json: @task.errors, status: :unprocessable_entity } 40 | end 41 | end 42 | end 43 | 44 | def destroy 45 | @task.destroy 46 | respond_to do |format| 47 | format.html { redirect_to tasks_url, notice: 'Task was successfully destroyed.' } 48 | format.json { head :no_content } 49 | end 50 | end 51 | 52 | private 53 | 54 | def set_task 55 | @task = Task.find(params[:id]) 56 | end 57 | 58 | # Never trust parameters from the scary internet, only allow the white list through. 59 | def task_params 60 | params.require(:task).permit(:state, :name) 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /examples/myapp/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /examples/myapp/app/helpers/import_tasks_helper.rb: -------------------------------------------------------------------------------- 1 | module ImportTasksHelper 2 | end 3 | -------------------------------------------------------------------------------- /examples/myapp/app/helpers/tasks_helper.rb: -------------------------------------------------------------------------------- 1 | module TasksHelper 2 | end 3 | -------------------------------------------------------------------------------- /examples/myapp/app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /examples/myapp/app/jobs/archive_done_tasks_job.rb: -------------------------------------------------------------------------------- 1 | class ArchiveDoneTasksJob < ApplicationJob 2 | include KubeQueue::Worker 3 | 4 | worker_name 'archive-done-task' 5 | image "gcr.io/#{ENV['PROJECT_ID']}/kube-queue-test-app" 6 | container_name 'kube-queue-test-app' 7 | 8 | env_from_secret 'myapp' 9 | env_from_config_map 'myapp' 10 | 11 | def perform 12 | Task.transaction do 13 | count = Task.done.delete_all 14 | logger.info "#{count} tasks were deleted." 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /examples/myapp/app/jobs/compute_pi_job.rb: -------------------------------------------------------------------------------- 1 | class ComputePiJob < ApplicationJob 2 | include KubeQueue::Worker 3 | 4 | worker_name 'pi' 5 | image 'perl' 6 | container_name 'pi' 7 | command "perl", "-Mbignum=bpi", "-wle", "print bpi(2000)" 8 | 9 | cpu_limit '0.3' 10 | cpu_request '0.2' 11 | memory_limit '100m' 12 | memory_request '50m' 13 | end 14 | -------------------------------------------------------------------------------- /examples/myapp/app/jobs/import_task_job.rb: -------------------------------------------------------------------------------- 1 | class ImportTaskJob < ApplicationJob 2 | include KubeQueue::Worker 3 | 4 | worker_name 'import-task' 5 | image "gcr.io/#{ENV['PROJECT_ID']}/kube-queue-test-app" 6 | container_name 'kube-queue-test-app' 7 | 8 | env_from_secret 'myapp' 9 | env_from_config_map 'myapp' 10 | 11 | def perform(csv) 12 | Task.transaction do 13 | csv.each do |row| 14 | Task.create!(name: row[0], state: row[1]) 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /examples/myapp/app/jobs/print_message_job.rb: -------------------------------------------------------------------------------- 1 | class PrintMessageJob < ApplicationJob 2 | include KubeQueue::Worker 3 | 4 | worker_name 'print-message' 5 | image "gcr.io/#{ENV['PROJECT_ID']}/kube-queue-test-app" 6 | container_name 'kube-queue-test-app' 7 | 8 | def perform(payload) 9 | logger.info payload[:message] 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /examples/myapp/app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /examples/myapp/app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /examples/myapp/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuemori/kube_queue/df546ebf7a927884fc2318bdd7cf8ca36af91f72/examples/myapp/app/models/concerns/.keep -------------------------------------------------------------------------------- /examples/myapp/app/models/task.rb: -------------------------------------------------------------------------------- 1 | class Task < ApplicationRecord 2 | enum state: { todo: 0, doing: 1, done: 2 } 3 | end 4 | -------------------------------------------------------------------------------- /examples/myapp/app/views/import_tasks/create.html.erb: -------------------------------------------------------------------------------- 1 |

ImportTasks#create

2 |

Find me in app/views/import_tasks/create.html.erb

3 | -------------------------------------------------------------------------------- /examples/myapp/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Myapp 5 | <%= csrf_meta_tags %> 6 | <%= csp_meta_tag %> 7 | 8 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> 9 | <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> 10 | 11 | 12 | 13 | <%= yield %> 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/myapp/app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/myapp/app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /examples/myapp/app/views/tasks/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: task, local: true) do |form| %> 2 | <% if task.errors.any? %> 3 |
4 |

<%= pluralize(task.errors.count, "error") %> prohibited this task from being saved:

5 | 6 |
    7 | <% task.errors.full_messages.each do |message| %> 8 |
  • <%= message %>
  • 9 | <% end %> 10 |
11 |
12 | <% end %> 13 | 14 |
15 | <%= form.label :state %> 16 | <%= form.select :state, Task.states.keys.to_a, {} %> 17 |
18 | 19 |
20 | <%= form.label :name %> 21 | <%= form.text_field :name %> 22 |
23 | 24 |
25 | <%= form.submit %> 26 |
27 | <% end %> 28 | -------------------------------------------------------------------------------- /examples/myapp/app/views/tasks/_import_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(url: task_import_path, local: true) do |form| %> 2 |
3 | <%= form.label :upload_file %> 4 | <%= form.file_field :upload_file %> 5 |
6 | 7 |
8 | <%= form.submit 'import CSV' %> 9 |
10 | <% end %> 11 | -------------------------------------------------------------------------------- /examples/myapp/app/views/tasks/_task.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.extract! task, :id, :state, :name, :created_at, :updated_at 2 | json.url task_url(task, format: :json) 3 | -------------------------------------------------------------------------------- /examples/myapp/app/views/tasks/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Editing Task

2 | 3 | <%= render 'form', task: @task %> 4 | 5 | <%= link_to 'Show', @task %> | 6 | <%= link_to 'Back', tasks_path %> 7 | -------------------------------------------------------------------------------- /examples/myapp/app/views/tasks/index.html.erb: -------------------------------------------------------------------------------- 1 |

<%= notice %>

2 | <% if alert %> 3 |

<%= alert %>

4 | <% end %> 5 | 6 |

Tasks

7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | <% @tasks.each do |task| %> 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | <% end %> 27 | 28 |
StateName
<%= task.state %><%= task.name %><%= link_to 'Show', task %><%= link_to 'Edit', edit_task_path(task) %><%= link_to 'Destroy', task, method: :delete, data: { confirm: 'Are you sure?' } %>
29 | 30 |
31 | 32 | <%= paginate @tasks %> 33 | 34 | <%= render 'import_form' %> 35 | 36 | <%= link_to 'New Task', new_task_path %> 37 | -------------------------------------------------------------------------------- /examples/myapp/app/views/tasks/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.array! @tasks, partial: "tasks/task", as: :task 2 | -------------------------------------------------------------------------------- /examples/myapp/app/views/tasks/new.html.erb: -------------------------------------------------------------------------------- 1 |

New Task

2 | 3 | <%= render 'form', task: @task %> 4 | 5 | <%= link_to 'Back', tasks_path %> 6 | -------------------------------------------------------------------------------- /examples/myapp/app/views/tasks/show.html.erb: -------------------------------------------------------------------------------- 1 |

<%= notice %>

2 | 3 |

4 | State: 5 | <%= @task.state %> 6 |

7 | 8 |

9 | Name: 10 | <%= @task.name %> 11 |

12 | 13 | <%= link_to 'Edit', edit_task_path(@task) %> | 14 | <%= link_to 'Back', tasks_path %> 15 | -------------------------------------------------------------------------------- /examples/myapp/app/views/tasks/show.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.partial! "tasks/task", task: @task 2 | -------------------------------------------------------------------------------- /examples/myapp/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /examples/myapp/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | APP_PATH = File.expand_path('../config/application', __dir__) 8 | require_relative '../config/boot' 9 | require 'rails/commands' 10 | -------------------------------------------------------------------------------- /examples/myapp/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | require_relative '../config/boot' 8 | require 'rake' 9 | Rake.application.run 10 | -------------------------------------------------------------------------------- /examples/myapp/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'fileutils' 3 | include FileUtils 4 | 5 | # path to your application root. 6 | APP_ROOT = File.expand_path('..', __dir__) 7 | 8 | def system!(*args) 9 | system(*args) || abort("\n== Command #{args} failed ==") 10 | end 11 | 12 | chdir APP_ROOT do 13 | # This script is a starting point to setup your application. 14 | # Add necessary setup steps to this file. 15 | 16 | puts '== Installing dependencies ==' 17 | system! 'gem install bundler --conservative' 18 | system('bundle check') || system!('bundle install') 19 | 20 | # Install JavaScript dependencies if using Yarn 21 | # system('bin/yarn') 22 | 23 | # puts "\n== Copying sample files ==" 24 | # unless File.exist?('config/database.yml') 25 | # cp 'config/database.yml.sample', 'config/database.yml' 26 | # end 27 | 28 | puts "\n== Preparing database ==" 29 | system! 'bin/rails db:setup' 30 | 31 | puts "\n== Removing old logs and tempfiles ==" 32 | system! 'bin/rails log:clear tmp:clear' 33 | 34 | puts "\n== Restarting application server ==" 35 | system! 'bin/rails restart' 36 | end 37 | -------------------------------------------------------------------------------- /examples/myapp/bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads Spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require 'rubygems' 8 | require 'bundler' 9 | 10 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) 11 | spring = lockfile.specs.detect { |spec| spec.name == 'spring' } 12 | if spring 13 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path 14 | gem 'spring', spring.version 15 | require 'spring/binstub' 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /examples/myapp/bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'fileutils' 3 | include FileUtils 4 | 5 | # path to your application root. 6 | APP_ROOT = File.expand_path('..', __dir__) 7 | 8 | def system!(*args) 9 | system(*args) || abort("\n== Command #{args} failed ==") 10 | end 11 | 12 | chdir APP_ROOT do 13 | # This script is a way to update your development environment automatically. 14 | # Add necessary update steps to this file. 15 | 16 | puts '== Installing dependencies ==' 17 | system! 'gem install bundler --conservative' 18 | system('bundle check') || system!('bundle install') 19 | 20 | # Install JavaScript dependencies if using Yarn 21 | # system('bin/yarn') 22 | 23 | puts "\n== Updating database ==" 24 | system! 'bin/rails db:migrate' 25 | 26 | puts "\n== Removing old logs and tempfiles ==" 27 | system! 'bin/rails log:clear tmp:clear' 28 | 29 | puts "\n== Restarting application server ==" 30 | system! 'bin/rails restart' 31 | end 32 | -------------------------------------------------------------------------------- /examples/myapp/bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_ROOT = File.expand_path('..', __dir__) 3 | Dir.chdir(APP_ROOT) do 4 | begin 5 | exec "yarnpkg", *ARGV 6 | rescue Errno::ENOENT 7 | $stderr.puts "Yarn executable was not detected in the system." 8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" 9 | exit 1 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /examples/myapp/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /examples/myapp/config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module Myapp 10 | class Application < Rails::Application 11 | # Initialize configuration defaults for originally generated Rails version. 12 | config.load_defaults 5.2 13 | config.active_job.queue_adapter = :kube_queue 14 | 15 | # Settings in config/environments/* take precedence over those specified here. 16 | # Application configuration can go into files in config/initializers 17 | # -- all .rb files in that directory are automatically loaded after loading 18 | # the framework and any gems in your application. 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /examples/myapp/config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | require 'bootsnap/setup' # Speed up boot time by caching expensive operations. 5 | -------------------------------------------------------------------------------- /examples/myapp/config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> 10 | channel_prefix: myapp_production 11 | -------------------------------------------------------------------------------- /examples/myapp/config/credentials.yml.enc: -------------------------------------------------------------------------------- 1 | OVJtl6K3mHGJqrIvfbz4GLTN66nr/FcFSiuAOhUfZ+JAwr/RJUTZIYdauM4lz40+j4FFZB+AaChp4cTDvTs/wqzfO2qTpTqvp/3IAV+a6545maHsDw9f/TKKgO0rmi6XsALuvrRkJErYJWQgZj76KfWDrkr/YFUHfmvJDW4W453uiMn0q/b7hpn/WcKfn4S6BUepx3W0Ja/e/CJ2hIenURBUdKA7FqU0SB/9VjIR4h16obKRek3uwYs4J06eitZhyn5pSNQQpR4+j+/UjX0eVCojF+kAdaISElYc65TfUrtLDCVy56qZ2Or/GomRqvg7Oe4UmxtwHPw98KWVXLM5Jb2yE5cghcUEPHlgYvV9GhMRcmU7ehUEeDDBK/9YT2CzQ5cu999rICJk+3EvvahQ67dtbw1o0c85eeMa--mQVKxrjgiu/YYhDs--DOUx27FrQilSjqQ1Y3meLg== -------------------------------------------------------------------------------- /examples/myapp/config/database.yml: -------------------------------------------------------------------------------- 1 | default: &default 2 | adapter: mysql2 3 | url: <%= ENV.fetch("DATABASE_URL") %> 4 | 5 | development: 6 | <<: *default 7 | database: kube_queue_myapp_development 8 | 9 | test: 10 | <<: *default 11 | database: kube_queue_myapp_test 12 | 13 | production: 14 | <<: *default 15 | database: kube_queue_myapp_production 16 | -------------------------------------------------------------------------------- /examples/myapp/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /examples/myapp/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | # Run rails dev:cache to toggle caching. 17 | if Rails.root.join('tmp', 'caching-dev.txt').exist? 18 | config.action_controller.perform_caching = true 19 | 20 | config.cache_store = :memory_store 21 | config.public_file_server.headers = { 22 | 'Cache-Control' => "public, max-age=#{2.days.to_i}" 23 | } 24 | else 25 | config.action_controller.perform_caching = false 26 | 27 | config.cache_store = :null_store 28 | end 29 | 30 | # Store uploaded files on the local file system (see config/storage.yml for options) 31 | config.active_storage.service = :local 32 | 33 | # Don't care if the mailer can't send. 34 | config.action_mailer.raise_delivery_errors = false 35 | 36 | config.action_mailer.perform_caching = false 37 | 38 | # Print deprecation notices to the Rails logger. 39 | config.active_support.deprecation = :log 40 | 41 | # Raise an error on page load if there are pending migrations. 42 | config.active_record.migration_error = :page_load 43 | 44 | # Highlight code that triggered database queries in logs. 45 | config.active_record.verbose_query_logs = true 46 | 47 | # Debug mode disables concatenation and preprocessing of assets. 48 | # This option may cause significant delays in view rendering with a large 49 | # number of complex assets. 50 | config.assets.debug = true 51 | 52 | # Suppress logger output for asset requests. 53 | config.assets.quiet = true 54 | 55 | # Raises error for missing translations 56 | # config.action_view.raise_on_missing_translations = true 57 | 58 | # Use an evented file watcher to asynchronously detect changes in source code, 59 | # routes, locales, etc. This feature depends on the listen gem. 60 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 61 | end 62 | -------------------------------------------------------------------------------- /examples/myapp/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] 18 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). 19 | # config.require_master_key = true 20 | 21 | # Disable serving static files from the `/public` folder by default since 22 | # Apache or NGINX already handles this. 23 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 24 | 25 | # Compress JavaScripts and CSS. 26 | config.assets.js_compressor = :uglifier 27 | # config.assets.css_compressor = :sass 28 | 29 | # Do not fallback to assets pipeline if a precompiled asset is missed. 30 | config.assets.compile = false 31 | 32 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 33 | 34 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 35 | # config.action_controller.asset_host = 'http://assets.example.com' 36 | 37 | # Specifies the header that your server uses for sending files. 38 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 39 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 40 | 41 | # Store uploaded files on the local file system (see config/storage.yml for options) 42 | config.active_storage.service = :local 43 | 44 | # Mount Action Cable outside main process or domain 45 | # config.action_cable.mount_path = nil 46 | # config.action_cable.url = 'wss://example.com/cable' 47 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] 48 | 49 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 50 | # config.force_ssl = true 51 | 52 | # Use the lowest log level to ensure availability of diagnostic information 53 | # when problems arise. 54 | config.log_level = :debug 55 | 56 | # Prepend all log lines with the following tags. 57 | config.log_tags = [ :request_id ] 58 | 59 | # Use a different cache store in production. 60 | # config.cache_store = :mem_cache_store 61 | 62 | # Use a real queuing backend for Active Job (and separate queues per environment) 63 | # config.active_job.queue_adapter = :resque 64 | # config.active_job.queue_name_prefix = "myapp_#{Rails.env}" 65 | 66 | config.action_mailer.perform_caching = false 67 | 68 | # Ignore bad email addresses and do not raise email delivery errors. 69 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 70 | # config.action_mailer.raise_delivery_errors = false 71 | 72 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 73 | # the I18n.default_locale when a translation cannot be found). 74 | config.i18n.fallbacks = true 75 | 76 | # Send deprecation notices to registered listeners. 77 | config.active_support.deprecation = :notify 78 | 79 | # Use default logging formatter so that PID and timestamp are not suppressed. 80 | config.log_formatter = ::Logger::Formatter.new 81 | 82 | # Use a different logger for distributed setups. 83 | # require 'syslog/logger' 84 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 85 | 86 | if ENV["RAILS_LOG_TO_STDOUT"].present? 87 | logger = ActiveSupport::Logger.new(STDOUT) 88 | logger.formatter = config.log_formatter 89 | config.logger = ActiveSupport::TaggedLogging.new(logger) 90 | end 91 | 92 | # Do not dump schema after migrations. 93 | config.active_record.dump_schema_after_migration = false 94 | end 95 | -------------------------------------------------------------------------------- /examples/myapp/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = true 17 | config.public_file_server.headers = { 18 | 'Cache-Control' => "public, max-age=#{1.hour.to_i}" 19 | } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | 31 | # Store uploaded files on the local file system in a temporary directory 32 | config.active_storage.service = :test 33 | 34 | config.action_mailer.perform_caching = false 35 | 36 | # Tell Action Mailer not to deliver emails to the real world. 37 | # The :test delivery method accumulates sent emails in the 38 | # ActionMailer::Base.deliveries array. 39 | config.action_mailer.delivery_method = :test 40 | 41 | # Print deprecation notices to the stderr. 42 | config.active_support.deprecation = :stderr 43 | 44 | # Raises error for missing translations 45 | # config.action_view.raise_on_missing_translations = true 46 | end 47 | -------------------------------------------------------------------------------- /examples/myapp/config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ActiveSupport::Reloader.to_prepare do 4 | # ApplicationController.renderer.defaults.merge!( 5 | # http_host: 'example.org', 6 | # https: false 7 | # ) 8 | # end 9 | -------------------------------------------------------------------------------- /examples/myapp/config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | # Add Yarn node_modules folder to the asset load path. 9 | Rails.application.config.assets.paths << Rails.root.join('node_modules') 10 | 11 | # Precompile additional assets. 12 | # application.js, application.css, and all non-JS/CSS in the app/assets 13 | # folder are already added. 14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 15 | -------------------------------------------------------------------------------- /examples/myapp/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /examples/myapp/config/initializers/content_security_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide content security policy 4 | # For further information see the following documentation 5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy 6 | 7 | # Rails.application.config.content_security_policy do |policy| 8 | # policy.default_src :self, :https 9 | # policy.font_src :self, :https, :data 10 | # policy.img_src :self, :https, :data 11 | # policy.object_src :none 12 | # policy.script_src :self, :https 13 | # policy.style_src :self, :https 14 | 15 | # # Specify URI for violation reports 16 | # # policy.report_uri "/csp-violation-report-endpoint" 17 | # end 18 | 19 | # If you are using UJS then enable automatic nonce generation 20 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } 21 | 22 | # Report CSP violations to a specified URI 23 | # For further information see the following documentation: 24 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only 25 | # Rails.application.config.content_security_policy_report_only = true 26 | -------------------------------------------------------------------------------- /examples/myapp/config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /examples/myapp/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /examples/myapp/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /examples/myapp/config/initializers/kube_queue.rb: -------------------------------------------------------------------------------- 1 | KubeQueue.kubernetes_configure do |client| 2 | client.url = ENV['K8S_URL'] 3 | client.ssl_ca_file = ENV['K8S_CA_CERT_FILE'] 4 | client.auth_token = File.read(ENV['K8S_BEARER_TOKEN_FILE']) if File.exists?(ENV['K8S_BEARER_TOKEN_FILE'].to_s) 5 | end 6 | -------------------------------------------------------------------------------- /examples/myapp/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /examples/myapp/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /examples/myapp/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # 'true': 'foo' 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at http://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /examples/myapp/config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers: a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum; this matches the default thread size of Active Record. 6 | # 7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 8 | threads threads_count, threads_count 9 | 10 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 11 | # 12 | port ENV.fetch("PORT") { 3000 } 13 | 14 | # Specifies the `environment` that Puma will run in. 15 | # 16 | environment ENV.fetch("RAILS_ENV") { "development" } 17 | 18 | # Specifies the number of `workers` to boot in clustered mode. 19 | # Workers are forked webserver processes. If using threads and workers together 20 | # the concurrency of the application would be max `threads` * `workers`. 21 | # Workers do not work on JRuby or Windows (both of which do not support 22 | # processes). 23 | # 24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 25 | 26 | # Use the `preload_app!` method when specifying a `workers` number. 27 | # This directive tells Puma to first boot the application and load code 28 | # before forking the application. This takes advantage of Copy On Write 29 | # process behavior so workers use less memory. 30 | # 31 | # preload_app! 32 | 33 | # Allow puma to be restarted by `rails restart` command. 34 | plugin :tmp_restart 35 | -------------------------------------------------------------------------------- /examples/myapp/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root to: 'tasks#index' 3 | 4 | resources :tasks 5 | post 'task/import', to: 'import_tasks#create' 6 | end 7 | -------------------------------------------------------------------------------- /examples/myapp/config/spring.rb: -------------------------------------------------------------------------------- 1 | %w[ 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ].each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /examples/myapp/config/storage.yml: -------------------------------------------------------------------------------- 1 | test: 2 | service: Disk 3 | root: <%= Rails.root.join("tmp/storage") %> 4 | 5 | local: 6 | service: Disk 7 | root: <%= Rails.root.join("storage") %> 8 | 9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) 10 | # amazon: 11 | # service: S3 12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> 13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> 14 | # region: us-east-1 15 | # bucket: your_own_bucket 16 | 17 | # Remember not to checkin your GCS keyfile to a repository 18 | # google: 19 | # service: GCS 20 | # project: your_project 21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> 22 | # bucket: your_own_bucket 23 | 24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) 25 | # microsoft: 26 | # service: AzureStorage 27 | # storage_account_name: your_account_name 28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> 29 | # container: your_container_name 30 | 31 | # mirror: 32 | # service: Mirror 33 | # primary: local 34 | # mirrors: [ amazon, google, microsoft ] 35 | -------------------------------------------------------------------------------- /examples/myapp/db/migrate/20190813062954_create_tasks.rb: -------------------------------------------------------------------------------- 1 | class CreateTasks < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :tasks do |t| 4 | t.integer :state, default: 0, null: false 5 | t.string :name, null: false 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /examples/myapp/db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # Note that this schema.rb definition is the authoritative source for your 6 | # database schema. If you need to create the application database on another 7 | # system, you should be using db:schema:load, not running all the migrations 8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 9 | # you'll amass, the slower it'll run and the greater likelihood for issues). 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(version: 2019_08_13_062954) do 14 | 15 | create_table "tasks", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t| 16 | t.integer "state", default: 0, null: false 17 | t.string "name", null: false 18 | t.datetime "created_at", null: false 19 | t.datetime "updated_at", null: false 20 | end 21 | 22 | end 23 | -------------------------------------------------------------------------------- /examples/myapp/db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) 7 | # Character.create(name: 'Luke', movie: movies.first) 8 | -------------------------------------------------------------------------------- /examples/myapp/k8s/app.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: kube-queue-test-app 5 | namespace: default 6 | --- 7 | apiVersion: rbac.authorization.k8s.io/v1 8 | kind: ClusterRoleBinding 9 | metadata: 10 | name: kube-queue-test-app 11 | roleRef: 12 | apiGroup: rbac.authorization.k8s.io 13 | kind: ClusterRole 14 | name: cluster-admin 15 | subjects: 16 | - kind: ServiceAccount 17 | name: kube-queue-test-app 18 | namespace: default 19 | --- 20 | apiVersion: v1 21 | kind: Secret 22 | metadata: 23 | name: myapp 24 | namespace: default 25 | data: 26 | DATABASE_URL: bXlzcWwyOi8vcm9vdEBkYXRhYmFzZTozMzA2 27 | SECRET_KEY_BASE: ZTkxMzUzYzcwZTYyMzUwZTJmMTE1YWVkNjlmNjhhNWQ0MjhiMjkyMzM3ZTdlOWIxYmIwMzRhY2Y5ZDZlYTA4YjQ5OTE1NmU1MGQxNWRlNjZkYTQ2YTAwYzFhZmQwNzNhM2ZiNzRmNzIwMTFiZThiYjliYThlNjliZWRiMTc3NjE= 28 | type: Opaque 29 | --- 30 | apiVersion: v1 31 | kind: ConfigMap 32 | metadata: 33 | name: myapp 34 | namespace: default 35 | data: 36 | K8S_URL: https://kubernetes.default:443 37 | K8S_BEARER_TOKEN_FILE: /var/run/secrets/kubernetes.io/serviceaccount/token 38 | K8S_CA_CERT_FILE: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt 39 | PROJECT_ID: savvy-temple-184102 40 | LANG: C.UTF-8 41 | LOG_LEVEL: DEBUG 42 | RAILS_ENV: production 43 | RAILS_LOG_TO_STDOUT: "true" 44 | RAILS_SERVE_STATIC_FILES: "1" 45 | --- 46 | apiVersion: v1 47 | kind: Service 48 | metadata: 49 | name: myapp 50 | namespace: default 51 | spec: 52 | type: ClusterIP 53 | ports: 54 | - name: myapp 55 | port: 3000 56 | targetPort: 3000 57 | protocol: TCP 58 | selector: 59 | app: myapp 60 | --- 61 | apiVersion: apps/v1 62 | kind: Deployment 63 | metadata: 64 | name: myapp 65 | namespace: default 66 | spec: 67 | replicas: 1 68 | selector: 69 | matchLabels: 70 | app: myapp 71 | template: 72 | metadata: 73 | name: myapp 74 | labels: 75 | app: myapp 76 | spec: 77 | serviceAccountName: kube-queue-test-app 78 | initContainers: 79 | - name: myapp-migration 80 | image: 'gcr.io/savvy-temple-184102/kube-queue-test-app' 81 | command: ["bundle", "exec", "rails", "db:create", "db:migrate"] 82 | envFrom: 83 | - configMapRef: 84 | name: myapp 85 | - secretRef: 86 | name: myapp 87 | containers: 88 | - name: myapp 89 | image: 'gcr.io/savvy-temple-184102/kube-queue-test-app' 90 | command: ["bundle", "exec", "rails", "server"] 91 | envFrom: 92 | - configMapRef: 93 | name: myapp 94 | - secretRef: 95 | name: myapp 96 | ports: 97 | - containerPort: 3000 98 | -------------------------------------------------------------------------------- /examples/myapp/k8s/database.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: database 5 | labels: 6 | app: database 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: database 12 | template: 13 | metadata: 14 | labels: 15 | app: database 16 | spec: 17 | containers: 18 | - image: mysql:5.7 19 | name: database 20 | imagePullPolicy: IfNotPresent 21 | env: 22 | - name: MYSQL_ALLOW_EMPTY_PASSWORD 23 | value: "true" 24 | ports: 25 | - containerPort: 3306 26 | livenessProbe: 27 | exec: 28 | command: 29 | - mysqladmin 30 | - ping 31 | readinessProbe: 32 | exec: 33 | command: 34 | - mysqladmin 35 | - ping 36 | restartPolicy: Always 37 | volumes: 38 | --- 39 | apiVersion: v1 40 | kind: Service 41 | metadata: 42 | labels: 43 | app: database 44 | name: database 45 | spec: 46 | type: ClusterIP 47 | ports: 48 | - name: mysql 49 | port: 3306 50 | protocol: TCP 51 | targetPort: 3306 52 | selector: 53 | app: database 54 | -------------------------------------------------------------------------------- /examples/myapp/lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuemori/kube_queue/df546ebf7a927884fc2318bdd7cf8ca36af91f72/examples/myapp/lib/assets/.keep -------------------------------------------------------------------------------- /examples/myapp/lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuemori/kube_queue/df546ebf7a927884fc2318bdd7cf8ca36af91f72/examples/myapp/lib/tasks/.keep -------------------------------------------------------------------------------- /examples/myapp/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuemori/kube_queue/df546ebf7a927884fc2318bdd7cf8ca36af91f72/examples/myapp/log/.keep -------------------------------------------------------------------------------- /examples/myapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "myapp", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /examples/myapp/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /examples/myapp/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /examples/myapp/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /examples/myapp/public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuemori/kube_queue/df546ebf7a927884fc2318bdd7cf8ca36af91f72/examples/myapp/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /examples/myapp/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuemori/kube_queue/df546ebf7a927884fc2318bdd7cf8ca36af91f72/examples/myapp/public/apple-touch-icon.png -------------------------------------------------------------------------------- /examples/myapp/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuemori/kube_queue/df546ebf7a927884fc2318bdd7cf8ca36af91f72/examples/myapp/public/favicon.ico -------------------------------------------------------------------------------- /examples/myapp/public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | -------------------------------------------------------------------------------- /examples/myapp/storage/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuemori/kube_queue/df546ebf7a927884fc2318bdd7cf8ca36af91f72/examples/myapp/storage/.keep -------------------------------------------------------------------------------- /examples/myapp/tasks.csv: -------------------------------------------------------------------------------- 1 | 2 | task1,doing 3 | task2,doing 4 | task3,doing 5 | task4,doing 6 | task5,doing 7 | task6,doing 8 | task7,doing 9 | task8,doing 10 | task9,doing 11 | task10,doing 12 | task11,doing 13 | task12,doing 14 | task13,doing 15 | task14,doing 16 | task15,doing 17 | task16,doing 18 | task17,doing 19 | task18,doing 20 | task19,doing 21 | task20,doing 22 | task21,doing 23 | task22,doing 24 | task23,doing 25 | task24,doing 26 | task25,doing 27 | task26,doing 28 | task27,doing 29 | task28,doing 30 | task29,doing 31 | task30,doing 32 | task31,doing 33 | task32,doing 34 | task33,doing 35 | task34,doing 36 | task35,doing 37 | task36,doing 38 | task37,doing 39 | task38,doing 40 | task39,doing 41 | task40,doing 42 | task41,doing 43 | task42,doing 44 | task43,doing 45 | task44,doing 46 | task45,doing 47 | task46,doing 48 | task47,doing 49 | task48,doing 50 | task49,doing 51 | task50,doing 52 | task51,doing 53 | task52,doing 54 | task53,doing 55 | task54,doing 56 | task55,doing 57 | task56,doing 58 | task57,doing 59 | task58,doing 60 | task59,doing 61 | task60,doing 62 | task61,doing 63 | task62,doing 64 | task63,doing 65 | task64,doing 66 | task65,doing 67 | task66,doing 68 | task67,doing 69 | task68,doing 70 | task69,doing 71 | task70,doing 72 | task71,doing 73 | task72,doing 74 | task73,doing 75 | task74,doing 76 | task75,doing 77 | task76,doing 78 | task77,doing 79 | task78,doing 80 | task79,doing 81 | task80,doing 82 | task81,doing 83 | task82,doing 84 | task83,doing 85 | task84,doing 86 | task85,doing 87 | task86,doing 88 | task87,doing 89 | task88,doing 90 | task89,doing 91 | task90,doing 92 | task91,doing 93 | task92,doing 94 | task93,doing 95 | task94,doing 96 | task95,doing 97 | task96,doing 98 | task97,doing 99 | task98,doing 100 | task99,doing 101 | task100,doing 102 | task101,todo 103 | task102,todo 104 | task103,todo 105 | task104,todo 106 | task105,todo 107 | task106,todo 108 | task107,todo 109 | task108,todo 110 | task109,todo 111 | task110,todo 112 | task111,todo 113 | task112,todo 114 | task113,todo 115 | task114,todo 116 | task115,todo 117 | task116,todo 118 | task117,todo 119 | task118,todo 120 | task119,todo 121 | task120,todo 122 | task121,todo 123 | task122,todo 124 | task123,todo 125 | task124,todo 126 | task125,todo 127 | task126,todo 128 | task127,todo 129 | task128,todo 130 | task129,todo 131 | task130,todo 132 | task131,todo 133 | task132,todo 134 | task133,todo 135 | task134,todo 136 | task135,todo 137 | task136,todo 138 | task137,todo 139 | task138,todo 140 | task139,todo 141 | task140,todo 142 | task141,todo 143 | task142,todo 144 | task143,todo 145 | task144,todo 146 | task145,todo 147 | task146,todo 148 | task147,todo 149 | task148,todo 150 | task149,todo 151 | task150,todo 152 | task151,todo 153 | task152,todo 154 | task153,todo 155 | task154,todo 156 | task155,todo 157 | task156,todo 158 | task157,todo 159 | task158,todo 160 | task159,todo 161 | task160,todo 162 | task161,todo 163 | task162,todo 164 | task163,todo 165 | task164,todo 166 | task165,todo 167 | task166,todo 168 | task167,todo 169 | task168,todo 170 | task169,todo 171 | task170,todo 172 | task171,todo 173 | task172,todo 174 | task173,todo 175 | task174,todo 176 | task175,todo 177 | task176,todo 178 | task177,todo 179 | task178,todo 180 | task179,todo 181 | task180,todo 182 | task181,todo 183 | task182,todo 184 | task183,todo 185 | task184,todo 186 | task185,todo 187 | task186,todo 188 | task187,todo 189 | task188,todo 190 | task189,todo 191 | task190,todo 192 | task191,todo 193 | task192,todo 194 | task193,todo 195 | task194,todo 196 | task195,todo 197 | task196,todo 198 | task197,todo 199 | task198,todo 200 | task199,todo 201 | task200,todo 202 | task201,done 203 | task202,done 204 | task203,done 205 | task204,done 206 | task205,done 207 | task206,done 208 | task207,done 209 | task208,done 210 | task209,done 211 | task210,done 212 | task211,done 213 | task212,done 214 | task213,done 215 | task214,done 216 | task215,done 217 | task216,done 218 | task217,done 219 | task218,done 220 | task219,done 221 | task220,done 222 | task221,done 223 | task222,done 224 | task223,done 225 | task224,done 226 | task225,done 227 | task226,done 228 | task227,done 229 | task228,done 230 | task229,done 231 | task230,done 232 | task231,done 233 | task232,done 234 | task233,done 235 | task234,done 236 | task235,done 237 | task236,done 238 | task237,done 239 | task238,done 240 | task239,done 241 | task240,done 242 | task241,done 243 | task242,done 244 | task243,done 245 | task244,done 246 | task245,done 247 | task246,done 248 | task247,done 249 | task248,done 250 | task249,done 251 | task250,done 252 | task251,done 253 | task252,done 254 | task253,done 255 | task254,done 256 | task255,done 257 | task256,done 258 | task257,done 259 | task258,done 260 | task259,done 261 | task260,done 262 | task261,done 263 | task262,done 264 | task263,done 265 | task264,done 266 | task265,done 267 | task266,done 268 | task267,done 269 | task268,done 270 | task269,done 271 | task270,done 272 | task271,done 273 | task272,done 274 | task273,done 275 | task274,done 276 | task275,done 277 | task276,done 278 | task277,done 279 | task278,done 280 | task279,done 281 | task280,done 282 | task281,done 283 | task282,done 284 | task283,done 285 | task284,done 286 | task285,done 287 | task286,done 288 | task287,done 289 | task288,done 290 | task289,done 291 | task290,done 292 | task291,done 293 | task292,done 294 | task293,done 295 | task294,done 296 | task295,done 297 | task296,done 298 | task297,done 299 | task298,done 300 | task299,done 301 | task300,done 302 | -------------------------------------------------------------------------------- /examples/myapp/test/application_system_test_case.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase 4 | driven_by :selenium, using: :chrome, screen_size: [1400, 1400] 5 | end 6 | -------------------------------------------------------------------------------- /examples/myapp/test/controllers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuemori/kube_queue/df546ebf7a927884fc2318bdd7cf8ca36af91f72/examples/myapp/test/controllers/.keep -------------------------------------------------------------------------------- /examples/myapp/test/controllers/import_tasks_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ImportTasksControllerTest < ActionDispatch::IntegrationTest 4 | test "should get create" do 5 | get import_tasks_create_url 6 | assert_response :success 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /examples/myapp/test/controllers/tasks_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class TasksControllerTest < ActionDispatch::IntegrationTest 4 | setup do 5 | @task = tasks(:one) 6 | end 7 | 8 | test "should get index" do 9 | get tasks_url 10 | assert_response :success 11 | end 12 | 13 | test "should get new" do 14 | get new_task_url 15 | assert_response :success 16 | end 17 | 18 | test "should create task" do 19 | assert_difference('Task.count') do 20 | post tasks_url, params: { task: { name: @task.name, state: @task.state } } 21 | end 22 | 23 | assert_redirected_to task_url(Task.last) 24 | end 25 | 26 | test "should show task" do 27 | get task_url(@task) 28 | assert_response :success 29 | end 30 | 31 | test "should get edit" do 32 | get edit_task_url(@task) 33 | assert_response :success 34 | end 35 | 36 | test "should update task" do 37 | patch task_url(@task), params: { task: { name: @task.name, state: @task.state } } 38 | assert_redirected_to task_url(@task) 39 | end 40 | 41 | test "should destroy task" do 42 | assert_difference('Task.count', -1) do 43 | delete task_url(@task) 44 | end 45 | 46 | assert_redirected_to tasks_url 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /examples/myapp/test/fixtures/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuemori/kube_queue/df546ebf7a927884fc2318bdd7cf8ca36af91f72/examples/myapp/test/fixtures/.keep -------------------------------------------------------------------------------- /examples/myapp/test/fixtures/files/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuemori/kube_queue/df546ebf7a927884fc2318bdd7cf8ca36af91f72/examples/myapp/test/fixtures/files/.keep -------------------------------------------------------------------------------- /examples/myapp/test/fixtures/tasks.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | state: 1 5 | name: MyString 6 | 7 | two: 8 | state: 1 9 | name: MyString 10 | -------------------------------------------------------------------------------- /examples/myapp/test/helpers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuemori/kube_queue/df546ebf7a927884fc2318bdd7cf8ca36af91f72/examples/myapp/test/helpers/.keep -------------------------------------------------------------------------------- /examples/myapp/test/integration/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuemori/kube_queue/df546ebf7a927884fc2318bdd7cf8ca36af91f72/examples/myapp/test/integration/.keep -------------------------------------------------------------------------------- /examples/myapp/test/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuemori/kube_queue/df546ebf7a927884fc2318bdd7cf8ca36af91f72/examples/myapp/test/mailers/.keep -------------------------------------------------------------------------------- /examples/myapp/test/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuemori/kube_queue/df546ebf7a927884fc2318bdd7cf8ca36af91f72/examples/myapp/test/models/.keep -------------------------------------------------------------------------------- /examples/myapp/test/models/task_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class TaskTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /examples/myapp/test/system/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuemori/kube_queue/df546ebf7a927884fc2318bdd7cf8ca36af91f72/examples/myapp/test/system/.keep -------------------------------------------------------------------------------- /examples/myapp/test/system/tasks_test.rb: -------------------------------------------------------------------------------- 1 | require "application_system_test_case" 2 | 3 | class TasksTest < ApplicationSystemTestCase 4 | setup do 5 | @task = tasks(:one) 6 | end 7 | 8 | test "visiting the index" do 9 | visit tasks_url 10 | assert_selector "h1", text: "Tasks" 11 | end 12 | 13 | test "creating a Task" do 14 | visit tasks_url 15 | click_on "New Task" 16 | 17 | fill_in "Name", with: @task.name 18 | fill_in "State", with: @task.state 19 | click_on "Create Task" 20 | 21 | assert_text "Task was successfully created" 22 | click_on "Back" 23 | end 24 | 25 | test "updating a Task" do 26 | visit tasks_url 27 | click_on "Edit", match: :first 28 | 29 | fill_in "Name", with: @task.name 30 | fill_in "State", with: @task.state 31 | click_on "Update Task" 32 | 33 | assert_text "Task was successfully updated" 34 | click_on "Back" 35 | end 36 | 37 | test "destroying a Task" do 38 | visit tasks_url 39 | page.accept_confirm do 40 | click_on "Destroy", match: :first 41 | end 42 | 43 | assert_text "Task was successfully destroyed" 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /examples/myapp/test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | require_relative '../config/environment' 3 | require 'rails/test_help' 4 | 5 | class ActiveSupport::TestCase 6 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 7 | fixtures :all 8 | 9 | # Add more helper methods to be used by all tests here... 10 | end 11 | -------------------------------------------------------------------------------- /examples/myapp/tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuemori/kube_queue/df546ebf7a927884fc2318bdd7cf8ca36af91f72/examples/myapp/tmp/.keep -------------------------------------------------------------------------------- /examples/myapp/vendor/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuemori/kube_queue/df546ebf7a927884fc2318bdd7cf8ca36af91f72/examples/myapp/vendor/.keep -------------------------------------------------------------------------------- /examples/myapp/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | -------------------------------------------------------------------------------- /exe/kube_queue: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'rubygems' 4 | require 'thor' 5 | require 'kube_queue' 6 | 7 | module KubeQueue 8 | class CLI < Thor 9 | default_task :version 10 | 11 | desc 'runner JOB_NAME [PAYLOAD]', 'run worker' 12 | method_option :require, aliases: '-r', type: :string, desc: 'Location of Rails application with workers or file to require' 13 | method_option :rails, aliases: '-R', type: :boolean, desc: 'Location of Rails application with workers or file to require' 14 | def runner(job_name, payload = nil) 15 | load_files! 16 | 17 | # Infer application work on rails if require option does not specified. 18 | load_rails! if !options[:require] || options[:rails] 19 | 20 | payload ||= ENV['KUBE_QUEUE_MESSAGE_PAYLOAD'] 21 | payload = JSON.parse(payload) if payload 22 | # Compatibility for ActiveJob serialized payload 23 | payload = [payload] unless payload.is_a?(Array) 24 | payload = ActiveJob::Arguments.deserialize(payload) if defined?(ActiveJob::Arguments) 25 | 26 | job = KubeQueue.fetch_worker(job_name).new(*payload) 27 | 28 | job.perform_now 29 | end 30 | 31 | desc 'version', 'Prints version' 32 | def version 33 | say "KubeQueue version #{KubeQueue::VERSION}" 34 | end 35 | 36 | private 37 | 38 | def load_files! 39 | return unless options[:require] 40 | 41 | raise "#{options[:require]} dosent exist." unless File.exist?(options[:require]) 42 | 43 | files = File.directory?(options[:require]) ? Dir.glob(File.join(options[:require], '**/*.rb')) : [options[:require]] 44 | 45 | files.each do |file| 46 | require file 47 | end 48 | end 49 | 50 | def load_rails! 51 | require "rails" 52 | 53 | raise "KubeQueue does not supports this version of Rails" if ::Rails::VERSION::MAJOR < 5 54 | 55 | require 'rails' 56 | require 'kube_queue/railties' 57 | require File.expand_path('config/environment.rb') 58 | Rails.application.eager_load! 59 | end 60 | end 61 | end 62 | 63 | KubeQueue::CLI.start 64 | -------------------------------------------------------------------------------- /gemfiles/.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_RETRY: "1" 3 | -------------------------------------------------------------------------------- /gemfiles/rails_5.1.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "activejob", "5.1.7" 6 | 7 | gemspec path: "../" 8 | -------------------------------------------------------------------------------- /gemfiles/rails_5.1.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | kube_queue (0.4.0) 5 | k8s-client 6 | thor 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | activejob (5.1.7) 12 | activesupport (= 5.1.7) 13 | globalid (>= 0.3.6) 14 | activesupport (5.1.7) 15 | concurrent-ruby (~> 1.0, >= 1.0.2) 16 | i18n (>= 0.7, < 2) 17 | minitest (~> 5.1) 18 | tzinfo (~> 1.1) 19 | appraisal (2.2.0) 20 | bundler 21 | rake 22 | thor (>= 0.14.0) 23 | ast (2.4.0) 24 | byebug (11.0.1) 25 | coderay (1.1.2) 26 | concurrent-ruby (1.1.5) 27 | diff-lcs (1.3) 28 | dry-configurable (0.8.3) 29 | concurrent-ruby (~> 1.0) 30 | dry-core (~> 0.4, >= 0.4.7) 31 | dry-container (0.7.2) 32 | concurrent-ruby (~> 1.0) 33 | dry-configurable (~> 0.1, >= 0.1.3) 34 | dry-core (0.4.9) 35 | concurrent-ruby (~> 1.0) 36 | dry-equalizer (0.2.2) 37 | dry-inflector (0.1.2) 38 | dry-logic (0.6.1) 39 | concurrent-ruby (~> 1.0) 40 | dry-core (~> 0.2) 41 | dry-equalizer (~> 0.2) 42 | dry-struct (0.5.1) 43 | dry-core (~> 0.4, >= 0.4.3) 44 | dry-equalizer (~> 0.2) 45 | dry-types (~> 0.13) 46 | ice_nine (~> 0.11) 47 | dry-types (0.13.4) 48 | concurrent-ruby (~> 1.0) 49 | dry-container (~> 0.3) 50 | dry-core (~> 0.4, >= 0.4.4) 51 | dry-equalizer (~> 0.2) 52 | dry-inflector (~> 0.1, >= 0.1.2) 53 | dry-logic (~> 0.4, >= 0.4.2) 54 | erbh (0.1.3) 55 | excon (0.66.0) 56 | ffi (1.11.1) 57 | formatador (0.2.5) 58 | globalid (0.4.2) 59 | activesupport (>= 4.2.0) 60 | guard (2.15.0) 61 | formatador (>= 0.2.4) 62 | listen (>= 2.7, < 4.0) 63 | lumberjack (>= 1.0.12, < 2.0) 64 | nenv (~> 0.1) 65 | notiffany (~> 0.0) 66 | pry (>= 0.9.12) 67 | shellany (~> 0.0) 68 | thor (>= 0.18.1) 69 | guard-compat (1.2.1) 70 | guard-rspec (4.7.3) 71 | guard (~> 2.1) 72 | guard-compat (~> 1.1) 73 | rspec (>= 2.99.0, < 4.0) 74 | guard-rubocop (1.3.0) 75 | guard (~> 2.0) 76 | rubocop (~> 0.20) 77 | hashdiff (1.0.0) 78 | i18n (1.6.0) 79 | concurrent-ruby (~> 1.0) 80 | ice_nine (0.11.2) 81 | jaro_winkler (1.5.3) 82 | jsonpath (0.9.9) 83 | multi_json 84 | to_regexp (~> 0.2.1) 85 | k8s-client (0.10.3) 86 | dry-struct (~> 0.5.0) 87 | dry-types (~> 0.13.0) 88 | excon (~> 0.66) 89 | hashdiff (~> 1.0.0) 90 | jsonpath (~> 0.9.5) 91 | recursive-open-struct (~> 1.1.0) 92 | yajl-ruby (~> 1.4.0) 93 | yaml-safe_load_stream (~> 0.1) 94 | listen (3.1.5) 95 | rb-fsevent (~> 0.9, >= 0.9.4) 96 | rb-inotify (~> 0.9, >= 0.9.7) 97 | ruby_dep (~> 1.2) 98 | lumberjack (1.0.13) 99 | method_source (0.9.2) 100 | minitest (5.11.3) 101 | multi_json (1.13.1) 102 | nenv (0.3.0) 103 | notiffany (0.1.3) 104 | nenv (~> 0.1) 105 | shellany (~> 0.0) 106 | parallel (1.17.0) 107 | parser (2.6.3.0) 108 | ast (~> 2.4.0) 109 | pry (0.12.2) 110 | coderay (~> 1.1.0) 111 | method_source (~> 0.9.0) 112 | pry-byebug (3.7.0) 113 | byebug (~> 11.0) 114 | pry (~> 0.10) 115 | rainbow (3.0.0) 116 | rake (10.5.0) 117 | rb-fsevent (0.10.3) 118 | rb-inotify (0.10.0) 119 | ffi (~> 1.0) 120 | recursive-open-struct (1.1.0) 121 | rspec (3.8.0) 122 | rspec-core (~> 3.8.0) 123 | rspec-expectations (~> 3.8.0) 124 | rspec-mocks (~> 3.8.0) 125 | rspec-core (3.8.2) 126 | rspec-support (~> 3.8.0) 127 | rspec-expectations (3.8.4) 128 | diff-lcs (>= 1.2.0, < 2.0) 129 | rspec-support (~> 3.8.0) 130 | rspec-mocks (3.8.1) 131 | diff-lcs (>= 1.2.0, < 2.0) 132 | rspec-support (~> 3.8.0) 133 | rspec-support (3.8.2) 134 | rubocop (0.74.0) 135 | jaro_winkler (~> 1.5.1) 136 | parallel (~> 1.10) 137 | parser (>= 2.6) 138 | rainbow (>= 2.2.2, < 4.0) 139 | ruby-progressbar (~> 1.7) 140 | unicode-display_width (>= 1.4.0, < 1.7) 141 | rubocop-performance (1.4.1) 142 | rubocop (>= 0.71.0) 143 | rubocop-rspec (1.35.0) 144 | rubocop (>= 0.60.0) 145 | ruby-progressbar (1.10.1) 146 | ruby_dep (1.5.0) 147 | shellany (0.0.1) 148 | thor (0.20.3) 149 | thread_safe (0.3.6) 150 | to_regexp (0.2.1) 151 | tzinfo (1.2.5) 152 | thread_safe (~> 0.1) 153 | unicode-display_width (1.6.0) 154 | yajl-ruby (1.4.1) 155 | yaml-safe_load_stream (0.1.1) 156 | 157 | PLATFORMS 158 | ruby 159 | 160 | DEPENDENCIES 161 | activejob (= 5.1.7) 162 | appraisal 163 | bundler (~> 2.0) 164 | erbh 165 | guard 166 | guard-rspec 167 | guard-rubocop 168 | kube_queue! 169 | pry-byebug 170 | rake (~> 10.0) 171 | rspec (~> 3.0) 172 | rubocop 173 | rubocop-performance 174 | rubocop-rspec 175 | 176 | BUNDLED WITH 177 | 2.0.2 178 | -------------------------------------------------------------------------------- /gemfiles/rails_5.2.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "activejob", "5.2.3" 6 | 7 | gemspec path: "../" 8 | -------------------------------------------------------------------------------- /gemfiles/rails_5.2.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | kube_queue (0.4.0) 5 | k8s-client 6 | thor 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | activejob (5.2.3) 12 | activesupport (= 5.2.3) 13 | globalid (>= 0.3.6) 14 | activesupport (5.2.3) 15 | concurrent-ruby (~> 1.0, >= 1.0.2) 16 | i18n (>= 0.7, < 2) 17 | minitest (~> 5.1) 18 | tzinfo (~> 1.1) 19 | appraisal (2.2.0) 20 | bundler 21 | rake 22 | thor (>= 0.14.0) 23 | ast (2.4.0) 24 | byebug (11.0.1) 25 | coderay (1.1.2) 26 | concurrent-ruby (1.1.5) 27 | diff-lcs (1.3) 28 | dry-configurable (0.8.3) 29 | concurrent-ruby (~> 1.0) 30 | dry-core (~> 0.4, >= 0.4.7) 31 | dry-container (0.7.2) 32 | concurrent-ruby (~> 1.0) 33 | dry-configurable (~> 0.1, >= 0.1.3) 34 | dry-core (0.4.9) 35 | concurrent-ruby (~> 1.0) 36 | dry-equalizer (0.2.2) 37 | dry-inflector (0.1.2) 38 | dry-logic (0.6.1) 39 | concurrent-ruby (~> 1.0) 40 | dry-core (~> 0.2) 41 | dry-equalizer (~> 0.2) 42 | dry-struct (0.5.1) 43 | dry-core (~> 0.4, >= 0.4.3) 44 | dry-equalizer (~> 0.2) 45 | dry-types (~> 0.13) 46 | ice_nine (~> 0.11) 47 | dry-types (0.13.4) 48 | concurrent-ruby (~> 1.0) 49 | dry-container (~> 0.3) 50 | dry-core (~> 0.4, >= 0.4.4) 51 | dry-equalizer (~> 0.2) 52 | dry-inflector (~> 0.1, >= 0.1.2) 53 | dry-logic (~> 0.4, >= 0.4.2) 54 | erbh (0.1.3) 55 | excon (0.66.0) 56 | ffi (1.11.1) 57 | formatador (0.2.5) 58 | globalid (0.4.2) 59 | activesupport (>= 4.2.0) 60 | guard (2.15.0) 61 | formatador (>= 0.2.4) 62 | listen (>= 2.7, < 4.0) 63 | lumberjack (>= 1.0.12, < 2.0) 64 | nenv (~> 0.1) 65 | notiffany (~> 0.0) 66 | pry (>= 0.9.12) 67 | shellany (~> 0.0) 68 | thor (>= 0.18.1) 69 | guard-compat (1.2.1) 70 | guard-rspec (4.7.3) 71 | guard (~> 2.1) 72 | guard-compat (~> 1.1) 73 | rspec (>= 2.99.0, < 4.0) 74 | guard-rubocop (1.3.0) 75 | guard (~> 2.0) 76 | rubocop (~> 0.20) 77 | hashdiff (1.0.0) 78 | i18n (1.6.0) 79 | concurrent-ruby (~> 1.0) 80 | ice_nine (0.11.2) 81 | jaro_winkler (1.5.3) 82 | jsonpath (0.9.9) 83 | multi_json 84 | to_regexp (~> 0.2.1) 85 | k8s-client (0.10.3) 86 | dry-struct (~> 0.5.0) 87 | dry-types (~> 0.13.0) 88 | excon (~> 0.66) 89 | hashdiff (~> 1.0.0) 90 | jsonpath (~> 0.9.5) 91 | recursive-open-struct (~> 1.1.0) 92 | yajl-ruby (~> 1.4.0) 93 | yaml-safe_load_stream (~> 0.1) 94 | listen (3.1.5) 95 | rb-fsevent (~> 0.9, >= 0.9.4) 96 | rb-inotify (~> 0.9, >= 0.9.7) 97 | ruby_dep (~> 1.2) 98 | lumberjack (1.0.13) 99 | method_source (0.9.2) 100 | minitest (5.11.3) 101 | multi_json (1.13.1) 102 | nenv (0.3.0) 103 | notiffany (0.1.3) 104 | nenv (~> 0.1) 105 | shellany (~> 0.0) 106 | parallel (1.17.0) 107 | parser (2.6.3.0) 108 | ast (~> 2.4.0) 109 | pry (0.12.2) 110 | coderay (~> 1.1.0) 111 | method_source (~> 0.9.0) 112 | pry-byebug (3.7.0) 113 | byebug (~> 11.0) 114 | pry (~> 0.10) 115 | rainbow (3.0.0) 116 | rake (10.5.0) 117 | rb-fsevent (0.10.3) 118 | rb-inotify (0.10.0) 119 | ffi (~> 1.0) 120 | recursive-open-struct (1.1.0) 121 | rspec (3.8.0) 122 | rspec-core (~> 3.8.0) 123 | rspec-expectations (~> 3.8.0) 124 | rspec-mocks (~> 3.8.0) 125 | rspec-core (3.8.2) 126 | rspec-support (~> 3.8.0) 127 | rspec-expectations (3.8.4) 128 | diff-lcs (>= 1.2.0, < 2.0) 129 | rspec-support (~> 3.8.0) 130 | rspec-mocks (3.8.1) 131 | diff-lcs (>= 1.2.0, < 2.0) 132 | rspec-support (~> 3.8.0) 133 | rspec-support (3.8.2) 134 | rubocop (0.74.0) 135 | jaro_winkler (~> 1.5.1) 136 | parallel (~> 1.10) 137 | parser (>= 2.6) 138 | rainbow (>= 2.2.2, < 4.0) 139 | ruby-progressbar (~> 1.7) 140 | unicode-display_width (>= 1.4.0, < 1.7) 141 | rubocop-performance (1.4.1) 142 | rubocop (>= 0.71.0) 143 | rubocop-rspec (1.35.0) 144 | rubocop (>= 0.60.0) 145 | ruby-progressbar (1.10.1) 146 | ruby_dep (1.5.0) 147 | shellany (0.0.1) 148 | thor (0.20.3) 149 | thread_safe (0.3.6) 150 | to_regexp (0.2.1) 151 | tzinfo (1.2.5) 152 | thread_safe (~> 0.1) 153 | unicode-display_width (1.6.0) 154 | yajl-ruby (1.4.1) 155 | yaml-safe_load_stream (0.1.1) 156 | 157 | PLATFORMS 158 | ruby 159 | 160 | DEPENDENCIES 161 | activejob (= 5.2.3) 162 | appraisal 163 | bundler (~> 2.0) 164 | erbh 165 | guard 166 | guard-rspec 167 | guard-rubocop 168 | kube_queue! 169 | pry-byebug 170 | rake (~> 10.0) 171 | rspec (~> 3.0) 172 | rubocop 173 | rubocop-performance 174 | rubocop-rspec 175 | 176 | BUNDLED WITH 177 | 2.0.2 178 | -------------------------------------------------------------------------------- /gemfiles/rails_6.0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "activejob", "6.0.0.beta2" 6 | 7 | gemspec path: "../" 8 | -------------------------------------------------------------------------------- /gemfiles/rails_6.0.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | kube_queue (0.4.0) 5 | k8s-client 6 | thor 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | activejob (6.0.0.beta2) 12 | activesupport (= 6.0.0.beta2) 13 | globalid (>= 0.3.6) 14 | activesupport (6.0.0.beta2) 15 | concurrent-ruby (~> 1.0, >= 1.0.2) 16 | i18n (>= 0.7, < 2) 17 | minitest (~> 5.1) 18 | tzinfo (~> 1.1) 19 | zeitwerk (~> 1.3, >= 1.3.1) 20 | appraisal (2.2.0) 21 | bundler 22 | rake 23 | thor (>= 0.14.0) 24 | ast (2.4.0) 25 | byebug (11.0.1) 26 | coderay (1.1.2) 27 | concurrent-ruby (1.1.5) 28 | diff-lcs (1.3) 29 | dry-configurable (0.8.3) 30 | concurrent-ruby (~> 1.0) 31 | dry-core (~> 0.4, >= 0.4.7) 32 | dry-container (0.7.2) 33 | concurrent-ruby (~> 1.0) 34 | dry-configurable (~> 0.1, >= 0.1.3) 35 | dry-core (0.4.9) 36 | concurrent-ruby (~> 1.0) 37 | dry-equalizer (0.2.2) 38 | dry-inflector (0.1.2) 39 | dry-logic (0.6.1) 40 | concurrent-ruby (~> 1.0) 41 | dry-core (~> 0.2) 42 | dry-equalizer (~> 0.2) 43 | dry-struct (0.5.1) 44 | dry-core (~> 0.4, >= 0.4.3) 45 | dry-equalizer (~> 0.2) 46 | dry-types (~> 0.13) 47 | ice_nine (~> 0.11) 48 | dry-types (0.13.4) 49 | concurrent-ruby (~> 1.0) 50 | dry-container (~> 0.3) 51 | dry-core (~> 0.4, >= 0.4.4) 52 | dry-equalizer (~> 0.2) 53 | dry-inflector (~> 0.1, >= 0.1.2) 54 | dry-logic (~> 0.4, >= 0.4.2) 55 | erbh (0.1.3) 56 | excon (0.66.0) 57 | ffi (1.11.1) 58 | formatador (0.2.5) 59 | globalid (0.4.2) 60 | activesupport (>= 4.2.0) 61 | guard (2.15.0) 62 | formatador (>= 0.2.4) 63 | listen (>= 2.7, < 4.0) 64 | lumberjack (>= 1.0.12, < 2.0) 65 | nenv (~> 0.1) 66 | notiffany (~> 0.0) 67 | pry (>= 0.9.12) 68 | shellany (~> 0.0) 69 | thor (>= 0.18.1) 70 | guard-compat (1.2.1) 71 | guard-rspec (4.7.3) 72 | guard (~> 2.1) 73 | guard-compat (~> 1.1) 74 | rspec (>= 2.99.0, < 4.0) 75 | guard-rubocop (1.3.0) 76 | guard (~> 2.0) 77 | rubocop (~> 0.20) 78 | hashdiff (1.0.0) 79 | i18n (1.6.0) 80 | concurrent-ruby (~> 1.0) 81 | ice_nine (0.11.2) 82 | jaro_winkler (1.5.3) 83 | jsonpath (0.9.9) 84 | multi_json 85 | to_regexp (~> 0.2.1) 86 | k8s-client (0.10.3) 87 | dry-struct (~> 0.5.0) 88 | dry-types (~> 0.13.0) 89 | excon (~> 0.66) 90 | hashdiff (~> 1.0.0) 91 | jsonpath (~> 0.9.5) 92 | recursive-open-struct (~> 1.1.0) 93 | yajl-ruby (~> 1.4.0) 94 | yaml-safe_load_stream (~> 0.1) 95 | listen (3.1.5) 96 | rb-fsevent (~> 0.9, >= 0.9.4) 97 | rb-inotify (~> 0.9, >= 0.9.7) 98 | ruby_dep (~> 1.2) 99 | lumberjack (1.0.13) 100 | method_source (0.9.2) 101 | minitest (5.11.3) 102 | multi_json (1.13.1) 103 | nenv (0.3.0) 104 | notiffany (0.1.3) 105 | nenv (~> 0.1) 106 | shellany (~> 0.0) 107 | parallel (1.17.0) 108 | parser (2.6.3.0) 109 | ast (~> 2.4.0) 110 | pry (0.12.2) 111 | coderay (~> 1.1.0) 112 | method_source (~> 0.9.0) 113 | pry-byebug (3.7.0) 114 | byebug (~> 11.0) 115 | pry (~> 0.10) 116 | rainbow (3.0.0) 117 | rake (10.5.0) 118 | rb-fsevent (0.10.3) 119 | rb-inotify (0.10.0) 120 | ffi (~> 1.0) 121 | recursive-open-struct (1.1.0) 122 | rspec (3.8.0) 123 | rspec-core (~> 3.8.0) 124 | rspec-expectations (~> 3.8.0) 125 | rspec-mocks (~> 3.8.0) 126 | rspec-core (3.8.2) 127 | rspec-support (~> 3.8.0) 128 | rspec-expectations (3.8.4) 129 | diff-lcs (>= 1.2.0, < 2.0) 130 | rspec-support (~> 3.8.0) 131 | rspec-mocks (3.8.1) 132 | diff-lcs (>= 1.2.0, < 2.0) 133 | rspec-support (~> 3.8.0) 134 | rspec-support (3.8.2) 135 | rubocop (0.74.0) 136 | jaro_winkler (~> 1.5.1) 137 | parallel (~> 1.10) 138 | parser (>= 2.6) 139 | rainbow (>= 2.2.2, < 4.0) 140 | ruby-progressbar (~> 1.7) 141 | unicode-display_width (>= 1.4.0, < 1.7) 142 | rubocop-performance (1.4.1) 143 | rubocop (>= 0.71.0) 144 | rubocop-rspec (1.35.0) 145 | rubocop (>= 0.60.0) 146 | ruby-progressbar (1.10.1) 147 | ruby_dep (1.5.0) 148 | shellany (0.0.1) 149 | thor (0.20.3) 150 | thread_safe (0.3.6) 151 | to_regexp (0.2.1) 152 | tzinfo (1.2.5) 153 | thread_safe (~> 0.1) 154 | unicode-display_width (1.6.0) 155 | yajl-ruby (1.4.1) 156 | yaml-safe_load_stream (0.1.1) 157 | zeitwerk (1.4.3) 158 | 159 | PLATFORMS 160 | ruby 161 | 162 | DEPENDENCIES 163 | activejob (= 6.0.0.beta2) 164 | appraisal 165 | bundler (~> 2.0) 166 | erbh 167 | guard 168 | guard-rspec 169 | guard-rubocop 170 | kube_queue! 171 | pry-byebug 172 | rake (~> 10.0) 173 | rspec (~> 3.0) 174 | rubocop 175 | rubocop-performance 176 | rubocop-rspec 177 | 178 | BUNDLED WITH 179 | 2.0.2 180 | -------------------------------------------------------------------------------- /kube_queue.gemspec: -------------------------------------------------------------------------------- 1 | lib = File.expand_path("lib", __dir__) 2 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 3 | require "kube_queue/version" 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = "kube_queue" 7 | spec.version = KubeQueue::VERSION 8 | spec.authors = ["yuemori"] 9 | spec.email = ["yuemori@aiming-inc.com"] 10 | 11 | spec.summary = "A background job processing with Kubernetes job for Ruby" 12 | spec.description = "A background job processing with Kubernetes job for Ruby" 13 | spec.homepage = "https://github.com/yuemori/kube_queue" 14 | spec.license = "MIT" 15 | 16 | spec.metadata["homepage_uri"] = spec.homepage 17 | spec.metadata["source_code_uri"] = "https://github.com/yuemori/kube_queue" 18 | spec.metadata["changelog_uri"] = "https://github.com/yuemori/kube_queue/CHANGELOG.md" 19 | 20 | spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do 21 | `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|examples|cloudbuild.yaml)/}) } 22 | end 23 | spec.bindir = "exe" 24 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 25 | spec.require_paths = ["lib"] 26 | 27 | spec.add_dependency "k8s-client" 28 | spec.add_dependency "thor" 29 | spec.add_development_dependency "bundler", "~> 2.0" 30 | spec.add_development_dependency "rake", "~> 10.0" 31 | spec.add_development_dependency "rspec", "~> 3.0" 32 | spec.add_development_dependency "erbh" 33 | spec.add_development_dependency "rubocop" 34 | spec.add_development_dependency "rubocop-performance" 35 | spec.add_development_dependency "rubocop-rspec" 36 | spec.add_development_dependency "pry-byebug" 37 | spec.add_development_dependency "guard" 38 | spec.add_development_dependency "guard-rspec" 39 | spec.add_development_dependency "guard-rubocop" 40 | spec.add_development_dependency "activejob" 41 | spec.add_development_dependency "appraisal" 42 | end 43 | -------------------------------------------------------------------------------- /lib/active_job/adapters/kube_queue_adapter.rb: -------------------------------------------------------------------------------- 1 | require 'kube_queue' 2 | 3 | module ActiveJob 4 | module QueueAdapters 5 | # == KubeQueue adapter for ActiveJob == 6 | # 7 | # To use KubeQueue set the queue_adapter config to +:kube_queue+. 8 | # Rails.application.config.active_job.queue_adapter = :kube_queue 9 | class KubeQueueAdapter 10 | class << self 11 | # Interface for ActiveJob 4.2 12 | def enqueue(job) 13 | KubeQueue.executor.enqueue(job) 14 | end 15 | 16 | def enqueue_at(job, timestamp) 17 | job.scheduled_at = timestamp 18 | KubeQueue.executor.enqueue(job) 19 | end 20 | end 21 | 22 | # Interface for ActiveJob 5.0 23 | def enqueue(job) 24 | KubeQueueAdapter.enqueue(job) 25 | end 26 | 27 | def enqueue_at(job, timestamp) 28 | job.scheduled_at = timestamp 29 | KubeQueueAdapter.enqueue(job) 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/kube_queue.rb: -------------------------------------------------------------------------------- 1 | require "kube_queue/version" 2 | require "kube_queue/executor" 3 | require "kube_queue/configuration" 4 | require "kube_queue/worker" 5 | require "kube_queue/client" 6 | require "active_job/adapters/kube_queue_adapter" if defined?(Rails) 7 | 8 | module KubeQueue 9 | class JobNotFound < StandardError; end 10 | 11 | class << self 12 | attr_writer :executor, :client 13 | 14 | def executor 15 | @executor ||= default_executor 16 | end 17 | 18 | def kubernetes_configure 19 | yield client 20 | end 21 | 22 | def client 23 | @client ||= default_client 24 | end 25 | 26 | def default_executor 27 | Executor.new 28 | end 29 | 30 | def default_client 31 | Client.new 32 | end 33 | 34 | def configure(&block) 35 | configuration.configure(&block) 36 | end 37 | 38 | def configuration 39 | @configuration ||= Configuration.new 40 | end 41 | 42 | attr_writer :default_env 43 | 44 | def default_env 45 | return @default_env if @default_env 46 | 47 | return {} unless defined?(Rails) 48 | 49 | { 50 | RAILS_LOG_TO_STDOUT: ENV['RAILS_LOG_TO_STDOUT'], 51 | RAILS_ENV: ENV['RAILS_ENV'] 52 | } 53 | end 54 | 55 | def fetch_worker(name) 56 | worker_registry.fetch(name) 57 | end 58 | 59 | def register_worker(name, klass) 60 | worker_registry[name] = klass 61 | end 62 | 63 | def worker_registry 64 | @worker_registry ||= {} 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /lib/kube_queue/client.rb: -------------------------------------------------------------------------------- 1 | require 'k8s-client' 2 | require 'pathname' 3 | 4 | module KubeQueue 5 | class Client 6 | def create_job(manifest) 7 | job = K8s::Resource.new(manifest) 8 | job.metadata.namespace ||= 'default' 9 | client.api('batch/v1').resource('jobs').create_resource(job) 10 | end 11 | 12 | def get_job(namespace, name) 13 | client.api('batch/v1').resource('jobs', namespace: namespace).get(name) 14 | end 15 | 16 | def list_job(job_class, namespace = nil) 17 | selector = { 'kube-queue-job': 'true', 'kube-queue-job-class': job_class } 18 | client.api('batch/v1').resource('jobs', namespace: namespace).list(labelSelector: selector) 19 | end 20 | 21 | def create_cron_job(manifest) 22 | cron_job = K8s::Resource.new(manifest) 23 | cron_job.metadata.namespace ||= 'default' 24 | client.api('batch/v1beta1').resource('cronjobs').create_resource(cron_job) 25 | end 26 | 27 | attr_accessor :url, :ssl_ca_file, :auth_token 28 | 29 | private 30 | 31 | def client 32 | @client ||= K8s.client(url, ssl_ca_file: ssl_ca_file, auth_token: auth_token) 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/kube_queue/configuration.rb: -------------------------------------------------------------------------------- 1 | module KubeQueue 2 | class Configuration 3 | def configure 4 | yield self 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/kube_queue/executor.rb: -------------------------------------------------------------------------------- 1 | require 'kube_queue/manifest_builder' 2 | 3 | module KubeQueue 4 | class Executor 5 | def enqueue(job) 6 | resource = if job.scheduled_at 7 | KubeQueue.client.create_cron_job(job.manifest) 8 | else 9 | KubeQueue.client.create_job(job.manifest) 10 | end 11 | 12 | job.resource = resource 13 | resource 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/kube_queue/job_specification.rb: -------------------------------------------------------------------------------- 1 | require 'erb' 2 | require 'yaml' 3 | 4 | module KubeQueue 5 | class JobSpecification 6 | class MissingParameterError < StandardError; end 7 | 8 | attr_reader :job_class 9 | 10 | attr_accessor :payload, :name, :active_deadline_seconds, :backoff_limit, :starting_deadline_seconds, 11 | :cpu_limit, :memory_limit, :cpu_request, :memory_request 12 | 13 | attr_writer :image, :namespace, :worker_name, :command, 14 | :container_name, :restart_policy, :job_labels, :pod_labels, 15 | :env_from_config_map, :env_from_secret, :concurrent_policy, :env 16 | 17 | def initialize(job_class) 18 | @job_class = job_class 19 | end 20 | 21 | def job_name(job_id) 22 | "#{worker_name}-#{job_id}" 23 | end 24 | 25 | def image 26 | @image || raise_not_found_required_parameter('image') 27 | end 28 | 29 | def namespace 30 | @namespace || 'default' 31 | end 32 | 33 | def worker_name 34 | @worker_name || raise_not_found_required_parameter('worker_name') 35 | end 36 | 37 | def container_name 38 | @container_name || worker_name 39 | end 40 | 41 | def command 42 | @command || ['bundle', 'exec', 'kube_queue', 'runner', job_class.name] 43 | end 44 | 45 | def restart_policy 46 | @restart_policy || 'Never' 47 | end 48 | 49 | def job_labels 50 | @job_labels || {} 51 | end 52 | 53 | def pod_labels 54 | @pod_labels || {} 55 | end 56 | 57 | def env 58 | KubeQueue.default_env.merge(@env || {}) 59 | end 60 | 61 | def env_from_config_map 62 | @env_from_config_map || [] 63 | end 64 | 65 | def env_from_secret 66 | @env_from_config_map || [] 67 | end 68 | 69 | def env_from_exists? 70 | !env_from_config_map.empty? && !env_from_secret.empty? 71 | end 72 | 73 | def concurrent_policy 74 | @concurrent_policy || 'Allow' 75 | end 76 | 77 | def resources_exists? 78 | @cpu_limit || @memory_limit || @cpu_request || @memory_request 79 | end 80 | 81 | def raise_not_found_required_parameter(field) 82 | raise MissingParameterError, "#{field} is required" 83 | end 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /lib/kube_queue/manifest_builder.rb: -------------------------------------------------------------------------------- 1 | require 'erb' 2 | require 'json' 3 | 4 | module KubeQueue 5 | class ManifestBuilder 6 | attr_reader :job 7 | 8 | def initialize(job) 9 | @job = job 10 | end 11 | 12 | def spec 13 | job.job_spec 14 | end 15 | 16 | def payload 17 | JSON.generate(job.serialized_payload, quirks_mode: true) 18 | end 19 | 20 | def build_job 21 | YAML.safe_load(ERB.new(job.read_template, nil, "-").result(binding)) 22 | end 23 | 24 | def build_cron_job(cron) 25 | template = YAML.safe_load(ERB.new(job.read_template, nil, "-").result(binding)) 26 | 27 | { 28 | apiVersion: "batch/v1beta1", 29 | kind: "CronJob", 30 | metadata: template["metadata"], 31 | spec: { 32 | startingDeadlineSeconds: job.job_spec.starting_deadline_seconds, 33 | concurrentPolicy: job.job_spec.concurrent_policy, 34 | schedule: cron, 35 | jobTemplate: { 36 | spec: template["spec"] 37 | } 38 | } 39 | } 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/kube_queue/railties.rb: -------------------------------------------------------------------------------- 1 | ActiveSupport.on_load(:active_job) do 2 | require "active_job/adapters/kube_queue_adapter" 3 | end 4 | -------------------------------------------------------------------------------- /lib/kube_queue/version.rb: -------------------------------------------------------------------------------- 1 | module KubeQueue 2 | VERSION = "0.4.1".freeze 3 | end 4 | -------------------------------------------------------------------------------- /lib/kube_queue/worker.rb: -------------------------------------------------------------------------------- 1 | require 'kube_queue/worker/dsl' 2 | require 'kube_queue/job_specification' 3 | 4 | module KubeQueue 5 | module Worker 6 | def self.included(base) 7 | base.extend(ClassMethods) 8 | 9 | KubeQueue.register_worker(base.name, base) 10 | end 11 | 12 | module ClassMethods 13 | include DSL 14 | 15 | attr_writer :template 16 | 17 | def template 18 | @template ||= File.expand_path('../../../template/job.yaml', __FILE__) 19 | end 20 | 21 | def active_job? 22 | defined?(ActiveJob) && ancestors.include?(ActiveJob::Base) 23 | end 24 | 25 | def list 26 | namespace = job_spec.namespace 27 | 28 | KubeQueue.client.list_job(job_spec.job_class, namespace).map do |res| 29 | worker = KubeQueue.fetch_worker(res.metadata.annotations['kube-queue-job-class']) 30 | job = worker.new(*payload) 31 | job.resource = res 32 | job 33 | end 34 | end 35 | 36 | def find(job_id) 37 | namespace = job_spec.namespace 38 | 39 | name = job_spec.job_name(job_id) 40 | 41 | res = KubeQueue.client.get_job(namespace, name) 42 | worker = KubeQueue.fetch_worker(res.metadata.annotations['kube-queue-job-class']) 43 | 44 | payload = deserialize_annotation_payload(res.metadata.annotations['kube-queue-job-payload']) 45 | 46 | job = worker.new(*payload) 47 | job.resource = res 48 | job 49 | end 50 | 51 | def enqueue(*args) 52 | job = new(*args) 53 | KubeQueue.executor.enqueue(job) 54 | job 55 | end 56 | alias_method :perform_async, :enqueue 57 | 58 | def enqueue_at(*args) 59 | args = args.dup 60 | timestamp = args.pop 61 | job = new(*args) 62 | job.scheduled_at = timestamp 63 | KubeQueue.executor.enqueue(job) 64 | job 65 | end 66 | 67 | def read_template 68 | File.read(template) 69 | end 70 | 71 | def manifest 72 | new.manifest 73 | end 74 | 75 | private 76 | 77 | def deserialize_annotation_payload(payload) 78 | return payload if payload.empty? 79 | 80 | payload = JSON.parse(payload) 81 | 82 | # Compatibility for ActiveJob serialized payload 83 | payload = [payload] unless payload.is_a?(Array) 84 | 85 | payload = ActiveJob::Arguments.deserialize(payload) if defined?(ActiveJob::Arguments) 86 | 87 | payload 88 | end 89 | end 90 | 91 | def read_template 92 | self.class.read_template 93 | end 94 | 95 | def job_spec 96 | self.class.job_spec 97 | end 98 | 99 | attr_accessor :job_id, :scheduled_at 100 | attr_reader :arguments, :resource 101 | 102 | alias_method :payload, :arguments 103 | 104 | def initialize(*arguments) 105 | # Compatibility for ActiveJob interface 106 | if method(__method__).super_method.arity.zero? 107 | super() 108 | else 109 | super 110 | end 111 | 112 | @arguments = arguments 113 | @job_id = SecureRandom.uuid 114 | @loaded = false 115 | end 116 | 117 | def perform_now 118 | # Compatibility for ActiveJob interface 119 | return super if defined?(super) 120 | 121 | perform(*arguments) 122 | end 123 | 124 | def perform(*) 125 | raise NotImplementedError 126 | end 127 | 128 | def status 129 | return @resource.status if loaded? 130 | 131 | load_target 132 | 133 | @resource.status 134 | end 135 | 136 | def loaded? 137 | @loaded 138 | end 139 | 140 | def reload! 141 | @loaded = false 142 | @resource = nil 143 | 144 | load_target 145 | end 146 | 147 | def manifest 148 | if scheduled_at 149 | # Kubernetes CronJob does not support timezone 150 | cron = Time.at(scheduled_at).utc.strftime("%M %H %d %m %w") 151 | ManifestBuilder.new(self).build_cron_job(cron) 152 | else 153 | ManifestBuilder.new(self).build_job 154 | end 155 | end 156 | 157 | def serialized_payload 158 | if self.class.active_job? 159 | ActiveJob::Arguments.serialize(arguments) 160 | else 161 | arguments 162 | end 163 | end 164 | 165 | def resource=(resource) 166 | @resource = resource 167 | self.job_id = resource.metadata.annotations['kube-queue-job-id'] 168 | end 169 | 170 | private 171 | 172 | def load_target 173 | self.resource = KubeQueue.client.get_job(job_spec.namespace, job_spec.job_name(job_id)) 174 | @loaded = true 175 | end 176 | end 177 | end 178 | -------------------------------------------------------------------------------- /lib/kube_queue/worker/dsl.rb: -------------------------------------------------------------------------------- 1 | module KubeQueue 2 | module Worker 3 | module DSL 4 | def job_spec 5 | @job_spec ||= JobSpecification.new(self) 6 | end 7 | 8 | def worker_name(name) 9 | job_spec.worker_name = name 10 | end 11 | 12 | def container_name(container_name) 13 | job_spec.container_name = container_name 14 | end 15 | 16 | def image(image) 17 | job_spec.image = image 18 | end 19 | 20 | def namespace(namespace) 21 | job_spec.namespace = namespace 22 | end 23 | 24 | def command(*command) 25 | job_spec.command = command 26 | end 27 | 28 | def restart_policy(policy) 29 | job_spec.restart_policy = policy 30 | end 31 | 32 | def active_deadline_seconds(seconds) 33 | job_spec.active_deadline_seconds = seconds.to_s 34 | end 35 | 36 | def backoff_limit(limit) 37 | job_spec.backoff_limit = limit 38 | end 39 | 40 | def env(env) 41 | job_spec.env = env 42 | end 43 | 44 | def labels(labels) 45 | job_spec.labels = labels 46 | end 47 | 48 | def env_from_config_map(*config_map_names) 49 | job_spec.env_from_config_map = config_map_names 50 | end 51 | 52 | def env_from_secret(*secret_names) 53 | job_spec.env_from_config_map = secret_names 54 | end 55 | 56 | def cpu_limit(limit) 57 | job_spec.cpu_limit = limit 58 | end 59 | 60 | def memory_limit(limit) 61 | job_spec.memory_limit = limit 62 | end 63 | 64 | def cpu_request(request) 65 | job_spec.cpu_request = request 66 | end 67 | 68 | def memory_request(request) 69 | job_spec.memory_request = request 70 | end 71 | 72 | def starting_deadline_seconds(seconds) 73 | job_spec.starting_deadline_seconds = seconds 74 | end 75 | 76 | def concurrent_policy(policy) 77 | job_spec.concurrent_policy = policy 78 | end 79 | end 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /spec/active_job/adapters/kube_queue_adapter_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'active_job' 4 | require "active_job/adapters/kube_queue_adapter" 5 | 6 | RSpec.describe ActiveJob::QueueAdapters::KubeQueueAdapter do 7 | let(:job_class) do 8 | Class.new(ActiveJob::Base) do 9 | self.queue_adapter = :kube_queue 10 | 11 | include KubeQueue::Worker 12 | 13 | worker_name 'print-message-job' 14 | image 'ruby' 15 | container_name 'ruby' 16 | 17 | def perform(payload) 18 | payload[:message] 19 | end 20 | end 21 | end 22 | 23 | before do 24 | ActiveJob::Base.logger = Logger.new('/dev/null') 25 | end 26 | 27 | describe '.perform_later' do 28 | subject { job_class.perform_later(arg) } 29 | 30 | let(:arg) { { message: 'hello' } } 31 | 32 | context 'when doesnt set delay' do 33 | before do 34 | expect_any_instance_of(job_class).to receive(:perform) 35 | expect(KubeQueue.client).to receive(:create_job).and_call_original 36 | end 37 | 38 | it { expect { subject }.not_to raise_error } 39 | end 40 | 41 | context 'when set delay' do 42 | subject { job_class.set(wait: 600).perform_later } 43 | 44 | before do 45 | expect_any_instance_of(job_class).to receive(:perform) 46 | expect(KubeQueue.client).to receive(:create_cron_job).and_call_original 47 | end 48 | 49 | it { expect { subject }.not_to raise_error } 50 | end 51 | end 52 | 53 | describe '.perform_now' do 54 | subject { job_class.perform_now(arg) } 55 | 56 | before { expect_any_instance_of(job_class).to receive(:perform).with(arg) } 57 | 58 | let(:arg) { { message: 'hello' } } 59 | 60 | it { expect { subject }.not_to raise_error } 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /spec/kube_queue/manifest_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'erbh' 3 | 4 | RSpec.describe 'KubeQueue generated manifest' do 5 | include ERBh 6 | 7 | class TestWorker 8 | include KubeQueue::Worker 9 | 10 | worker_name 'test-worker' 11 | image 'test-image' 12 | end 13 | 14 | let(:job) { TestWorker.new } 15 | 16 | let(:manifest) { job.manifest } 17 | 18 | describe 'default' do 19 | it 'matches default manifest' do 20 | expect(manifest).to match YAML.safe_load(erbh(<<~ERB, job_id: job.job_id)) 21 | apiVersion: batch/v1 22 | kind: Job 23 | metadata: 24 | annotations: 25 | kube-queue-job-class: "TestWorker" 26 | kube-queue-job-id: "<%= @job_id %>" 27 | kube-queue-job-payload: '[]' 28 | name: "test-worker-<%= @job_id %>" 29 | namespace: default 30 | labels: 31 | kube-queue-job: "true" 32 | kube-queue-worker-name: "test-worker" 33 | kube-queue-job-class: "TestWorker" 34 | kube-queue-job-id: "<%= @job_id %>" 35 | spec: 36 | template: 37 | metadata: 38 | annotations: 39 | kube-queue-job-class: "TestWorker" 40 | kube-queue-job-id: "<%= @job_id %>" 41 | kube-queue-job-payload: '[]' 42 | labels: 43 | kube-queue-job: "true" 44 | kube-queue-worker-name: "test-worker" 45 | kube-queue-job-class: "TestWorker" 46 | kube-queue-job-id: "<%= @job_id %>" 47 | spec: 48 | containers: 49 | - name: "test-worker" 50 | image: "test-image" 51 | command: ["bundle", "exec", "kube_queue", "runner", "TestWorker"] 52 | env: 53 | - name: "KUBE_QUEUE_MESSAGE_PAYLOAD" 54 | value: '[]' 55 | resources: {} 56 | restartPolicy: "Never" 57 | ERB 58 | end 59 | end 60 | 61 | describe 'payload' do 62 | let(:job) { TestWorker.new(test: true) } 63 | 64 | subject(:env) { manifest['spec']['template']['spec']['containers'][0]['env'] } 65 | 66 | it 'sets to KUBE_QUEUE_MESSAGE_PAYLOAD' do 67 | expect(env).to include( 68 | "name" => "KUBE_QUEUE_MESSAGE_PAYLOAD", 69 | "value" => "[#{{ test: true }.to_json}]" 70 | ) 71 | end 72 | end 73 | 74 | describe 'env' do 75 | before do 76 | TestWorker.env( 77 | TEST: true, 78 | RAILS_ENV: "production" 79 | ) 80 | end 81 | 82 | after do 83 | TestWorker.env(nil) 84 | end 85 | 86 | subject(:env) { manifest['spec']['template']['spec']['containers'][0]['env'] } 87 | 88 | it 'includes env' do 89 | expect(env).to match_array( 90 | [ 91 | { 92 | "name" => "KUBE_QUEUE_MESSAGE_PAYLOAD", 93 | "value" => "[]" 94 | }, 95 | { 96 | "name" => "TEST", 97 | "value" => "true" 98 | }, 99 | { 100 | "name" => "RAILS_ENV", 101 | "value" => "production" 102 | } 103 | ] 104 | ) 105 | end 106 | end 107 | 108 | describe 'template' do 109 | before do 110 | TestWorker.template = File.expand_path('../../template/test.yaml', __FILE__) 111 | end 112 | 113 | it 'matches test manifest' do 114 | expect(manifest).to match YAML.safe_load(erbh(<<~ERB, job_id: job.job_id)) 115 | apiVersion: batch/v1 116 | kind: Job 117 | metadata: 118 | annotations: 119 | kube-queue-job-class: "TestWorker" 120 | kube-queue-job-id: "<%= @job_id %>" 121 | kube-queue-job-payload: '[]' 122 | name: "test-worker-<%= @job_id %>" 123 | namespace: default 124 | labels: 125 | kube-queue-job: "true" 126 | kube-queue-worker-name: "test-worker" 127 | kube-queue-job-class: "TestWorker" 128 | kube-queue-job-id: "<%= @job_id %>" 129 | spec: 130 | template: 131 | metadata: 132 | annotations: 133 | kube-queue-job-class: "TestWorker" 134 | kube-queue-job-id: "<%= @job_id %>" 135 | kube-queue-job-payload: '[]' 136 | labels: 137 | kube-queue-job: "true" 138 | kube-queue-worker-name: "test-worker" 139 | kube-queue-job-class: "TestWorker" 140 | kube-queue-job-id: "<%= @job_id %>" 141 | spec: 142 | containers: 143 | - name: "test-worker" 144 | ERB 145 | end 146 | 147 | after do 148 | TestWorker.template = nil 149 | end 150 | end 151 | end 152 | -------------------------------------------------------------------------------- /spec/kube_queue/worker_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'pry-byebug' 3 | 4 | RSpec.describe KubeQueue::Worker do 5 | include ERBh 6 | 7 | describe '.enqueue' do 8 | subject(:job) { PrintMessageJob.enqueue(arg) } 9 | 10 | let(:arg) { { message: 'hello' } } 11 | 12 | before do 13 | expect_any_instance_of(PrintMessageJob).to receive(:perform).with(arg) 14 | expect(KubeQueue.client).to receive(:create_job).and_call_original 15 | end 16 | 17 | it { expect { subject }.not_to raise_error } 18 | it { expect(job.arguments).to eq [arg] } 19 | end 20 | 21 | describe '.enqueue_at' do 22 | subject(:job) { PrintMessageJob.enqueue_at(arg, timestamp) } 23 | 24 | let(:arg) { { message: 'hello' } } 25 | let(:timestamp) { Time.utc(2019, 8, 15, 18, 30, 0).to_i } 26 | 27 | before do 28 | expect_any_instance_of(PrintMessageJob).to receive(:perform).with(arg) 29 | expect(KubeQueue.client).to receive(:create_cron_job).and_call_original 30 | end 31 | 32 | it { expect { subject }.not_to raise_error } 33 | it { expect(job.arguments).to eq [arg] } 34 | it { expect(job.scheduled_at).to eq timestamp } 35 | it { expect(job.resource.spec.schedule).to eq "30 18 15 08 4" } 36 | 37 | context 'when given timezone is not utc' do 38 | let(:timestamp) { Time.new(2019, 8, 15, 18, 30, 0, "+09:00").to_i } 39 | 40 | it { expect(job.resource.spec.schedule).to eq "30 09 15 08 4" } 41 | end 42 | end 43 | 44 | describe '.find' do 45 | let(:payload) { { message: 'hello' } } 46 | 47 | let(:job_manifest) do 48 | YAML.safe_load(erbh(<<~MANIFEST, job_id: job.job_id, payload: JSON.generate([payload], quirks_mode: true))) 49 | apiVersion: batch/v1 50 | kind: Job 51 | metadata: 52 | annotations: 53 | kube-queue-job-class: PrintMessageJob 54 | kube-queue-job-id: <%= @job_id %> 55 | kube-queue-job-payload: '<%= @payload %>' 56 | name: print-message-job-<%= @job_id %> 57 | namespace: default 58 | labels: 59 | kube-queue-job: 'true' 60 | kube-queue-worker-name: print-message-job 61 | kube-queue-job-class: PrintMessageJob 62 | kube-queue-job-id: <%= @job_id %> 63 | spec: 64 | template: 65 | metadata: 66 | annotations: 67 | kube-queue-job-class: PrintMessageJob 68 | kube-queue-job-id: <%= @job_id %> 69 | kube-queue-job-payload: '<%= @payload %>' 70 | labels: 71 | kube-queue-job: 'true' 72 | kube-queue-worker-name: print-message-job 73 | kube-queue-job-class: PrintMessageJob 74 | kube-queue-job-id: <%= @job_id %> 75 | spec: 76 | containers: 77 | - name: ruby 78 | image: ruby 79 | command: 80 | - bundle 81 | - exec 82 | - kube_queue 83 | - runner 84 | - PrintMessageJob 85 | env: 86 | - name: KUBE_QUEUE_MESSAGE_PAYLOAD 87 | value: '<%= @payload %>' 88 | resources: {} 89 | restartPolicy: Never 90 | MANIFEST 91 | end 92 | 93 | let(:resource) { K8s::Resource.new(job_manifest) } 94 | 95 | before do 96 | expect(KubeQueue.client).to receive(:get_job).and_return(resource) 97 | end 98 | 99 | subject { PrintMessageJob.find(job.job_id) } 100 | 101 | let(:job) { PrintMessageJob.new(payload) } 102 | 103 | it { expect(subject.job_id).to eq job.job_id } 104 | it { expect(subject.resource).to eq resource } 105 | 106 | context 'when error raised' do 107 | before { expect(JSON).to receive(:parse).and_raise(JSON::ParserError) } 108 | 109 | it { expect { subject }.to raise_error JSON::ParserError } 110 | end 111 | end 112 | 113 | describe '#manifest' do 114 | subject { job.manifest } 115 | 116 | let(:job) { PrintMessageJob.new(message: 'hello') } 117 | 118 | let(:job_manifest) do 119 | YAML.safe_load(erbh(<<~MANIFEST, job_id: job.job_id, payload: JSON.generate([{ message: 'hello' }], quirks_mode: true))) 120 | --- 121 | apiVersion: batch/v1 122 | kind: Job 123 | metadata: 124 | annotations: 125 | kube-queue-job-class: PrintMessageJob 126 | kube-queue-job-id: <%= @job_id %> 127 | kube-queue-job-payload: '<%= @payload %>' 128 | name: print-message-job-<%= @job_id %> 129 | namespace: default 130 | labels: 131 | kube-queue-job: 'true' 132 | kube-queue-worker-name: print-message-job 133 | kube-queue-job-class: PrintMessageJob 134 | kube-queue-job-id: <%= @job_id %> 135 | spec: 136 | template: 137 | metadata: 138 | annotations: 139 | kube-queue-job-class: PrintMessageJob 140 | kube-queue-job-id: <%= @job_id %> 141 | kube-queue-job-payload: '<%= @payload %>' 142 | labels: 143 | kube-queue-job: 'true' 144 | kube-queue-worker-name: print-message-job 145 | kube-queue-job-class: PrintMessageJob 146 | kube-queue-job-id: <%= @job_id %> 147 | spec: 148 | containers: 149 | - name: ruby 150 | image: ruby 151 | command: 152 | - bundle 153 | - exec 154 | - kube_queue 155 | - runner 156 | - PrintMessageJob 157 | env: 158 | - name: KUBE_QUEUE_MESSAGE_PAYLOAD 159 | value: '<%= @payload %>' 160 | resources: {} 161 | restartPolicy: Never 162 | MANIFEST 163 | end 164 | 165 | context 'when job' do 166 | it { is_expected.to eq job_manifest } 167 | end 168 | 169 | context 'when cronjob' do 170 | before { job.scheduled_at = timestamp } 171 | 172 | let(:timestamp) { Time.utc(2019, 8, 15, 18, 30).to_i } 173 | 174 | let(:cron_job_manifest) do 175 | { 176 | apiVersion: "batch/v1beta1", 177 | kind: "CronJob", 178 | metadata: job_manifest["metadata"], 179 | spec: { 180 | startingDeadlineSeconds: nil, 181 | concurrentPolicy: "Allow", 182 | schedule: "30 18 15 08 4", 183 | jobTemplate: { 184 | spec: job_manifest["spec"] 185 | } 186 | } 187 | } 188 | end 189 | 190 | it { is_expected.to eq cron_job_manifest } 191 | end 192 | end 193 | end 194 | -------------------------------------------------------------------------------- /spec/kube_queue_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe KubeQueue do 4 | describe '.fetch_worker' do 5 | subject { KubeQueue.fetch_worker('TestWorker') } 6 | 7 | let!(:worker) do 8 | Class.new do 9 | def self.name 10 | 'TestWorker' 11 | end 12 | 13 | include KubeQueue::Worker 14 | end 15 | end 16 | 17 | after { KubeQueue.worker_registry.clear } 18 | 19 | it { is_expected.to eq worker } 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH << File.expand_path(__dir__) 2 | 3 | require "bundler/setup" 4 | require "kube_queue" 5 | 6 | require 'erbh' 7 | 8 | require_relative './workers' 9 | 10 | RSpec.configure do |config| 11 | # Enable flags like --only-failures and --next-failure 12 | config.example_status_persistence_file_path = ".rspec_status" 13 | 14 | # Disable RSpec exposing methods globally on `Module` and `main` 15 | config.disable_monkey_patching! 16 | 17 | config.expect_with :rspec do |c| 18 | c.syntax = :expect 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /spec/template/test.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | annotations: 5 | kube-queue-job-class: "<%= spec.job_class.name %>" 6 | kube-queue-job-id: "<%= job.job_id %>" 7 | kube-queue-job-payload: '<%= payload %>' 8 | name: "<%= spec.job_name(job.job_id) %>" 9 | namespace: <%= spec.namespace %> 10 | labels: 11 | kube-queue-job: "true" 12 | kube-queue-worker-name: "<%= spec.worker_name %>" 13 | kube-queue-job-class: "<%= spec.job_class.name %>" 14 | kube-queue-job-id: "<%= job.job_id %>" 15 | spec: 16 | template: 17 | metadata: 18 | annotations: 19 | kube-queue-job-class: "<%= spec.job_class.name %>" 20 | kube-queue-job-id: "<%= job.job_id %>" 21 | kube-queue-job-payload: '<%= payload %>' 22 | labels: 23 | kube-queue-job: "true" 24 | kube-queue-worker-name: "<%= spec.worker_name %>" 25 | kube-queue-job-class: "<%= spec.job_class.name %>" 26 | kube-queue-job-id: "<%= job.job_id %>" 27 | spec: 28 | containers: 29 | - name: "test-worker" 30 | -------------------------------------------------------------------------------- /spec/workers.rb: -------------------------------------------------------------------------------- 1 | class PrintMessageJob 2 | include KubeQueue::Worker 3 | 4 | worker_name 'print-message-job' 5 | image 'ruby' 6 | container_name 'ruby' 7 | 8 | def perform(payload) 9 | payload[:message] 10 | end 11 | end 12 | 13 | class MockClient 14 | def create_job(manifest) 15 | job = K8s::Resource.new(manifest) 16 | job.metadata.namespace ||= 'default' 17 | job 18 | end 19 | 20 | # overrider on test 21 | def get_job(_namespace, _name) 22 | raise NotImplementedError 23 | end 24 | 25 | # overrider on test 26 | def list_job(_job_class, _namespace = nil) 27 | [] 28 | end 29 | 30 | def create_cron_job(manifest) 31 | cron_job = K8s::Resource.new(manifest) 32 | cron_job.metadata.namespace ||= 'default' 33 | cron_job 34 | end 35 | end 36 | 37 | class MockExecutor < KubeQueue::Executor 38 | def enqueue(job) 39 | resource = super 40 | 41 | job.perform_now 42 | 43 | resource 44 | end 45 | end 46 | 47 | KubeQueue.client = MockClient.new 48 | KubeQueue.executor = MockExecutor.new 49 | -------------------------------------------------------------------------------- /template/job.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | annotations: 5 | kube-queue-job-class: "<%= spec.job_class.name %>" 6 | kube-queue-job-id: "<%= job.job_id %>" 7 | kube-queue-job-payload: '<%= payload %>' 8 | name: "<%= spec.job_name(job.job_id) %>" 9 | namespace: <%= spec.namespace %> 10 | labels: 11 | kube-queue-job: "true" 12 | kube-queue-worker-name: "<%= spec.worker_name %>" 13 | kube-queue-job-class: "<%= spec.job_class.name %>" 14 | kube-queue-job-id: "<%= job.job_id %>" 15 | spec: 16 | template: 17 | metadata: 18 | annotations: 19 | kube-queue-job-class: "<%= spec.job_class.name %>" 20 | kube-queue-job-id: "<%= job.job_id %>" 21 | kube-queue-job-payload: '<%= payload %>' 22 | labels: 23 | kube-queue-job: "true" 24 | kube-queue-worker-name: "<%= spec.worker_name %>" 25 | kube-queue-job-class: "<%= spec.job_class.name %>" 26 | kube-queue-job-id: "<%= job.job_id %>" 27 | <%- spec.job_labels.each do |key, value| %> 28 | <%= key %>: "<%= value %>" 29 | <%- end %> 30 | spec: 31 | containers: 32 | - name: "<%= spec.container_name %>" 33 | image: "<%= spec.image %>" 34 | command: <%= spec.command %> 35 | env: 36 | - name: "KUBE_QUEUE_MESSAGE_PAYLOAD" 37 | value: '<%= payload %>' 38 | <%- spec.env.each do |key, value| %> 39 | - name: "<%= key %>" 40 | value: "<%= value %>" 41 | <%- end %> 42 | <%- if spec.env_from_exists? %> 43 | envFrom: 44 | <%- spec.env_from_config_map.each do |name| %> 45 | - configMapRef: 46 | name: "<%= name %>" 47 | <%- end %> 48 | <%- spec.env_from_secret.each do |name| %> 49 | - secretRef: 50 | name: "<%= name %>" 51 | <%- end %> 52 | <%- end %> 53 | <%- if spec.resources_exists? %> 54 | resources: 55 | <%- if spec.cpu_limit || spec.memory_limit %> 56 | limits: 57 | <%- if spec.cpu_limit %> 58 | cpu: <%= spec.cpu_limit %> 59 | <%- end %> 60 | <%- if spec.memory_limit %> 61 | memory: <%= spec.memory_limit %> 62 | <%- end %> 63 | <%- end %> 64 | <%- if spec.cpu_request || spec.memory_request %> 65 | requests: 66 | <%- if spec.cpu_request %> 67 | cpu: <%= spec.cpu_request %> 68 | <%- end %> 69 | <%- if spec.memory_request %> 70 | memory: <%= spec.memory_request %> 71 | <%- end %> 72 | <%- end %> 73 | <%- else %> 74 | resources: {} 75 | <%- end %> 76 | restartPolicy: "<%= spec.restart_policy %>" 77 | <%- if spec.backoff_limit %> 78 | backoffLimit: <%= spec.backoff_limit %> 79 | <%- end %> 80 | <%- if spec.active_deadline_seconds %> 81 | activeDeadlineSeconds: <%= spec.active_deadline_seconds %> 82 | <%- end %> 83 | --------------------------------------------------------------------------------