foreman_scc_manager 1.6.1 → 1.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/scc_accounts_controller.rb +17 -11
  3. data/app/lib/actions/scc_manager/subscribe_product.rb +1 -0
  4. data/app/lib/actions/scc_manager/sync.rb +1 -0
  5. data/app/lib/actions/scc_manager/sync_plan_account_repositories.rb +38 -0
  6. data/app/lib/actions/scc_manager/sync_repositories.rb +3 -2
  7. data/app/lib/scc_manager.rb +1 -0
  8. data/app/models/concerns/recurring_logic_extensions.rb +9 -0
  9. data/app/models/scc_account.rb +123 -1
  10. data/app/models/scc_account_sync_plan_task_group.rb +7 -0
  11. data/app/models/scc_product.rb +1 -0
  12. data/app/views/scc_account_sync_plan_task_groups/_scc_account_sync_plan_task_groups.html.erb +4 -0
  13. data/app/views/scc_accounts/_form.html.erb +4 -0
  14. data/config/routes.rb +1 -0
  15. data/db/migrate/20190417202427_add_recurring_sync.foreman_scc_manager.rb +26 -0
  16. data/lib/foreman_scc_manager/engine.rb +4 -0
  17. data/lib/foreman_scc_manager/version.rb +1 -1
  18. data/lib/tasks/rubocop.rake +32 -0
  19. data/lib/tasks/test.rake +26 -0
  20. data/test/features/data_products_page1.json +10468 -0
  21. data/test/features/data_products_page2.json +11277 -0
  22. data/test/features/data_subscriptions.json +88 -0
  23. data/test/features/sync_test.rb +141 -0
  24. data/test/fixtures/models/scc_accounts.yml +24 -0
  25. data/test/fixtures/models/scc_products.yml +21 -0
  26. data/test/models/scc_account_test.rb +74 -0
  27. data/test/models/scc_product_test.rb +32 -0
  28. data/test/support/fixtures_support.rb +12 -0
  29. data/test/test_plugin_helper.rb +54 -0
  30. metadata +43 -5
  31. data/lib/tasks/foreman_scc_manager_tasks.rake +0 -45
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6322d1af0ee5027a7cdbdf56bf3b9e837dbd55e3f3a6bb1550ec3f9de9d08390
4
- data.tar.gz: 1ec9da3d0aa4e905906c0640f80eebf6c7b635565264ddcf455f9f2ee8912b4d
3
+ metadata.gz: 3107d456ab7e1c5f822a4cbd7bd7ab1892d9f44fbcceb8bd60ec0ffb0746a685
4
+ data.tar.gz: 4d048099707ca69af205df6ffa0dc31b178e96fcd4a817f4ec4106e6927b255d
5
5
  SHA512:
6
- metadata.gz: 3c52d141ba0f4f743fc24cf60f21a6fc2273f28025c87a571ead35981d97f525daaf6ac6c7d75baab784281ac1a44ffb0507b65d2f6c732239a945eb921cdae0
7
- data.tar.gz: ffd154f4d1d7783bfb339d1048958a76f23ea077560188d9a33919b41a7f4818dd554cea643ce7e686e3d23ae6ca0e5aea0473405c973bb40f27f1acd1e2f6c6
6
+ metadata.gz: ba3d694f7bdf5978877b7304b6d71a2a035bf58122ac7e1969914d4cd8b6a55adabd174965b10092b32986ec31d31f9e585f0d6a6ebf8045edae20305223538c
7
+ data.tar.gz: 998d9f9d79624bb1f42e69c8ad546d6dd1c309c373a9a5db11cf849da266ecf0e83d54b77a1e260476b40169d1183c600cafcef1bf99fb8854fc61a3594e783d
@@ -18,11 +18,10 @@ class SccAccountsController < ApplicationController
18
18
  # POST /scc_accounts
19
19
  def create
20
20
  @scc_account = SccAccount.new(scc_account_params)
21
- if @scc_account.save
22
- process_success
23
- else
24
- process_error
25
- end
21
+ @scc_account.save_with_logic!
22
+ process_success
23
+ rescue ActiveRecord::RecordInvalid
24
+ process_error
26
25
  end
27
26
 
28
27
  # GET /scc_accounts/1/edit
@@ -43,11 +42,10 @@ class SccAccountsController < ApplicationController
43
42
 
44
43
  # PATCH/PUT /scc_accounts/1
45
44
  def update
46
- if @scc_account.update(scc_account_params)
47
- process_success
48
- else
49
- process_error
50
- end
45
+ @scc_account.update_attributes_with_logic!(scc_account_params)
46
+ process_success
47
+ rescue ActiveRecord::RecordInvalid
48
+ process_error
51
49
  end
52
50
 
53
51
  # DELETE /scc_accounts/1
@@ -103,7 +101,15 @@ class SccAccountsController < ApplicationController
103
101
  # Only allow a trusted parameter "white list" through.
104
102
  def scc_account_params
105
103
  params[:scc_account].delete(:password) if params[:scc_account][:password].blank?
106
- params.require(:scc_account).permit(:name, :login, :password, :base_url, :organization_id)
104
+ params.require(:scc_account).permit(
105
+ :name,
106
+ :login,
107
+ :password,
108
+ :base_url,
109
+ :interval,
110
+ :sync_date,
111
+ :organization_id
112
+ )
107
113
  end
108
114
 
109
115
  def scc_bulk_subscribe_params
@@ -3,6 +3,7 @@ module Actions
3
3
  class SubscribeProduct < Actions::EntryAction
4
4
  def plan(scc_product)
5
5
  raise _('Product already subscribed!') if scc_product.product
6
+
6
7
  ::Foreman::Logging.logger('foreman_scc_manager')
7
8
  .info("Initiating subscription for SccProduct '#{scc_product.friendly_name}'.")
8
9
  sequence do
@@ -15,6 +15,7 @@ module Actions
15
15
  def finalize
16
16
  scc_account = SccAccount.find(input[:scc_account][:id])
17
17
  raise 'Updating failed' unless input[:repo_status] == 'SUCCESS' && input[:prod_status] == 'SUCCESS'
18
+
18
19
  scc_account.update! synced: Time.current
19
20
  end
20
21
 
@@ -0,0 +1,38 @@
1
+ module Actions
2
+ module SccManager
3
+ class SyncPlanAccountRepositories < Actions::EntryAction
4
+ include Actions::RecurringAction
5
+ def plan(scc_account)
6
+ add_missing_task_group(scc_account)
7
+ action_subject(scc_account)
8
+
9
+ User.as_anonymous_admin do
10
+ plan_action(::Actions::SccManager::SyncRepositories, scc_account)
11
+ plan_self(:sync_plan_name => scc_account.name)
12
+ end
13
+ end
14
+
15
+ def add_missing_task_group(sync_plan)
16
+ if sync_plan.task_group.nil?
17
+ sync_plan.task_group = ::SccAccountSyncPlanTaskGroup.create!
18
+ sync_plan.save!
19
+ end
20
+ task.add_missing_task_groups(sync_plan.task_group)
21
+ end
22
+
23
+ def rescue_strategy
24
+ # Dynflow::Action::Rescue::Skip
25
+ # REMOVEME
26
+ Dynflow::Action::Rescue::Fail
27
+ end
28
+
29
+ def humanized_name
30
+ if input.try(:[], :sync_plan_name)
31
+ _('Update SUSE repositories %s') % (input[:sync_plan_name] || _('Unknown'))
32
+ else
33
+ _('Update SUSE repositories')
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -9,12 +9,13 @@ module Actions
9
9
  end
10
10
 
11
11
  def run
12
- output[:status] = 'SUCCESS'
12
+ output[:status] = 'IN PROGRESS'
13
13
  begin
14
14
  output[:data] = ::SccManager.get_scc_data(input[:base_url],
15
15
  '/connect/organizations/repositories',
16
16
  input[:login],
17
17
  input[:password])
18
+ output[:status] = 'SUCCESS'
18
19
  rescue StandardError
19
20
  output[:status] = 'FAILURE'
20
21
  end
@@ -37,7 +38,7 @@ module Actions
37
38
  end
38
39
 
39
40
  def humanized_output
40
- output.dup.update(data: 'Trimmed')
41
+ output.dup.update(data: "Trimmed (got #{output[:data]&.length} repositories")
41
42
  end
42
43
  end
43
44
  end
@@ -21,6 +21,7 @@ module SccManager
21
21
  loop do
22
22
  response = RestClient.get url, auth_header
23
23
  raise 'Connection to SUSE costomer center failed.' unless response.code == 200
24
+
24
25
  links = (response.headers[:link] || '').split(', ').map do |link|
25
26
  href, rel = /<(.*?)>; rel="(\w+)"/.match(link).captures
26
27
  [rel.to_sym, href]
