Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #37455 - Create Katello push repositories as needed at container push time, handle non-auth validation. #10995

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

qcjames53
Copy link
Contributor

@qcjames53 qcjames53 commented May 14, 2024

What are the changes introduced in this pull request?

  • When using podman to push containers to Katello, ensure that the container name is of a valid format:
    • <org_label>/<product_label>/<container_name>[:tag] (down-case all letters)
    • id/<org_id>/<product_id>/<container_name>[:tag]
  • Limits to container names:
    • Org and product fields must always correspond to a valid, existing org and product, respectively. No fields can be blank or omitted.
    • Org and product labels must be downcased. Container names must be lowercase and contain only letters and underscores (podman rejects these automatically).
    • Org and product labels cannot be ambiguous (for example 'foo_bar' and 'Foo_Bar' both map to 'foo_bar'). For ambiguous orgs or products, the user is directed to use the id format.
    • Org and product IDs must be integers with no leading zeros.
    • A user must stick to a specific format for each org, product, name combination. Pushes are not permitted to alternate between label and id or vice versa.
    • If a label format push becomes ambiguous due to external changes, the user will be directed to destroy either the offending org/product or will be directed to destroy the existing push container and re-push using the id format.
  • Create a new push repository matching the container name under the appropriate product where required.
  • Update existing push repos where required.
  • Run content indexing after completion of the container upload.
  • Ensure the root repository fields are correct after upload:
    • name: matches container_name
    • label: matches container_name
    • content_type: docker
    • is_container_push (new field): true
    • container_push_name (new field): name used in podman command e.g. "id/0/0/foo"
    • container_push_name_format (new field): name format used, label or id
  • Ensure that the instance repository fields are correct after upload:
    • relative_path: matches the container_push_name above
    • container_repository_name: matches the container_push_name above
    • environment_id: correct for product
    • content_view_version_id: correct for product
    • latest_version_href: pulp latest version href
    • pulp_href: pulp href

Considerations taken when implementing this change?

What are the testing steps for this pull request?

  1. Pull a few different podman images:
podman pull quay.io/aptible/hello-world
  1. Launch katello and create an empty product. Log in with podman:
podman login <url>
  1. Get the image ID for one of the test podman images. Push that image to katello using the correct organization label and product label. Make sure to down-case the labels or podman will reject the container name:
podman image list
podman push <img_id> <url>/<organization_label>/<product_label>/<container_name>
  1. Use the rake console to ensure the root repository and repository were created properly:
bundle exec rake console
::Katello::Products.find(<id>).root_repositories.where(label: "<container_name>").first
::Katello::Products.find(<id>).root_repositories.where(label: "<container_name>").first.library_instance
  1. Repeat steps 3 and 4 using the id push format. You will need to change the container name.
podman push <img_id> <url>/id/<organization_id>/<product_id>/<container_name>
  1. Repeat steps 3 and 4 with a [:tag] field. This should work as usual.
  2. Repeat steps 3 and 4 while trying every error in the above 'Limits to container names' list. This is an extensive but not exhaustive list of all the error handling included. List duplicated below:
  • Org and product fields must always correspond to a valid, existing org and product, respectively. No fields can be blank or omitted.
  • Org and product labels must be downcased. Container names must be lowercase and contain only letters and underscores (podman rejects these automatically).
  • Org and product labels cannot be ambiguous (for example 'foo_bar' and 'Foo_Bar' both map to 'foo_bar'). For ambiguous orgs or products, the user is directed to use the id format.
  • Org and product IDs must be integers with no leading zeros.
  • A user must stick to a specific format for each org, product, name combination. Pushes are not permitted to alternate between label and id or vice versa for a given container.
  • If a label format push becomes ambiguous due to external changes, the user will be directed to destroy either the offending org/product or will be directed to destroy the existing push container and re-push using the id format.
  1. Validate the test cases for coverage and accuracy.

@qcjames53
Copy link
Contributor Author

The errors thrown are a little messy. They are all generic errors and they tend to get truncated on the podman side. I'm happy to improve them if you think it's a good idea.

@qcjames53
Copy link
Contributor Author

Containers with tags such as default_organization/dummy_product/foo:bar end up losing the tag when pushed. I think this is okay since the tag is on the podman side; however, this means that users would technically need to pull using a different name than the name they pushed with. This may need further discussion since I could see users expecting either behavior (tag loss vs tag specification on pull).

Copy link
Member

@ianballou ianballou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw this error on my first push:

container_repository_name: ["for repository 'fedora-bootc' is not unique and cannot be created in 'Library'. Its Container Repository Name (default_organization-buttermilk_biscuits-fedora-bootc) conflicts with an existing repository.  Consider changing the Lifecycle Environment's Registry Name Pattern to something more specific."]

I do have another container repo, but its label is centos-bootc_fedora-bootc

The error didn't throw until later. Was repo creation attempted twice maybe?

I pushed:

podman push 75eca4dcedfb `hostname`/default_organization/buttermilk_biscuits/fedora-bootc

@qcjames53
Copy link
Contributor Author

qcjames53 commented May 14, 2024

I saw this error on my first push:

container_repository_name: ["for repository 'fedora-bootc' is not unique and cannot be created in 'Library'. Its Container Repository Name (default_organization-buttermilk_biscuits-fedora-bootc) conflicts with an existing repository.  Consider changing the Lifecycle Environment's Registry Name Pattern to something more specific."]

I do have another container repo, but its label is centos-bootc_fedora-bootc

The error didn't throw until later. Was repo creation attempted twice maybe?

I pushed:

podman push 75eca4dcedfb `hostname`/default_organization/buttermilk_biscuits/fedora-bootc

Well shoot. Podman calls the endpoint that calls start_upload_blob multiple times. Sometimes once, sometimes 3 or 4 times. Big pain in the butt. If I were a betting person, the logic in that method is getting tripped up and is unable to find the repo created on the previous run and is trying to create the repo again.

Another option is that the centos-bootc_fedora-bootc is named fedora-bootc because of a weird LE pattern?

@ianballou
Copy link
Member

I deleted all of my repos and tried again, ended up hitting:

2024-05-14T21:18:25 [E|kat|2a1f0b55] NoMethodError: undefined method `library_instance' for nil:NilClass
 2a1f0b55 | /home/vagrant/katello/app/controllers/katello/api/registry/registry_proxies_controller.rb:397:in `push_manifest'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_controller/metal/basic_implicit_render.rb:6:in `send_action'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/abstract_controller/base.rb:228:in `process_action'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_controller/metal/rendering.rb:30:in `process_action'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/abstract_controller/callbacks.rb:42:in `block in process_action'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/activesupport-6.1.7.7/lib/active_support/callbacks.rb:117:in `block in run_callbacks'
 2a1f0b55 | /home/vagrant/katello/app/controllers/katello/api/registry/registry_proxies_controller.rb:24:in `repackage_message'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/activesupport-6.1.7.7/lib/active_support/callbacks.rb:126:in `block in run_callbacks'
 2a1f0b55 | /home/vagrant/foreman/app/controllers/concerns/foreman/controller/timezone.rb:10:in `set_timezone'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/activesupport-6.1.7.7/lib/active_support/callbacks.rb:126:in `block in run_callbacks'
 2a1f0b55 | /home/vagrant/foreman/app/models/concerns/foreman/thread_session.rb:32:in `clear_thread'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/activesupport-6.1.7.7/lib/active_support/callbacks.rb:126:in `block in run_callbacks'
 2a1f0b55 | /home/vagrant/foreman/app/controllers/concerns/foreman/controller/topbar_sweeper.rb:12:in `set_topbar_sweeper_controller'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/activesupport-6.1.7.7/lib/active_support/callbacks.rb:126:in `block in run_callbacks'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/audited-5.6.0/lib/audited/sweeper.rb:16:in `around'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/activesupport-6.1.7.7/lib/active_support/callbacks.rb:126:in `block in run_callbacks'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/audited-5.6.0/lib/audited/sweeper.rb:16:in `around'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/activesupport-6.1.7.7/lib/active_support/callbacks.rb:126:in `block in run_callbacks'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/activesupport-6.1.7.7/lib/active_support/callbacks.rb:137:in `run_callbacks'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/abstract_controller/callbacks.rb:41:in `process_action'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_controller/metal/rescue.rb:22:in `process_action'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_controller/metal/instrumentation.rb:34:in `block in process_action'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/activesupport-6.1.7.7/lib/active_support/notifications.rb:203:in `block in instrument'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/activesupport-6.1.7.7/lib/active_support/notifications/instrumenter.rb:24:in `instrument'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/activesupport-6.1.7.7/lib/active_support/notifications.rb:203:in `instrument'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_controller/metal/instrumentation.rb:33:in `process_action'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_controller/metal/params_wrapper.rb:249:in `process_action'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/activerecord-6.1.7.7/lib/active_record/railties/controller_runtime.rb:27:in `process_action'
 2a1f0b55 | /home/vagrant/katello/app/controllers/katello/api/registry/registry_proxies_controller.rb:581:in `call'
 2a1f0b55 | /home/vagrant/katello/app/controllers/katello/api/registry/registry_proxies_controller.rb:581:in `process_action'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/abstract_controller/base.rb:165:in `process'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionview-6.1.7.7/lib/action_view/rendering.rb:39:in `process'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_controller/metal.rb:190:in `dispatch'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_controller/metal.rb:254:in `dispatch'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/routing/route_set.rb:50:in `dispatch'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/routing/route_set.rb:33:in `serve'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/journey/router.rb:50:in `block in serve'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/journey/router.rb:32:in `each'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/journey/router.rb:32:in `serve'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/routing/route_set.rb:842:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/railties-6.1.7.7/lib/rails/engine.rb:539:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/railties-6.1.7.7/lib/rails/railtie.rb:207:in `public_send'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/railties-6.1.7.7/lib/rails/railtie.rb:207:in `method_missing'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/routing/mapper.rb:20:in `block in <class:Constraints>'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/routing/mapper.rb:49:in `serve'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/journey/router.rb:50:in `block in serve'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/journey/router.rb:32:in `each'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/journey/router.rb:32:in `serve'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/routing/route_set.rb:842:in `call'
 2a1f0b55 | /home/vagrant/katello/lib/katello/middleware/organization_created_enforcer.rb:18:in `call'
 2a1f0b55 | /home/vagrant/katello/lib/katello/middleware/event_daemon.rb:10:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/static.rb:24:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/static.rb:24:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/apipie-dsl-2.6.2/lib/apipie_dsl/static_dispatcher.rb:67:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/apipie-rails-1.3.0/lib/apipie/static_dispatcher.rb:74:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/bullet-7.1.6/lib/bullet/rack.rb:17:in `call'
 2a1f0b55 | /home/vagrant/foreman/lib/foreman/middleware/libvirt_connection_cleaner.rb:9:in `call'
 2a1f0b55 | /home/vagrant/foreman/lib/foreman/middleware/telemetry.rb:10:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/apipie-rails-1.3.0/lib/apipie/middleware/checksum_in_headers.rb:27:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/rack-openid-1.4.2/lib/rack/openid.rb:98:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/rack-2.2.9/lib/rack/tempfile_reaper.rb:15:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/rack-2.2.9/lib/rack/etag.rb:27:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/rack-2.2.9/lib/rack/conditional_get.rb:40:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/rack-2.2.9/lib/rack/head.rb:12:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/http/permissions_policy.rb:22:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/http/content_security_policy.rb:19:in `call'
 2a1f0b55 | /home/vagrant/foreman/lib/foreman/middleware/logging_context_session.rb:22:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/rack-2.2.9/lib/rack/session/abstract/id.rb:266:in `context'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/rack-2.2.9/lib/rack/session/abstract/id.rb:260:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/cookies.rb:697:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/activerecord-6.1.7.7/lib/active_record/migration.rb:601:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/callbacks.rb:27:in `block in call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/activesupport-6.1.7.7/lib/active_support/callbacks.rb:98:in `run_callbacks'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/callbacks.rb:26:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/executor.rb:14:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/actionable_exceptions.rb:18:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/debug_exceptions.rb:29:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/show_exceptions.rb:33:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/railties-6.1.7.7/lib/rails/rack/logger.rb:37:in `call_app'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/railties-6.1.7.7/lib/rails/rack/logger.rb:28:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/sprockets-rails-3.4.2/lib/sprockets/rails/quiet_assets.rb:13:in `call'
 2a1f0b55 | /home/vagrant/foreman/lib/foreman/middleware/logging_context_request.rb:11:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/remote_ip.rb:81:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/request_id.rb:26:in `call'
 2a1f0b55 | /home/vagrant/katello/lib/katello/prevent_json_parsing.rb:12:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/rack-2.2.9/lib/rack/method_override.rb:24:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/rack-2.2.9/lib/rack/runtime.rb:22:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/activesupport-6.1.7.7/lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/executor.rb:14:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/static.rb:24:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/rack-2.2.9/lib/rack/sendfile.rb:110:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/host_authorization.rb:148:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/secure_headers-6.5.0/lib/secure_headers/middleware.rb:11:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/railties-6.1.7.7/lib/rails/engine.rb:539:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/railties-6.1.7.7/lib/rails/railtie.rb:207:in `public_send'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/railties-6.1.7.7/lib/rails/railtie.rb:207:in `method_missing'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/rack-2.2.9/lib/rack/urlmap.rb:74:in `block in call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/rack-2.2.9/lib/rack/urlmap.rb:58:in `each'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/rack-2.2.9/lib/rack/urlmap.rb:58:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/puma-6.4.2/lib/puma/configuration.rb:272:in `call'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/puma-6.4.2/lib/puma/request.rb:100:in `block in handle_request'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/puma-6.4.2/lib/puma/thread_pool.rb:378:in `with_force_shutdown'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/puma-6.4.2/lib/puma/request.rb:99:in `handle_request'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/puma-6.4.2/lib/puma/server.rb:464:in `process_client'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/puma-6.4.2/lib/puma/server.rb:245:in `block in run'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/puma-6.4.2/lib/puma/thread_pool.rb:155:in `block in spawn_thread'
 2a1f0b55 | /home/vagrant/foreman/.vendor/ruby/3.0.0/gems/logging-2.3.1/lib/logging/diagnostic_context.rb:474:in `block in create_with_logging_context'
2024-05-14T21:18:25 [I|app|2a1f0b55] Completed 500 Internal Server Error in 221ms (Views: 0.2ms | ActiveRecord: 6.6ms | Allocations: 16103)

@ianballou
Copy link
Member

I realize that we will definitely need some way to tell push repositories apart from normal ones. Relying on the version_href to say "container-push" in it could be a pretty good way to do so. When making content views, we'll need it to determine how contents should be copied. We'll also need it for deleting container push repositories, since it's the distribution that needs to be deleted rather than the repo itself.

@ianballou
Copy link
Member

Containers with tags such as default_organization/dummy_product/foo:bar end up losing the tag when pushed. I think this is okay since the tag is on the podman side; however, this means that users would technically need to pull using a different name than the name they pushed with. This may need further discussion since I could see users expecting either behavior (tag loss vs tag specification on pull).

I missed this comment before. The tag is separate from the repository name -- it is tagging the manifest that the user is pushing up. Under the hood, if not tag is noted, podman will auto-assign latest to the manifest being pushed. I'm guessing in this case then that all pushed content is getting the latest tag. It's important that tags are taken in exactly as they're pushed.

@ianballou
Copy link
Member

I tested passing in custom tags and it works :)

vagrant@centos9-katello-devel ~/foreman $ podman push 66640e9653de  `hostname`/default_organization/buttermilk_biscuits/busybox:tacos
Getting image source signatures
Copying blob 29e363cff31e skipped: already exists  
Copying config 66640e9653 done   | 
Writing manifest to image destination
vagrant@centos9-katello-devel ~/foreman $ sudo pulp show --href /pulp/api/v3/content/container/tags/?repository_version=/pulp/api/v3/repositories/container/container-push/018f9759-7d55-7b1e-82b0-f2f062ebd606/versions/1/
{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "pulp_href": "/pulp/api/v3/content/container/tags/018f9759-8181-7602-b74e-db7c22a9981b/",
      "pulp_created": "2024-05-20T18:52:56.577888Z",
      "pulp_last_updated": "2024-05-20T18:52:56.577901Z",
      "name": "latest",
      "tagged_manifest": "/pulp/api/v3/content/container/manifests/018f9759-816b-7ed1-a0a9-6f9b8aabfbbc/"
    }
  ]
}

vagrant@centos9-katello-devel ~/foreman $ sudo pulp show --href /pulp/api/v3/content/container/tags/?repository_version=/pulp/api/v3/repositories/container/container-push/018f9759-7d55-7b1e-82b0-f2f062ebd606/versions/2/
{
  "count": 2,
  "next": null,
  "previous": null,
  "results": [
    {
      "pulp_href": "/pulp/api/v3/content/container/tags/018f975c-858c-774c-9271-e67a92edcf5e/",
      "pulp_created": "2024-05-20T18:56:14.221447Z",
      "pulp_last_updated": "2024-05-20T18:56:14.221459Z",
      "name": "tacos",
      "tagged_manifest": "/pulp/api/v3/content/container/manifests/018f975c-857f-7709-9ab5-d8e897713f8e/"
    },
    {
      "pulp_href": "/pulp/api/v3/content/container/tags/018f9759-8181-7602-b74e-db7c22a9981b/",
      "pulp_created": "2024-05-20T18:52:56.577888Z",
      "pulp_last_updated": "2024-05-20T18:52:56.577901Z",
      "name": "latest",
      "tagged_manifest": "/pulp/api/v3/content/container/manifests/018f9759-816b-7ed1-a0a9-6f9b8aabfbbc/"
    }
  ]
}

@qcjames53
Copy link
Contributor Author

Containers with tags such as default_organization/dummy_product/foo:bar end up losing the tag when pushed. I think this is okay since the tag is on the podman side; however, this means that users would technically need to pull using a different name than the name they pushed with. This may need further discussion since I could see users expecting either behavior (tag loss vs tag specification on pull).

I missed this comment before. The tag is separate from the repository name -- it is tagging the manifest that the user is pushing up. Under the hood, if not tag is noted, podman will auto-assign latest to the manifest being pushed. I'm guessing in this case then that all pushed content is getting the latest tag. It's important that tags are taken in exactly as they're pushed.

ianballou and I talked about this outside of the PR and we came to the conclusion that we are handling tags correctly at the moment. Tag info shouldn't need to be stored anywhere in the repo; handling everything with Pulp should be completely fine.

Copy link
Member

@ianballou ianballou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird errors aside, pushing via name and via IDs are both creating repositories in the proper places 👍

@ianballou
Copy link
Member

ianballou commented May 20, 2024

As we discussed in-person before, the main thing missing now is being able to index content. To do so, I'd recommend a strategy like:

  1. Set up the container push repositories API for easy use by adding the following to app/services/katello/pulp3/api/docker.rb:
def container_push_repositories_api
  PulpContainerClient::RepositoriesContainerPushApi.new(api_client)
end
  1. Use the API to look up the Pulp repository's version_href after Pulp has taken in the proxied push request:
# ...
repo.update(version_href: ::Katello::Pulp3::Repository.api(SmartProxy.pulp_primary, ::Katello::Repository::DOCKER_TYPE).
  container_push_repositories_api.list(name: container_repo_name_here).results.first.latest_version_href)
# ...

(Determined via https://www.rubydoc.info/gems/pulp_container_client/2.20.0/PulpContainerClient/RepositoriesContainerPushApi)

  1. New model I forgot to mention before:

Create the ::Katello::Pulp3::RepositoryReference that would normally happen during the Pulp 3 repo creation. This is skipped because Pulp is making the repo for us.

          # pulp_repo_href looks like /pulp/api/v3/repositories/container/container-push/018f9759-7d55-7b1e-82b0-f2f062ebd606/
          # Note that there is no version -- this is just the repository reference, not a reference to any repository version
          RepositoryReference.where(
            root_repository_id: repo.root_id,
            content_view_id: repo.content_view.id,
            repository_href: pulp_api_response.pulp_href).create!
  1. Index the repository via repo.index_content.

Bonus points:

Using a similar method as above, also find the distribution and save it as a Katello::Pulp3::DistributionReference distribution reference record on the ::Katello::Repository

-> API: PulpContainerClient::DistributionsContainerApi

@qcjames53
Copy link
Contributor Author

@ianballou I really appreciate all the help with content indexing above (too big to quote). The newest update to the PR should fix the duplicate Pulp entry, set the root repo's version_href properly, and handle errors so that Podman can display them.

@ianballou
Copy link
Member

Already can see one improvement:

vagrant@centos9-katello-devel ~/katello $ podman push 66640e9653de  `hostname`/default_organization/buttermilk_biscuits/busybox:latest
Getting image source signatures
Copying blob b7d21803af9e [--------------------------------------] 8.0b / 3.0MiB | 1.3 MiB/s
WARN[0001] Failed, retrying in 1s ... (1/3). Error: writing blob: initiating layer upload to /v2/default_organization/buttermilk_biscuits/busybox/blobs/uploads/ in centos9-katello-devel.manicotto.example.com: name invalid: Product label 'buttermilk_biscuits' is ambiguous. Try using an id-based container name. 
Getting image source signatures
Copying blob b7d21803af9e [--------------------------------------] 8.0b / 3.0MiB | 1.8 MiB/s
WARN[0004] Failed, retrying in 1s ... (2/3). Error: writing blob: initiating layer upload to /v2/default_organization/buttermilk_biscuits/busybox/blobs/uploads/ in centos9-katello-devel.manicotto.example.com: name invalid: Product label 'buttermilk_biscuits' is ambiguous. Try using an id-based container name. 
Getting image source signatures
Copying blob b7d21803af9e [--------------------------------------] 8.0b / 3.0MiB | 2.0 MiB/s
WARN[0007] Failed, retrying in 1s ... (3/3). Error: writing blob: initiating layer upload to /v2/default_organization/buttermilk_biscuits/busybox/blobs/uploads/ in centos9-katello-devel.manicotto.example.com: name invalid: Product label 'buttermilk_biscuits' is ambiguous. Try using an id-based container name. 
Getting image source signatures
Copying blob b7d21803af9e [--------------------------------------] 8.0b / 3.0MiB | 1.7 MiB/s
Error: writing blob: initiating layer upload to /v2/default_organization/buttermilk_biscuits/busybox/blobs/uploads/ in centos9-katello-devel.manicotto.example.com: name invalid: Product label 'buttermilk_biscuits' is ambiguous. Try using an id-based container name.

@ianballou
Copy link
Member

I wonder if this is starting to get more complicated than we need, especially since this capitalization scenario we're guarding against should be very rare.

I'm trying to think if we could completely throw out the "by ID" style to ensure that Katello and Pulp are always aligned.

@ianballou
Copy link
Member

Throwing out the ID method entirely will force the rare ambiguous org / product owner to recreate the org / product. I think the safer middle ground would be to check if a user is trying to push content to a repository with a different name / ID scheme and to tell them to destroy it first. It makes for a decent experience for the very rare ambiguous org/product user, and shouldn't over-complicate the code for the 99% use-case.

Copy link
Member

@ianballou ianballou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error messages are looking better! One issue I noticed:

  1. Push an image by name/label
  2. Create another org with the same name
  3. Create a product in the new org with the same name as the product in the old org
  4. Push the same image in step (1) to the product in step (3) by ID
  5. The ID repo gets created in Pulp, but no repository is created in the new org/product.

It looks like the Root repository was created under the new org. They do have the proper product association as well. However, neither of them have any Repository records.

The erroneous Root repositories also don't have any RepositoryReferences.

The first repository pushed by name is all correct, it seems.

Edit: I was able to push a new repo via ID to the new org, but it doesn't seem to have the version_href or distribution references set up.

I think in my Pulp the arianna repository already existed from a prior push.
-> It looks like this didn't matter.

Copy link
Member

@ianballou ianballou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Things have changed a bit -- now I'm seeing:

Error: writing manifest: uploading manifest latest to centos9-katello-devel.manicotto.example.com/id/3/2/curl: blob upload unknown: Could not locate local uploaded repository for content indexing.

when trying to push the same container repository (curl) to a second org with the same name by ID.

However, if I push a brand new container repository, it seems to work just fine now.

@ianballou
Copy link
Member

I think the commit and title can be changed to a "Fixes" instead of "Refs"

@ianballou
Copy link
Member

It looks like pulling the pushed content broke somewhere along the way:

vagrant@centos9-katello-devel ~/katello $ podman search `hostname`/
NAME                                                                                          DESCRIPTION
centos9-katello-devel.manicotto.example.com/default_organization-buttermilk_biscuits-curl     
centos9-katello-devel.manicotto.example.com/default_organization-buttermilk_biscuits-busybox

It seems Katello still thinks we're distributing the contents using the old container name format.

However, I'd be okay with pushing this to another PR, since in our plan we did have distribution as a separate task. Up to you. I know this PR has been lingering a while!

@ianballou
Copy link
Member

ianballou commented Jun 4, 2024

Pushing the same repo to multiple products seems to be failing, looks like the container repository name is being saved just as the name of the repository and not the full path with org and product:

2024-06-04T20:47:41 [D|app|4773c508] With body: {"displayMessage":"Validation failed: Container repository name for repository 'busybox' is not unique and cannot be created in 'Library'. Its Container Repository Name (default_organization-buttermilk_biscuits-busybox) conflicts with an existing repository.  Consider changing the Lifecycle Environment's Registry Name Pattern to something more specific.","errors":{"container_repository_name":["for repository 'busybox' is not unique and cannot be created in 'Library'. Its Container Repository Name (default_organization-buttermilk_biscuits-busybox) conflicts with an existing repository.  Consider changing the Lifecycle Environment's Registry Name Pattern to something more specific."]}}

Looks like it's because, even though the relative_path is getting set, the set_container_repository_name validation is still running. I might suggest adding a check for "push" here: https://github.com/Katello/katello/blob/master/app/models/katello/repository.rb#L121

    def skip_container_name?
      self.library_instance? && self.root.docker? && self.root.is_container_push
    end

That could be used perhaps instead of docker?. We check if the repo is a library instance since we want to use the normal container name for repos published in content views.

@ianballou
Copy link
Member

I think I may have fixed all the issues with duplicate org / product, try this patch:

diff --git a/app/lib/actions/katello/repository/create_root.rb b/app/lib/actions/katello/repository/create_root.rb
index c3770f8fd9..89f7c07f82 100644
--- a/app/lib/actions/katello/repository/create_root.rb
+++ b/app/lib/actions/katello/repository/create_root.rb
@@ -8,6 +8,7 @@ module Actions
                                       :content_view_version => root.organization.library.default_content_view_version,
                                       :root => root)
 
+          repository.container_repository_name = relative_path
           repository.relative_path = relative_path || repository.custom_repo_path
           repository.save!
 
diff --git a/app/models/katello/repository.rb b/app/models/katello/repository.rb
index a893959ec6..2bae545206 100644
--- a/app/models/katello/repository.rb
+++ b/app/models/katello/repository.rb
@@ -118,7 +118,7 @@ module Katello
     end}
 
     before_validation :set_pulp_id
-    before_validation :set_container_repository_name, :if => :docker?
+    before_validation :set_container_repository_name, :unless => :skip_container_name?
 
     scope :has_url, -> { joins(:root).where.not("#{RootRepository.table_name}.url" => nil) }
     scope :on_demand, -> { joins(:root).where("#{RootRepository.table_name}.download_policy" => ::Katello::RootRepository::DOWNLOAD_ON_DEMAND) }
@@ -268,6 +268,10 @@ module Katello
       self.content_view_version.content_view
     end
 
+    def skip_container_name?
+      self.library_instance? && self.root.docker? && self.root.is_container_push
+    end
+
     def library_instance?
       self.content_view.default?
     end

@ianballou
Copy link
Member

The container repository name was getting duplicated and throwing errors.

@qcjames53 qcjames53 changed the title Refs #37455 - Create Katello push repositories as needed at container push time Fixes #37455 - Create Katello push repositories as needed at container push time, handle non-auth validation. Jun 6, 2024
@qcjames53 qcjames53 force-pushed the 37455 branch 2 times, most recently from d4e8151 to 626a443 Compare June 6, 2024 20:58
@ianballou
Copy link
Member

Looks like just a single test failure: Error: Failure: test_docker_repo_unprotected

@qcjames53
Copy link
Contributor Author

I just updated the PR description and testing steps. I'll get right on the failing test, thanks.

…r push time, handle non-auth validation.

Co-authored-by: Ian Ballou <ianballou67@gmail.com>
@qcjames53
Copy link
Contributor Author

The failing test should be fixed. I ran into a few issues with content indexing after making the changes in the most recent push so you may need to reset the db.

Copy link
Member

@ianballou ianballou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is working well now! I've finished testing a bunch of scenarios, pushing and pulling content with different levels of ambiguity.

I noticed one error that could be improved:

vagrant@centos9-katello-devel ~/katello $ podman push 66640e9653de `hostname`/id/1/339/arianna-id:nachos
Getting image source signatures
Copying blob b7d21803af9e [--------------------------------------] 8.0b / 3.0MiB | 1.7 MiB/s
Error: writing blob: initiating layer upload to /v2/id/1/339/arianna-id/blobs/uploads/ in centos9-katello-devel.manicotto.example.com: StatusCode: 404, "{\"displayMessage\":\"Couldn't find Organization with...

which happens when pushing by-ID to an org that doesn't exist.

If it's obvious how to fix it, great. If not, we can leave this for later. I'm guessing the JSON returned for the error is just malformed.

:bad_request
)
end
@organization = Organization.find(org_id.to_i)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is where the error I mentioned before is originating. It seems that, when pushing by-ID, the organization will fail here rather than being caught elsewhere. It just means a more ugly error is returned to podman, but it's still a 404.

Copy link
Member

@ianballou ianballou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Impressive testing! Besides my comment above everything is looking good. I did some more testing as well. On another note, smart proxy syncing seems to work as well!

Once you reply about the only issue I found above, I can do my FinalFinalV2.txt review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants