rails-cloud-tasks 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2b46bc62d822e6eb44925df67eb91ce37e862c48b3c2e7a7543f9ae14a7e6caa
4
- data.tar.gz: 26455a3286ab916efca56b6947bf867a877976fa0215229927454b166fdf90bc
3
+ metadata.gz: 924a936920255b90f05426a1a02d8fcb7c0b688f05ecf23f47d5574cfd830100
4
+ data.tar.gz: dac09c6a8c32a41ce9b8849b57892cac630f54b9592faf88400d248ac32d4661
5
5
  SHA512:
6
- metadata.gz: 3eef0b2920d83f6fea101e10eb1386cfab2300fc06f9c89f85ba3279496d7897d223c69227dd10d770ce42ec5f726443e7e785de6567cea353c0657f8400e4f8
7
- data.tar.gz: ca08441f14690818fa2203f73de2c22c6cd5ab98f25b264c67291cad76094d8a378bb69aa91246fba293cc08f3d609d13a0451a8b21ec4fd9366b1a9d781ff65
6
+ metadata.gz: b4bb22ec37af949357f944d2e5dc5f80c955fd65426aadc35f1ac4d7bc53cfdc8d5703813553f824e2024c137087a3d891f16d2a7859eb6a26fef771da7a894f
7
+ data.tar.gz: 4ca848172ca6473f08348f0b5ff6d9f9ddad8ff2a9b95ab9b2f8e77d933c5d6a1ebd4c29b1eac6afa43ecd3e665913fb9864d22bc8b451a1ee946b1279be6cb4
@@ -1,13 +1,55 @@
1
- name: Release CI
1
+ name: Tag & Release Package
2
2
 
3
3
  on:
4
4
  push:
5
- tags:
6
- - v*
5
+ branches:
6
+ - main
7
7
 
8
8
  jobs:
9
+ checks:
10
+ runs-on: ubuntu-latest
11
+ outputs:
12
+ pre_release: ${{ steps.versioning.outputs.pre_release }}
13
+ upgraded: ${{ steps.versioning.outputs.upgraded }}
14
+ package_version: ${{ steps.versioning.outputs.package_version }}
15
+ steps:
16
+ - uses: actions/checkout@v2
17
+
18
+ - name: Set up Ruby 2.7
19
+ uses: actions/setup-ruby@v1
20
+ with:
21
+ ruby-version: 2.7
22
+
23
+ - name: Install bundler
24
+ run: gem install bundler
25
+
26
+ - name: Cache dependencies
27
+ uses: actions/cache@v1
28
+ with:
29
+ path: vendor/bundle
30
+ key: ${{ runner.os }}-gem-${{ hashFiles('**/rails-cloud-tasks.gemspec') }}-2.7
31
+
32
+ - name: Install dependencies
33
+ run: bundle install
34
+
35
+ - name: Fetching Tags
36
+ run: git fetch -t
37
+
38
+ - name: Detect version upgrade
39
+ id: versioning
40
+ run: |
41
+ pkg='rails-cloud-tasks'
42
+ package_version=$(bundle info $pkg | grep -o "$pkg \(.*\)" | sed "s/$pkg (\(.*\))/\1/")
43
+ echo "::set-output name=package_version::"$package_version
44
+ upgraded=$(git tag --list | grep -q "${package_version}$" && echo "false" || echo "true")
45
+ echo "::set-output name=upgraded::"$upgraded
46
+ pre_release=$([[ $package_version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && echo "false" || echo "true")
47
+ echo "::set-output name=pre_release::"$pre_release
48
+
9
49
  release:
10
50
  runs-on: ubuntu-latest
51
+ needs: checks
52
+ if: needs.checks.outputs.upgraded == 'true'
11
53
  steps:
12
54
  - uses: actions/checkout@v2
13
55
 
@@ -28,6 +70,19 @@ jobs:
28
70
  - name: Install dependencies
29
71
  run: bundle install
30
72
 
73
+ - name: Create Release
74
+ uses: actions/create-release@v1
75
+ env:
76
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
77
+ with:
78
+ tag_name: ${{ needs.checks.outputs.package_version }}
79
+ release_name: Release ${{ needs.checks.outputs.package_version }}
80
+ body: |
81
+ Auto-released by bot.
82
+ See commit changes.
83
+ draft: false
84
+ prerelease: ${{ needs.checks.outputs.pre_release }}
85
+
31
86
  - name: Build package
32
87
  run: bundle exec gem build -o rails-cloud-tasks.gem
33
88
 
data/README.md CHANGED
@@ -36,8 +36,11 @@ gem 'rails-cloud-tasks'
36
36
  require 'rails-cloud-tasks'
37
37
 
38
38
  RailsCloudTasks.configure do |config|
39
+ config.service_account_email = 'test-account@test-project.iam.gserviceaccount.com'
39
40
  config.project_id = 'my-gcp-project' # This is not needed if running on GCE
40
41
  config.location_id = 'us-central1'
42
+ config.scheduler_file_path = './custom_path/scheduler_jobs.yml'
43
+ config.scheduler_prefix_name = 'my-app-name'
41
44
 
42
45
  # Base url used by Cloud Tasks to reach your application and run the tasks
43
46
  config.host = 'https://myapplication.host.com'
@@ -48,6 +51,19 @@ RailsCloudTasks.configure do |config|
48
51
  end
49
52
  ```
50
53
 
54
+ Check out the available configs and its usage description:
55
+
56
+ | attribute | description | env support | app engine fallback | default value |
57
+ |----------------------- |------------------------------------------------------------------------------------------------------------- |--------------------- |-------------------- |-------------------------- |
58
+ | service_account_email | The app service account email. It''s used to impersonate an user on schedule job | GCP_SERVICE_ACCOUNT | ✓ | |
59
+ | project_id | The Project ID | GCP_PROJECT | ✓ | |
60
+ | location_id | The region where you app is running (eg: us-central1, us-east1...) | GCP_LOCATION | ✓ | |
61
+ | host | The app endpoint which the app is running. *Do not use custom domain* Use the generated domain by Cloud Run | GCP_APP_ENDPOINT | | |
62
+ | scheduler_file_path | Path which the scheduler file is located | 𐄂 | | './config/scheduler.yml' |
63
+ | scheduler_prefix_name | The prefix to be set into scheduler job name | 𐄂 | | 'rails-cloud' |
64
+ | tasks_path | The path to run tasks | 𐄂 | | '/tasks' |
65
+
66
+
51
67
  - Add a Job class:
52
68
  ```ruby
53
69
  # ./app/jobs/application_job.rb
@@ -73,6 +89,34 @@ end
73
89
  ```ruby
74
90
  MyJob.perform_later(attrs)
75
91
  ```
92
+
93
+ ### Scheduled Jobs
94
+
95
+ We have support to Google Cloud Schedule. It's based on Cloud tasks, the jobs are scheduled with HTTP Target. We do not support Pub/Sub or App Engine HTTP for now.
96
+
97
+ Check out the follow sample of config file:
98
+ ```yaml
99
+ # config/scheduler.yml
100
+ - name: Users::SyncJob
101
+ schedule: 0 8 * * *
102
+ description: Sync user data
103
+ time_zone: "America/Los_Angeles"
104
+ args:
105
+ arg1: 100
106
+ arg2: 200
107
+ ```
108
+
109
+ | attribute | description | required |
110
+ |------------- |---------------------------------------------------------------- |---------- |
111
+ | name | The Job class namespace | ✓ |
112
+ | schedule | The frequency to run your job. It should be a unix-cron format | ✓ |
113
+ | description | What this job does | ✓ |
114
+ | time_zone | Choose which one timezone your job must run | ✓ |
115
+ | args | What are the job's arguments | ✓ |
116
+
117
+
118
+
119
+
76
120
  ## Tests
77
121
 
78
122
  To run tests:
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'rails-cloud-tasks'
2
+
3
+ path = File.expand_path(__dir__)
4
+ Dir.glob("#{path}/lib/tasks/*.rake").each { |f| load f }
@@ -1,9 +1,13 @@
1
+ require_relative './railtie'
2
+
1
3
  require 'active_support'
2
4
  require 'rails_cloud_tasks/rack/errors'
3
5
 
4
6
  module RailsCloudTasks
5
7
  extend ActiveSupport::Autoload
6
8
 
9
+ autoload :Scheduler
10
+ autoload :Credentials
7
11
  autoload :Adapter
8
12
  autoload :AppEngine
9
13
  autoload :Configuration
@@ -25,4 +29,14 @@ module RailsCloudTasks
25
29
  def self.config
26
30
  @config ||= Configuration.new
27
31
  end
32
+
33
+ def self.logger
34
+ return @logger if @logger
35
+
36
+ @logger ||= (Rails.logger || Logger.new($stdout)).tap do |logger|
37
+ logger.formatter = proc do |severity, datetime, _progname, msg|
38
+ "[#{datetime}] #{severity} [rails-cloud-tasks]: #{msg}\n"
39
+ end
40
+ end
41
+ end
28
42
  end
@@ -39,7 +39,7 @@ module RailsCloudTasks
39
39
  http_request: {
40
40
  http_method: :POST,
41
41
  url: url,
42
- body: { job: job.serialize }.to_json
42
+ body: { job: job.serialize }.to_json.force_encoding('ASCII-8BIT')
43
43
  }.merge(auth),
44
44
  schedule_time: timestamp && Google::Protobuf::Timestamp.new.tap do |ts|
45
45
  ts.seconds = timestamp
@@ -1,12 +1,22 @@
1
1
  module RailsCloudTasks
2
2
  class Configuration
3
- attr_accessor :project_id, :location_id, :host, :tasks_path, :jobs, :auth
3
+ attr_accessor :location_id, :host, :tasks_path, :service_account_email, :scheduler_file_path,
4
+ :scheduler_prefix_name
4
5
 
5
- def initialize
6
- @jobs = Set.new
7
- @project_id = AppEngine.project_id
6
+ attr_writer :project_id
7
+ attr_reader :app_engine, :google_auth
8
+
9
+ def initialize(app_engine = AppEngine, google_auth = Google::Auth)
10
+ @service_account_email = ENV['GCP_SERVICE_ACCOUNT']
11
+ @location_id = ENV['GCP_LOCATION']
12
+ @project_id = ENV['GCP_PROJECT']
13
+ @host = ENV['GCP_APP_ENDPOINT']
8
14
  @tasks_path = '/tasks'
9
- @auth = authenticate
15
+ @scheduler_file_path = './config/scheduler.yml'
16
+ @scheduler_prefix_name = 'rails-cloud'
17
+
18
+ @app_engine = app_engine
19
+ @google_auth = google_auth
10
20
  end
11
21
 
12
22
  def inject_routes
@@ -18,20 +28,26 @@ module RailsCloudTasks
18
28
  end
19
29
  end
20
30
 
31
+ def project_id
32
+ @project_id ||= app_engine.project_id
33
+ end
34
+
35
+ def auth
36
+ @auth ||= authenticate
37
+ end
38
+
21
39
  private
22
40
 
23
41
  def authenticate
24
- email = AppEngine.service_account_email || Google::Auth.get_application_default.issuer
42
+ email = service_account_email ||
43
+ app_engine.service_account_email ||
44
+ google_auth.get_application_default.issuer
25
45
 
26
46
  {
27
47
  oidc_token: {
28
48
  service_account_email: email
29
49
  }
30
50
  }
31
- rescue RuntimeError, Errno::EHOSTDOWN
32
- # EHOSTDOWN occurs sporadically when trying to resolve the metadata endpoint
33
- # locally. It is unlikely to occur when running on GCE.
34
- {}
35
51
  end
36
52
  end
37
53
  end
@@ -0,0 +1,38 @@
1
+ module RailsCloudTasks
2
+ class Credentials
3
+ require 'googleauth'
4
+ require 'google/apis/cloudscheduler_v1'
5
+ require 'google/apis/iamcredentials_v1'
6
+
7
+ DEFAULT_SCOPES = ['https://www.googleapis.com/auth/cloud-platform'].freeze
8
+ attr_reader :request_options, :iam_credential, :token_request, :auth
9
+
10
+ def initialize(
11
+ request_options: Google::Apis::RequestOptions.new,
12
+ iam_credential: Google::Apis::IamcredentialsV1::IAMCredentialsService.new,
13
+ token_request: Google::Apis::IamcredentialsV1::GenerateAccessTokenRequest,
14
+ auth: Google::Auth
15
+ )
16
+ @auth = auth
17
+ @request_options = request_options
18
+ @iam_credential = iam_credential
19
+ @token_request = token_request
20
+ end
21
+
22
+ def generate(impersonate_account = nil, scopes = [])
23
+ current_scopes = DEFAULT_SCOPES + scopes
24
+ authorization = auth.get_application_default(current_scopes).dup
25
+ request_options.authorization = authorization
26
+
27
+ if impersonate_account
28
+ iam_credential.generate_service_account_access_token(
29
+ "projects/-/serviceAccounts/#{impersonate_account}",
30
+ token_request.new(scope: current_scopes, lifetime: '3600s'),
31
+ options: request_options
32
+ )
33
+ end
34
+
35
+ request_options.authorization
36
+ end
37
+ end
38
+ end
@@ -12,7 +12,7 @@ module RailsCloudTasks
12
12
  job_args = extract_args(request)
13
13
  job_class.perform_now(job_args)
14
14
 
15
- response(200, { error: nil })
15
+ response(200, {})
16
16
  rescue Rack::InvalidPayloadError => e
17
17
  response(422, { error: e.message })
18
18
  rescue StandardError => e
@@ -11,7 +11,7 @@ module RailsCloudTasks
11
11
 
12
12
  ActiveJob::Base.execute(job)
13
13
 
14
- response(200, { error: nil })
14
+ response(200, {})
15
15
  rescue Rack::InvalidPayloadError => e
16
16
  response(400, { error: e.cause.message })
17
17
  rescue StandardError => e
@@ -0,0 +1,62 @@
1
+ module RailsCloudTasks
2
+ class Scheduler
3
+ delegate :project_id, :location_id, :host, :auth, :tasks_path,
4
+ :scheduler_file_path, :scheduler_prefix_name,
5
+ :service_account_email, to: 'RailsCloudTasks.config'
6
+
7
+ attr_reader :client, :credentials
8
+
9
+ def initialize(
10
+ client: Google::Cloud::Scheduler.cloud_scheduler,
11
+ credentials: RailsCloudTasks::Credentials.new
12
+ )
13
+ client.configure do |config|
14
+ config.credentials = credentials.generate(service_account_email)
15
+ end
16
+ @client = client
17
+ end
18
+
19
+ # Create & Update scheduler job on Google Cloud
20
+ # TODO: Support to delete scheduled jobs
21
+ def upsert
22
+ scheduler_jobs.each do |job|
23
+ begin
24
+ client.create_job parent: location_path, job: job
25
+ rescue Google::Cloud::AlreadyExistsError
26
+ client.update_job job: job
27
+ end
28
+ end
29
+ end
30
+
31
+ protected
32
+
33
+ def location_path
34
+ @location_path ||= client.location_path project: project_id, location: location_id
35
+ end
36
+
37
+ def scheduler_jobs
38
+ parse_jobs_from_file.map(&method(:build_job))
39
+ end
40
+
41
+ def build_job(job)
42
+ {
43
+ name: "#{location_path}/jobs/#{scheduler_prefix_name}--#{job[:name]}",
44
+ schedule: job[:schedule],
45
+ description: job[:description],
46
+ time_zone: job[:time_zone],
47
+ http_target: {
48
+ uri: "#{host}#{tasks_path}/#{job[:name]}",
49
+ http_method: 'POST',
50
+ body: job[:args].to_json
51
+ }.merge(auth)
52
+ }
53
+ end
54
+
55
+ def parse_jobs_from_file
56
+ settings = File.read(File.expand_path(scheduler_file_path))
57
+ YAML.safe_load(ERB.new(settings).result).map(&:deep_symbolize_keys)
58
+ rescue Errno::ENOENT
59
+ []
60
+ end
61
+ end
62
+ end
@@ -1,3 +1,3 @@
1
1
  module RailsCloudTasks
2
- VERSION = '0.0.1'.freeze
2
+ VERSION = '0.0.2'.freeze
3
3
  end
data/lib/railtie.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'rails-cloud-tasks'
2
+ require 'rails'
3
+
4
+ module RailsCloudTasks
5
+ class Railtie < Rails::Railtie
6
+ railtie_name :rails_cloud_tasks
7
+
8
+ rake_tasks do
9
+ namespace :rails_cloud_tasks do
10
+ path = File.expand_path(__dir__)
11
+ Dir.glob("#{path}/tasks/**/*.rake").each { |f| load f }
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,6 @@
1
+ namespace :scheduler do
2
+ desc 'Sync all scheduled jobs to Google Cloud'
3
+ task sync: :environment do
4
+ RailsCloudTasks::Scheduler.new.upsert
5
+ end
6
+ end
@@ -23,6 +23,9 @@ Gem::Specification.new do |spec|
23
23
  end
24
24
 
25
25
  spec.add_dependency 'activesupport', '>= 4'
26
+ spec.add_dependency 'google-apis-cloudscheduler_v1'
27
+ spec.add_dependency 'google-apis-iamcredentials_v1'
28
+ spec.add_dependency 'google-cloud-scheduler', '>= 2'
26
29
  spec.add_dependency 'google-cloud-tasks', '>= 2'
27
30
  spec.add_development_dependency 'rails', '>= 4'
28
31
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-cloud-tasks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Guilherme Araújo
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-29 00:00:00.000000000 Z
11
+ date: 2021-02-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -24,6 +24,48 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: google-apis-cloudscheduler_v1
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: google-apis-iamcredentials_v1
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: google-cloud-scheduler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '2'
27
69
  - !ruby/object:Gem::Dependency
28
70
  name: google-cloud-tasks
29
71
  requirement: !ruby/object:Gem::Requirement
@@ -236,14 +278,19 @@ files:
236
278
  - Gemfile
237
279
  - LICENSE
238
280
  - README.md
281
+ - Rakefile
239
282
  - lib/rails-cloud-tasks.rb
240
283
  - lib/rails_cloud_tasks/adapter.rb
241
284
  - lib/rails_cloud_tasks/app_engine.rb
242
285
  - lib/rails_cloud_tasks/configuration.rb
286
+ - lib/rails_cloud_tasks/credentials.rb
243
287
  - lib/rails_cloud_tasks/rack/errors.rb
244
288
  - lib/rails_cloud_tasks/rack/jobs.rb
245
289
  - lib/rails_cloud_tasks/rack/tasks.rb
290
+ - lib/rails_cloud_tasks/scheduler.rb
246
291
  - lib/rails_cloud_tasks/version.rb
292
+ - lib/railtie.rb
293
+ - lib/tasks/scheduler.rake
247
294
  - rails-cloud-tasks.gemspec
248
295
  homepage: http://github.com/flamingo-run/rails-cloud-tasks
249
296
  licenses: