rails-cloud-tasks 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: