kuroko2 0.2.3 → 0.3.0

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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/kuroko2/definition_linker.js +1 -1
  3. data/app/assets/stylesheets/kuroko2/application.scss +4 -0
  4. data/app/controllers/kuroko2/api/job_instances_controller.rb +3 -2
  5. data/app/controllers/kuroko2/dashboard_controller.rb +1 -1
  6. data/app/controllers/kuroko2/job_definitions_controller.rb +2 -2
  7. data/app/controllers/kuroko2/job_instances_controller.rb +8 -18
  8. data/app/controllers/kuroko2/tokens_controller.rb +3 -3
  9. data/app/controllers/kuroko2/users_controller.rb +17 -1
  10. data/app/models/kuroko2/job_definition.rb +11 -0
  11. data/app/models/kuroko2/job_instance.rb +19 -2
  12. data/app/models/kuroko2/job_schedule.rb +3 -6
  13. data/app/models/kuroko2/job_suspend_schedule.rb +1 -1
  14. data/app/models/kuroko2/token.rb +8 -0
  15. data/app/views/kuroko2/execution_logs/index.json.jbuilder +1 -1
  16. data/app/views/kuroko2/job_definitions/_form.html.slim +6 -1
  17. data/app/views/kuroko2/job_definitions/_list.html.slim +12 -5
  18. data/app/views/kuroko2/job_definitions/_search_results.html.slim +10 -3
  19. data/app/views/kuroko2/job_definitions/show.html.slim +5 -3
  20. data/app/views/kuroko2/job_timelines/dataset.json.jbuilder +1 -1
  21. data/app/views/kuroko2/logs/index.html.slim +6 -6
  22. data/app/views/kuroko2/tokens/index.html.slim +2 -2
  23. data/app/views/kuroko2/users/edit.html.slim +37 -0
  24. data/app/views/kuroko2/users/show.html.slim +8 -4
  25. data/db/migrate/026_add_webhook_url_to_job_definitions.rb +5 -0
  26. data/lib/autoload/kuroko2/workflow/engine.rb +4 -6
  27. data/lib/autoload/kuroko2/workflow/notifier/concerns/chat_message_builder.rb +12 -0
  28. data/lib/autoload/kuroko2/workflow/notifier/hipchat.rb +30 -4
  29. data/lib/autoload/kuroko2/workflow/notifier/mail.rb +9 -1
  30. data/lib/autoload/kuroko2/workflow/notifier/slack.rb +31 -4
  31. data/lib/autoload/kuroko2/workflow/notifier/webhook.rb +173 -0
  32. data/lib/autoload/kuroko2/workflow/task/queue.rb +2 -2
  33. data/lib/autoload/kuroko2/workflow/task/sub_process.rb +2 -4
  34. data/lib/kuroko2/version.rb +1 -1
  35. data/spec/controllers/users_controller_spec.rb +51 -2
  36. data/spec/dummy/config/kuroko2.yml +2 -0
  37. data/spec/dummy/db/schema.rb +76 -92
  38. data/spec/dummy/log/development.log +1143 -0
  39. data/spec/dummy/log/test.log +271238 -0
  40. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/-Q/-QhKaYdB1JJGAsq6Ih7uQZJD46XkY5Gw0-38DBDVg3Y.cache +1 -0
  41. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/-h/-h4P8jVHGGfJVwG36J8kBUkHFa3HHGNiJQz0936uaxg.cache +1 -0
  42. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/24/24laeo7m53bkbst3Gxu4hlJY-EbnK-rQxH-DA4ujzwY.cache +1 -0
  43. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/2e/2eEFzw4UUZHJizptl3nT5jVv3IL25_RdYImr3lAVlJ4.cache +1 -0
  44. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/2z/2z8OmZrK1FFsPb8zq9RFli2IVM0gOna92hieZ4cK36c.cache +0 -0
  45. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/4K/4Ky9Fg4qo8d_i8bJF6NOhDpxHuJ5kIX8n0w6C8wdHDU.cache +3 -0
  46. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Di/Di9ZEIp2OLO96tkqFN21AWfdoAhc0OCBOJb3o8RUk1I.cache +0 -0
  47. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Ex/ExgjnrLZM8Hc_uT_sWVaQSNR26tGYCGUsbJDXs9GYO0.cache +1 -0
  48. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/FJ/FJW4oZDLKhsjg_UHdycQAY88BFtnMeeLCuArx6HIMdM.cache +0 -0
  49. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Iq/IqSpSELRWrOQtOK8F6mGZFPvfafM4yn8gB_VquH9E50.cache +1 -0
  50. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/JJ/JJ2dvNSAjiGkZZs_1Dz2TMWDJs4HypQ95b26cHN13I0.cache +1 -0
  51. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/LJ/LJsVKu6GCC14s20BfJEDXwajK6-xWF72ANX5ONOTX1w.cache +1 -0
  52. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/LL/LLCxb9El_km568UsA8DDOe8Vh3pPKE8IU6ZNww9ie8s.cache +1 -0
  53. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Pq/pqeS6zqHUdv-_25W2PSQMm31ql_cIpaK1iqEcfiNyUQ.cache +0 -0
  54. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Q9/Q9TwXzoqqs8Dm9v25gjZxYIkZW1UKm2X8g2koo5aYhQ.cache +1 -0
  55. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/QJ/QJNiKbGgll2T_0mhQus6ZL9a2SM4Xqz7vs2V2KbELPw.cache +1 -0
  56. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/QV/QVVeFw88kC-7HfzOk_dCVoFBnz9W_7-gmJFYh-Xlh-0.cache +0 -0
  57. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Qs/QsTn160WUaI8_u8tE8rz37XiSV9qLvSxiXAR-tCtV3Y.cache +0 -0
  58. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/V2/V2QhbjrEGa7in5Uj1P-3E1Ziw2AGno_ADefUhPNHw_A.cache +0 -0
  59. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Vj/VjICw6h8uQ_278ekFlu1znnPR8Gz1vWF_mRgy0KjZXs.cache +1 -0
  60. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/dH/dHFrKzzqtF3aoKs2wAI8z0CNHZoNuytfiHur9Zgz9z0.cache +1 -0
  61. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/fE/fEFAQG1Uqt2e58VitSFoPsjVjntfPtgL9RKoKHUTS9U.cache +1 -0
  62. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/fM/FMdA_jabowoG5SAWE93G7Hu8A-5GhfzC9Gjy3EJp1Ts.cache +1 -0
  63. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/hI/hIIciXZzgFWXdW0lamhs4r5zFFr4hnXvWAuOihCS5qI.cache +0 -0
  64. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/hW/hWOkA9ZbQJy1YfEXThjYPOiXRDVRgAeW8TmLm-uQCYk.cache +1 -0
  65. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/iv/ivrIChCv2olhH20VXJQTJgVWlx3t9ncsYxV0QfLT0Bs.cache +0 -0
  66. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/k3/k31T18tx4gR2rhpmDMYMLUa8KPDt7QRIR_qRK56LOdE.cache +0 -0
  67. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/oB/oB0c7AuqQVdgqJDgphXXOHw3SXqbDUYfZWxxm9-v_is.cache +0 -0
  68. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ou/ouMQatdL-PPhPVPw5o0xm5EBkVeVWLti06EUEVmfOBo.cache +1 -0
  69. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/qM/qMZbagtANt1BI7qOJXWEpzxx1qa-IKMkyOy6EKEIIbE.cache +0 -0
  70. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/rc/rcceqGk009vh5_tK6Y-i1dvbYQwuUtRF888xZcnZrj8.cache +1 -0
  71. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/sJ/sJOLo6McRe1YhfgIaGhcDaAoulIdYY7Rs07TAKmrSoY.cache +0 -0
  72. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/sh/shGRyWpXjqxMTGB3As4Vibs8VGS0bZHHd4s0qabhwXI.cache +1 -0
  73. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/sr/SRfo3vCtDVy0qd-OB9dv5a-xIVTHkJZDn8XwY_ymy7g.cache +1 -0
  74. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/vV/vV3Loym0NolgPoXFEVPOr5rDgB_Q5kvUA3InuEEvEvQ.cache +1 -0
  75. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/wK/wK5S37PPHfwFm-4mrLMMPdOZ29-H75zo1T2gmErWjLI.cache +0 -0
  76. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/wm/wm_qI7_LCxbnH6p7BK1Pqcocl-6TcULzTERICGZIdL0.cache +1 -0
  77. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/zC/zCYIl3BglB_fMHME2oqK30CF97CBUG09R0ICLpE-pIM.cache +0 -0
  78. data/spec/features/dashborad_spec.rb +10 -3
  79. data/spec/features/users_spec.rb +20 -4
  80. data/spec/models/job_instance_spec.rb +1 -1
  81. data/spec/models/token_spec.rb +38 -0
  82. data/spec/workflow/notifier/hipchat_spec.rb +91 -0
  83. data/spec/workflow/notifier/mail_spec.rb +18 -2
  84. data/spec/workflow/notifier/slack_spec.rb +85 -0
  85. data/spec/workflow/notifier/webhook_spec.rb +187 -0
  86. data/spec/workflow/task/queue_spec.rb +11 -0
  87. metadata +93 -16
