Skip to content

Instantly share code, notes, and snippets.

@sjha4
Created March 19, 2026 18:43
Show Gist options
  • Select an option

  • Save sjha4/82c4bf25248dd44d3bc0f2d1b05cfd36 to your computer and use it in GitHub Desktop.

Select an option

Save sjha4/82c4bf25248dd44d3bc0f2d1b05cfd36 to your computer and use it in GitHub Desktop.
Vendored OpenAPI Specs with Dev-Time Generation

Implementation Plan: Vendored OpenAPI Specs with Dev-Time Generation

Context

Problem: Katello depends on 9 pre-packaged Pulp Ruby client gems that the Pulp team struggles to maintain and package. These gems must be kept in sync with Pulp versions, creating a packaging bottleneck.

Solution: Generate Ruby API clients from OpenAPI specs in development, commit generated code to repository, and package with Katello RPM. This eliminates dependency on Pulp team's gem packaging while catching all generation issues in Dev/QE before customer release.

Why This Approach:

  • Pulp upgrades occur every 3-6 months (manageable manual process)
  • Generation issues caught in Dev/QE, not on customer systems
  • Customers get pre-generated, tested code with zero startup delay
  • Complete control over when and how API clients are updated
  • Eliminates 9 gem RPM dependencies, simplifies packaging

Current Architecture

Pulp Client Gems (9 total):

  • pulpcore_client, pulp_file_client, pulp_ansible_client, pulp_container_client, pulp_deb_client, pulp_rpm_client, pulp_certguard_client, pulp_python_client, pulp_ostree_client
  • All pinned to specific minor versions (e.g., >= 3.85.0, < 3.86.0)

Integration Points:

  • /home/sajha/mnt1/katello/katello.gemspec - Defines gem dependencies
  • /home/sajha/mnt1/katello/app/models/katello/concerns/smart_proxy_extensions.rb:372-398 - pulp3_configuration() configures API clients with auth, SSL, timeouts
  • /home/sajha/mnt1/katello/app/services/katello/pulp3/api/core.rb - Base class, instantiates PulpcoreClient::ApiClient and provides common methods
  • /home/sajha/mnt1/katello/app/services/katello/pulp3/api/{yum,apt,docker,file,ansible_collection,content_guard}.rb - Type-specific classes extend Core, instantiate specialized APIs
  • /home/sajha/mnt1/katello/lib/katello/repository_types/{yum,deb,docker,file,ansible_collection,python,ostree}.rb - Register client module classes, API classes, configuration classes
  • /home/sajha/mnt1/katello/lib/monkeys/pulp_polymorphic_remote_response.rb - Monkey patches for polymorphic remote responses

Current Client Instantiation Pattern:

# In Api::Core
def api_client_class(client)
  client.default_headers['Correlation-ID'] = request_id if request_id
  client
end

# In repository types
client_module_class PulpRpmClient
api_class PulpRpmClient::ApiClient
configuration_class PulpRpmClient::Configuration

# In type-specific APIs (e.g., Api::Yum)
def copy_api
  PulpRpmClient::RpmCopyApi.new(api_client)
end

Target Architecture

What Changes

  • Remove: 9 Pulp client gem dependencies from katello.gemspec
  • Add: Vendored OpenAPI specs in vendor/pulp_openapi_specs/
  • Add: Generated Ruby client code in lib/pulp_generated_clients/
  • Add: Rake tasks for fetching specs and regenerating clients
  • Modify: Repository type registrations to require generated clients
  • Update: katello.spec (RPM packaging) to include generated code

What Stays the Same

  • SmartProxy configuration methods (no changes needed)
  • API class structure (Api::Core, Api::Yum, etc.)
  • Client instantiation pattern (same module names, same API classes)
  • All business logic using Pulp APIs

Generated Code Structure

lib/pulp_generated_clients/
├── pulpcore/
│   └── lib/
│       ├── pulpcore_client.rb              # Main module
│       ├── pulpcore_client/api/
│       │   ├── api_client.rb
│       │   ├── artifacts_api.rb
│       │   └── repositories_api.rb
│       ├── pulpcore_client/models/
│       └── pulpcore_client/configuration.rb
├── pulp_rpm/
│   └── lib/
│       ├── pulp_rpm_client.rb
│       ├── pulp_rpm_client/api/
│       │   ├── api_client.rb
│       │   ├── remotes_rpm_api.rb
│       │   └── repositories_rpm_api.rb
│       └── pulp_rpm_client/models/
└── ... (similar for 7 other plugins)

Implementation Approach

Key Benefits:

  • Generation issues caught in Dev/QE - Never on customer systems
  • Zero customer startup delay - Pre-generated code ready to use
  • No runtime dependencies - No openapi-generator on customer systems
  • Easy rollback - Git revert if issues found
  • QE validation - Tests run against exact code customers get
  • Simpler packaging - Eliminates 9 gem RPM dependencies

Trade-offs:

  • Larger git repository (~30MB generated code)
  • Larger git diffs during regeneration (~30K lines)
  • Manual regeneration needed (every 3-6 months with Pulp upgrades)

Team Task Breakdown

This project divides into 4 parallel workstreams that can be distributed to different team members/agents:

Agent 1: Infrastructure & Rake Tasks

  • Create vendored specs directory structure
  • Implement rake tasks for fetching specs from Pulp
  • Implement rake tasks for generating clients from specs
  • Set up proper error handling and logging

Agent 2: Client Generation & Integration

  • Configure openapi-generator for Ruby client generation
  • Generate initial clients from current Pulp version
  • Verify generated code structure matches expected pattern
  • Update autoload paths to include generated clients

Agent 3: Repository Type Updates

  • Update all repository type registrations to use generated clients
  • Verify API class mappings
  • Update monkey patches if needed
  • Test each repository type

Agent 4: Packaging & Documentation

  • Update katello.gemspec (remove Pulp gem dependencies)
  • Update katello.spec for RPM packaging
  • Create Pulp upgrade workflow documentation
  • Document troubleshooting procedures

Detailed Implementation Plan

Phase 1: Infrastructure Setup (Week 1)

Task 1.1: Create Directory Structure (Agent 1)

File to create: N/A (directory creation)

Actions:

cd /home/sajha/mnt1/katello
mkdir -p vendor/pulp_openapi_specs
mkdir -p lib/pulp_generated_clients

Verification:

ls -la vendor/pulp_openapi_specs
ls -la lib/pulp_generated_clients

Deliverable: Empty directories ready for specs and generated code


Task 1.2: Implement Spec Fetcher Rake Task (Agent 1)

File to create: /home/sajha/mnt1/katello/lib/tasks/pulp_client_generation.rake

Requirements:

  • Fetch OpenAPI specs from Pulp primary SmartProxy
  • Support all 9 Pulp plugins
  • Include version in filename
  • Handle HTTP errors gracefully
  • Log progress

Implementation:

namespace :katello do
  namespace :pulp do
    desc "Fetch OpenAPI specs from Pulp instance and save to vendor/"
    task update_specs: :environment do
      require 'json'
      require 'net/http'
      require 'uri'

      smart_proxy = SmartProxy.pulp_primary
      unless smart_proxy
        puts "ERROR: No Pulp primary SmartProxy configured"
        exit 1
      end

      base_url = smart_proxy.pulp3_uri!.to_s
      puts "Fetching specs from: #{base_url}"

      specs = {
        'pulpcore' => "#{base_url}/pulp/api/v3/docs/api.json",
        'pulp_rpm' => "#{base_url}/pulp/api/v3/plugins/rpm/docs/api.json",
        'pulp_file' => "#{base_url}/pulp/api/v3/plugins/file/docs/api.json",
        'pulp_container' => "#{base_url}/pulp/api/v3/plugins/container/docs/api.json",
        'pulp_deb' => "#{base_url}/pulp/api/v3/plugins/deb/docs/api.json",
        'pulp_ansible' => "#{base_url}/pulp/api/v3/plugins/ansible/docs/api.json",
        'pulp_certguard' => "#{base_url}/pulp/api/v3/plugins/certguard/docs/api.json",
        'pulp_python' => "#{base_url}/pulp/api/v3/plugins/python/docs/api.json",
        'pulp_ostree' => "#{base_url}/pulp/api/v3/plugins/ostree/docs/api.json"
      }

      specs.each do |name, url|
        begin
          puts "\nFetching #{name}..."
          spec_json = fetch_spec(url, smart_proxy)
          spec = JSON.parse(spec_json)

          version = spec.dig('info', 'version') || 'unknown'
          filename = Rails.root.join("vendor/pulp_openapi_specs/#{name}-#{version}.json")

          File.write(filename, JSON.pretty_generate(spec))
          puts "✓ Wrote #{filename} (version #{version})"
        rescue => e
          puts "✗ ERROR fetching #{name}: #{e.message}"
          raise
        end
      end

      puts "\n✓ All specs fetched successfully"
    end

    private

    def fetch_spec(url, smart_proxy)
      uri = URI(url)

      http = Net::HTTP.new(uri.host, uri.port)
      http.use_ssl = (uri.scheme == 'https')

      # Use SmartProxy SSL configuration
      if http.use_ssl?
        http.verify_mode = OpenSSL::SSL::VERIFY_PEER
        # Add SSL cert configuration from smart_proxy if needed
      end

      request = Net::HTTP::Get.new(uri.request_uri)

      # Add authentication
      username = smart_proxy.setting('Pulp3', 'username')
      password = smart_proxy.setting('Pulp3', 'password')
      request.basic_auth(username, password) if username && password

      response = http.request(request)

      unless response.code == '200'
        raise "HTTP #{response.code}: #{response.message}"
      end

      response.body
    end
  end
end

Verification:

cd /home/sajha/mnt1/katello
bundle exec rake katello:pulp:update_specs

# Should create files like:
# vendor/pulp_openapi_specs/pulpcore-3.85.0.json
# vendor/pulp_openapi_specs/pulp_rpm-3.32.0.json
# etc.

Deliverable: Rake task that fetches all 9 OpenAPI specs from Pulp

Error Handling:

  • Exit with error if no Pulp primary proxy configured
  • Show clear error messages for HTTP failures
  • Fail fast on first error (don't continue if one fails)

Task 1.3: Implement Client Generator Rake Task (Agent 1)

File to modify: /home/sajha/mnt1/katello/lib/tasks/pulp_client_generation.rake

Requirements:

  • Generate Ruby clients using openapi-generator
  • Output to lib/pulp_generated_clients/{plugin_name}/
  • Configure proper module names
  • Handle generation errors
  • Support incremental regeneration

Add to same file:

namespace :katello do
  namespace :pulp do
    desc "Generate Pulp API clients from vendored OpenAPI specs"
    task generate_clients: :environment do
      require 'fileutils'

      spec_dir = Rails.root.join('vendor/pulp_openapi_specs')
      output_base = Rails.root.join('lib/pulp_generated_clients')

      unless Dir.exist?(spec_dir)
        puts "ERROR: Spec directory not found: #{spec_dir}"
        puts "Run 'rake katello:pulp:update_specs' first"
        exit 1
      end

      specs = Dir.glob(spec_dir.join('*.json')).sort
      if specs.empty?
        puts "ERROR: No spec files found in #{spec_dir}"
        exit 1
      end

      puts "Generating clients from #{specs.count} specs..."
      specs.each do |spec_file|
        generate_client_from_spec(spec_file, output_base)
      end

      puts "\n✓ All clients generated successfully"
      puts "Generated code location: #{output_base}"
    end

    desc "Clear generated clients cache"
    task clear_generated: :environment do
      output_base = Rails.root.join('lib/pulp_generated_clients')
      if Dir.exist?(output_base)
        FileUtils.rm_rf(output_base)
        puts "✓ Cleared #{output_base}"
      else
        puts "Nothing to clear"
      end
    end

    desc "Update specs and regenerate clients (combined task)"
    task update_and_generate: :environment do
      Rake::Task['katello:pulp:update_specs'].invoke
      Rake::Task['katello:pulp:generate_clients'].invoke
    end

    private

    def generate_client_from_spec(spec_path, output_base)
      # Parse filename: pulpcore-3.85.0.json -> pulpcore
      basename = File.basename(spec_path, '.json')
      plugin_name = basename.split('-').first

      puts "\nGenerating #{plugin_name} client..."

      # Determine module name
      module_name = case plugin_name
                    when 'pulpcore'
                      'PulpcoreClient'
                    when 'pulp_rpm'
                      'PulpRpmClient'
                    when 'pulp_file'
                      'PulpFileClient'
                    when 'pulp_container'
                      'PulpContainerClient'
                    when 'pulp_deb'
                      'PulpDebClient'
                    when 'pulp_ansible'
                      'PulpAnsibleClient'
                    when 'pulp_certguard'
                      'PulpCertguardClient'
                    when 'pulp_python'
                      'PulpPythonClient'
                    when 'pulp_ostree'
                      'PulpOstreeClient'
                    else
                      raise "Unknown plugin: #{plugin_name}"
                    end

      output_dir = output_base.join(plugin_name)
      FileUtils.mkdir_p(output_dir)

      # Build openapi-generator command
      cmd = [
        'openapi-generator-cli', 'generate',
        '-i', spec_path,
        '-g', 'ruby',
        '-o', output_dir,
        '--skip-validate-spec',  # Some Pulp specs have minor issues
        "--additional-properties=moduleName=#{module_name}",
        "--additional-properties=gemName=#{plugin_name}",
        '--additional-properties=gemVersion=1.0.0',
        '--additional-properties=gemLicense=GPLv2'
      ].join(' ')

      puts "Running: #{cmd}"

      success = system(cmd)

      unless success
        puts "✗ ERROR: Client generation failed for #{plugin_name}"
        exit 1
      end

      puts "✓ Generated #{module_name} to #{output_dir}"
    end
  end
end

Verification:

# Generate all clients
bundle exec rake katello:pulp:generate_clients

# Should create:
# lib/pulp_generated_clients/pulpcore/lib/pulpcore_client.rb
# lib/pulp_generated_clients/pulp_rpm/lib/pulp_rpm_client.rb
# etc.

# Verify module structure
ls -la lib/pulp_generated_clients/pulpcore/lib/
ls -la lib/pulp_generated_clients/pulp_rpm/lib/

Deliverable: Rake task that generates all 9 Ruby clients from specs

Dependencies:

  • Requires openapi-generator-cli to be installed
  • Installation: gem install openapi_generator or npm install -g @openapitools/openapi-generator-cli

Task 1.4: Configure Client Autoloading (Agent 2)

File to modify: /home/sajha/mnt1/katello/lib/katello/engine.rb

Current state: Engine configuration for Rails autoloading

Changes needed: Add generated client directories to autoload path

Implementation:

# Find the config section (around line 10-30)
# Add after existing autoload_paths configuration:

config.autoload_paths += Dir[
  "#{config.root}/lib/pulp_generated_clients/*/lib"
]

# Also add eager_load_paths for production
config.eager_load_paths += Dir[
  "#{config.root}/lib/pulp_generated_clients/*/lib"
]

Alternative if above doesn't work: Create initializer /home/sajha/mnt1/katello/config/initializers/pulp_generated_clients.rb:

# Load all generated Pulp clients
Rails.application.config.to_prepare do
  Dir.glob(Rails.root.join('lib/pulp_generated_clients', '*', 'lib', '*.rb')).each do |client_file|
    require client_file
  end
end

Verification:

# Start Rails console
bundle exec rails console

# Should be able to access generated classes
> PulpcoreClient::ApiClient
> PulpRpmClient::RemotesRpmApi
> PulpFileClient::RepositoriesFileApi

Deliverable: Rails can load and access all generated client modules


Task 1.5: Verify Generated Code Structure (Agent 2)

Purpose: Ensure generated code matches expected structure for integration

Actions:

  1. Check that each plugin has required classes:

    # For each plugin, verify these exist:
    ls lib/pulp_generated_clients/pulpcore/lib/pulpcore_client.rb
    ls lib/pulp_generated_clients/pulpcore/lib/pulpcore_client/api_client.rb
    ls lib/pulp_generated_clients/pulpcore/lib/pulpcore_client/configuration.rb
    
    ls lib/pulp_generated_clients/pulp_rpm/lib/pulp_rpm_client.rb
    ls lib/pulp_generated_clients/pulp_rpm/lib/pulp_rpm_client/api_client.rb
    # etc.
  2. Check module names match expectations:

    # In Rails console
    PulpcoreClient.constants  # Should include :ApiClient, :Configuration, etc.
    PulpRpmClient.constants
  3. Compare structure to current gems:

    # Check current gem structure
    bundle show pulpcore_client
    ls $(bundle show pulpcore_client)/lib/
    
    # Compare to generated
    ls lib/pulp_generated_clients/pulpcore/lib/
  4. Verify API classes exist:

    PulpcoreClient::ArtifactsApi
    PulpRpmClient::RemotesRpmApi
    PulpFileClient::RepositoriesFileApi
    PulpContainerClient::DistributionsContainerApi

Expected Results:

  • All modules loadable
  • API classes instantiable
  • Configuration classes present
  • Structure matches current gem layout

Deliverable: Verification report confirming generated code structure is compatible


Phase 1 Completion Criteria

  • Directories created: vendor/pulp_openapi_specs/ and lib/pulp_generated_clients/
  • Rake task katello:pulp:update_specs implemented and tested
  • Rake task katello:pulp:generate_clients implemented and tested
  • Rake task katello:pulp:clear_generated implemented
  • Rake task katello:pulp:update_and_generate (combined) implemented
  • All 9 OpenAPI specs fetched and saved to vendor/
  • All 9 Ruby clients generated to lib/pulp_generated_clients/
  • Rails autoload configured for generated clients
  • Generated code structure verified and compatible
  • All generated modules loadable in Rails console
  • Initial commit to git (specs + generated code)

Phase 2: Repository Type Integration (Week 2)

Task 2.1: Update YUM Repository Type (Agent 3)

File to modify: /home/sajha/mnt1/katello/lib/katello/repository_types/yum.rb

Current state:

Katello::RepositoryTypeManager.register(::Katello::Repository::YUM_TYPE) do
  service_class Katello::Pulp3::Repository::Yum
  pulp3_skip_publication false
  pulp3_service_class Katello::Pulp3::Rpm
  pulp3_plugin 'pulp_rpm'
  client_module_class PulpRpmClient  # From gem
  api_class PulpRpmClient::ApiClient
  configuration_class PulpRpmClient::Configuration
  # ... more configuration
end

Changes needed: None if autoloading works correctly

Verification:

  1. Check that PulpRpmClient is accessible without changes:

    # In Rails console
    Katello::RepositoryTypeManager.find(Katello::Repository::YUM_TYPE).client_module
    # Should return PulpRpmClient (from generated code)
  2. If autoloading doesn't work, add explicit require at top of file:

    # At top of yum.rb
    require Rails.root.join('lib/pulp_generated_clients/pulp_rpm/lib/pulp_rpm_client')
  3. Test repository type loading:

    bundle exec rails console
    > type = Katello::RepositoryTypeManager.find('yum')
    > type.client_module_class
    => PulpRpmClient
    > type.api_class
    => PulpRpmClient::ApiClient

Deliverable: YUM repository type works with generated client


Task 2.2: Update All Other Repository Types (Agent 3)

Files to check/modify:

  1. APT/Deb: /home/sajha/mnt1/katello/lib/katello/repository_types/deb.rb

    • Uses: PulpDebClient
    • Verify: Katello::RepositoryTypeManager.find('deb').client_module_class
  2. Docker: /home/sajha/mnt1/katello/lib/katello/repository_types/docker.rb

    • Uses: PulpContainerClient
    • Verify: Katello::RepositoryTypeManager.find('docker').client_module_class
  3. File: /home/sajha/mnt1/katello/lib/katello/repository_types/file.rb

    • Uses: PulpFileClient
    • Verify: Katello::RepositoryTypeManager.find('file').client_module_class
  4. Ansible: /home/sajha/mnt1/katello/lib/katello/repository_types/ansible_collection.rb

    • Uses: PulpAnsibleClient
    • Verify: Katello::RepositoryTypeManager.find('ansible_collection').client_module_class
  5. Python: /home/sajha/mnt1/katello/lib/katello/repository_types/python.rb

    • Uses: PulpPythonClient
    • Verify: Katello::RepositoryTypeManager.find('python').client_module_class
  6. OSTree: /home/sajha/mnt1/katello/lib/katello/repository_types/ostree.rb

    • Uses: PulpOstreeClient
    • Verify: Katello::RepositoryTypeManager.find('ostree').client_module_class

Actions for each:

  1. Verify client module class is accessible
  2. Add explicit require if autoloading fails
  3. Test in Rails console

Verification script:

# test_repository_types.rb
types = [
  { name: 'yum', client: PulpRpmClient },
  { name: 'deb', client: PulpDebClient },
  { name: 'docker', client: PulpContainerClient },
  { name: 'file', client: PulpFileClient },
  { name: 'ansible_collection', client: PulpAnsibleClient },
  { name: 'python', client: PulpPythonClient },
  { name: 'ostree', client: PulpOstreeClient }
]

types.each do |type|
  rt = Katello::RepositoryTypeManager.find(type[:name])
  if rt.client_module_class == type[:client]
    puts "✓ #{type[:name]}: #{type[:client]}"
  else
    puts "✗ #{type[:name]}: Expected #{type[:client]}, got #{rt.client_module_class}"
  end
end

Deliverable: All 7 repository types use generated clients


Task 2.3: Verify API Class Instantiation (Agent 3)

Purpose: Ensure API classes from generated clients work with existing code

Test file: Create test/unit/pulp_generated_clients_test.rb

require 'test_helper'

class PulpGeneratedClientsTest < ActiveSupport::TestCase
  def setup
    @proxy = FactoryBot.create(:smart_proxy, :default_smart_proxy, :with_pulp3)
  end

  test "pulpcore client instantiation" do
    config = @proxy.pulp3_configuration(PulpcoreClient::Configuration)
    assert_instance_of PulpcoreClient::Configuration, config

    client = PulpcoreClient::ApiClient.new(config)
    assert_instance_of PulpcoreClient::ApiClient, client

    api = PulpcoreClient::ArtifactsApi.new(client)
    assert_instance_of PulpcoreClient::ArtifactsApi, api
  end

  test "pulp_rpm client instantiation" do
    config = @proxy.pulp3_configuration(PulpRpmClient::Configuration)
    client = PulpRpmClient::ApiClient.new(config)
    api = PulpRpmClient::RemotesRpmApi.new(client)

    assert_instance_of PulpRpmClient::RemotesRpmApi, api
  end

  test "pulp_file client instantiation" do
    config = @proxy.pulp3_configuration(PulpFileClient::Configuration)
    client = PulpFileClient::ApiClient.new(config)
    api = PulpFileClient::RepositoriesFileApi.new(client)

    assert_instance_of PulpFileClient::RepositoriesFileApi, api
  end

  # Add similar tests for other plugins
end

Run test:

bundle exec rake test TEST=test/unit/pulp_generated_clients_test.rb

Expected: All tests pass

Deliverable: Test suite confirming API classes instantiate correctly


Task 2.4: Test Actual API Integration (Agent 3)

Purpose: Verify generated clients make real API calls to Pulp

Test with File repository (simplest):

bundle exec rails console

# Get Pulp primary proxy
proxy = SmartProxy.pulp_primary

# Create File repository backend service
repo = Katello::Repository.file_type.first || begin
  root = Katello::RootRepository.create!(
    name: 'TestFileRepo',
    product: Katello::Product.first,
    content_type: 'file'
  )
  root.library_instance
end

service = Katello::Pulp3::Repository.new(repo, proxy)
api = service.api  # Should use generated PulpFileClient

# Test API call
begin
  api.repositories_api.list({limit: 1})
  puts "✓ File repository API works"
rescue => e
  puts "✗ Error: #{e.message}"
end

Deliverable: Confirmation that generated clients make successful API calls


Task 2.5: Review and Update Monkey Patches (Agent 3)

File: /home/sajha/mnt1/katello/lib/monkeys/pulp_polymorphic_remote_response.rb

Current purpose: Patches Remote API update methods for polymorphic responses

Action:

  1. Read current monkey patch implementation

  2. Check if generated code has same issue

  3. Test if patch still applies cleanly:

    # Verify patch methods exist in generated code
    bundle exec rails console
    > PulpRpmClient::RemotesRpmApi.instance_methods.include?(:update)
    > PulpAnsibleClient::RemotesCollectionApi.instance_methods.include?(:update)
  4. If patch fails, update for generated code structure

Likely outcome: No changes needed (generated code structure should match)

If changes needed:

# Current patch targets gem classes
# May need to adjust for generated code module structure
# But openapi-generator should produce same structure

Verification:

bundle exec rake test TEST=test/actions/pulp3/repository/update_remote_test.rb

Deliverable: Monkey patches work with generated clients (or updated if needed)


Task 2.6: Run Existing Tests with Generated Clients (Agent 3)

Purpose: Verify generated clients don't break existing functionality

Test suites to run:

  1. Repository tests:

    bundle exec rake test TEST=test/models/repository_test.rb
    bundle exec rake test TEST=test/models/repository/file_test.rb
    bundle exec rake test TEST=test/models/repository/yum_test.rb
  2. Service tests:

    bundle exec rake test TEST=test/services/katello/pulp3/repository_test.rb
  3. Action tests:

    bundle exec rake test TEST=test/actions/katello/repository/sync_test.rb
    bundle exec rake test TEST=test/actions/pulp3/*_test.rb
  4. API tests:

    bundle exec rake test TEST=test/controllers/api/v2/repositories_controller_test.rb

Expected results:

  • All existing tests should pass
  • If failures occur, investigate whether issue is with:
    • Generated code structure
    • Autoloading configuration
    • Missing dependencies
    • Actual incompatibilities

Deliverable: Test results showing compatibility (or documented issues to fix)


Phase 2 Completion Criteria

  • All 7 repository types verified to use generated clients
  • API class instantiation tests created and passing
  • Live API calls work with generated clients (tested with File repository)
  • Monkey patches reviewed and updated if needed
  • Existing repository model tests pass
  • Existing service tests pass
  • Existing action tests pass
  • Existing API controller tests pass
  • Any issues documented with resolution plans

Phase 3: Comprehensive Testing (Week 3)

Task 3.1: Test All Content Types (Agent 3)

Purpose: Verify each content type works end-to-end with generated clients

Test matrix:

Content Type Repository Sync Create Content Publish
YUM
Deb
Docker
File
Ansible
Python
OSTree

Test script for each type:

# test_yum_repository.rb
repo = Katello::Repository.yum_type.first || create_yum_repo
service = repo.backend_service(SmartProxy.pulp_primary)

# Test repository creation
service.create

# Test sync (if has remote)
service.sync if repo.url

# Test content operations
service.api.repositories_api.list

# Test publication
service.create_publication

puts "✓ YUM repository operations successful"

Repeat for each content type.

Run tests:

bundle exec rake test TEST=test/models/repository/yum_test.rb
bundle exec rake test TEST=test/models/repository/deb_test.rb
bundle exec rake test TEST=test/models/repository/docker_test.rb
bundle exec rake test TEST=test/models/repository/file_test.rb

Deliverable: Verification that all content types work


Task 3.2: VCR Cassette Review and Regeneration (Agent 3)

Purpose: Update VCR cassettes if request/response structure changed

Background: VCR records HTTP interactions. If generated clients format requests differently, cassettes need regeneration.

Check for VCR cassette failures:

bundle exec rake test:katello 2>&1 | grep "VCR::Errors"

If cassettes need updating:

# Regenerate all Pulp-related cassettes
VCR_RECORD=all bundle exec rake test TEST=test/actions/pulp3/

# Or regenerate specific cassettes
VCR_RECORD=all bundle exec rake test TEST=test/actions/pulp3/repository/create_test.rb

Cassette locations:

  • test/fixtures/vcr_cassettes/actions/pulp3/
  • test/fixtures/vcr_cassettes/services/pulp3/

Validation:

  1. Run tests with regenerated cassettes
  2. Check cassette diffs - should only be formatting differences, not functional changes
  3. Commit updated cassettes with clear message

Deliverable: All VCR cassettes updated and tests passing


Task 3.3: Full Test Suite Run (All Agents)

Purpose: Ensure no regressions across entire codebase

Run complete Katello test suite:

cd /home/sajha/mnt1/katello
bundle exec rake test:katello

Expected duration: 30-60 minutes

Monitor for:

  • Pulp client-related failures
  • Repository operation failures
  • Sync/publish failures
  • API endpoint failures

If failures occur:

  1. Categorize by type (client instantiation, API calls, configuration, etc.)
  2. Create GitHub issues for each category
  3. Prioritize by severity
  4. Fix critical issues before proceeding

Deliverable: Full test suite passing (or documented acceptable failures)


Task 3.4: JavaScript/React Tests (Agent 3)

Purpose: Verify frontend still works (may use Pulp APIs via backend)

Run JavaScript tests:

cd /home/sajha/mnt1/katello
npm test

Expected: No impact (frontend doesn't directly use Ruby clients)

If failures occur: Investigate backend API endpoints that frontend depends on

Deliverable: JavaScript tests passing


Task 3.5: Integration Test in Development Environment (Agent 3)

Purpose: Manual end-to-end testing

Test scenarios:

1. YUM Repository:

# Create product and repository
product = Katello::Product.create!(
  name: 'Test Product',
  organization: Organization.first,
  provider: Provider.first
)

root = Katello::RootRepository.create!(
  name: 'Test YUM Repo',
  product: product,
  content_type: 'yum',
  url: 'https://fixtures.pulpproject.org/rpm-unsigned/'
)

repo = root.library_instance

# Sync repository
ForemanTasks.sync_task(Actions::Katello::Repository::Sync, repo)

# Verify content synced
puts "Packages: #{repo.rpms.count}"
puts "Errata: #{repo.errata.count}"

2. Docker Repository:

# Create Docker repository
docker_repo = create_docker_repository
ForemanTasks.sync_task(Actions::Katello::Repository::Sync, docker_repo)
puts "Manifests: #{docker_repo.docker_manifests.count}"

3. File Repository:

file_repo = create_file_repository
ForemanTasks.sync_task(Actions::Katello::Repository::Sync, file_repo)
puts "Files: #{file_repo.files.count}"

Deliverable: Manual testing confirmation for key workflows


Task 3.6: Performance Comparison (Agent 3)

Purpose: Ensure generated clients don't introduce performance regression

Benchmark script:

# benchmark_pulp_clients.rb
require 'benchmark'

repo = Katello::Repository.yum_type.first
service = repo.backend_service(SmartProxy.pulp_primary)

# Benchmark API calls
Benchmark.bm do |x|
  x.report("list repositories") do
    100.times { service.api.repositories_api.list(limit: 10) }
  end

  x.report("list remotes") do
    100.times { service.api.remotes_api.list(limit: 10) }
  end
end

Baseline: Compare with current gem-based performance Acceptable: Within 5% of baseline

Deliverable: Performance report showing no significant regression


Phase 3 Completion Criteria

  • All 7 content types tested individually
  • Repository create/sync/publish works for each type
  • VCR cassettes reviewed and regenerated if needed
  • Full Katello test suite passing
  • JavaScript test suite passing
  • Manual integration tests successful for YUM, Docker, File
  • Performance benchmarks acceptable
  • All test failures documented and addressed

Phase 4: Packaging and Documentation (Week 4)

Task 4.1: Update katello.gemspec (Agent 4)

File to modify: /home/sajha/mnt1/katello/katello.gemspec

Current dependencies (lines 52-63):

s.add_runtime_dependency 'pulpcore_client', '>= 3.85.0', '< 3.86.0'
s.add_runtime_dependency 'pulp_file_client', '>= 3.85.0', '< 3.86.0'
s.add_runtime_dependency 'pulp_ansible_client', '>= 0.28.0', '< 0.29.0'
s.add_runtime_dependency 'pulp_container_client', '>= 2.26.0', '< 2.27.0'
s.add_runtime_dependency 'pulp_deb_client', '>= 3.8.0', '< 3.9.0'
s.add_runtime_dependency 'pulp_rpm_client', '>= 3.32.0', '< 3.33.0'
s.add_runtime_dependency 'pulp_certguard_client', '>= 3.85.0', '< 3.86.0'
s.add_runtime_dependency 'pulp_python_client', '>= 3.19.0', '< 3.20.0'
s.add_runtime_dependency 'pulp_ostree_client', '>= 2.5.0', '< 2.6.0'

Changes:

# DELETE all 9 Pulp client dependencies above

# ADD development dependency for regeneration
s.add_development_dependency 'openapi_generator', '~> 6.0'

Verification:

bundle install
# Should NOT pull in pulp client gems anymore
bundle list | grep pulp
# Should show nothing (or only local generated code)

Deliverable: katello.gemspec updated, no Pulp gem dependencies


Task 4.2: Update RPM Spec File (Agent 4)

File to modify: Packaging repository's katello.spec file

Location: Usually in separate packaging repository (foreman-packaging or katello-packaging)

Current requirements:

Requires: rubygem(pulpcore_client) >= 3.85.0
Requires: rubygem(pulpcore_client) < 3.86.0
Requires: rubygem(pulp_rpm_client) >= 3.32.0
Requires: rubygem(pulp_rpm_client) < 3.33.0
# ... (7 more similar lines)

Changes:

# DELETE all Pulp client gem requirements

# REMOVE from BuildRequires if present:
# BuildRequires: rubygem(pulpcore_client)
# etc.

# UPDATE %files section - ADD these lines:
%{katello_dir}/lib/pulp_generated_clients/
%{katello_dir}/vendor/pulp_openapi_specs/

# If using bundler-style installation, ensure these directories are included

Note: Exact spec file location and syntax depends on packaging approach. Coordinate with packaging team.

Deliverable: RPM spec updated to include generated clients


Task 4.3: Create Pulp Upgrade Workflow Documentation (Agent 4)

File to create: /home/sajha/mnt1/katello/docs/pulp_client_update_workflow.md

Content:

# Pulp API Client Update Workflow

This document describes how to update Pulp API clients when upgrading Pulp.

## Overview

Katello uses Ruby API clients generated from Pulp's OpenAPI specifications. These clients are:
- Generated from vendored OpenAPI spec files in `vendor/pulp_openapi_specs/`
- Committed to the repository in `lib/pulp_generated_clients/`
- Packaged with Katello RPM (no separate gem dependencies)

## When to Update

Update clients when:
- Upgrading Pulp to a new version
- New Pulp plugin installed
- Pulp API changes require new features

**Typical frequency:** Every 3-6 months with Pulp upgrades

## Prerequisites

- Development environment with Pulp installed and running
- Pulp upgraded to target version
- `openapi-generator-cli` installed (`gem install openapi_generator`)

## Update Procedure

### Step 1: Upgrade Pulp in Development

```bash
# Upgrade Pulp to new version (example)
sudo dnf update pulp-server
systemctl restart pulpcore pulpcore-api pulpcore-content

Verify Pulp is running:

curl -u admin:password https://$(hostname)/pulp/api/v3/status/

Step 2: Fetch New OpenAPI Specs

cd /home/sajha/mnt1/katello

# Fetch updated specs from upgraded Pulp instance
bundle exec rake katello:pulp:update_specs

This will create files like:

  • vendor/pulp_openapi_specs/pulpcore-3.86.0.json (new version)
  • vendor/pulp_openapi_specs/pulp_rpm-3.33.0.json (new version)
  • etc.

Step 3: Review Spec Changes

# See what changed in the specs
git diff vendor/pulp_openapi_specs/

# Look for:
# - New API endpoints
# - Removed endpoints
# - Changed parameter types
# - New required fields

Important changes to note:

  • New required parameters (may break existing code)
  • Removed endpoints (need to update Katello code)
  • Changed response formats (may affect parsing)

Step 4: Regenerate Clients

# Clear old generated code and regenerate
bundle exec rake katello:pulp:clear_generated
bundle exec rake katello:pulp:generate_clients

Or use combined task:

bundle exec rake katello:pulp:update_and_generate

Generation takes 2-5 minutes. Watch for errors.

Step 5: Review Generated Code Changes

# See what changed in generated clients
git diff lib/pulp_generated_clients/ | less

# Focus on:
# - New API classes
# - Modified method signatures
# - Changed model attributes

Large diffs are normal (~30K lines). Focus on:

  • Method signature changes in APIs you use
  • New required parameters
  • Deprecated methods

Step 6: Update Katello Code if Needed

If APIs changed, update Katello code:

Example: New required parameter added

# Before
service.api.remotes_api.update(remote_href, remote_data)

# After (with new required parameter)
service.api.remotes_api.update(remote_href, remote_data, validate: true)

Check these locations:

  • /home/sajha/mnt1/katello/app/services/katello/pulp3/
  • /home/sajha/mnt1/katello/app/models/katello/concerns/
  • /home/sajha/mnt1/katello/lib/katello/repository_types/

Step 7: Run Tests

# Run full test suite
bundle exec rake test:katello

# Pay attention to:
# - Repository sync tests
# - Content creation tests
# - Publication tests

If tests fail:

  1. Check error messages for API-related issues
  2. Update code to match new API requirements
  3. Regenerate VCR cassettes if needed: VCR_RECORD=all bundle exec rake test:katello

Step 8: Manual Testing

Test key workflows in development:

bundle exec rails console

# Test YUM repository sync
repo = Katello::Repository.yum_type.first
ForemanTasks.sync_task(Actions::Katello::Repository::Sync, repo)

# Test Docker repository
docker_repo = Katello::Repository.docker_type.first
ForemanTasks.sync_task(Actions::Katello::Repository::Sync, docker_repo)

# Test content view publish
cv = Katello::ContentView.first
ForemanTasks.sync_task(Actions::Katello::ContentView::Publish, cv)

Step 9: Commit Changes

git status
# Should show changes in:
# - vendor/pulp_openapi_specs/
# - lib/pulp_generated_clients/
# - (optionally) Katello code if APIs changed

git add vendor/pulp_openapi_specs/ lib/pulp_generated_clients/

# If you updated Katello code
git add app/services/ app/models/ lib/

git commit -m "Update Pulp API clients to Pulp 3.X

- Updated OpenAPI specs from Pulp 3.X.Y
- Regenerated Ruby clients
- New APIs: [list any new endpoints if applicable]
- Breaking changes: [list any breaking changes]
- Updated Katello code for API changes in [affected areas]
"

Step 10: Create Pull Request

git push origin pulp-3.x-upgrade

Create PR with:

  • Description of Pulp version upgrade
  • Summary of API changes
  • Test results
  • Any manual testing performed

PR review checklist:

  • OpenAPI spec versions match target Pulp version
  • Generated client code compiles (no syntax errors)
  • Test suite passes
  • Manual testing successful
  • Breaking changes documented
  • Katello code updated for API changes

Step 11: Merge and Release

After PR approval:

  1. Merge to main branch
  2. Build RPM with updated clients
  3. Test RPM installation
  4. Release to customers

Troubleshooting

Generation Fails

Error: openapi-generator-cli: command not found

Solution:

gem install openapi_generator
# or
npm install -g @openapitools/openapi-generator-cli

Spec Fetch Fails

Error: ERROR fetching pulp_rpm: Connection refused

Solution:

  • Verify Pulp is running: systemctl status pulpcore
  • Check Pulp URL in SmartProxy settings
  • Verify credentials

Tests Fail After Regeneration

Common causes:

  1. API parameters changed - update Katello code
  2. VCR cassettes out of date - regenerate: VCR_RECORD=all
  3. Response format changed - update parsing code
  4. Endpoint removed - find alternative API

Large Git Diff

Expected: Generated code diffs can be 20K-40K lines

What to review:

  • Don't review every line
  • Focus on spec file changes (small, readable)
  • Scan generated code for obvious errors
  • Rely on tests to catch issues

Rollback Procedure

If issues found after upgrade:

# Revert to previous specs and clients
git revert <commit-hash>

# Or manually:
git checkout HEAD~1 vendor/pulp_openapi_specs/
git checkout HEAD~1 lib/pulp_generated_clients/

# Regenerate from old specs
bundle exec rake katello:pulp:generate_clients

# Test and commit
bundle exec rake test:katello
git commit -m "Rollback Pulp clients to previous version"

References

  • Pulp OpenAPI docs: https://<pulp-server>/pulp/api/v3/docs/
  • OpenAPI Generator: https://openapi-generator.tech/
  • Katello Pulp3 integration: /home/sajha/mnt1/katello/app/services/katello/pulp3/

**Deliverable:** Complete workflow documentation for Pulp upgrades

---

#### Task 4.4: Create Troubleshooting Guide (Agent 4)

**File to create:** `/home/sajha/mnt1/katello/docs/pulp_generated_clients_troubleshooting.md`

**Content:**
```markdown
# Pulp Generated Clients Troubleshooting

## Common Issues

### Client Class Not Found

**Error:** `uninitialized constant PulpRpmClient`

**Causes:**
- Generated code not loaded
- Autoload path not configured
- Code not generated yet

**Solutions:**
1. Check if generated code exists:
   ```bash
   ls lib/pulp_generated_clients/pulp_rpm/lib/
  1. Regenerate if missing:

    bundle exec rake katello:pulp:generate_clients
  2. Verify autoload configuration in lib/katello/engine.rb

  3. Restart Rails server

API Method Not Found

Error: undefined method 'some_api_method' for PulpRpmClient::RemotesRpmApi

Causes:

  • Method removed in new Pulp version
  • Wrong API class
  • Generated code out of date

Solutions:

  1. Check OpenAPI spec for method:

    grep -r "some_api_method" vendor/pulp_openapi_specs/
  2. Find new method in Pulp docs: https://<pulp>/pulp/api/v3/docs/

  3. Update Katello code to use new API

Module Name Conflicts

Error: TypeError: superclass mismatch for class Configuration

Cause: Module name collision between different plugins

Solution: Check generated code module structure - should be namespaced:

  • PulpcoreClient::Configuration
  • PulpRpmClient::Configuration
  • etc.

Performance Issues

Symptom: Slow API calls, timeouts

Debugging:

  1. Enable debug logging in SmartProxy configuration
  2. Check Pulp server performance
  3. Verify network connectivity
  4. Check Correlation-ID in logs for tracing

Development Issues

Regeneration Hangs

Symptom: rake katello:pulp:generate_clients doesn't complete

Solutions:

  1. Check openapi-generator process: ps aux | grep openapi
  2. Kill stuck processes: pkill -f openapi
  3. Clear output directory: rm -rf lib/pulp_generated_clients/*
  4. Try again

Git Conflicts in Generated Code

Symptom: Merge conflicts in lib/pulp_generated_clients/

Solution:

# Accept one side
git checkout --theirs lib/pulp_generated_clients/

# OR accept other side
git checkout --ours lib/pulp_generated_clients/

# Then regenerate from merged specs
git checkout --theirs vendor/pulp_openapi_specs/  # or --ours
bundle exec rake katello:pulp:generate_clients
git add lib/pulp_generated_clients/

Testing Issues

VCR Cassette Mismatches

Error: VCR::Errors::UnhandledHTTPRequestError

Cause: API request changed, cassette doesn't match

Solution:

# Regenerate specific cassette
VCR_RECORD=all bundle exec rake test TEST=path/to/specific_test.rb

# Or regenerate all
VCR_RECORD=all bundle exec rake test:katello

Monkey Patch Failures

Error: Methods not found when applying patches

Cause: Generated code structure changed

Solution:

  1. Review /home/sajha/mnt1/katello/lib/monkeys/pulp_polymorphic_remote_response.rb
  2. Check if patched methods still exist in generated code
  3. Update patch for new structure or remove if no longer needed

Production Issues

Startup Failures

Should not happen with this approach (code pre-generated)

If it does:

  1. Check file permissions on lib/pulp_generated_clients/
  2. Verify all files packaged in RPM
  3. Check Rails logs for load errors

Runtime API Errors

Error: PulpRpmClient::ApiError: 404 Not Found

Debugging:

  1. Check Pulp server status
  2. Verify endpoint exists in current Pulp version
  3. Check Correlation-ID in logs
  4. Compare client code version with Pulp version

Getting Help

  1. Check generated code matches expectations
  2. Review Pulp API documentation
  3. Check Katello integration code
  4. Ask in #katello-dev
  5. Create GitHub issue with:
    • Pulp version
    • Generated client version
    • Error message and stack trace
    • Steps to reproduce

**Deliverable:** Comprehensive troubleshooting guide

---

#### Task 4.5: Update README and Development Docs (Agent 4)

**File to modify:** `/home/sajha/mnt1/katello/README.md`

**Add section:**
```markdown
## Pulp API Clients

Katello uses Ruby API clients for Pulp that are generated from OpenAPI specifications.

**Key files:**
- `vendor/pulp_openapi_specs/` - OpenAPI spec JSON files (version controlled)
- `lib/pulp_generated_clients/` - Generated Ruby clients (committed to repo)

**For normal development:** You don't need to regenerate clients.

**When updating Pulp:** See [Pulp Client Update Workflow](docs/pulp_client_update_workflow.md)

**Troubleshooting:** See [Troubleshooting Guide](docs/pulp_generated_clients_troubleshooting.md)

Deliverable: README updated with client information


Task 4.6: Create Git Ignore Rules (Agent 4)

File to modify: /home/sajha/mnt1/katello/.gitignore

Add (if needed):

# Do NOT ignore generated clients - they're committed
# lib/pulp_generated_clients/  # <-- Don't add this

# Ignore temporary generation files
vendor/pulp_openapi_specs/*.tmp
lib/pulp_generated_clients/**/.openapi-generator/

Deliverable: Git ignore configured


Phase 4 Completion Criteria

  • katello.gemspec updated (9 Pulp gem dependencies removed)
  • Development dependency on openapi_generator added
  • RPM spec file updated (coordinate with packaging team)
  • Pulp upgrade workflow documentation complete
  • Troubleshooting guide complete
  • README updated with client information
  • Git ignore rules configured
  • Test RPM build successful (if packaging access available)
  • Documentation reviewed and approved

Generated Code Structure

Directory layout:

lib/pulp_generated_clients/
├── pulpcore/
│   ├── lib/
│   │   ├── pulpcore_client.rb              # Main module
│   │   ├── pulpcore_client/api/
│   │   │   ├── artifacts_api.rb
│   │   │   ├── repositories_api.rb
│   │   │   └── ...
│   │   ├── pulpcore_client/models/
│   │   │   ├── artifact.rb
│   │   │   ├── repository.rb
│   │   │   └── ...
│   │   └── pulpcore_client/configuration.rb
│   └── pulpcore_client.gemspec             # Metadata
├── pulp_rpm/
│   ├── lib/
│   │   ├── pulp_rpm_client.rb
│   │   ├── pulp_rpm_client/api/
│   │   │   ├── remotes_rpm_api.rb
│   │   │   ├── repositories_rpm_api.rb
│   │   │   └── ...
│   │   └── ...
│   └── pulp_rpm_client.gemspec
└── ... (similar for other plugins)

Module structure matches current gems:

  • PulpcoreClient::ApiClient
  • PulpcoreClient::Configuration
  • PulpRpmClient::RemotesRpmApi
  • etc.

Loading generated code:

# In lib/katello.rb or engine.rb
Dir.glob(File.join(__dir__, 'pulp_generated_clients', '*', 'lib', '*.rb')).each do |client|
  require client
end

Pulp Upgrade Workflow

Every 3-6 months when Pulp is upgraded:

Step 1: Update Pulp in Development

# Upgrade Pulp in your dev environment
sudo dnf update pulp-server
systemctl restart pulpcore

Step 2: Fetch New OpenAPI Specs

cd /home/sajha/mnt1/katello

# Fetch updated specs from upgraded Pulp
bundle exec rake katello:pulp:update_specs

# Review what changed
git diff vendor/pulp_openapi_specs/

Step 3: Regenerate Clients

# Regenerate from new specs
bundle exec rake katello:pulp:generate_clients

# Review generated code changes
git diff lib/pulp_generated_clients/ | less
# Look for breaking changes, new APIs, removed APIs

Step 4: Test Locally

# Start Foreman
bundle exec foreman start

# Run tests
bundle exec rake test:katello

# Manual testing
bundle exec rails console
> # Test each repository type
> SmartProxy.pulp_primary.pulp3_api(PulpRpmClient::ApiClient)

Step 5: Commit and Push

git add vendor/pulp_openapi_specs/ lib/pulp_generated_clients/
git commit -m "Update Pulp API clients to Pulp 3.X

- Updated OpenAPI specs from Pulp 3.X
- Regenerated Ruby clients
- New APIs: [list any new endpoints]
- Breaking changes: [list any breaking changes]
"

git push origin pulp-3.x-upgrade

Step 6: PR and Review

  • Create PR for review
  • CI/CD runs full test suite
  • QE tests the exact generated code
  • Code review can see what changed in generated clients

Step 7: Merge and Release

  • Merge to main
  • Build RPM with updated clients
  • Release to customers
  • Zero customer impact - they just get the new RPM

RPM Packaging Details

Current RPM Structure (9 Gem Dependencies)

katello.spec:

Requires: rubygem(pulpcore_client) >= 3.85.0, rubygem(pulpcore_client) < 3.86.0
Requires: rubygem(pulp_rpm_client) >= 3.32.0, rubygem(pulp_rpm_client) < 3.33.0
... (7 more)

Total download for customers:

  • Katello RPM: ~10MB
  • 9 Pulp client gem RPMs: ~50MB
  • Total: ~60MB

Installation process:

yum install katello
# Resolves 9 Pulp gem dependencies
# Downloads all 9 gems
# Installs Katello + 9 gem RPMs

New RPM Structure (Vendored Approach - Variant B)

katello.spec:

# No Pulp gem dependencies!

%files
%{katello_bundledir}/lib/pulp_generated_clients/
%{katello_bundledir}/vendor/pulp_openapi_specs/

Total download for customers:

  • Katello RPM: ~40MB (includes generated clients + specs)
  • Total: ~40MB

Installation process:

yum install katello
# No additional dependencies
# Installs single RPM
# Foreman starts normally, zero generation

RPM Size Comparison

Component Current Variant A Variant B
Katello RPM 10MB 15MB 40MB
Pulp client gems 9 × ~5MB = 45MB 0MB 0MB
openapi-generator 0MB ~5MB 0MB
Total Download 60MB 20MB 40MB
Number of RPMs 10 2 1
Startup Delay None 5-15s first time None

Variant B wins on:

  • Fewer RPM packages (simpler dependency tree)
  • Moderate download size (between current and Variant A)
  • Zero startup delay
  • Zero runtime dependencies

Risk Assessment and Mitigation

Risk 1: Generated Code Doesn't Match Existing Patterns

Risk Level: Medium

Description: Generated code structure might differ from current gem structure, breaking existing code.

Mitigation:

  1. Generate clients in Phase 1, test with one repository type before proceeding
  2. Compare generated code structure to current gem structure
  3. Adjust openapi-generator config if needed (--additional-properties)
  4. Monkey patches can bridge any gaps
  5. Rollback is easy (git revert, use old gems temporarily)

Risk 2: Large Git Diffs Difficult to Review

Risk Level: Low

Description: 30K lines of generated code changes hard to review in PRs.

Mitigation:

  1. Review OpenAPI spec diffs first (small, readable)
  2. Focus PR review on:
    • Spec changes
    • Test results
    • Any manual code changes
  3. Generated code review is optional (trust the generator)
  4. Can use [skip ci] for pure regeneration commits

Risk 3: Forgetting to Regenerate After Spec Updates

Risk Level: Low

Description: Developer updates specs but forgets to regenerate clients.

Mitigation:

  1. Document workflow clearly
  2. Add CI check that compares spec versions to generated code versions
  3. Pre-commit hook that detects spec changes and reminds to regenerate
  4. Rake task that does both: rake katello:pulp:update_and_generate

Risk 4: openapi-generator Breaking Changes

Risk Level: Low

Description: openapi-generator updates might change generated code structure.

Mitigation:

  1. Pin openapi-generator version in Gemfile.lock
  2. Test generator upgrades in dev before committing
  3. Generated code is in git, so changes are visible
  4. Can revert to previous generator version if needed

Risk 5: Merge Conflicts in Generated Code

Risk Level: Medium

Description: Multiple branches updating generated code creates conflicts.

Mitigation:

  1. Coordinate Pulp updates (one branch at a time)
  2. If conflicts occur, regenerate from merged specs:
    git checkout --theirs vendor/pulp_openapi_specs/
    bundle exec rake katello:pulp:generate_clients
  3. Generated code conflicts are easy to resolve (just regenerate)

Success Criteria

Phase 1 Success (Week 1)

  • Vendored spec directory created
  • All 9 OpenAPI specs fetched and committed
  • Rake tasks implemented and tested
  • Initial client generation successful
  • Generated code structure reviewed and approved

Phase 2 Success (Week 2)

  • Repository type registrations updated
  • Generated clients loaded correctly
  • One repository type (File) fully tested
  • API calls work with generated clients
  • Monkey patches reviewed/updated if needed

Phase 3 Success (Week 3)

  • All repository types updated
  • Full test suite passes
  • VCR cassettes regenerated if needed
  • Manual testing across all content types
  • Performance comparable to gem-based approach

Phase 4 Success (Week 4)

  • katello.gemspec cleaned up (no Pulp gem deps)
  • RPM spec updated
  • Documentation complete
  • Test RPM builds successfully
  • Test RPM installs and runs correctly

Production Success Criteria

  • Zero startup delay on customer systems
  • All content types work correctly
  • No generation errors in production
  • Easy Pulp upgrade workflow
  • Positive feedback from QE and customers

Timeline

Total Duration: 3-4 weeks

Week Phase Key Deliverables
1 Setup Specs vendored, rake tasks, initial generation
2 Integration Repository types updated, one type fully tested
3 Testing All types tested, test suite passes, VCR updated
4 Cleanup Gemspec/RPM updated, docs complete, ready for release

Buffer: Add 1 week for unexpected issues, edge cases, or additional testing.


Summary for Team Assignment

This plan implements vendored OpenAPI spec approach with dev-time code generation for Pulp API clients.

Approach: Generate Ruby clients from OpenAPI specs in development, commit generated code to repository, package with Katello RPM.

Key Benefits:

  • ✅ Generation issues caught in Dev/QE (never on customer systems)
  • ✅ Zero customer startup delay
  • ✅ Eliminates 9 gem RPM dependencies
  • ✅ Easy rollback via git revert
  • ✅ QE tests exact code customers receive

Timeline: 3-4 weeks across 4 phases

Team Distribution:

  • Agent 1: Infrastructure and rake tasks (Phase 1)
  • Agent 2: Client generation and integration (Phase 1)
  • Agent 3: Repository type updates and testing (Phases 2-3)
  • Agent 4: Packaging and documentation (Phase 4)

Prerequisites:

  • Development environment with Pulp installed
  • openapi-generator-cli installed (gem install openapi_generator)
  • Access to Pulp primary SmartProxy for fetching specs

Critical Success Factors:

  1. Generated code structure matches current gem structure
  2. All existing tests pass with generated clients
  3. Documentation clear for future Pulp upgrades
  4. RPM packaging updated correctly

Start with: Phase 1, Task 1.1 - Create directory structure

@sjha4
Copy link
Copy Markdown
Author

sjha4 commented Mar 20, 2026

1st pass impementation results:
● 🎉 Success! All 9 Pulp API Clients Generated!

The vendored OpenAPI specs implementation is now complete. Here's what was accomplished:

Generated Clients

All 9 Ruby API clients have been successfully generated to /home/vagrant/katello/lib/pulp_generated_clients/:

  1. ✅ pulpcore_client - PulpcoreClient module
  2. ✅ pulp_rpm_client - PulpRpmClient module
  3. ✅ pulp_file_client - PulpFileClient module
  4. ✅ pulp_container_client - PulpContainerClient module
  5. ✅ pulp_deb_client - PulpDebClient module
  6. ✅ pulp_ansible_client - PulpAnsibleClient module
  7. ✅ pulp_certguard_client - PulpCertguardClient module
  8. ✅ pulp_python_client - PulpPythonClient module
  9. ✅ pulp_ostree_client - PulpOstreeClient module

What Changed

Created:

  • vendor/pulp_openapi_specs/ - 9 OpenAPI spec JSON files (~3.7 MB)
  • lib/pulp_generated_clients/ - 9 generated Ruby client libraries
  • lib/katello/tasks/pulp_client_generation.rake - Rake tasks for regeneration
  • docs/pulp_client_update_workflow.md - Upgrade workflow documentation
  • docs/pulp_generated_clients_troubleshooting.md - Troubleshooting guide
  • docs/rpm_spec_changes.md - RPM packaging documentation

Modified:

  • katello.gemspec - Removed 9 Pulp gem dependencies
  • lib/katello/engine.rb - Added autoloading for generated clients
  • lib/monkeys/*.rb - Updated to work without explicit gem requires
  • README.md - Added Pulp API clients documentation section
  • .gitignore - Added rules for temp files

Next Steps

  1. Test the Generated Clients

cd /home/vagrant/foreman
bundle exec rails console

Then test loading the clients:

Should load without errors

PulpcoreClient::ApiClient
PulpRpmClient::RemotesRpmApi
PulpFileClient::RepositoriesFileApi

Test with repository types

Katello::RepositoryTypeManager.find('yum').client_module_class
=> PulpRpmClient

  1. Commit the Changes

cd /home/vagrant/katello
git status
git add vendor/pulp_openapi_specs/
git add lib/pulp_generated_clients/
git add lib/katello/tasks/pulp_client_generation.rake
git add lib/katello/engine.rb
git add lib/monkeys/
git add katello.gemspec
git add docs/
git add README.md
git add .gitignore

git commit -m "Implement vendored Pulp API clients with dev-time generation

  • Remove 9 Pulp client gem dependencies from gemspec
  • Add rake tasks for fetching OpenAPI specs and generating clients
  • Generate clients from Pulp 3.85 OpenAPI specifications
  • Configure Rails autoloading for generated clients
  • Update monkey patches to work with generated code
  • Add comprehensive documentation for Pulp upgrades

Generated clients (~30MB) are committed to eliminate runtime
dependencies and catch generation issues in Dev/QE before release.

Co-Authored-By: Claude Sonnet 4.5 noreply@anthropic.com"

  1. Optional: Run Tests

cd /home/vagrant/foreman
bundle exec rake test:katello

Future Pulp Upgrades

When Pulp is upgraded (every 3-6 months), simply run:

cd /home/vagrant/foreman
bundle exec rake katello:pulp:update_and_generate

This will:

  1. Fetch updated specs from the new Pulp version
  2. Regenerate all clients
  3. Show what changed (via git diff)

See docs/pulp_client_update_workflow.md for detailed upgrade instructions.

The implementation is complete! 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment