kuroko2 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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