cloudmunda 0.1.0 → 0.1.1

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: 0224d338965547c5fb715c3e3b3f6443606fe3688ecf3bef13c7fedd10ded234
4
- data.tar.gz: ba347d1a7aa1624624d30d3459110c81e0a6ff98e9e4d06dfb9c9c08e7d75dd3
3
+ metadata.gz: '0882dcb50d23ad283984404e67f589a28d39a08c19ec44b9c2ca00659386962e'
4
+ data.tar.gz: 7d03f8ddd3c6cc8ba8a464e64e84a14234a791540f947a25cb94dd36fd5bc8c1
5
5
  SHA512:
6
- metadata.gz: 72ce642c5be6dc2a8d12585c88aaa8c89f9ac6a8d89ee7ae83f0dae71788689b6f09cbbce4543582c1f1af734045dc230b44bb33041d7a57cd7e705611df9625
7
- data.tar.gz: 49d5cf58b015e80c233eebb989a0c855fcd59b91a252aaf91e7bf127c068ad027eb119a491284e769b1be04518234c7506f353ddd6e6fb093f84a79c0195a04b
6
+ metadata.gz: 1aa60b038071944496032564eb73a21c3344211ab4339cc77d9d74de6ecaf7814e31eb69fafada0cd6f25432f9e4a85f2cf9003edf209735605986f9c5c26f9b
7
+ data.tar.gz: 5e5d47130dc95f8d98a12facaf4dc8830cded1f3420e3b96df838d5730c01339dd0187177d9f5d92c6c1ac175b67d647530dc497383686eda2d46a16cba57355
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.1.1] - 2021-12-16
4
+
5
+ - Camunda Cloud Access Token Creation
6
+ - Zeebe task workers
7
+ - Camunda Cloud Usertasks graphQL connection
8
+
3
9
  ## [0.1.0] - 2021-12-16
4
10
 
5
11
  - Initial release
12
+ _
data/Gemfile CHANGED
@@ -5,8 +5,7 @@ source "https://rubygems.org"
5
5
  # Specify your gem's dependencies in cloudmunda.gemspec
6
6
  gemspec
7
7
 
8
+ gem "rails", ">= 6"
8
9
  gem "rake", "~> 13.0"
9
-
10
10
  gem "rspec", "~> 3.0"
11
-
12
11
  gem "rubocop", "~> 1.21"
data/Gemfile.lock CHANGED
@@ -1,19 +1,151 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cloudmunda (0.1.0)
4
+ cloudmunda (0.1.1)
5
+ concurrent-ruby (~> 1.0)
6
+ rest-client (~> 2.0)
7
+ zeebe-client (~> 0.16)
5
8
 
6
9
  GEM
7
10
  remote: https://rubygems.org/
8
11
  specs:
12
+ actioncable (6.1.4.1)
13
+ actionpack (= 6.1.4.1)
14
+ activesupport (= 6.1.4.1)
15
+ nio4r (~> 2.0)
16
+ websocket-driver (>= 0.6.1)
17
+ actionmailbox (6.1.4.1)
18
+ actionpack (= 6.1.4.1)
19
+ activejob (= 6.1.4.1)
20
+ activerecord (= 6.1.4.1)
21
+ activestorage (= 6.1.4.1)
22
+ activesupport (= 6.1.4.1)
23
+ mail (>= 2.7.1)
24
+ actionmailer (6.1.4.1)
25
+ actionpack (= 6.1.4.1)
26
+ actionview (= 6.1.4.1)
27
+ activejob (= 6.1.4.1)
28
+ activesupport (= 6.1.4.1)
29
+ mail (~> 2.5, >= 2.5.4)
30
+ rails-dom-testing (~> 2.0)
31
+ actionpack (6.1.4.1)
32
+ actionview (= 6.1.4.1)
33
+ activesupport (= 6.1.4.1)
34
+ rack (~> 2.0, >= 2.0.9)
35
+ rack-test (>= 0.6.3)
36
+ rails-dom-testing (~> 2.0)
37
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
38
+ actiontext (6.1.4.1)
39
+ actionpack (= 6.1.4.1)
40
+ activerecord (= 6.1.4.1)
41
+ activestorage (= 6.1.4.1)
42
+ activesupport (= 6.1.4.1)
43
+ nokogiri (>= 1.8.5)
44
+ actionview (6.1.4.1)
45
+ activesupport (= 6.1.4.1)
46
+ builder (~> 3.1)
47
+ erubi (~> 1.4)
48
+ rails-dom-testing (~> 2.0)
49
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
50
+ activejob (6.1.4.1)
51
+ activesupport (= 6.1.4.1)
52
+ globalid (>= 0.3.6)
53
+ activemodel (6.1.4.1)
54
+ activesupport (= 6.1.4.1)
55
+ activerecord (6.1.4.1)
56
+ activemodel (= 6.1.4.1)
57
+ activesupport (= 6.1.4.1)
58
+ activestorage (6.1.4.1)
59
+ actionpack (= 6.1.4.1)
60
+ activejob (= 6.1.4.1)
61
+ activerecord (= 6.1.4.1)
62
+ activesupport (= 6.1.4.1)
63
+ marcel (~> 1.0.0)
64
+ mini_mime (>= 1.1.0)
65
+ activesupport (6.1.4.1)
66
+ concurrent-ruby (~> 1.0, >= 1.0.2)
67
+ i18n (>= 1.6, < 2)
68
+ minitest (>= 5.1)
69
+ tzinfo (~> 2.0)
70
+ zeitwerk (~> 2.3)
9
71
  ast (2.4.2)
72
+ builder (3.2.4)
73
+ concurrent-ruby (1.1.9)
74
+ crass (1.0.6)
10
75
  diff-lcs (1.4.4)
76
+ domain_name (0.5.20190701)
77
+ unf (>= 0.0.5, < 1.0.0)
78
+ erubi (1.10.0)
79
+ globalid (1.0.0)
80
+ activesupport (>= 5.0)
81
+ google-protobuf (3.19.1-x86_64-darwin)
82
+ googleapis-common-protos-types (1.3.0)
83
+ google-protobuf (~> 3.14)
84
+ grpc (1.42.0-x86_64-darwin)
85
+ google-protobuf (~> 3.18)
86
+ googleapis-common-protos-types (~> 1.0)
87
+ http-accept (1.7.0)
88
+ http-cookie (1.0.4)
89
+ domain_name (~> 0.5)
90
+ i18n (1.8.11)
91
+ concurrent-ruby (~> 1.0)
92
+ loofah (2.13.0)
93
+ crass (~> 1.0.2)
94
+ nokogiri (>= 1.5.9)
95
+ mail (2.7.1)
96
+ mini_mime (>= 0.1.1)
97
+ marcel (1.0.2)
98
+ method_source (1.0.0)
99
+ mime-types (3.4.1)
100
+ mime-types-data (~> 3.2015)
101
+ mime-types-data (3.2021.1115)
102
+ mini_mime (1.1.2)
103
+ minitest (5.14.4)
104
+ netrc (0.11.0)
105
+ nio4r (2.5.8)
106
+ nokogiri (1.12.5-x86_64-darwin)
107
+ racc (~> 1.4)
11
108
  parallel (1.21.0)
12
109
  parser (3.0.3.2)
13
110
  ast (~> 2.4.1)
111
+ racc (1.6.0)
112
+ rack (2.2.3)
113
+ rack-test (1.1.0)
114
+ rack (>= 1.0, < 3)
115
+ rails (6.1.4.1)
116
+ actioncable (= 6.1.4.1)
117
+ actionmailbox (= 6.1.4.1)
118
+ actionmailer (= 6.1.4.1)
119
+ actionpack (= 6.1.4.1)
120
+ actiontext (= 6.1.4.1)
121
+ actionview (= 6.1.4.1)
122
+ activejob (= 6.1.4.1)
123
+ activemodel (= 6.1.4.1)
124
+ activerecord (= 6.1.4.1)
125
+ activestorage (= 6.1.4.1)
126
+ activesupport (= 6.1.4.1)
127
+ bundler (>= 1.15.0)
128
+ railties (= 6.1.4.1)
129
+ sprockets-rails (>= 2.0.0)
130
+ rails-dom-testing (2.0.3)
131
+ activesupport (>= 4.2.0)
132
+ nokogiri (>= 1.6)
133
+ rails-html-sanitizer (1.4.2)
134
+ loofah (~> 2.3)
135
+ railties (6.1.4.1)
136
+ actionpack (= 6.1.4.1)
137
+ activesupport (= 6.1.4.1)
138
+ method_source
139
+ rake (>= 0.13)
140
+ thor (~> 1.0)
14
141
  rainbow (3.0.0)
15
142
  rake (13.0.6)
16
143
  regexp_parser (2.2.0)
144
+ rest-client (2.1.0)
145
+ http-accept (>= 1.7.0, < 2.0)
146
+ http-cookie (>= 1.0.2, < 2.0)
147
+ mime-types (>= 1.16, < 4.0)
148
+ netrc (~> 0.8)
17
149
  rexml (3.2.5)
18
150
  rspec (3.10.0)
19
151
  rspec-core (~> 3.10.0)
@@ -40,13 +172,33 @@ GEM
40
172
  rubocop-ast (1.15.0)
41
173
  parser (>= 3.0.1.1)
42
174
  ruby-progressbar (1.11.0)
175
+ sprockets (4.0.2)
176
+ concurrent-ruby (~> 1.0)
177
+ rack (> 1, < 3)
178
+ sprockets-rails (3.4.1)
179
+ actionpack (>= 5.2)
180
+ activesupport (>= 5.2)
181
+ sprockets (>= 3.0.0)
182
+ thor (1.1.0)
183
+ tzinfo (2.0.4)
184
+ concurrent-ruby (~> 1.0)
185
+ unf (0.1.4)
186
+ unf_ext
187
+ unf_ext (0.0.8)
43
188
  unicode-display_width (2.1.0)
189
+ websocket-driver (0.7.5)
190
+ websocket-extensions (>= 0.1.0)
191
+ websocket-extensions (0.1.5)
192
+ zeebe-client (0.16.0)
193
+ grpc (~> 1.32)
194
+ zeitwerk (2.5.1)
44
195
 
45
196
  PLATFORMS
46
197
  x86_64-darwin-20
47
198
 
48
199
  DEPENDENCIES
49
200
  cloudmunda!
201
+ rails (>= 6)
50
202
  rake (~> 13.0)
51
203
  rspec (~> 3.0)
52
204
  rubocop (~> 1.21)
data/README.md CHANGED
@@ -22,7 +22,16 @@ Or install it yourself as:
22
22
 
23
23
  ## Usage
24
24
 
25
- TODO: Write usage instructions here
25
+ ```ruby
26
+ Cloudmunda.configure do |config|
27
+ config.env = ENV['APP_ENV'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
28
+ config.require = '.'
29
+ config.timeout = 30
30
+ config.zeebe_url = ENV['ZEEBE_URL']
31
+ config.auth_url = ENV['ZEEBE_AUTHORIZATION_SERVER_URL']
32
+ end
33
+ ```
34
+
26
35
 
27
36
  ## Development
28
37
 
data/exe/cloudmunda ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ require "bundler/setup"
3
+ require "cloudmunda/cli"
4
+
5
+ begin
6
+ cli = ::Cloudmunda::CLI.instance
7
+ cli.parse
8
+ cli.run
9
+ rescue => e
10
+ raise e if $DEBUG
11
+ STDERR.puts e.message
12
+ STDERR.puts e.backtrace.join("\n")
13
+ exit 1
14
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cloudmunda
4
+ module API
5
+ class AccessToken < Cloudmunda::API::OAuthResource
6
+ def self.create(audience_url: Cloudmunda.audience)
7
+ uri = Cloudmunda.auth_url
8
+ payload = {
9
+ "grant_type":"client_credentials",
10
+ "audience": audience_url,
11
+ "client_id": Cloudmunda.client_id,
12
+ "client_secret": Cloudmunda.client_secret
13
+ }
14
+
15
+ create_by_uri(uri: uri, payload: payload)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rest-client'
4
+ require 'json'
5
+ require 'uri'
6
+ require 'base64'
7
+
8
+ module Cloudmunda
9
+ module API
10
+ class Client
11
+ def self.post(url, params = {})
12
+ response = RestClient.post(url, params.to_json, headers.merge(content_headers))
13
+ JSON.parse(response.body)
14
+ end
15
+
16
+ def self.headers
17
+ {
18
+ authorization: "Basic " + Base64.strict_encode64("#{Cloudmunda.client_id}:#{Cloudmunda.client_secret}"),
19
+ }
20
+ end
21
+
22
+ def self.content_headers
23
+ {
24
+ 'Content-Type': 'application/json'
25
+ }
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ostruct'
4
+
5
+ module Cloudmunda
6
+ module API
7
+ class OAuthResource < OpenStruct
8
+ def self.create_by_uri(uri:, payload:)
9
+ raw_item = Cloudmunda::API::Client.post(uri, payload)
10
+ raw_item = {} if raw_item == ""
11
+ new(raw_item)
12
+ end
13
+
14
+ def initialize(raw)
15
+ super(raw)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,4 @@
1
+ require_relative 'api/o_auth_resource'
2
+ require_relative 'api/access_token'
3
+ require_relative 'api/client'
4
+
@@ -0,0 +1,35 @@
1
+ require_relative 'supervisor'
2
+
3
+ module Cloudmunda
4
+ class Launcher
5
+
6
+ attr_reader :supervisor
7
+
8
+ def initialize
9
+ @supervisor = ::Cloudmunda::Supervisor.new
10
+ end
11
+
12
+ # Starts the supervisor and job processors.
13
+ def start
14
+ supervisor.start
15
+ end
16
+
17
+ # Tells the supervisor to stop processing any more jobs.
18
+ def quiet
19
+ supervisor.quiet
20
+ end
21
+
22
+ # Tells the supervisor to stop job processors. This method blocks until
23
+ # all processors are complete and stopped. It can take up to configurable
24
+ # timeout.
25
+ def stop
26
+ supervisor.stop
27
+ end
28
+
29
+ private
30
+
31
+ def client
32
+ ::Cloudmunda.client
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,109 @@
1
+ module Cloudmunda
2
+ class Processor
3
+
4
+ attr_reader :client, :worker_class, :busy_count, :timer
5
+
6
+ def initialize(client: ::Cloudmunda.client, worker_class:)
7
+ @client = client
8
+ @worker_class = worker_class
9
+ @busy_count = ::Concurrent::AtomicFixnum.new(0)
10
+ @timer = ::Concurrent::TimerTask.new(
11
+ run_now: true,
12
+ execution_interval: worker_poll_interval,
13
+ timeout_interval: worker_timeout
14
+ ) { run }
15
+ end
16
+
17
+ def start
18
+ timer.execute
19
+ self
20
+ end
21
+
22
+ def stop
23
+ timer.shutdown
24
+ self
25
+ end
26
+
27
+ def should_activate_jobs?
28
+ busy_count.value <= worker_max_jobs_to_activate
29
+ end
30
+
31
+ private
32
+
33
+ def run
34
+ fetch if should_activate_jobs?
35
+ end
36
+
37
+ def fetch
38
+ activate_jobs_request.each do |response|
39
+ busy_count.increment(response.jobs.count)
40
+ response.jobs.each do |job|
41
+ ::Concurrent::Future.execute do
42
+ process(job)
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ def process(job)
49
+ worker = worker_class.new(client)
50
+ begin
51
+ logger.info "class=#{worker_class} jid=#{job.key} Start processing #{job.type}"
52
+
53
+ worker.process(job)
54
+ worker.complete_job(job)
55
+
56
+ logger.info "class=#{worker_class} jid=#{job.key} Done processing #{job.type}"
57
+ rescue => exception
58
+ logger.info "class=#{worker_class} jid=#{job.key} Failed processing #{job.type}: #{exception.message}"
59
+
60
+ worker.fail_job(job, reason: exception.message)
61
+ raise exception
62
+ ensure
63
+ busy_count.decrement
64
+ end
65
+ end
66
+
67
+ def activate_jobs_request
68
+ client.activate_jobs(
69
+ type: worker_type,
70
+ worker: worker_name,
71
+ timeout: worker_timeout * 1000,
72
+ maxJobsToActivate: max_jobs_to_activate,
73
+ fetchVariable: worker_variables_to_fetch,
74
+ )
75
+ end
76
+
77
+ def worker_type
78
+ worker_class.get_type
79
+ end
80
+
81
+ def worker_name
82
+ worker_class.get_name
83
+ end
84
+
85
+ def worker_max_jobs_to_activate
86
+ worker_class.get_max_jobs_to_activate
87
+ end
88
+
89
+ def worker_timeout
90
+ worker_class.get_timeout
91
+ end
92
+
93
+ def worker_variables_to_fetch
94
+ worker_class.get_variables_to_fetch
95
+ end
96
+
97
+ def worker_poll_interval
98
+ worker_class.get_poll_interval
99
+ end
100
+
101
+ def max_jobs_to_activate
102
+ worker_max_jobs_to_activate - busy_count.value
103
+ end
104
+
105
+ def logger
106
+ ::Cloudmunda.logger
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,37 @@
1
+ require_relative 'processor'
2
+
3
+ module Cloudmunda
4
+ class Supervisor
5
+ def initialize
6
+ @processors = []
7
+ end
8
+
9
+ def start
10
+ @processors = workers.map do |worker_class|
11
+ processor = ::Cloudmunda::Processor.new(worker_class: worker_class)
12
+ processor.start
13
+ end
14
+ end
15
+
16
+ def quiet
17
+ logger.info "Terminating workers"
18
+ @processors.each(&:stop)
19
+ end
20
+
21
+ def stop(timeout: ::Cloudmunda.timeout)
22
+ quiet
23
+ logger.info "Pausing #{timeout}s to allow workers to finish..."
24
+ sleep timeout
25
+ end
26
+
27
+ private
28
+
29
+ def workers
30
+ ::Cloudmunda.workers.to_a
31
+ end
32
+
33
+ def logger
34
+ ::Cloudmunda.logger
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,163 @@
1
+ require 'json'
2
+
3
+ module Cloudmunda
4
+ module Worker
5
+ attr_accessor :client, :type, :max_jobs_to_activate, :poll_interval, :timeout, :variables
6
+
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ Cloudmunda.register_worker(base)
10
+ end
11
+
12
+ def initialize(client)
13
+ @client = client
14
+ end
15
+
16
+ def complete_job(job, variables: {})
17
+ logger.info "Completed processing job #{job.type} #{job.key}"
18
+ client.complete_job(
19
+ jobKey: job.key,
20
+ variables: Hash(variables).to_json,
21
+ )
22
+ end
23
+
24
+ def fail_job(job, reason: "")
25
+ logger.error "Failed processing job #{job.type} #{job.key}: #{reason}"
26
+ client.fail_job(
27
+ jobKey: job.key,
28
+ retries: job.retries - 1,
29
+ errorMessage: reason,
30
+ )
31
+ rescue => e
32
+ logger.error e.message
33
+ end
34
+
35
+ def logger
36
+ ::Cloudmunda.logger
37
+ end
38
+
39
+ module ClassMethods
40
+ # Sets the type of service task the worker should subscribe to.
41
+ #
42
+ # @example
43
+ # class MyWorker
44
+ # include ::Cloudmunda::Worker
45
+ # type "some-service-task-type"
46
+ # end
47
+ #
48
+ # @param [String] type
49
+ # @return [String]
50
+ def type(type)
51
+ @type = type
52
+ end
53
+
54
+ # Returns the type of service task the worker should subscribe to.
55
+ #
56
+ # @return [String]
57
+ def get_type
58
+ @type
59
+ end
60
+
61
+ # Sets the maximum number of jobs to send to the worker for processing at once.
62
+ # As jobs get completed by the worker, more jobs will be sent to the worker
63
+ # but always within this limit.
64
+ #
65
+ # @example
66
+ # class MyWorker
67
+ # include ::Cloudmunda::Worker
68
+ # max_jobs_to_activate 5
69
+ # end
70
+ #
71
+ # @param [Integer] max_jobs_to_activate
72
+ # @return [Integer]
73
+ def max_jobs_to_activate(max_jobs_to_activate)
74
+ @max_jobs_to_activate = max_jobs_to_activate
75
+ end
76
+
77
+ # Returns the maximum number of jobs to send to the worker for processing at once.
78
+ # As jobs get completed by the worker, more jobs will be sent to the worker
79
+ # but always within this limit.
80
+ #
81
+ # @return [Integer]
82
+ def get_max_jobs_to_activate
83
+ @max_jobs_to_activate || 1
84
+ end
85
+
86
+ # Sets the interval duration in seconds between polls to the broker.
87
+ #
88
+ # @example
89
+ # class MyWorker
90
+ # include ::Cloudmunda::Worker
91
+ # poll_interval 5
92
+ # end
93
+ #
94
+ # @param [Integer] poll_interval
95
+ # @return [Integer]
96
+ def poll_interval(poll_interval)
97
+ @poll_interval = poll_interval
98
+ end
99
+
100
+ # Returns the interval duration in seconds between polls to the broker.
101
+ #
102
+ # @return [Integer]
103
+ def get_poll_interval
104
+ @poll_interval || 5
105
+ end
106
+
107
+ # Sets the time in seconds the worker has to process the job before
108
+ # the broker consider it as expired and can schedule it to another worker.
109
+ #
110
+ # @example
111
+ # class MyWorker
112
+ # include ::Cloudmunda::Worker
113
+ # timeout 30
114
+ # end
115
+ #
116
+ # @param [Integer] timeout
117
+ # @return [Integer]
118
+ def timeout(timeout)
119
+ @timeout = timeout
120
+ end
121
+
122
+ # Returns the time in seconds the worker has to process the job before
123
+ # the broker consider it as expired and can schedule it to another worker.
124
+ #
125
+ # @return [Integer]
126
+ def get_timeout
127
+ @timeout || 30
128
+ end
129
+
130
+ # Sets the worker's variables to fetch from the broker when polling for new
131
+ # jobs.
132
+ #
133
+ # @example
134
+ # class MyWorker
135
+ # include ::Cloudmunda::Worker
136
+ # variables [:foo, :bar]
137
+ # end
138
+ #
139
+ # @param [Array<String, Symbol>] variables
140
+ # @return [Array<String, Symbol>]
141
+ def variables(variables)
142
+ @variables = variables
143
+ end
144
+
145
+ # Returns the worker's variables to fetch from the broker when polling for new
146
+ # jobs.
147
+ #
148
+ # @return [Array<String, Symbol>]
149
+ def get_variables_to_fetch
150
+ @variables.to_a
151
+ end
152
+
153
+ # Returns the worker's name.
154
+ #
155
+ # @return [String]
156
+ def get_name
157
+ name = self.name.gsub(/::/, ':')
158
+ name.gsub!(/([^A-Z:])([A-Z])/) { "#{$1}_#{$2}" }
159
+ name.downcase
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,169 @@
1
+ require 'singleton'
2
+ require 'optparse'
3
+ require 'fileutils'
4
+ require 'cloudmunda'
5
+ require 'cloudmunda/cli/launcher'
6
+
7
+ $stdout.sync = true
8
+
9
+ module Cloudmunda
10
+ class CLI
11
+ include Singleton
12
+
13
+ attr_accessor :launcher
14
+
15
+ def parse(argv = ARGV)
16
+ parse_options(argv)
17
+ end
18
+
19
+ def run
20
+ boot
21
+
22
+ self_read, self_write = IO.pipe
23
+ sigs = %w[INT TERM]
24
+ sigs.each do |sig|
25
+ trap sig do
26
+ self_write.write("#{sig}\n")
27
+ end
28
+ rescue ArgumentError
29
+ logger.warn "Signal #{sig} not supported"
30
+ end
31
+
32
+ launch(self_read)
33
+ end
34
+
35
+ private
36
+
37
+ def parse_options(argv)
38
+ option_parser.parse!(argv)
39
+ end
40
+
41
+ def option_parser
42
+ OptionParser.new.tap do |p|
43
+ p.on "-e", "--env ENV", "Application environment" do |arg|
44
+ config.env = arg
45
+ end
46
+
47
+ p.on "-i", "--client-id CLIENT_ID", "Client ID" do |arg|
48
+ config.client_id = arg
49
+ end
50
+
51
+ p.on "-r", "--require [PATH|DIR]", "Location of Rails application with workers or file to require" do |arg|
52
+ if !File.exist?(arg) ||
53
+ (File.directory?(arg) && !File.exist?("#{arg}/config/application.rb"))
54
+ raise ArgumentError, "#{arg} is not a ruby file nor a rails application"
55
+ else
56
+ config.require = arg
57
+ end
58
+ end
59
+
60
+ p.on "-s", "--client-secret CLIENT_SECRET", "Client Secret" do |arg|
61
+ config.client_secret = arg
62
+ end
63
+
64
+ p.on "-u", "--zeebe-url ZEEBE_URL", "Zeebe URL" do |arg|
65
+ config.zeebe_url = arg
66
+ end
67
+
68
+ p.on "-U", "--zeebe-auth-url ZEEBE_AUTH_URL", "Zeebe Authorization Server URL" do |arg|
69
+ config.auth_url = arg
70
+ end
71
+
72
+ p.on "-a", "--audience URL", "Zeebe Audience" do |arg|
73
+ config.audience = arg
74
+ end
75
+
76
+ p.on "-T", "--use-access-token STRING", "Zeebe Audience" do |arg|
77
+ config.use_access_token = arg.to_s.downcase == 'true'
78
+ end
79
+
80
+ p.on "-t", "--timeout NUM", "Shutdown timeout" do |arg|
81
+ timeout = Integer(arg)
82
+ raise ArgumentError, "timeout must be a positive integer" if timeout <= 0
83
+ config.timeout = timeout
84
+ end
85
+
86
+ # p.on "-v", "--verbose", "Print more verbose output" do |arg|
87
+ # ::Cloudmuna.logger.level = ::Logger::DEBUG
88
+ # end
89
+
90
+ p.on "-V", "--version", "Print version and exit" do |arg|
91
+ puts "Cloudmunda #{::Cloudmunda::VERSION}"
92
+ exit(0)
93
+ end
94
+
95
+ p.banner = "Usage: cloudmunda [options]"
96
+ p.on_tail "-h", "--help", "Show help" do
97
+ puts p
98
+
99
+ exit(1)
100
+ end
101
+ end
102
+ end
103
+
104
+ def boot
105
+ ENV["RACK_ENV"] = ENV["RAILS_ENV"] = config.env
106
+
107
+ if File.directory?(config.require)
108
+ require 'rails'
109
+ if ::Rails::VERSION::MAJOR < 6
110
+ raise "Cloudmunda does not supports this version of Rails"
111
+ else
112
+ require File.expand_path("#{config.require}/config/environment.rb")
113
+ Dir[Rails.root.join('app/jobs/**/*.rb')].each { |f| require f }
114
+
115
+ logger.info "Booted Rails #{::Rails.version} application in #{config.env} environment"
116
+ end
117
+ else
118
+ require config.require
119
+ end
120
+ end
121
+
122
+ def launch(self_read)
123
+ @launcher = ::Cloudmunda::Launcher.new
124
+
125
+ if config.env == "development" && $stdout.tty?
126
+ logger.info "Starting processing, hit Ctrl-C to stop"
127
+ end
128
+
129
+ begin
130
+ launcher.start
131
+
132
+ while readable_io = IO.select([self_read])
133
+ signal = readable_io.first[0].gets.strip
134
+ handle_signal(signal)
135
+ end
136
+ rescue Interrupt
137
+ logger.info "Shutting down"
138
+ launcher.stop
139
+ logger.info "Bye!"
140
+
141
+ exit(0)
142
+ end
143
+ end
144
+
145
+ def handle_signal(signal)
146
+ handler = signal_handlers[signal]
147
+ if handler
148
+ handler.call(self)
149
+ else
150
+ logger.warn "No signal handler for #{signal}"
151
+ end
152
+ end
153
+
154
+ def signal_handlers
155
+ {
156
+ "INT" => ->(cli) { raise Interrupt },
157
+ "TERM" => ->(cli) { raise Interrupt },
158
+ }
159
+ end
160
+
161
+ def config
162
+ ::Cloudmunda
163
+ end
164
+
165
+ def logger
166
+ ::Cloudmunda.logger
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,35 @@
1
+ module Cloudmunda
2
+ module Configuration
3
+ VALID_OPTIONS_KEYS = %i[env require logger timeout zeebe_url auth_url client_id client_secret audience graphql_url].freeze
4
+ attr_accessor(*VALID_OPTIONS_KEYS)
5
+
6
+ # Sets all configuration options to their default values when this module is extended.
7
+ def self.extended(base)
8
+ base.reset
9
+ end
10
+
11
+ def configure
12
+ yield self
13
+ end
14
+
15
+ def config
16
+ VALID_OPTIONS_KEYS.inject({}) do |option, key|
17
+ option.merge!(key => send(key))
18
+ end
19
+ end
20
+
21
+ # Resets all configuration options to the defaults.
22
+ def reset
23
+ @env = ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
24
+ @logger = Logger.new($stdout)
25
+ @require = "."
26
+ @timeout = 30
27
+ @zeebe_url = ENV['ZEEBE_URL']
28
+ @auth_url = ENV['ZEEBE_AUTHORIZATION_SERVER_URL']
29
+ @client_id = ENV['ZEEBE_CLIENT_ID']
30
+ @client_secret = ENV['ZEEBE_CLIENT_SECRET']
31
+ @audience = ENV['ZEEBE_AUDIENCE']
32
+ @graphql_url = ENV['GRAPHQL_URL']
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rest-client'
4
+ require 'json'
5
+ require 'uri'
6
+ require 'base64'
7
+
8
+ module Cloudmunda
9
+ module Graphql
10
+ class Client
11
+ def self.post(params = {})
12
+ response = RestClient.post(Cloudmunda.graphql_url, params.to_json, headers.merge(content_headers))
13
+ JSON.parse(response.body)
14
+ end
15
+
16
+ def self.headers
17
+ {
18
+ authorization: "Bearer " + Cloudmunda::API::AccessToken.create(audience_url: 'tasklist.camunda.io').access_token
19
+ }
20
+ end
21
+
22
+ def self.content_headers
23
+ {
24
+ 'Content-Type': 'application/json'
25
+ }
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cloudmunda
4
+ module Graphql
5
+ class UserTasks
6
+ def self.all
7
+ query = "{
8
+ tasks(query: { state: CREATED })
9
+ {
10
+ id
11
+ taskDefinitionId
12
+ name
13
+ taskState
14
+ assignee
15
+ taskState
16
+ isFirst
17
+ formKey
18
+ processDefinitionId
19
+ completionTime
20
+ processName
21
+ variables {
22
+ name
23
+ value
24
+ }
25
+ }
26
+ }"
27
+ Cloudmunda::Graphql::Client.post(query: query)['data']['tasks']
28
+ end
29
+
30
+ def self.run_mutation(mutation)
31
+ Cloudmunda::Graphql::Client.post(query: mutation)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,2 @@
1
+ require_relative 'graphql/client'
2
+ require_relative 'graphql/user_tasks'
@@ -0,0 +1,15 @@
1
+ module Cloudmunda
2
+ module Loggable
3
+ def logger
4
+ @logger || setup_logger
5
+ end
6
+
7
+ def logger=(logger)
8
+ @logger = logger
9
+ end
10
+
11
+ def setup_logger
12
+ @logger = Beez.config.logger
13
+ end
14
+ end
15
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cloudmunda
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.1"
5
5
  end
@@ -0,0 +1,107 @@
1
+ require 'zeebe/client'
2
+
3
+ module Cloudmunda
4
+ module Zeebe
5
+ class Client
6
+
7
+ attr_reader :client
8
+
9
+ def initialize(url: ::Cloudmunda.zeebe_url)
10
+ @client = ::Zeebe::Client::GatewayProtocol::Gateway::Stub.new(url, authentication_headers)
11
+ end
12
+
13
+ def activate_jobs(params = {})
14
+ run(:activate_jobs,
15
+ ::Zeebe::Client::GatewayProtocol::ActivateJobsRequest.new(params)
16
+ )
17
+ end
18
+
19
+ def cancel_workflow_instance(params = {})
20
+ run(:cancel_workflow_instance,
21
+ ::Zeebe::Client::GatewayProtocol::CancelWorkflowInstanceRequest.new(params)
22
+ )
23
+ end
24
+
25
+ def complete_job(params = {})
26
+ run(:complete_job,
27
+ ::Zeebe::Client::GatewayProtocol::CompleteJobRequest.new(params)
28
+ )
29
+ end
30
+
31
+ def create_process_instance(params = {})
32
+ run(:create_process_instance,
33
+ ::Zeebe::Client::GatewayProtocol::CreateProcessInstanceRequest.new(params)
34
+ )
35
+ end
36
+
37
+ def deploy_process(params = {})
38
+ run(:deploy_process,
39
+ ::Zeebe::Client::GatewayProtocol::DeployProcessRequest.new(params)
40
+ )
41
+ end
42
+
43
+ def fail_job(params = {})
44
+ run(:fail_job,
45
+ ::Zeebe::Client::GatewayProtocol::FailJobRequest.new(params)
46
+ )
47
+ end
48
+
49
+ def throw_error(params = {})
50
+ run(:throw_error,
51
+ ::Zeebe::Client::GatewayProtocol::ThrowErrorRequest.new(params)
52
+ )
53
+ end
54
+
55
+ def publish_message(params = {})
56
+ run(:publish_message,
57
+ ::Zeebe::Client::GatewayProtocol::PublishMessageRequest.new(params)
58
+ )
59
+ end
60
+
61
+ def resolve_incident(params = {})
62
+ run(:resolve_incident,
63
+ ::Zeebe::Client::GatewayProtocol::ResolveIncidentRequest.new(params)
64
+ )
65
+ end
66
+
67
+ def set_variables(params = {})
68
+ run(:set_variables,
69
+ ::Zeebe::Client::GatewayProtocol::SetVariablesRequest.new(params)
70
+ )
71
+ end
72
+
73
+ def topology(params = {})
74
+ run(:topology,
75
+ ::Zeebe::Client::GatewayProtocol::TopologyRequest.new(params)
76
+ )
77
+ end
78
+
79
+ def update_job_retries(params = {})
80
+ run(:update_job_retries,
81
+ ::Zeebe::Client::GatewayProtocol::UpdateJobRetriesRequest.new(params)
82
+ )
83
+ end
84
+
85
+ private
86
+
87
+ def run(method, params = {})
88
+ client.public_send(method, params)
89
+ rescue ::GRPC::Unavailable => exception
90
+ logger.error exception.message
91
+ raise exception
92
+ end
93
+
94
+ def logger
95
+ # ::Cloudmunda.logger
96
+ end
97
+
98
+ def authentication_headers
99
+ token = Cloudmunda::API::AccessToken.create
100
+ channel_creds = GRPC::Core::ChannelCredentials.new()
101
+ auth_proc = proc { { 'authorization' => "Bearer #{token.access_token}" } }
102
+ call_creds = GRPC::Core::CallCredentials.new(auth_proc)
103
+ channel_creds.compose(call_creds)
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1 @@
1
+ require_relative 'zeebe/client'
data/lib/cloudmunda.rb CHANGED
@@ -1,8 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'concurrent'
4
+
3
5
  require_relative "cloudmunda/version"
6
+ require_relative 'cloudmunda/loggable'
7
+ require_relative 'cloudmunda/cli/worker'
8
+ require_relative 'cloudmunda/configuration'
9
+ require_relative 'cloudmunda/api'
10
+ require_relative 'cloudmunda/zeebe'
11
+ require_relative 'cloudmunda/graphql'
4
12
 
5
13
  module Cloudmunda
6
- class Error < StandardError; end
7
- # Your code goes here...
14
+ extend Configuration
15
+ extend Loggable
16
+
17
+ def self.client
18
+ @client ||= ::Cloudmunda::Zeebe::Client.new
19
+ end
20
+
21
+ def self.register_worker(worker)
22
+ self.workers << worker
23
+ end
24
+
25
+ def self.workers
26
+ @workers ||= []
27
+ end
8
28
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudmunda
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lien Van Den Steen
@@ -9,11 +9,54 @@ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
11
  date: 2021-12-16 00:00:00.000000000 Z
12
- dependencies: []
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rest-client
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: zeebe-client
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.16'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.16'
41
+ - !ruby/object:Gem::Dependency
42
+ name: concurrent-ruby
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
13
55
  description: Ruby wrapper for Camunda Cloud
14
56
  email:
15
57
  - lienvandensteen@gmail.com
16
- executables: []
58
+ executables:
59
+ - cloudmunda
17
60
  extensions: []
18
61
  extra_rdoc_files: []
19
62
  files:
@@ -28,8 +71,25 @@ files:
28
71
  - Rakefile
29
72
  - bin/console
30
73
  - bin/setup
74
+ - exe/cloudmunda
31
75
  - lib/cloudmunda.rb
76
+ - lib/cloudmunda/api.rb
77
+ - lib/cloudmunda/api/access_token.rb
78
+ - lib/cloudmunda/api/client.rb
79
+ - lib/cloudmunda/api/o_auth_resource.rb
80
+ - lib/cloudmunda/cli.rb
81
+ - lib/cloudmunda/cli/launcher.rb
82
+ - lib/cloudmunda/cli/processor.rb
83
+ - lib/cloudmunda/cli/supervisor.rb
84
+ - lib/cloudmunda/cli/worker.rb
85
+ - lib/cloudmunda/configuration.rb
86
+ - lib/cloudmunda/graphql.rb
87
+ - lib/cloudmunda/graphql/client.rb
88
+ - lib/cloudmunda/graphql/user_tasks.rb
89
+ - lib/cloudmunda/loggable.rb
32
90
  - lib/cloudmunda/version.rb
91
+ - lib/cloudmunda/zeebe.rb
92
+ - lib/cloudmunda/zeebe/client.rb
33
93
  homepage: https://github.com/lienvdsteen/cloudmunda
34
94
  licenses:
35
95
  - MIT