katello 3.7.1.1 → 3.8.0.rc1
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.
- checksums.yaml +4 -4
- data/README.md +8 -7
- data/app/assets/javascripts/katello/common/index.js +0 -1
- data/app/assets/javascripts/katello/sync_management/index.js +0 -1
- data/app/controllers/katello/api/registry/registry_proxies_controller.rb +477 -0
- data/app/controllers/katello/api/rhsm/candlepin_proxies_controller.rb +1 -1
- data/app/controllers/katello/api/v2/activation_keys_controller.rb +2 -2
- data/app/controllers/katello/api/v2/api_controller.rb +3 -1
- data/app/controllers/katello/api/v2/content_credentials_controller.rb +1 -1
- data/app/controllers/katello/api/v2/content_view_filter_rules_controller.rb +1 -1
- data/app/controllers/katello/api/v2/content_view_versions_controller.rb +15 -1
- data/app/controllers/katello/api/v2/environments_controller.rb +5 -2
- data/app/controllers/katello/api/v2/errata_controller.rb +48 -34
- data/app/controllers/katello/api/v2/gpg_keys_controller.rb +1 -1
- data/app/controllers/katello/api/v2/host_collections_controller.rb +1 -1
- data/app/controllers/katello/api/v2/host_packages_controller.rb +5 -1
- data/app/controllers/katello/api/v2/organizations_controller.rb +1 -1
- data/app/controllers/katello/api/v2/packages_controller.rb +33 -22
- data/app/controllers/katello/api/v2/products_controller.rb +1 -1
- data/app/controllers/katello/api/v2/repositories_controller.rb +6 -5
- data/app/controllers/katello/api/v2/repository_sets_controller.rb +1 -10
- data/app/controllers/katello/api/v2/sync_plans_controller.rb +1 -1
- data/app/controllers/katello/concerns/api/v2/repository_content_controller.rb +20 -12
- data/app/controllers/katello/remote_execution_controller.rb +6 -6
- data/app/helpers/katello/hosts_and_hostgroups_helper.rb +9 -37
- data/app/lib/actions/katello/content_view/promote.rb +6 -2
- data/app/lib/actions/katello/content_view_version/after_promote_hook.rb +11 -0
- data/app/lib/actions/katello/content_view_version/before_promote_hook.rb +11 -0
- data/app/lib/actions/katello/host/remove_subscriptions.rb +1 -1
- data/app/lib/actions/pulp/consumer/abstract_content_action.rb +0 -12
- data/app/lib/actions/pulp/consumer/content_install.rb +1 -1
- data/app/lib/actions/pulp/consumer/content_uninstall.rb +1 -1
- data/app/lib/actions/pulp/consumer/content_update.rb +1 -1
- data/app/lib/katello/resources/registry.rb +40 -0
- data/app/lib/katello/util/package.rb +9 -4
- data/app/models/katello/concerns/subscription_facet_host_extensions.rb +1 -1
- data/app/models/katello/content_view.rb +4 -12
- data/app/models/katello/content_view_version.rb +26 -0
- data/app/models/katello/glue/candlepin/owner.rb +8 -0
- data/app/models/katello/glue/candlepin/pool.rb +11 -11
- data/app/models/katello/glue/candlepin/repository.rb +1 -1
- data/app/models/katello/glue/pulp/repos.rb +1 -0
- data/app/models/katello/host/content_facet.rb +1 -2
- data/app/models/katello/kt_environment.rb +6 -0
- data/app/models/katello/product_content.rb +1 -4
- data/app/models/katello/repository.rb +2 -0
- data/app/models/katello/rpm.rb +118 -14
- data/app/services/katello/puppet_class_importer_extensions.rb +17 -20
- data/app/services/katello/ui_notifications/pulp/proxy_disk_space.rb +1 -3
- data/app/views/katello/api/v2/common/copy.json.rabl +3 -0
- data/app/views/katello/api/v2/environments/show.json.rabl +1 -1
- data/app/views/katello/api/v2/repositories/base.json.rabl +1 -0
- data/app/views/katello/api/v2/repositories/show.json.rabl +4 -2
- data/app/views/overrides/activation_keys/_host_environment_select.html.erb +3 -2
- data/config/katello.yaml +89 -0
- data/config/routes.rb +0 -1
- data/config/routes/api/registry.rb +29 -0
- data/config/routes/api/v2.rb +1 -1
- data/db/migrate/20180612163403_add_foreign_key_to_hypervisor_id.rb +0 -3
- data/db/migrate/20180614184822_add_unauthenticated_pull.rb +9 -0
- data/db/migrate/20180618195941_add_description_to_repository.rb +5 -0
- data/db/seeds.d/75-job_templates.rb +2 -5
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/content-hosts-bulk-repository-sets-modal.controller.js +3 -4
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/content-hosts-bulk-subscriptions-modal.controller.js +1 -4
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/views/content-hosts-bulk-subscriptions-modal.html +1 -1
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/content-host-register.controller.js +1 -1
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/content/content-host-packages-installed.controller.js +1 -1
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/details/content-host-add-subscriptions.controller.js +1 -1
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/content-views.routes.js +1 -0
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/versions/content-view-version.controller.js +32 -18
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/versions/views/content-view-version-details.html +1 -1
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/versions/views/content-view-version-docker.html +2 -0
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/environments/details/views/environment-details.html +32 -23
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-info.html +6 -0
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/new/views/new-repository.html +8 -0
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/discovery/discovery.controller.js +1 -1
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/discovery/views/discovery-create.html +1 -1
- data/engines/bastion_katello/app/assets/stylesheets/bastion_katello/bastion_katello.scss +0 -5
- data/lib/katello/engine.rb +1 -0
- data/lib/katello/permission_creator.rb +2 -0
- data/lib/katello/permissions/registry_permissions.rb +20 -0
- data/lib/katello/plugin.rb +1 -0
- data/lib/katello/tasks/clean_backend_objects.rake +3 -12
- data/lib/katello/version.rb +1 -1
- data/package.json +7 -10
- data/webpack/components/Search/Search.test.js +1 -3
- data/webpack/containers/Application/config.js +2 -9
- data/webpack/containers/Application/index.js +2 -4
- data/webpack/mockRequest.js +3 -3
- data/webpack/move_to_foreman/common/helpers.js +8 -45
- data/webpack/move_to_foreman/components/common/{EmptyState → emptyState}/index.js +3 -16
- data/webpack/move_to_foreman/components/common/table/components/Table.js +1 -1
- data/webpack/move_to_foreman/components/common/table/components/__snapshots__/CollapseSubscriptionGroupButton.test.js.snap +2 -2
- data/webpack/move_to_foreman/components/common/table/components/__snapshots__/TableSelectionCell.test.js.snap +1 -1
- data/webpack/move_to_foreman/components/common/table/components/__snapshots__/TableSelectionHeaderCell.test.js.snap +1 -1
- data/webpack/move_to_pf/LoadingState/LoadingState.js +14 -27
- data/webpack/move_to_pf/LoadingState/LoadingState.test.js +4 -8
- data/webpack/move_to_pf/react-bootstrap-select/index.js +1 -12
- data/webpack/redux/actions/RedHatRepositories/enabled.js +1 -0
- data/webpack/redux/actions/RedHatRepositories/helpers.js +5 -5
- data/webpack/redux/actions/RedHatRepositories/sets.js +1 -1
- data/webpack/redux/consts.js +0 -6
- data/webpack/redux/reducers/index.js +0 -2
- data/webpack/scenes/RedHatRepositories/components/EnabledRepository.js +23 -14
- data/webpack/scenes/RedHatRepositories/components/RepositorySetRepository.js +1 -1
- data/webpack/scenes/RedHatRepositories/components/SearchBar.js +0 -1
- data/webpack/scenes/RedHatRepositories/components/__tests__/__snapshots__/RecommendedRepositorySetsToggler.test.js.snap +1 -3
- data/webpack/scenes/RedHatRepositories/index.js +3 -7
- data/webpack/scenes/RedHatRepositories/index.scss +0 -1
- data/webpack/scenes/Subscriptions/Details/SubscriptionDetailActions.js +8 -3
- data/webpack/scenes/Subscriptions/Details/SubscriptionDetailReducer.js +1 -30
- data/webpack/scenes/Subscriptions/Details/SubscriptionDetails.js +28 -111
- data/webpack/scenes/Subscriptions/Details/__tests__/SubscriptionDetailReducer.test.js +1 -3
- data/webpack/scenes/Subscriptions/Details/__tests__/SubscriptionDetails.test.js +1 -6
- data/webpack/scenes/Subscriptions/Details/__tests__/__snapshots__/SubscriptionDetails.test.js.snap +424 -519
- data/webpack/scenes/Subscriptions/Details/__tests__/subscriptionDetails.fixtures.js +4 -7
- data/webpack/scenes/Subscriptions/Details/index.js +3 -5
- data/webpack/scenes/Subscriptions/Manifest/ManageManifestModal.js +34 -78
- data/webpack/scenes/Subscriptions/Manifest/ManifestActions.js +24 -5
- data/webpack/scenes/Subscriptions/Manifest/ManifestHistoryReducer.js +1 -9
- data/webpack/scenes/Subscriptions/Manifest/__tests__/ManageManifestModal.test.js +0 -3
- data/webpack/scenes/Subscriptions/Manifest/__tests__/ManifestActions.test.js +8 -20
- data/webpack/scenes/Subscriptions/Manifest/__tests__/ManifestHistoryReducer.test.js +1 -3
- data/webpack/scenes/Subscriptions/Manifest/__tests__/__snapshots__/ManageManifestModal.test.js.snap +7 -34
- data/webpack/scenes/Subscriptions/Manifest/__tests__/manifest.fixtures.js +16 -9
- data/webpack/scenes/Subscriptions/Manifest/index.js +0 -1
- data/webpack/scenes/Subscriptions/SubscriptionActions.js +26 -5
- data/webpack/scenes/Subscriptions/SubscriptionConstants.js +0 -1
- data/webpack/scenes/Subscriptions/SubscriptionHelpers.js +0 -3
- data/webpack/scenes/Subscriptions/SubscriptionReducer.js +4 -11
- data/webpack/scenes/Subscriptions/SubscriptionsPage.js +36 -31
- data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsActions.js +12 -3
- data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsPage.js +27 -57
- data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsReducer.js +3 -2
- data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsTableSchema.js +5 -10
- data/webpack/scenes/Subscriptions/UpstreamSubscriptions/__tests__/UpstreamSubscriptionsActions.test.js +5 -10
- data/webpack/scenes/Subscriptions/UpstreamSubscriptions/__tests__/UpstreamSubscriptionsPage.test.js +4 -49
- data/webpack/scenes/Subscriptions/UpstreamSubscriptions/__tests__/UpstreamSubscriptionsReducer.test.js +3 -8
- data/webpack/scenes/Subscriptions/UpstreamSubscriptions/__tests__/__snapshots__/UpstreamSubscriptionsPage.test.js.snap +11 -21
- data/webpack/scenes/Subscriptions/UpstreamSubscriptions/__tests__/upstreamSubscriptions.fixtures.js +8 -5
- data/webpack/scenes/Subscriptions/__tests__/SubscriptionsPage.test.js +0 -2
- data/webpack/scenes/Subscriptions/__tests__/SubscriptionsReducer.test.js +3 -9
- data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsPage.test.js.snap +2 -14
- data/webpack/scenes/Subscriptions/__tests__/subscriptions.fixtures.js +17 -11
- data/webpack/scenes/Subscriptions/components/SubscriptionsTable/EntitlementsInlineEditFormatter.js +5 -8
- data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionsTable.js +58 -45
- data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionsTableHelpers.js +4 -11
- data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionsTableSchema.js +2 -2
- data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/SubscriptionsTable.test.js +3 -16
- data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/__snapshots__/SubscriptionsTable.test.js.snap +365 -392
- data/webpack/scenes/Subscriptions/index.js +0 -1
- data/webpack/services/api/fixtures.js +353 -0
- data/webpack/services/api/index.js +1 -17
- data/webpack/test_setup.js +0 -2
- metadata +16 -37
- data/config/katello.yml +0 -59
- data/webpack/__mocks__/foremanReact/components/BreadcrumbBar.js +0 -3
- data/webpack/__mocks__/foremanReact/redux.js +0 -3
- data/webpack/__mocks__/foremanReact/redux/actions/toasts.js +0 -8
- data/webpack/components/SelectOrg/SelectOrg.scss +0 -3
- data/webpack/components/SelectOrg/SelectOrgAction.js +0 -41
- data/webpack/components/SelectOrg/SelectOrgReducer.js +0 -33
- data/webpack/components/SelectOrg/SetOrganization.js +0 -116
- data/webpack/components/WithOrganization/withOrganization.js +0 -28
- data/webpack/global_test_setup.js +0 -6
- data/webpack/helpers/caret.js +0 -6
- data/webpack/move_to_pf/Select/Select.js +0 -40
- data/webpack/scenes/Products/ProductActions.js +0 -24
- data/webpack/scenes/Products/ProductConstants.js +0 -3
- data/webpack/scenes/Products/__tests__/ProductActions.test.js +0 -40
- data/webpack/scenes/Products/__tests__/products.fixtures.js +0 -90
- data/webpack/scenes/RedHatRepositories/components/EnabledRepositoryContent.js +0 -34
- data/webpack/scenes/RedHatRepositories/components/__tests__/EnabledRepository.test.js +0 -36
- data/webpack/scenes/RedHatRepositories/components/__tests__/EnabledRepositoryContent.test.js +0 -27
- data/webpack/scenes/RedHatRepositories/components/__tests__/__snapshots__/EnabledRepository.test.js.snap +0 -25
- data/webpack/scenes/RedHatRepositories/components/__tests__/__snapshots__/EnabledRepositoryContent.test.js.snap +0 -47
- data/webpack/scenes/Subscriptions/Details/SubscriptionDetailEnabledProducts.js +0 -54
- data/webpack/scenes/Subscriptions/Details/SubscriptionDetailProduct.js +0 -29
- data/webpack/scenes/Subscriptions/Details/SubscriptionDetails.scss +0 -9
- data/webpack/scenes/Subscriptions/Details/__tests__/SubscriptionDetailEnabledProducts.test.js +0 -18
- data/webpack/scenes/Subscriptions/Details/__tests__/SubscriptionDetailProduct.test.js +0 -13
- data/webpack/scenes/Subscriptions/Details/__tests__/__snapshots__/SubscriptionDetailEnabledProducts.test.js.snap +0 -45
- data/webpack/scenes/Subscriptions/Details/__tests__/__snapshots__/SubscriptionDetailProduct.test.js.snap +0 -67
- data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/EntitlementsInlineEditFormatter.test.js +0 -110
- data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/__snapshots__/EntitlementsInlineEditFormatter.test.js.snap +0 -228
- data/webpack/scenes/Tasks/helpers.js +0 -52
- data/webpack/services/api/testHelpers.js +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b618c5ea8ea521a3bc6e5c240bb41675162b2a7
|
4
|
+
data.tar.gz: 7ee6a2bc9a14c84fb699d749cc05b5956a72ef0b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 03e1ed24d9276d21a18323c34be085e1970eff16c2b999e252733b1da420170d54c2689bbb993cc7b68fb75322fb3994089615ecaac7c29efb10a4085637b8f3
|
7
|
+
data.tar.gz: 6f5ebc555ebcaadc3b6653979195e0959cc63259327c8dff05c33df1d635081ec97a80a33d0dcc359fdf9156680d0fc14e981c610979fe2590b6a1426aea816f
|
data/README.md
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
# Katello
|
2
2
|
|
3
|
-
[![Build Status](
|
3
|
+
[![Build Status](https://ci.theforeman.org/buildStatus/icon?job=test_katello)](https://ci.theforeman.org/job/test_katello)
|
4
4
|
[![Code Climate](https://codeclimate.com/github/Katello/katello/badges/gpa.svg)](https://codeclimate.com/github/Katello/katello)
|
5
|
-
[![Dependency Status](https://gemnasium.com/Katello/katello.svg)](https://gemnasium.com/Katello/katello)
|
6
5
|
|
7
6
|
Full documentation is at https://www.theforeman.org/plugins/katello/
|
8
7
|
|
9
8
|
## About
|
10
9
|
|
11
10
|
[Katello](https://www.theforeman.org/plugins/katello/) is a systems life cycle management
|
12
|
-
plugin to [Foreman](
|
11
|
+
plugin to [Foreman](https://www.theforeman.org). Katello allows you to manage
|
13
12
|
thousands of machines with one click. Katello can pull content
|
14
13
|
from remote repositories into isolated environments, and make subscriptions
|
15
14
|
management a breeze.
|
@@ -60,7 +59,7 @@ rake katello:reset
|
|
60
59
|
## Found a bug?
|
61
60
|
|
62
61
|
That's rather unfortunate. But don't worry! We can help. Just file a bug
|
63
|
-
[in our project tracker](
|
62
|
+
[in our project tracker](https://projects.theforeman.org/projects/katello).
|
64
63
|
|
65
64
|
|
66
65
|
## Contributing
|
@@ -74,9 +73,11 @@ See the [annotation docs](./test/scenarios/README.md) for more information.
|
|
74
73
|
## Contact & Resources
|
75
74
|
|
76
75
|
* [theforeman.org](https://theforeman.org/plugins/katello)
|
77
|
-
* [
|
78
|
-
*
|
79
|
-
|
76
|
+
* [Discourse Forum](https://theforeman.org/support.html#DiscourseForum)
|
77
|
+
* Archived mailing lists:
|
78
|
+
* [Foreman User Mailing List](https://groups.google.com/forum/?fromgroups#!forum/foreman-users)
|
79
|
+
* [Foreman Developer mailing list](https://groups.google.com/forum/?fromgroups#!forum/foreman-dev)
|
80
|
+
* [IRC Freenode](https://theforeman.org/support.html#IRClivechat): #theforeman-dev, #theforeman
|
80
81
|
|
81
82
|
## Documentation
|
82
83
|
|
@@ -0,0 +1,477 @@
|
|
1
|
+
module Katello
|
2
|
+
# rubocop:disable Metrics/ClassLength
|
3
|
+
class Api::Registry::RegistryProxiesController < Api::V2::ApiController
|
4
|
+
before_action :disable_strong_params
|
5
|
+
before_action :confirm_settings
|
6
|
+
skip_before_action :authorize
|
7
|
+
before_action :optional_authorize, only: [:token]
|
8
|
+
before_action :registry_authorize, except: [:token, :v1_search]
|
9
|
+
before_action :authorize_repository_read, only: [:pull_manifest, :tags_list]
|
10
|
+
before_action :authorize_repository_write, only: [:push_manifest]
|
11
|
+
skip_before_action :check_content_type, :only => [:start_upload_blob, :upload_blob, :finish_upload_blob,
|
12
|
+
:chunk_upload_blob, :push_manifest]
|
13
|
+
skip_after_action :log_response_body, :only => [:pull_blob]
|
14
|
+
|
15
|
+
wrap_parameters false
|
16
|
+
|
17
|
+
around_action :repackage_message
|
18
|
+
|
19
|
+
def repackage_message
|
20
|
+
yield
|
21
|
+
ensure
|
22
|
+
response.headers['Docker-Distribution-API-Version'] = 'registry/2.0'
|
23
|
+
end
|
24
|
+
|
25
|
+
rescue_from RestClient::Exception do |e|
|
26
|
+
Rails.logger.error pp_exception(e)
|
27
|
+
if request_from_katello_cli?
|
28
|
+
render json: { errors: [e.http_body] }, status: e.http_code
|
29
|
+
else
|
30
|
+
render plain: e.http_body, status: e.http_code
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def redirect_authorization_headers
|
35
|
+
response.headers['Docker-Distribution-API-Version'] = 'registry/2.0'
|
36
|
+
response.headers['Www-Authenticate'] = "Bearer realm=\"#{request_url}/v2/token\"," \
|
37
|
+
"service=\"#{request.host}\"," \
|
38
|
+
"scope=\"repository:registry:pull,push\""
|
39
|
+
end
|
40
|
+
|
41
|
+
def optional_authorize
|
42
|
+
@repository = find_scope_repository
|
43
|
+
if @repository && @repository.environment.registry_unauthenticated_pull
|
44
|
+
true
|
45
|
+
else
|
46
|
+
authorize
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def registry_authorize
|
51
|
+
@repository = find_readable_repository
|
52
|
+
return true if request.method == 'GET' && @repository && @repository.environment.registry_unauthenticated_pull
|
53
|
+
|
54
|
+
token = request.headers['Authorization']
|
55
|
+
if token
|
56
|
+
token_type, token = token.split
|
57
|
+
if token_type == 'Bearer' && token
|
58
|
+
personal_token = PersonalAccessToken.find_by_token(token)
|
59
|
+
if personal_token && !personal_token.expired?
|
60
|
+
User.current = User.unscoped.find(personal_token.user_id)
|
61
|
+
return true if User.current
|
62
|
+
end
|
63
|
+
elsif token_type == 'Basic' && token
|
64
|
+
return true if authorize
|
65
|
+
redirect_authorization_headers
|
66
|
+
return false
|
67
|
+
end
|
68
|
+
end
|
69
|
+
redirect_authorization_headers
|
70
|
+
render_error('unauthorized', :status => :unauthorized)
|
71
|
+
return false
|
72
|
+
end
|
73
|
+
|
74
|
+
def find_writable_repository
|
75
|
+
Repository.docker_type.syncable.find_by_container_repository_name(params[:repository])
|
76
|
+
end
|
77
|
+
|
78
|
+
def authorize_repository_write
|
79
|
+
@repository = find_writable_repository
|
80
|
+
unless @repository
|
81
|
+
not_found params[:repository]
|
82
|
+
return false
|
83
|
+
end
|
84
|
+
true
|
85
|
+
end
|
86
|
+
|
87
|
+
# Reduce visible repos to include lifecycle env permissions
|
88
|
+
# http://projects.theforeman.org/issues/22914
|
89
|
+
# Also include repositories in lifecycle environments with registry_unauthenticated_pull=true
|
90
|
+
def readable_repositories
|
91
|
+
table_name = Repository.table_name
|
92
|
+
in_products = Repository.where(:product_id => Katello::Product.authorized(:view_products))
|
93
|
+
.select(:id)
|
94
|
+
in_environments = Repository.where(:environment_id => Katello::KTEnvironment.authorized(:view_lifecycle_environments)).select(:id)
|
95
|
+
in_unauth_environments = Repository.joins(:environment).where("#{Katello::KTEnvironment.table_name}.registry_unauthenticated_pull" => true).select(:id)
|
96
|
+
in_content_views = Repository.joins(:content_view_repositories).where("#{ContentViewRepository.table_name}.content_view_id" => Katello::ContentView.readable).select(:id)
|
97
|
+
in_versions = Repository.joins(:content_view_version).where("#{Katello::ContentViewVersion.table_name}.content_view_id" => Katello::ContentView.readable).select(:id)
|
98
|
+
Repository.where("#{table_name}.id in (?) or #{table_name}.id in (?) or #{table_name}.id in (?) or #{table_name}.id in (?) or #{table_name}.id in (?)", in_products, in_content_views, in_versions, in_environments, in_unauth_environments)
|
99
|
+
end
|
100
|
+
|
101
|
+
def find_readable_repository
|
102
|
+
return nil unless params[:repository]
|
103
|
+
repository = Repository.docker_type.find_by_container_repository_name(params[:repository])
|
104
|
+
if repository && !repository.environment.registry_unauthenticated_pull
|
105
|
+
repository = readable_repositories.docker_type.find_by_container_repository_name(params[:repository])
|
106
|
+
end
|
107
|
+
repository
|
108
|
+
end
|
109
|
+
|
110
|
+
def authorize_repository_read
|
111
|
+
@repository = find_readable_repository
|
112
|
+
unless @repository
|
113
|
+
not_found params[:repository]
|
114
|
+
return false
|
115
|
+
end
|
116
|
+
|
117
|
+
if params[:tag]
|
118
|
+
if params[:tag][0..6] == 'sha256:'
|
119
|
+
manifest = Katello::DockerManifestList.where(digest: params[:tag]).first || Katello::DockerManifest.where(digest: params[:tag]).first
|
120
|
+
not_found params[:tag] unless manifest
|
121
|
+
else
|
122
|
+
tag = DockerMetaTag.where(repository_id: @repository.id, name: params[:tag]).first
|
123
|
+
not_found params[:tag] unless tag
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
true
|
128
|
+
end
|
129
|
+
|
130
|
+
def token
|
131
|
+
if @repository && @repository.environment.registry_unauthenticated_pull
|
132
|
+
personal_token = OpenStruct.new(token: 'unauthenticated', issued_at: Time.now, expires_at: Time.now + 3)
|
133
|
+
else
|
134
|
+
personal_token = PersonalAccessToken.where(user_id: User.current.id, name: 'registry').first
|
135
|
+
if personal_token.nil?
|
136
|
+
personal_token = PersonalAccessToken.new(user: User.current, name: 'registry', expires_at: 6.minutes.from_now)
|
137
|
+
personal_token.generate_token
|
138
|
+
personal_token.save!
|
139
|
+
else
|
140
|
+
personal_token.expires_at = 6.minutes.from_now
|
141
|
+
personal_token.save!
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
response.headers['Docker-Distribution-API-Version'] = 'registry/2.0'
|
146
|
+
render json: { token: personal_token.token, expires_at: personal_token.expires_at, issued_at: personal_token.created_at }
|
147
|
+
end
|
148
|
+
|
149
|
+
def pull_manifest
|
150
|
+
headers = {}
|
151
|
+
env = request.env.select do |key, _value|
|
152
|
+
key.match("^HTTP.*")
|
153
|
+
end
|
154
|
+
env.each do |header|
|
155
|
+
headers[header[0].split('_')[1..-1].join('-')] = header[1]
|
156
|
+
end
|
157
|
+
|
158
|
+
r = Resources::Registry::Proxy.get(@_request.fullpath, headers)
|
159
|
+
logger.debug r
|
160
|
+
results = JSON.parse(r)
|
161
|
+
|
162
|
+
response.header['Docker-Content-Digest'] = "sha256:#{Digest::SHA256.hexdigest(r)}"
|
163
|
+
render json: r, content_type: results['mediaType']
|
164
|
+
end
|
165
|
+
|
166
|
+
def check_blob
|
167
|
+
begin
|
168
|
+
r = Resources::Registry::Proxy.get(@_request.fullpath, 'Accept' => request.headers['Accept'])
|
169
|
+
response.header['Content-Length'] = "#{r.body.size}"
|
170
|
+
rescue RestClient::NotFound
|
171
|
+
digest_file = tmp_file("#{params[:digest][7..-1]}.tar")
|
172
|
+
raise unless File.exist? digest_file
|
173
|
+
response.header['Content-Length'] = "#{File.size digest_file}"
|
174
|
+
end
|
175
|
+
render json: {}
|
176
|
+
end
|
177
|
+
|
178
|
+
def pull_blob
|
179
|
+
r = Resources::Registry::Proxy.get(@_request.fullpath, 'Accept' => request.headers['Accept'])
|
180
|
+
render json: r
|
181
|
+
end
|
182
|
+
|
183
|
+
def push_manifest
|
184
|
+
repository = params[:repository]
|
185
|
+
tag = params[:tag]
|
186
|
+
|
187
|
+
manifest = create_manifest
|
188
|
+
return if manifest.nil?
|
189
|
+
|
190
|
+
begin
|
191
|
+
files = get_manifest_files(repository, manifest)
|
192
|
+
return if files.nil?
|
193
|
+
|
194
|
+
tar_file = create_tar_file(files, repository, tag)
|
195
|
+
return if tar_file.nil?
|
196
|
+
|
197
|
+
digest = upload_manifest(tar_file)
|
198
|
+
return if digest.nil?
|
199
|
+
|
200
|
+
tag = upload_tag(digest, tag)
|
201
|
+
return if tag.nil?
|
202
|
+
ensure
|
203
|
+
File.delete(tmp_file('manifest.json')) if File.exist? tmp_file('manifest.json')
|
204
|
+
end
|
205
|
+
|
206
|
+
render json: {}
|
207
|
+
end
|
208
|
+
|
209
|
+
def pulp_content
|
210
|
+
Katello.pulp_server.resources.content
|
211
|
+
end
|
212
|
+
|
213
|
+
def start_upload_blob
|
214
|
+
uuid = SecureRandom.hex(16)
|
215
|
+
response.header['Location'] = "#{request_url}/v2/#{params[:repository]}/blobs/uploads/#{uuid}"
|
216
|
+
response.header['Docker-Upload-UUID'] = uuid
|
217
|
+
response.header['Range'] = '0-0'
|
218
|
+
head 202
|
219
|
+
end
|
220
|
+
|
221
|
+
def status_upload_blob
|
222
|
+
response.header['Location'] = "#{request_url}/v2/#{params[:repository]}/blobs/uploads/#{params[:uuid]}"
|
223
|
+
response.header['Range'] = "123"
|
224
|
+
response.header['Docker-Upload-UUID'] = "123"
|
225
|
+
render plain: '', status: 204
|
226
|
+
end
|
227
|
+
|
228
|
+
def chunk_upload_blob
|
229
|
+
response.header['Location'] = "#{request_url}/v2/#{params[:repository]}/blobs/uploads/#{params[:uuid]}"
|
230
|
+
render plain: '', status: 202
|
231
|
+
end
|
232
|
+
|
233
|
+
def upload_blob
|
234
|
+
File.open(tmp_file("#{params[:uuid]}.tar"), 'ab', 0600) do |file|
|
235
|
+
file.write request.body.read
|
236
|
+
end
|
237
|
+
|
238
|
+
# ???? true chunked data?
|
239
|
+
if request.headers['Content-Range']
|
240
|
+
render_error 'unprocessable_entity', :status => :unprocessable_entity
|
241
|
+
end
|
242
|
+
|
243
|
+
response.header['Location'] = "#{request_url}/v2/#{params[:repository]}/blobs/uploads/#{params[:uuid]}"
|
244
|
+
response.header['Range'] = "1-#{request.body.size}"
|
245
|
+
response.header['Docker-Upload-UUID'] = params[:uuid]
|
246
|
+
head 204
|
247
|
+
end
|
248
|
+
|
249
|
+
def finish_upload_blob
|
250
|
+
# error by client if no params[:digest]
|
251
|
+
|
252
|
+
uuid_file = tmp_file("#{params[:uuid]}.tar")
|
253
|
+
digest_file = tmp_file("#{params[:digest][7..-1]}.tar")
|
254
|
+
|
255
|
+
File.delete(digest_file) if File.exist? digest_file
|
256
|
+
File.rename(uuid_file, digest_file)
|
257
|
+
|
258
|
+
response.header['Location'] = "#{request_url}/v2/#{params[:repository]}/blobs/#{params[:digest]}"
|
259
|
+
response.header['Docker-Content-Digest'] = params[:digest]
|
260
|
+
response.header['Content-Range'] = "1-#{File.size(digest_file)}"
|
261
|
+
response.header['Content-Length'] = "0"
|
262
|
+
response.header['Docker-Upload-UUID'] = params[:uuid]
|
263
|
+
head 201
|
264
|
+
end
|
265
|
+
|
266
|
+
def cancel_upload_blob
|
267
|
+
render plain: '', status: 200
|
268
|
+
end
|
269
|
+
|
270
|
+
def ping
|
271
|
+
response.headers['Docker-Distribution-API-Version'] = 'registry/2.0'
|
272
|
+
render json: {}, status: 200
|
273
|
+
end
|
274
|
+
|
275
|
+
def v1_ping
|
276
|
+
head 200
|
277
|
+
end
|
278
|
+
|
279
|
+
def v1_search
|
280
|
+
authenticate # to set current_user, not to enforce
|
281
|
+
options = {
|
282
|
+
resource_class: Katello::Repository
|
283
|
+
}
|
284
|
+
params[:per_page] = params[:n] || 25
|
285
|
+
params[:search] = params[:q]
|
286
|
+
|
287
|
+
search_results = scoped_search(readable_repositories.where(content_type: 'docker').distinct,
|
288
|
+
:container_repository_name, :asc, options)
|
289
|
+
results = {
|
290
|
+
num_results: search_results[:subtotal],
|
291
|
+
query: params[:search]
|
292
|
+
}
|
293
|
+
results[:results] = search_results[:results].collect do |repository|
|
294
|
+
{ name: repository[:container_repository_name], description: repository[:description] }
|
295
|
+
end
|
296
|
+
render json: results, status: 200
|
297
|
+
end
|
298
|
+
|
299
|
+
def catalog
|
300
|
+
repositories = readable_repositories.where(content_type: 'docker').collect do |repository|
|
301
|
+
repository.container_repository_name
|
302
|
+
end
|
303
|
+
render json: { repositories: repositories }
|
304
|
+
end
|
305
|
+
|
306
|
+
def tags_list
|
307
|
+
tags = @repository.docker_tags.collect do |tag|
|
308
|
+
tag.name
|
309
|
+
end
|
310
|
+
tags.uniq!
|
311
|
+
tags.sort!
|
312
|
+
render json: {
|
313
|
+
name: @repository.container_repository_name,
|
314
|
+
tags: tags
|
315
|
+
}
|
316
|
+
end
|
317
|
+
|
318
|
+
def create_manifest
|
319
|
+
filename = tmp_file('manifest.json')
|
320
|
+
if File.exist? filename
|
321
|
+
render_error('custom_error', :status => :unprocessable_entity,
|
322
|
+
:locals => { :message => "Upload already in progress" })
|
323
|
+
return nil
|
324
|
+
end
|
325
|
+
manifest = request.body.read
|
326
|
+
File.open(tmp_file('manifest.json'), 'wb', 0600) do |file|
|
327
|
+
file.write manifest
|
328
|
+
end
|
329
|
+
manifest = JSON.parse(manifest)
|
330
|
+
rescue
|
331
|
+
File.delete(tmp_file('manifest.json')) if File.exist? tmp_file('manifest.json')
|
332
|
+
end
|
333
|
+
|
334
|
+
def get_manifest_files(repository, manifest)
|
335
|
+
files = ['manifest.json']
|
336
|
+
if manifest['schemaVersion'] == 1
|
337
|
+
if manifest['fsLayers']
|
338
|
+
files += manifest['fsLayers'].collect do |layer|
|
339
|
+
layerfile = "#{layer['blobSum'][7..-1]}.tar"
|
340
|
+
force_include_layer(repository, layer['blobSum'], layerfile)
|
341
|
+
layerfile
|
342
|
+
end
|
343
|
+
end
|
344
|
+
elsif manifest['schemaVersion'] == 2
|
345
|
+
if manifest['layers']
|
346
|
+
files += manifest['layers'].collect do |layer|
|
347
|
+
layerfile = "#{layer['digest'][7..-1]}.tar"
|
348
|
+
force_include_layer(repository, layer['digest'], layerfile)
|
349
|
+
layerfile
|
350
|
+
end
|
351
|
+
end
|
352
|
+
files << "#{manifest['config']['digest'][7..-1]}.tar"
|
353
|
+
else
|
354
|
+
render_error 'custom_error', :status => :internal_server_error,
|
355
|
+
:locals => { :message => "Unsupported schema #{manifest['schemaVersion']}" }
|
356
|
+
return nil
|
357
|
+
end
|
358
|
+
files
|
359
|
+
end
|
360
|
+
|
361
|
+
def create_tar_file(files, repository, tag)
|
362
|
+
tar_file = "#{repository}_#{tag}.tar"
|
363
|
+
`/usr/bin/tar cf #{tmp_file(tar_file)} -C #{tmp_dir} #{files.join(' ')}`
|
364
|
+
|
365
|
+
files.each do |file|
|
366
|
+
filename = tmp_file(file)
|
367
|
+
File.delete(filename) if File.exist? filename
|
368
|
+
end
|
369
|
+
tar_file
|
370
|
+
end
|
371
|
+
|
372
|
+
def upload_manifest(tar_file)
|
373
|
+
upload_id = pulp_content.create_upload_request['upload_id']
|
374
|
+
filename = tmp_file(tar_file)
|
375
|
+
File.open(filename, 'rb') do |file|
|
376
|
+
pulp_content.upload_bits(upload_id, 0, file.read)
|
377
|
+
|
378
|
+
file.rewind
|
379
|
+
content = file.read
|
380
|
+
unit_keys = [{
|
381
|
+
name: filename,
|
382
|
+
size: file.size,
|
383
|
+
checksum: Digest::SHA256.hexdigest(content)
|
384
|
+
}]
|
385
|
+
unit_type_id = 'docker_manifest'
|
386
|
+
task = sync_task(::Actions::Katello::Repository::ImportUpload,
|
387
|
+
@repository, [upload_id], :unit_type_id => unit_type_id,
|
388
|
+
:unit_keys => unit_keys,
|
389
|
+
:generate_metadata => true, :sync_capsule => true)
|
390
|
+
digest = task.output['upload_results'][0]['digest']
|
391
|
+
|
392
|
+
File.delete(filename)
|
393
|
+
|
394
|
+
digest
|
395
|
+
end
|
396
|
+
ensure
|
397
|
+
pulp_content.delete_upload_request(upload_id) if upload_id
|
398
|
+
end
|
399
|
+
|
400
|
+
def upload_tag(digest, tag)
|
401
|
+
upload_id = pulp_content.create_upload_request['upload_id']
|
402
|
+
unit_keys = [{
|
403
|
+
name: tag,
|
404
|
+
digest: digest
|
405
|
+
}]
|
406
|
+
unit_type_id = 'docker_tag'
|
407
|
+
sync_task(::Actions::Katello::Repository::ImportUpload,
|
408
|
+
@repository, [upload_id], :unit_type_id => unit_type_id,
|
409
|
+
:unit_keys => unit_keys,
|
410
|
+
:generate_metadata => true, :sync_capsule => true)
|
411
|
+
|
412
|
+
tag
|
413
|
+
ensure
|
414
|
+
pulp_content.delete_upload_request(upload_id) if upload_id
|
415
|
+
end
|
416
|
+
|
417
|
+
def tmp_dir
|
418
|
+
"#{Rails.root}/tmp"
|
419
|
+
end
|
420
|
+
|
421
|
+
def tmp_file(filename)
|
422
|
+
File.join(tmp_dir, filename)
|
423
|
+
end
|
424
|
+
|
425
|
+
# TODO: Until pulp supports optional upload of layers, include all layers
|
426
|
+
# https://pulp.plan.io/issues/3497
|
427
|
+
def force_include_layer(repository, digest, layer)
|
428
|
+
unless File.exist? tmp_file(layer)
|
429
|
+
logger.debug "Getting blob #{digest} to write to #{layer}"
|
430
|
+
fullpath = "/v2/#{repository}/blobs/#{digest}"
|
431
|
+
request = Resources::Registry::Proxy.get(fullpath)
|
432
|
+
File.open(tmp_file(layer), 'wb', 0600) do |file|
|
433
|
+
file.write request.body
|
434
|
+
end
|
435
|
+
logger.debug "Wrote blob #{digest} to #{layer}"
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
def find_scope_repository
|
440
|
+
scope = params['scope']
|
441
|
+
return nil unless scope
|
442
|
+
|
443
|
+
scopes = scope.split(':')
|
444
|
+
scopes[2] == 'pull' ? Repository.docker_type.find_by_container_repository_name(scopes[1]) : nil
|
445
|
+
end
|
446
|
+
|
447
|
+
def disable_strong_params
|
448
|
+
params.permit!
|
449
|
+
end
|
450
|
+
|
451
|
+
def confirm_settings
|
452
|
+
return true if SETTINGS[:katello][:registry]
|
453
|
+
render_error('custom_error', :status => :not_found,
|
454
|
+
:locals => { :message => "Registry not configured" })
|
455
|
+
false
|
456
|
+
end
|
457
|
+
|
458
|
+
def request_url
|
459
|
+
request.protocol + request.host_with_port
|
460
|
+
end
|
461
|
+
|
462
|
+
def logger
|
463
|
+
::Foreman::Logging.logger('katello/registry_proxy')
|
464
|
+
end
|
465
|
+
|
466
|
+
def route_name
|
467
|
+
Engine.routes.router.recognize(request) do |_, params|
|
468
|
+
break params[:action] if params[:action]
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
def process_action(method_name, *args)
|
473
|
+
::Api::V2::BaseController.instance_method(:process_action).bind(self).call(method_name, *args)
|
474
|
+
Rails.logger.debug "With body: #{response.body}\n" unless route_name == 'pull_blob'
|
475
|
+
end
|
476
|
+
end
|
477
|
+
end
|