katello 4.0.0 → 4.0.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of katello might be problematic. Click here for more details.

Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/katello/api/registry/registry_proxies_controller.rb +5 -7
  3. data/app/controllers/katello/api/v2/repositories_controller.rb +4 -3
  4. data/app/lib/actions/katello/agent_action.rb +26 -17
  5. data/app/lib/actions/katello/bulk_agent_action.rb +18 -5
  6. data/app/lib/actions/katello/capsule_content/sync.rb +1 -1
  7. data/app/lib/actions/katello/capsule_content/sync_capsule.rb +44 -6
  8. data/app/lib/actions/katello/content_view/promote.rb +25 -0
  9. data/app/lib/actions/katello/content_view/publish.rb +29 -0
  10. data/app/lib/actions/katello/content_view_version/incremental_update.rb +14 -3
  11. data/app/lib/actions/katello/jail_concern/content_view.rb +30 -0
  12. data/app/lib/actions/katello/jail_concern/organization.rb +30 -0
  13. data/app/lib/actions/katello/orphan_cleanup/remove_orphans.rb +2 -2
  14. data/app/lib/actions/katello/repository/check_matching_content.rb +3 -4
  15. data/app/lib/actions/katello/repository/sync.rb +59 -0
  16. data/app/lib/actions/pulp3/content_guard/refresh.rb +6 -10
  17. data/app/lib/actions/pulp3/orchestration/orphan_cleanup/remove_orphans.rb +1 -1
  18. data/app/lib/katello/agent/client_message_handler.rb +12 -3
  19. data/app/lib/katello/errors.rb +1 -1
  20. data/app/lib/katello/event_daemon/monitor.rb +1 -0
  21. data/app/lib/katello/event_daemon/services/agent_event_receiver.rb +6 -10
  22. data/app/lib/katello/util/hostgroup_facets_helper.rb +126 -0
  23. data/app/models/katello/agent/dispatch_history.rb +2 -0
  24. data/app/models/katello/authorization/content_view.rb +12 -0
  25. data/app/models/katello/authorization/repository.rb +18 -0
  26. data/app/models/katello/concerns/hostgroup_extensions.rb +4 -2
  27. data/app/models/katello/concerns/redhat_extensions.rb +23 -18
  28. data/app/models/katello/concerns/smart_proxy_extensions.rb +23 -0
  29. data/app/models/katello/content_view.rb +13 -7
  30. data/app/models/katello/glue/pulp/repo.rb +0 -19
  31. data/app/models/katello/ping.rb +3 -10
  32. data/app/models/katello/repository.rb +16 -2
  33. data/app/models/katello/root_repository.rb +1 -1
  34. data/app/services/cert/certs.rb +4 -0
  35. data/app/services/katello/agent/dispatcher.rb +18 -24
  36. data/app/services/katello/applicability/applicable_content_helper.rb +8 -6
  37. data/app/services/katello/managed_content_medium_provider.rb +3 -3
  38. data/app/services/katello/pulp3/api/content_guard.rb +39 -5
  39. data/app/services/katello/pulp3/migration.rb +4 -3
  40. data/app/services/katello/pulp3/repository.rb +32 -4
  41. data/app/services/katello/pulp3/repository/apt.rb +1 -2
  42. data/app/services/katello/pulp3/repository/yum.rb +1 -1
  43. data/app/services/katello/ui_notifications/pulp/proxy_disk_space.rb +1 -0
  44. data/app/views/overrides/smart_proxies/_download_policy.erb +1 -1
  45. data/db/migrate/20200514092553_move_katello_fields_from_hostgroups.katello.rb +1 -1
  46. data/db/migrate/20210119162528_delete_puppet_and_ostree_repos.rb +25 -22
  47. data/db/migrate/20210512192745_fix_red_hat_root_repository_arch.rb +11 -0
  48. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/bulk/views/products-bulk-advanced-sync-modal.html +2 -2
  49. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/new/views/new-repository.html +3 -4
  50. data/lib/katello.rb +1 -1
  51. data/lib/katello/engine.rb +11 -11
  52. data/lib/katello/plugin.rb +5 -2
  53. data/lib/katello/tasks/clean_backend_objects.rake +0 -25
  54. data/lib/katello/tasks/fix_hostgroup_facets.rake +8 -0
  55. data/lib/katello/version.rb +1 -1
  56. data/lib/proxy_api/container_gateway.rb +22 -11
  57. metadata +28 -10
  58. data/app/lib/actions/pulp3/capsule_content/refresh_content_guard.rb +0 -17
@@ -18,6 +18,7 @@ module Katello
18
18
  PULP3_FEATURE = "Pulpcore".freeze
19
19
  PULP_FEATURE = "Pulp".freeze
20
20
  PULP_NODE_FEATURE = "Pulp Node".freeze
21
+ CONTAINER_GATEWAY_FEATURE = "Container_Gateway".freeze
21
22
 
22
23
  DOWNLOAD_INHERIT = 'inherit'.freeze
23
24
  DOWNLOAD_POLICIES = ::Runcible::Models::YumImporter::DOWNLOAD_POLICIES + [DOWNLOAD_INHERIT]
@@ -89,6 +90,10 @@ module Katello
89
90
  end
90
91
 
91
92
  def self.with_environment(environment, include_default = false)
93
+ (pulp2_proxies_with_environment(environment, include_default) + pulpcore_proxies_with_environment(environment)).try(:uniq)
94
+ end
95
+
96
+ def self.pulp2_proxies_with_environment(environment, include_default = false)
92
97
  features = [PULP_NODE_FEATURE]
93
98
  features << PULP_FEATURE if include_default
94
99
 
@@ -96,6 +101,11 @@ module Katello
96
101
  where(katello_capsule_lifecycle_environments: { lifecycle_environment_id: environment.id })
97
102
  end
98
103
 
104
+ def self.pulpcore_proxies_with_environment(environment)
105
+ unscoped.where(id: unscoped.select { |p| p.pulp_mirror? }.pluck(:id)).joins(:capsule_lifecycle_environments).
106
+ where(katello_capsule_lifecycle_environments: { lifecycle_environment_id: environment.id })
107
+ end
108
+
99
109
  def self.sync_needed?(environment)
100
110
  Setting[:foreman_proxy_content_auto_sync] && unscoped.with_environment(environment).any?
101
111
  end
@@ -119,6 +129,19 @@ module Katello
119
129
  path
120
130
  end
121
131
 
132
+ def update_container_repo_list(repo_list)
133
+ ProxyAPI::ContainerGateway.new(url: self.url).repository_list({ repositories: repo_list })
134
+ end
135
+
136
+ def update_user_container_repo_mapping(user_repo_map)
137
+ ProxyAPI::ContainerGateway.new(url: self.url).user_repository_mapping(user_repo_map)
138
+ end
139
+
140
+ def container_gateway_users
141
+ usernames = ProxyAPI::ContainerGateway.new(url: self.url).users
142
+ ::User.where(login: usernames['users'])
143
+ end
144
+
122
145
  def pulp_url
123
146
  uri = URI.parse(url)
124
147
  "#{uri.scheme}://#{uri.host}/pulp/api/v2/"
@@ -6,6 +6,7 @@ module Katello
6
6
  include Ext::LabelFromName
7
7
  include Katello::Authorization::ContentView
8
8
  include ForemanTasks::Concerns::ActionSubject
9
+ include Foreman::ObservableModel
9
10
 
10
11
  CONTENT_DIR = "content_views".freeze
11
12
  IMPORT_LIBRARY = "Import-Library".freeze
@@ -90,6 +91,17 @@ module Katello
90
91
  scoped_search :on => :label, :complete_value => true
91
92
  scoped_search :on => :composite, :complete_value => true
92
93
 
94
+ set_crud_hooks :content_view
95
+
96
+ apipie :class, desc: "A class representing #{model_name.human} object" do
97
+ name 'Content View'
98
+ refs 'ContentView'
99
+ sections only: %w[all additional]
100
+ prop_group :katello_idname_props, Katello::Model, meta: { friendly_name: 'Content View' }
101
+ property :label, String, desc: 'Returns label of the Content View'
102
+ property :organization, 'Organization', desc: 'Returns organization object'
103
+ end
104
+
93
105
  def self.in_environment(env)
94
106
  joins(:content_view_environments).
95
107
  where("#{Katello::ContentViewEnvironment.table_name}.environment_id = ?", env.id)
@@ -757,14 +769,8 @@ module Katello
757
769
  self.organization
758
770
  end
759
771
 
760
- apipie :class, desc: "A class representing #{model_name.human} object" do
761
- name 'Content View'
762
- refs 'ContentView'
763
- sections only: %w[all additional]
764
- prop_group :katello_basic_props, Katello::Model, meta: { friendly_name: 'Content View' }
765
- end
766
772
  class Jail < ::Safemode::Jail
767
- allow :name, :label, :version
773
+ allow :id, :name, :label, :version, :organization
768
774
  end
769
775
  end
770
776
  end
@@ -74,11 +74,6 @@ module Katello
74
74
  pulp_repo_facts['content_unit_counts']['srpm']
75
75
  end
76
76
 
77
- def uri
78
- uri = URI.parse(SETTINGS[:katello][:pulp][:url])
79
- "https://#{uri.host}/pulp/content/#{relative_path}"
80
- end
81
-
82
77
  def to_hash
83
78
  pulp_repo_facts.merge(as_json).merge(:sync_state => sync_state)
84
79
  end
@@ -364,19 +359,5 @@ module Katello
364
359
  end
365
360
  end
366
361
  end
367
-
368
- def full_path(smart_proxy = nil, force_http = false)
369
- pulp_uri = URI.parse(smart_proxy ? smart_proxy.url : SETTINGS[:katello][:pulp][:url])
370
- scheme = force_http ? 'http' : 'https'
371
- if docker?
372
- "#{pulp_uri.host.downcase}/#{container_repository_name}"
373
- elsif ostree?
374
- "#{scheme}://#{pulp_uri.host.downcase}/pulp/content/web/#{relative_path}"
375
- elsif ansible_collection?
376
- "#{scheme}://#{pulp_uri.host.downcase}/pulp_ansible/galaxy/#{relative_path}/api"
377
- else
378
- "#{scheme}://#{pulp_uri.host.downcase}/pulp/content/#{relative_path}/"
379
- end
380
- end
381
362
  end
382
363
  end
@@ -5,17 +5,10 @@ module Katello
5
5
  PACKAGES = %w(katello candlepin pulp qpid foreman tfm hammer).freeze
6
6
 
7
7
  class << self
8
- def pulpcore_enabled # for downstream 6.9, remove in 6.10
9
- SETTINGS[:katello][:use_pulp_2_for_content_type].nil? || (!SETTINGS[:katello][:use_pulp_2_for_content_type][:yum] &&
10
- !SETTINGS[:katello][:use_pulp_2_for_content_type][:docker] &&
11
- !SETTINGS[:katello][:use_pulp_2_for_content_type][:file]) ||
12
- system('systemctl is-enabled pulpcore-api.service &>/dev/null')
13
- end
14
-
15
8
  def services(capsule_id = nil)
16
9
  proxy = fetch_proxy(capsule_id)
17
- services = [:candlepin, :candlepin_auth, :foreman_tasks, :katello_events, :candlepin_events, :katello_agent]
18
- services += [:pulp3] if proxy&.pulp3_enabled? && pulpcore_enabled
10
+ services = [:candlepin, :candlepin_auth, :foreman_tasks, :katello_events, :candlepin_events]
11
+ services += [:pulp3] if proxy&.pulp3_enabled?
19
12
  if proxy.nil? || proxy.has_feature?(SmartProxy::PULP_NODE_FEATURE) || proxy.has_feature?(SmartProxy::PULP_FEATURE)
20
13
  services += [:pulp, :pulp_auth]
21
14
  end
@@ -72,7 +65,7 @@ module Katello
72
65
 
73
66
  def ping_katello_agent(result)
74
67
  exception_watch(result) do
75
- status = Katello::EventDaemon::Services::AgentEventReceiver.status(refresh: false)
68
+ status = Katello::EventDaemon::Runner.service_status(:katello_agent_events)
76
69
  event_daemon_status(status, result)
77
70
  end
78
71
  end
@@ -395,8 +395,22 @@ module Katello
395
395
  all_instances
396
396
  end
397
397
 
398
- def to_hash(content_source = nil)
399
- {id: id, name: label, url: full_path(content_source)}
398
+ def full_path(smart_proxy = nil, force_http = false)
399
+ pulp_uri = URI.parse(smart_proxy ? smart_proxy.url : ::SmartProxy.pulp_primary.url)
400
+ scheme = force_http ? 'http' : 'https'
401
+ if docker?
402
+ "#{pulp_uri.host.downcase}/#{container_repository_name}"
403
+ elsif ostree?
404
+ "#{scheme}://#{pulp_uri.host.downcase}/pulp/content/web/#{relative_path}"
405
+ elsif ansible_collection?
406
+ "#{scheme}://#{pulp_uri.host.downcase}/pulp_ansible/galaxy/#{relative_path}/api"
407
+ else
408
+ "#{scheme}://#{pulp_uri.host.downcase}/pulp/content/#{relative_path}/"
409
+ end
410
+ end
411
+
412
+ def to_hash(content_source = nil, force_http = false)
413
+ {id: id, name: label, url: full_path(content_source, force_http)}
400
414
  end
401
415
 
402
416
  #is the repo cloned in the specified environment
@@ -139,7 +139,7 @@ module Katello
139
139
  def ensure_compatible_download_policy
140
140
  if !url.blank? && URI(url).scheme == 'file' &&
141
141
  [::Runcible::Models::YumImporter::DOWNLOAD_ON_DEMAND, ::Runcible::Models::YumImporter::DOWNLOAD_BACKGROUND].include?(download_policy)
142
- errors.add(:download_policy, _("Cannot sync file:// repositories with On Demand or Background Download Policies"))
142
+ errors.add(:download_policy, _("Cannot sync file:// repositories with the On Demand Download Policy"))
143
143
  end
144
144
  end
145
145
 
@@ -8,6 +8,10 @@ module Cert
8
8
  File.open(Setting[:ssl_ca_file], 'r').read
9
9
  end
10
10
 
11
+ def self.candlepin_client_ca_cert
12
+ File.read(SETTINGS[:katello][:candlepin][:ca_cert_file])
13
+ end
14
+
11
15
  def self.ssl_client_cert
12
16
  @ssl_client_cert ||= OpenSSL::X509::Certificate.new(File.open(ssl_client_cert_filename, 'r').read)
13
17
  end
@@ -14,37 +14,31 @@ module Katello
14
14
  register_message(:install_package_group, Katello::Agent::InstallPackageGroupMessage)
15
15
  register_message(:remove_package_group, Katello::Agent::RemovePackageGroupMessage)
16
16
 
17
- def self.dispatch(message_type, host_ids, args)
17
+ def self.dispatch(message_type, histories, args)
18
18
  message_class = @supported_messages[message_type]
19
-
20
- fail("Unsupported message type") unless message_class
21
-
22
- uuid_data = ::Katello::Host::ContentFacet.where(host_id: host_ids).pluck(:host_id, :uuid)
23
- fail("Couldn't find all hosts specified") unless host_ids.size == uuid_data.size
24
-
25
- host_data = uuid_data.map do |host_id, consumer_id|
26
- {
27
- host_id: host_id,
28
- consumer_id: consumer_id,
29
- history: Katello::Agent::DispatchHistory.new(host_id: host_id),
30
- message: message_class.new(**args.merge(consumer_id: consumer_id))
31
- }
19
+ fail("Unsupported message type: #{message_type}") unless message_class
20
+
21
+ messages = histories.map do |history|
22
+ message = message_class.new(**args.merge(consumer_id: history.host.subscription_facet.uuid))
23
+ message.dispatch_history_id = history.id
24
+ message.recipient_address = settings[:client_queue_format] % history.host.subscription_facet.uuid
25
+ message.reply_to = settings[:event_queue_name]
26
+ message
32
27
  end
33
28
 
34
- histories = host_data.map { |attrs| attrs[:history] }
35
- ActiveRecord::Base.transaction do
36
- Katello::Agent::DispatchHistory.import(histories)
29
+ connection = Connection.new
30
+ connection.send_messages(messages)
37
31
 
38
- host_data.each do |d|
39
- d[:message].dispatch_history_id = d[:history].id
40
- d[:message].recipient_address = settings[:client_queue_format] % [d[:consumer_id]]
41
- d[:message].reply_to = settings[:event_queue_name]
42
- end
32
+ histories
33
+ end
43
34
 
44
- connection = Connection.new
45
- connection.send_messages(host_data.map { |d| d[:message] })
35
+ def self.create_histories(host_ids:)
36
+ histories = host_ids.map do |id|
37
+ Katello::Agent::DispatchHistory.new(host_id: id)
46
38
  end
47
39
 
40
+ Katello::Agent::DispatchHistory.import(histories)
41
+
48
42
  histories
49
43
  end
50
44
 
@@ -122,20 +122,22 @@ module Katello
122
122
  end
123
123
 
124
124
  def applicable_differences
125
- consumer_ids = content_facet.send(applicable_units).pluck("#{content_unit_class.table_name}.id")
126
- content_ids = fetch_content_ids
125
+ ActiveRecord::Base.connection.uncached do
126
+ consumer_ids = content_facet.send(applicable_units).pluck("#{content_unit_class.table_name}.id")
127
+ content_ids = fetch_content_ids
127
128
 
128
- to_remove = consumer_ids - content_ids
129
- to_add = content_ids - consumer_ids
129
+ to_remove = consumer_ids - content_ids
130
+ to_add = content_ids - consumer_ids
130
131
 
131
- [to_add, to_remove]
132
+ [to_add, to_remove]
133
+ end
132
134
  end
133
135
 
134
136
  def insert(applicable_ids)
135
137
  unless applicable_ids.empty?
136
138
  inserts = applicable_ids.map { |applicable_id| "(#{applicable_id.to_i}, #{content_facet.id.to_i})" }
137
139
  sql = "INSERT INTO #{content_facet_association_class.table_name} (#{content_unit_association_id}, content_facet_id) VALUES #{inserts.join(', ')}"
138
- ActiveRecord::Base.connection.execute(sql)
140
+ ActiveRecord::Base.connection.exec_insert(sql)
139
141
  end
140
142
  end
141
143
 
@@ -14,11 +14,11 @@ module Katello
14
14
  URI.parse(url)
15
15
  end
16
16
 
17
- # If there is an 'AppStream' variant, we need to make it
17
+ # If there are any 'AppStream' variants, we need to make them
18
18
  # available to Anaconda
19
19
  def additional_media
20
- appstream = entity.operatingsystem.variant_repo(entity, 'AppStream')
21
- super + (appstream ? [appstream] : [])
20
+ appstream_repos = entity.operatingsystem.variant_repos(entity, 'AppStream')
21
+ super + (appstream_repos.present? ? appstream_repos : [])
22
22
  end
23
23
 
24
24
  def unique_id
@@ -4,6 +4,10 @@ module Katello
4
4
  module Pulp3
5
5
  module Api
6
6
  class ContentGuard < Core
7
+ def default_name
8
+ 'RHSMCertGuard'
9
+ end
10
+
7
11
  def self.client_module
8
12
  PulpCertguardClient
9
13
  end
@@ -20,19 +24,49 @@ module Katello
20
24
  PulpCertguardClient::ContentguardsRhsmApi.new(api_client)
21
25
  end
22
26
 
23
- def create(name = "RHSMCertGuard", ca_certificate = Cert::Certs.ca_cert)
24
- data = PulpCertguardClient::CertguardRHSMCertGuard.new(name: name, ca_certificate: ca_certificate)
27
+ def ca_cert
28
+ Cert::Certs.candlepin_client_ca_cert
29
+ end
30
+
31
+ def refresh
32
+ found = list(name: default_name).results.first
33
+ if found && found.ca_certificate != ca_cert
34
+ partial_update(found.pulp_href)
35
+ else
36
+ found = create
37
+ end
38
+ persist_if_needed(found.pulp_href)
39
+ end
40
+
41
+ def persist_if_needed(href)
42
+ return if self.smart_proxy.pulp_mirror?
43
+ Katello::Util::Support.active_record_retry do
44
+ found = Katello::Pulp3::ContentGuard.find_by(:name => default_name)
45
+ if found
46
+ found.update(pulp_href: href)
47
+ else
48
+ Katello::Pulp3::ContentGuard.create(name: default_name, pulp_href: href)
49
+ end
50
+ end
51
+ end
52
+
53
+ def create(name = default_name)
54
+ data = PulpCertguardClient::CertguardRHSMCertGuard.new(name: name, ca_certificate: ca_cert)
25
55
  rhsm_api_client.create(data)
26
56
  rescue self.class.api_exception_class => e
27
- raise e unless list&.results&.first
57
+ if (found = list&.results&.first) #check for possible race condition
58
+ found
59
+ else
60
+ raise e
61
+ end
28
62
  end
29
63
 
30
64
  def list(options = {})
31
65
  rhsm_api_client.list options
32
66
  end
33
67
 
34
- def partial_update(href, ca_certificate = Cert::Certs.ca_cert)
35
- data = PulpCertguardClient::CertguardRHSMCertGuard.new(ca_certificate: ca_certificate)
68
+ def partial_update(href)
69
+ data = PulpCertguardClient::CertguardRHSMCertGuard.new(ca_certificate: ca_cert)
36
70
  rhsm_api_client.partial_update(href, data)
37
71
  end
38
72
 
@@ -293,10 +293,11 @@ module Katello
293
293
  to_import[[errata_id, repo_id]] ||= {erratum_id: errata_id, erratum_pulp3_href: pulp3_href, repository_id: repo_id}
294
294
  end
295
295
  end
296
- end
297
296
 
298
- Katello::RepositoryErratum.import([:erratum_id, :erratum_pulp3_href, :repository_id], to_import.values, :validate => false,
299
- on_duplicate_key_update: {conflict_target: [:erratum_id, :repository_id], columns: [:erratum_pulp3_href]})
297
+ Katello::RepositoryErratum.import([:erratum_id, :erratum_pulp3_href, :repository_id], to_import.values, :validate => false,
298
+ on_duplicate_key_update: {conflict_target: [:erratum_id, :repository_id], columns: [:erratum_pulp3_href]})
299
+ to_import = {}
300
+ end
300
301
  end
301
302
 
302
303
  def import_content_type(content_type)
@@ -222,11 +222,38 @@ module Katello
222
222
  end
223
223
 
224
224
  def refresh_distributions
225
- dist_ref = distribution_reference
226
- if dist_ref
227
- update_distribution
225
+ if repo.docker?
226
+ dist = lookup_distributions(base_path: repo.container_repository_name).first
228
227
  else
228
+ dist = lookup_distributions(base_path: repo.relative_path).first
229
+ end
230
+
231
+ # First check if the distribution exists
232
+ if dist
233
+ dist_ref = distribution_reference
234
+ # If we have a DistributionReference, update the distribution
235
+ if dist_ref
236
+ return update_distribution
237
+ # If no DistributionReference, create a DistributionReference and return
238
+ else
239
+ save_distribution_references([dist.pulp_href])
240
+ return []
241
+ end
242
+ end
243
+
244
+ # So far, it looks like there is no distribution. Try to create one.
245
+ begin
229
246
  create_distribution(relative_path)
247
+ rescue api.class.client_module::ApiError => e
248
+ # Now it seems there is a distribution. Fetch it and save the reference.
249
+ if e.message.include?("\"base_path\":[\"This field must be unique.\"]") ||
250
+ e.message.include?("\"base_path\":[\"Overlaps with existing distribution\"")
251
+ dist = lookup_distributions(base_path: repo.relative_path).first
252
+ save_distribution_references([dist.pulp_href])
253
+ return []
254
+ else
255
+ raise e
256
+ end
230
257
  end
231
258
  end
232
259
 
@@ -276,7 +303,8 @@ module Katello
276
303
  pulp3_distribution_data = api.get_distribution(href)
277
304
  path, content_guard_href = pulp3_distribution_data&.base_path, pulp3_distribution_data&.content_guard
278
305
  unless distribution_reference
279
- DistributionReference.create!(path: path, href: href, repository_id: repo.id, content_guard_href: content_guard_href)
306
+ # Ensure that duplicates won't be created in the case of a race condition
307
+ DistributionReference.where(path: path, href: href, repository_id: repo.id, content_guard_href: content_guard_href).first_or_create!
280
308
  end
281
309
  end
282
310
  end
@@ -27,8 +27,7 @@ module Katello
27
27
  popts = super(repository_version)
28
28
  popts.merge!(
29
29
  {
30
- # structured is not necessary for subscription-manager
31
- #structured: true, # publish real suites (e.g. 'stable')
30
+ structured: true, # publish real suites (e.g. 'stable')
32
31
  simple: true # publish all into 'default'-suite
33
32
  }
34
33
  )