@@ -0,0 +1,9 @@
1
+ module Concerns
2
+ module RecurringLogicExtensions
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ has_one :scc_account, :inverse_of => :foreman_tasks_recurring_logic, :class_name => 'SccAccount'
7
+ end
8
+ end
9
+ end
@@ -4,12 +4,20 @@ class SccAccount < ApplicationRecord
4
4
  include ForemanTasks::Concerns::ActionSubject
5
5
  encrypts :password
6
6
 
7
+ NEVER = 'never'.freeze
8
+ DAILY = 'daily'.freeze
9
+ WEEKLY = 'weekly'.freeze
10
+ MONTHLY = 'monthly'.freeze
11
+ TYPES = [NEVER, DAILY, WEEKLY, MONTHLY].freeze
12
+
7
13
  self.include_root_in_json = false
8
14
 
9
15
  belongs_to :organization
10
16
  belongs_to :sync_task, class_name: 'ForemanTasks::Task'
11
17
  has_many :scc_products, dependent: :destroy
12
18
  has_many :scc_repositories, dependent: :destroy
19
+ belongs_to :foreman_tasks_recurring_logic, :inverse_of => :scc_account, :class_name => 'ForemanTasks::RecurringLogic', :dependent => :destroy
20
+ belongs_to :task_group, :class_name => 'SccAccountSyncPlanTaskGroup', :inverse_of => :scc_account
13
21
 
14
22
  validates_lengths_from_database
15
23
  validates :name, presence: true
@@ -17,11 +25,30 @@ class SccAccount < ApplicationRecord
17
25
  validates :login, presence: true
18
26
  validates :password, presence: true
19
27
  validates :base_url, presence: true
28
+ validates :interval, :inclusion => { :in => TYPES }, :allow_blank => false
29
+ validate :sync_date_is_valid_datetime
30
+
31
+ after_initialize :init
32
+ before_destroy :cancel_recurring_logic
20
33
 
21
34
  default_scope -> { order(:login) }
22
35
 
23
36
  scoped_search on: :login, complete_value: true
24
37
 
38
+ def init
39
+ # set default values
40
+ self.sync_date ||= Time.new if self.new_record?
41
+ end
42
+
43
+ def sync_date_is_valid_datetime
44
+ errors.add(:sync_date, 'must be a valid datetime') if interval != NEVER &&
45
+ sync_date.present? &&
46
+ !sync_date.respond_to?(:min) &&
47
+ !sync_date.respond_to?(:hour) &&
48
+ !sync_date.respond_to?(:wday) &&
49
+ !sync_date.respond_to?(:day)
50
+ end
51
+
25
52
  def to_s
26
53
  name
27
54
  end
@@ -40,8 +67,103 @@ class SccAccount < ApplicationRecord
40
67
  end
41
68
  end
42
69
 
70
+ def use_recurring_logic?
71
+ self.interval != NEVER
72
+ end
73
+
74
+ def save_with_logic!
75
+ self.task_group ||= SccAccountSyncPlanTaskGroup.create!
76
+
77
+ associate_recurring_logic if self.valid?
78
+
79
+ self.save!
80
+ start_recurring_logic if self.use_recurring_logic?
81
+
82
+ true
83
+ end
84
+
85
+ def update_attributes_with_logic!(params)
86
+ transaction do
87
+ self.update_attributes!(params)
88
+ if rec_logic_changed?
89
+ old_rec_logic = self.foreman_tasks_recurring_logic
90
+ associate_recurring_logic
91
+ self.save!
92
+ old_rec_logic&.cancel
93
+ # Can/Should we do that???
94
+ old_rec_logic&.destroy
95
+ start_recurring_logic
96
+ end
97
+ toggle_enabled if enabled_toggle?
98
+ end
99
+ true
100
+ end
101
+
102
+ def add_recurring_logic(sync_date, interval)
103
+ sd = sync_date
104
+
105
+ raise _('Interval cannot be nil') if interval.nil?
106
+
107
+ cron = case interval.downcase
108
+ when DAILY then "#{sd.min} #{sd.hour} * * *"
109
+ when WEEKLY then "#{sd.min} #{sd.hour} * * #{sd.wday}"
110
+ when MONTHLY then "#{sd.min} #{sd.hour} #{sd.day} * *"
111
+ else
112
+ raise _('Interval not set correctly')
113
+ end
114
+
115
+ recurring_logic = ForemanTasks::RecurringLogic.new_from_cronline(cron)
116
+
117
+ raise _('Cron expression is not valid!') unless recurring_logic.valid_cronline?
118
+
119
+ recurring_logic.save!
120
+ recurring_logic
121
+ end
122
+
123
+ def associate_recurring_logic
124
+ if self.use_recurring_logic?
125
+ self.foreman_tasks_recurring_logic = add_recurring_logic(self.sync_date, self.interval)
126
+ else
127
+ self.foreman_tasks_recurring_logic = nil
128
+ end
129
+ end
130
+
131
+ def toggle_enabled
132
+ self.foreman_tasks_recurring_logic&.enabled = self.enabled
133
+ end
134
+
135
+ def start_recurring_logic
136
+ # rubocop:disable Style/GuardClause
137
+ if self.use_recurring_logic?
138
+ User.as_anonymous_admin do
139
+ if self.sync_date.to_time < Time.now
140
+ self.foreman_tasks_recurring_logic.start(::Actions::SccManager::SyncPlanAccountRepositories, self)
141
+ else
142
+ self.foreman_tasks_recurring_logic.start_after(::Actions::SccManager::SyncPlanAccountRepositories, self.sync_date, self)
143
+ end
144
+ end
145
+ end
146
+ # rubocop:enable Style/GuardClause
147
+ end
148
+
149
+ def cancel_recurring_logic
150
+ self.foreman_tasks_recurring_logic&.cancel
151
+ end
152
+
153
+ def rec_logic_changed?
154
+ saved_change_to_attribute?(:sync_date) || saved_change_to_attribute?(:interval)
155
+ end
156
+
157
+ def enabled_toggle?
158
+ saved_change_to_attribute?(:enabled)
159
+ end
160
+
161
+ def get_scc_data(rel_url)
162
+ SccManager.get_scc_data(base_url, rel_url, login, password)
163
+ end
164
+
43
165
  def test_connection
44
- SccManager.get_scc_data(base_url, '/connect/organizations/subscriptions', login, password)
166
+ get_scc_data('/connect/organizations/subscriptions')
45
167
  true
46
168
  rescue StandardError
47
169
  false
@@ -0,0 +1,7 @@
1
+ class SccAccountSyncPlanTaskGroup < ::ForemanTasks::TaskGroup
2
+ has_one :scc_account, :foreign_key => :task_group_id, :dependent => :nullify, :inverse_of => :task_group, :class_name => 'SccAccount'
3
+
4
+ def resource_name
5
+ N_('SUSE Subscription')
6
+ end
7
+ end
@@ -26,6 +26,7 @@ class SccProduct < ApplicationRecord
26
26
 
27
27
  def subscribe
28
28
  raise 'Product already subscribed!' if product
29
+
29
30
  new_product = Katello::Product.new
30
31
  new_product.name = uniq_name
31
32
  new_product.description = description
@@ -0,0 +1,4 @@
1
+ <%= _("SUSE Subscription: ") %>
2
+
3
+ <%= link_to(task_groups.first.scc_account.name, "/scc_accounts/#{task_groups.first.scc_account.id}/edit") %>
4
+
@@ -13,6 +13,10 @@
13
13
  <%= text_f f, :login, :help_block => _("Use your 'Organization credentials' obtained from the SUSE Customer Center.") %>
14
14
  <%= password_f f, :password %>
15
15
  <%= text_f f, :base_url, label: _('Base URL') %>
16
+ <%= selectable_f f, :interval, SccAccount::TYPES, label: _('Interval') %>
17
+ <%= field f, :sync_date, label: _('Sync Date') do
18
+ f.datetime_field :sync_date, placeholder: Time.now
19
+ end %>
16
20
  <div class='clearfix'>
17
21
  <div class='form-group'>
18
22
  <div class='col-md-2'></div>
@@ -2,6 +2,7 @@ Rails.application.routes.draw do
2
2
  resources :scc_accounts do
3
3
  collection do
4
4
  put 'test_connection'
5
+ get 'auto_complete_search'
5
6
  end
6
7
  member do
7
8
  put 'sync'
@@ -0,0 +1,26 @@
1
+ class AddRecurringSync < ActiveRecord::Migration[5.2]
2
+ class FakeSccAccount < ApplicationRecord
3
+ self.table_name = 'scc_accounts'
4
+ end
5
+
6
+ def up
7
+ add_column :scc_accounts, :foreman_tasks_recurring_logic_id, :integer
8
+ add_column :scc_accounts, :interval, :string, default: 'never'
9
+ add_column :scc_accounts, :sync_date, :datetime
10
+ add_foreign_key :scc_accounts, :foreman_tasks_recurring_logics, :name => 'scc_accounts_foreman_tasks_recurring_logic_fk', :column => 'foreman_tasks_recurring_logic_id'
11
+ add_column :scc_accounts, :task_group_id, :integer, index: true
12
+ add_foreign_key :scc_accounts, :foreman_tasks_task_groups, column: :task_group_id
13
+ FakeSccAccount.all.each do |scca|
14
+ scca.task_group_id ||= SccAccountSyncPlanTaskGroup.create!.id
15
+ scca.save!
16
+ end
17
+ end
18
+
19
+ def down
20
+ remove_column :scc_accounts, :foreman_tasks_recurring_logic_id
21
+ remove_column :scc_accounts, :interval
22
+ remove_column :scc_accounts, :sync_date
23
+ remove_column :scc_accounts, :task_group_id
24
+ ForemanTasks::TaskGroup.where(:type => 'SccAccountSyncPlanTaskGroup').delete_all
25
+ end
26
+ end
@@ -9,6 +9,10 @@ module ForemanSccManager
9
9
  config.autoload_paths += Dir["#{config.root}/app/models/concerns"]
10
10
  config.autoload_paths += Dir["#{config.root}/app/overrides"]
11
11
 
12
+ config.to_prepare do
13
+ ForemanTasks::RecurringLogic.send :include, Concerns::RecurringLogicExtensions
14
+ end
15
+
12
16
  # Add any db migrations
13
17
  initializer 'foreman_scc_manager.load_app_instance_data' do |app|
14
18
  ForemanSccManager::Engine.paths['db/migrate'].existent.each do |path|
@@ -1,3 +1,3 @@
1
1
  module ForemanSccManager
2
- VERSION = '1.6.1'.freeze
2
+ VERSION = '1.6.2'.freeze
3
3
  end
@@ -0,0 +1,32 @@
1
+ begin
2
+ require 'rubocop/rake_task'
3
+
4
+ test_patterns = [
5
+ "#{ForemanSccManager::Engine.root}/*.gemspec",
6
+ "#{ForemanSccManager::Engine.root}/*.rb",
7
+ "#{ForemanSccManager::Engine.root}/app/**/*.rb",
8
+ "#{ForemanSccManager::Engine.root}/config/**/*.rb",
9
+ "#{ForemanSccManager::Engine.root}/db/**/*.rb",
10
+ "#{ForemanSccManager::Engine.root}/lib/**/*.rake",
11
+ "#{ForemanSccManager::Engine.root}/lib/**/*.rb",
12
+ "#{ForemanSccManager::Engine.root}/locale/**/*.rb",
13
+ "#{ForemanSccManager::Engine.root}/test/**/*.rb"
14
+ ]
15
+
16
+ namespace :foreman_scc_manager do
17
+ desc 'Runs Rubocop style checker'
18
+ RuboCop::RakeTask.new(:rubocop) do |task|
19
+ task.patterns = test_patterns
20
+ end
21
+
22
+ desc 'Runs Rubocop style checker with xml output for Jenkins'
23
+ RuboCop::RakeTask.new('rubocop:jenkins') do |task|
24
+ task.patterns = test_patterns
25
+ task.requires = ['rubocop/formatter/checkstyle_formatter']
26
+ task.formatters = ['RuboCop::Formatter::CheckstyleFormatter']
27
+ task.options = ['--no-color', '--out', 'rubocop.xml']
28
+ end
29
+ end
30
+ rescue LoadError
31
+ puts 'Rubocop not loaded.'
32
+ end
@@ -0,0 +1,26 @@
1
+ require 'rake/testtask'
2
+
3
+ # Tests
4
+ namespace :test do
5
+ desc 'Test ForemanSccManager'
6
+ Rake::TestTask.new(:foreman_scc_manager) do |t|
7
+ test_dir = File.join(File.dirname(__FILE__), '../..', 'test')
8
+ t.libs << ['test', test_dir]
9
+ t.pattern = "#{test_dir}/**/*_test.rb"
10
+ t.verbose = true
11
+ t.warning = false
12
+ end
13
+ end
14
+
15
+ namespace :jenkins do
16
+ desc 'Test ForemanSccManager with XML output for jenkins'
17
+ task 'foreman_scc_manager' do
18
+ Rake::Task['jenkins:setup:minitest'].invoke
19
+ Rake::Task['rake:test:foreman_scc_manager'].invoke
20
+ end
21
+ end
22
+
23
+ Rake::Task[:test].enhance ['test:foreman_scc_manager']
24
+
25
+ load 'tasks/jenkins.rake'
26
+ Rake::Task['jenkins:unit'].enhance ['test:foreman_scc_manager', 'foreman_scc_manager:rubocop'] if Rake::Task.task_defined?(:'jenkins:unit')