@@ -12,13 +12,13 @@ ul class="nav nav-pills" role="tablist"
12
12
  table.table.table-condensed.table-hover data-reload="#{@instance.working? && !@instance.error?}"
13
13
  thead
14
14
  tr
15
- th.col-md-1  
15
+ th  
16
16
  th.col-md-1 Time
17
17
  th.col-md-10 Message
18
18
  - for log in @logs
19
19
  tr
20
20
  td= labeled_log_level(log.level)
21
- td.nowrap= l(log.created_at, format: :short)
21
+ td.nowrap= log.created_at
22
22
  td.log= rinku_auto_link(log.message, :urls)
23
23
  = javascript_include_tag 'kuroko2/instance_linker'
24
24
 
@@ -26,8 +26,8 @@ ul class="nav nav-pills" role="tablist"
26
26
  table.table.table-condensed.table-hover
27
27
  thead
28
28
  tr
29
- th.col-md-1 Time
30
- th.col-md-1 PID
31
- th.col-md-2 Token
32
- th.col-md-8 Message
29
+ th Time
30
+ th PID
31
+ th Token
32
+ th.col-md-7 Message
33
33
  tbody#execution_log_body
@@ -21,14 +21,14 @@
21
21
  params: { invoke: 'retry' },
22
22
  class: 'btn btn-default btn-block btn-xs',
23
23
  role: 'button',
24
- disabled: !token.failure?)
24
+ disabled: !token.retryable?)
25
25
  td.col-md-3= button_to('Skip',
26
26
  job_definition_job_instance_token_path(job_definition_id: @definition.id, job_instance_id: @instance.id, id: token.id),
27
27
  method: :patch,
28
28
  params: { invoke: 'skip' },
29
29
  class: 'btn btn-default btn-block btn-xs',
30
30
  role: 'button',
31
- disabled: !token.failure?,
31
+ disabled: !token.skippable?,
32
32
  data: { confirm: 'Continue executing job script? If not, use "Cancel" button.' })
33
33
  - if (execution = token.execution)
34
34
  tr
@@ -0,0 +1,37 @@
1
+ - @title = "#{@user.name_was} « Users"
2
+ - @content_title = "Edit User"
3
+
4
+ - content_for :breadcrumb do
5
+ ol.breadcrumb
6
+ li= link_to raw('<i class="fa fa-users"></i> Users'), users_path
7
+ li= link_to 'Details', user_path(@user)
8
+ li.active Edit User
9
+
10
+ .row
11
+ .col-md-12
12
+ .box
13
+ = form_for @user, method: :put do |form|
14
+ .box-header
15
+ h2.box-title ##{@user.id} #{@user.name_was}
16
+ .box-body.table-responsive
17
+ - if @user.errors.present?
18
+ .row
19
+ ul.alert.alert-danger
20
+ - @user.errors.full_messages.each do |message|
21
+ li #{message}
22
+
23
+ .row
24
+ .col-md-2.visible-lg-block.visible-md-block
25
+ img.img-circle src='#{@user.image}' alt="#{@user.name_was}"
26
+ .col-md-10
27
+ div class="form-group #{@user.errors.has_key?(:name) ? 'has-error' : ''}"
28
+ = form.label :name
29
+ = form.text_field :name, class: 'form-control', placeholder: 'Group Name'
30
+ div class="form-group #{@user.errors.has_key?(:email) ? 'has-error' : ''}"
31
+ = form.label :email
32
+ = form.text_field :email, class: 'form-control', placeholder: 'group@example.com'
33
+ .box-footer
34
+ .row
35
+ .col-md-3.col-md-offset-9
36
+ .form-group
37
+ = form.submit 'Update', class: 'btn btn-default btn-block'
@@ -26,10 +26,14 @@
26
26
  - unless @user.google_account?
27
27
  th Note
28
28
  td This account is group account.
29
- .box-footer
30
- .row
31
- .col-md-3.col-md-offset-9
32
- = link_to 'Destroy User', @user, method: :delete, class: 'btn btn-default btn-block', role: 'button', disabled: @user.google_account?, data: { confirm: 'Are you sure?' }
29
+
30
+ - unless @user.google_account?
31
+ .box-footer
32
+ .row
33
+ .col-md-3.col-md-offset-6
34
+ = link_to 'Edit User', edit_user_path(@user), class: 'btn btn-default btn-block', role: 'button'
35
+ .col-md-3
36
+ = link_to 'Destroy User', @user, method: :delete, class: 'btn btn-default btn-block', role: 'button', data: { confirm: 'Are you sure?' }
33
37
  .col-md-7
34
38
  .box#definitions
35
39
  .box-header
@@ -0,0 +1,5 @@
1
+ class AddWebhookUrlToJobDefinitions < ActiveRecord::Migration
2
+ def change
3
+ add_column :job_definitions, :webhook_url, :text
4
+ end
5
+ end
@@ -30,7 +30,7 @@ module Kuroko2
30
30
 
31
31
  Kuroko2.logger.info(message)
32
32
 
33
- Notifier.notify(:working, token.job_instance)
33
+ Notifier.notify(:retrying, token.job_instance)
34
34
  end
35
35
  end
36
36
 
@@ -49,7 +49,7 @@ module Kuroko2
49
49
 
50
50
  Kuroko2.logger.info(message)
51
51
 
52
- Notifier.notify(:working, token.job_instance)
52
+ Notifier.notify(:skipping, token.job_instance)
53
53
  end
54
54
  end
55
55
 
@@ -100,15 +100,13 @@ module Kuroko2
100
100
  message = "(token #{token.uuid}) Marked as 'finished'."
101
101
 
102
102
  token.job_instance.logs.info(message)
103
+ Kuroko2.logger.info(message)
103
104
  token.mark_as_finished
104
105
  unless token.parent
105
106
  token.job_instance.touch(:finished_at)
107
+ Notifier.notify(:finished, token.job_instance)
106
108
  token.destroy!
107
109
  end
108
-
109
- Kuroko2.logger.info(message)
110
-
111
- Notifier.notify(:finished, token.job_instance)
112
110
  end
113
111
  end
114
112
 
@@ -16,6 +16,18 @@ module Kuroko2
16
16
  "Finished to execute '#{@definition.name}'"
17
17
  end
18
18
 
19
+ def launched_text
20
+ "Launched '#{@definition.name}'"
21
+ end
22
+
23
+ def retrying_text
24
+ "Retrying the current task in '#{@definition.name}'"
25
+ end
26
+
27
+ def skipping_text
28
+ "Skipping the current task in '#{@definition.name}'"
29
+ end
30
+
19
31
  def long_elapsed_time_text
20
32
  "The running time is longer than expected '#{@definition.name}'."
21
33
  end
@@ -13,15 +13,41 @@ module Kuroko2
13
13
  @message_builder = Workflow::Notifier::Concerns::ChatMessageBuilder.new(instance)
14
14
  end
15
15
 
16
- def notify_working
17
- # do nothing
16
+ def notify_launch
17
+ if @definition.hipchat_notify_finished?
18
+ message = build_message(level: 'SUCCESS', text: message_builder.launched_text)
19
+ message << "<br>"
20
+ message << @instance.logs.reverse.detect{ |log| log.level == 'INFO' }.try!(:message)
21
+
22
+ send_to_hipchat(message, color: 'yellow')
23
+ end
24
+ end
25
+
26
+ def notify_retrying
27
+ if @definition.hipchat_notify_finished
28
+ message = build_message(level: 'SUCCESS', text: message_builder.retrying_text)
29
+ message << "<br>"
30
+ message << @instance.logs.last(2).first.message
31
+
32
+ send_to_hipchat(message, color: 'yellow')
33
+ end
34
+ end
35
+
36
+ def notify_skipping
37
+ if @definition.hipchat_notify_finished
38
+ message = build_message(level: 'SUCCESS', text: message_builder.skipping_text)
39
+ message << "<br>"
40
+ message << @instance.logs.last(2).first.message
41
+
42
+ send_to_hipchat(message, color: 'yellow')
43
+ end
18
44
  end
19
45
 
20
46
  def notify_cancellation
21
- if @definition.notify_cancellation
47
+ if @definition.notify_cancellation || @definition.hipchat_notify_finished?
22
48
  message = build_message(level: 'WARNING', text: message_builder.failure_text)
23
49
  message << "<br>"
24
- message << @instance.logs.last(2).first.message
50
+ message << @instance.logs.reverse.detect{ |log| log.level == 'WARN' }.try!(:message)
25
51
 
26
52
  send_to_hipchat(message, color: 'yellow')
27
53
  end
@@ -7,7 +7,15 @@ module Kuroko2
7
7
  @definition = job_instance.job_definition
8
8
  end
9
9
 
10
- def notify_working
10
+ def notify_launch
11
+ # do nothing
12
+ end
13
+
14
+ def notify_retrying
15
+ # do nothing
16
+ end
17
+
18
+ def notify_skipping
11
19
  # do nothing
12
20
  end
13
21
 
@@ -9,6 +9,7 @@ module Kuroko2
9
9
  FAILURE = 'danger'
10
10
  CRITICAL = 'danger'
11
11
  SUCCESS = 'good'
12
+ INFO = '#439FE0'
12
13
  end
13
14
 
14
15
  def initialize(instance)
@@ -18,16 +19,42 @@ module Kuroko2
18
19
  @webhook_url = Kuroko2.config.notifiers.slack.webhook_url
19
20
  end
20
21
 
21
- def notify_working
22
- # do nothing
22
+ def notify_launch
23
+ if @definition.hipchat_notify_finished?
24
+ send_attachment_message_to_slack(
25
+ level: 'INFO',
26
+ text: message_builder.launched_text,
27
+ body: @instance.logs.reverse.detect{ |log| log.level == 'INFO' }.try!(:message),
28
+ )
29
+ end
30
+ end
31
+
32
+ def notify_retrying
33
+ if @definition.hipchat_notify_finished?
34
+ send_attachment_message_to_slack(
35
+ level: 'INFO',
36
+ text: message_builder.retrying_text,
37
+ body: @instance.logs.last(2).first.message,
38
+ )
39
+ end
40
+ end
41
+
42
+ def notify_skipping
43
+ if @definition.hipchat_notify_finished?
44
+ send_attachment_message_to_slack(
45
+ level: 'INFO',
46
+ text: message_builder.skipping_text,
47
+ body: @instance.logs.last(2).first.message,
48
+ )
49
+ end
23
50
  end
24
51
 
25
52
  def notify_cancellation
26
- if @definition.notify_cancellation
53
+ if @definition.notify_cancellation || @definition.hipchat_notify_finished?
27
54
  send_attachment_message_to_slack(
28
55
  level: 'WARNING',
29
56
  text: message_builder.failure_text,
30
- body: @instance.logs.last(2).first.message,
57
+ body: @instance.logs.reverse.detect{ |log| log.level == 'WARN' }.try!(:message),
31
58
  )
32
59
  end
33
60
  end
@@ -0,0 +1,173 @@
1
+ module Kuroko2
2
+ module Workflow
3
+ module Notifier
4
+ class Webhook
5
+ attr_reader :message_builder
6
+
7
+ HASH_ALGORITHM = 'sha256'
8
+ HMAC_DIGEST = OpenSSL::Digest.new(HASH_ALGORITHM)
9
+
10
+ def initialize(instance)
11
+ @instance = instance
12
+ @definition = instance.job_definition
13
+ @message_builder = Workflow::Notifier::Concerns::ChatMessageBuilder.new(instance)
14
+ @secret_token = Kuroko2.config.notifiers.webhook.try!(:secret_token)
15
+ end
16
+
17
+ def notify_launch
18
+ if @definition.hipchat_notify_finished?
19
+ request(
20
+ build_payload(
21
+ action: 'notify_launch',
22
+ level: 'INFO',
23
+ subject: message_builder.launched_text,
24
+ message: @instance.logs.reverse.detect{ |log| log.level == 'INFO' }.try!(:message),
25
+ )
26
+ )
27
+ end
28
+ end
29
+
30
+ def notify_retrying
31
+ if @definition.hipchat_notify_finished?
32
+ request(
33
+ build_payload(
34
+ action: 'notify_retrying',
35
+ level: 'INFO',
36
+ subject: message_builder.retrying_text,
37
+ message: @instance.logs.last(2).first.message,
38
+ )
39
+ )
40
+ end
41
+ end
42
+
43
+ def notify_skipping
44
+ if @definition.hipchat_notify_finished?
45
+ request(
46
+ build_payload(
47
+ action: 'notify_skipping',
48
+ level: 'INFO',
49
+ subject: message_builder.skipping_text,
50
+ message: @instance.logs.last(2).first.message,
51
+ )
52
+ )
53
+ end
54
+ end
55
+
56
+ def notify_cancellation
57
+ if @definition.notify_cancellation || @definition.hipchat_notify_finished?
58
+ request(
59
+ build_payload(
60
+ action: 'notify_cancellation',
61
+ level: 'WARNING',
62
+ subject: message_builder.failure_text,
63
+ message: @instance.logs.reverse.detect{ |log| log.level == 'WARN' }.try!(:message),
64
+ )
65
+ )
66
+ end
67
+ end
68
+
69
+ def notify_failure
70
+ request(
71
+ build_payload(
72
+ action: 'notify_failure',
73
+ level: 'FAILURE',
74
+ subject: message_builder.failure_text,
75
+ message: @instance.logs.last(2).first.message,
76
+ )
77
+ )
78
+ end
79
+
80
+ def notify_critical
81
+ request(
82
+ build_payload(
83
+ action: 'notify_critical',
84
+ level: 'CRITICAL',
85
+ subject: message_builder.failure_text,
86
+ message: @instance.logs.last(2).first.message,
87
+ )
88
+ )
89
+ end
90
+
91
+ def notify_finished
92
+ if @definition.hipchat_notify_finished?
93
+ request(
94
+ build_payload(
95
+ action: 'notify_finished',
96
+ level: 'SUCCESS',
97
+ subject: message_builder.finished_text,
98
+ )
99
+ )
100
+ end
101
+ end
102
+
103
+ def notify_long_elapsed_time
104
+ request(
105
+ build_payload(
106
+ action: 'notify_long_elapsed_time',
107
+ level: 'WARNING',
108
+ subject: message_builder.long_elapsed_time_text,
109
+ )
110
+ )
111
+ end
112
+
113
+ private
114
+
115
+ def request(body)
116
+ return unless @definition.webhook_url.present?
117
+
118
+ url = URI.parse(@definition.webhook_url)
119
+ conn = Faraday.new(url: "#{url.scheme}://#{url.host}") do |faraday|
120
+ faraday.port = url.port
121
+ faraday.adapter Faraday.default_adapter
122
+ end
123
+
124
+ json = body.to_json
125
+ response = conn.post do |req|
126
+ req.url(url.path)
127
+
128
+ req.headers['X-Kuroko2-Id'] = SecureRandom.uuid
129
+ if @secret_token.present?
130
+ req.headers['X-Kuroko2-Signature'] = "#{HASH_ALGORITHM}=#{OpenSSL::HMAC.hexdigest(HMAC_DIGEST, @secret_token, json)}"
131
+ end
132
+
133
+ req.headers['User-Agent'] = 'Kuroko2-Webhook'
134
+ req.headers['Content-Type'] = 'application/json'
135
+ req.body = json
136
+ end
137
+
138
+ unless response.success?
139
+ Kuroko2.logger.fatal("Failure sending webhook: status=#{response.status} body=#{response.body}")
140
+ end
141
+ end
142
+
143
+ def build_payload(action:, level:, subject:, message: nil)
144
+ {
145
+ action: action,
146
+ level: level,
147
+ subject: subject,
148
+ message: message,
149
+ job_instance: {
150
+ url: message_builder.job_instance_path,
151
+ id: @instance.id,
152
+ script: @instance.script,
153
+ finished_at: @instance.finished_at.try!(:iso8601),
154
+ canceled_at: @instance.canceled_at.try!(:iso8601),
155
+ error_at: @instance.error_at.try!(:iso8601),
156
+ created_at: @instance.created_at.try!(:iso8601),
157
+ },
158
+ job_definition: {
159
+ url: Kuroko2::Engine.routes.url_helpers.job_definition_url(
160
+ @definition,
161
+ host: Kuroko2.config.url_host,
162
+ protocol: Kuroko2.config.url_scheme,
163
+ ),
164
+ id: @definition.id,
165
+ name: @definition.name,
166
+ description: @definition.description,
167
+ }
168
+ }
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end