RedHatSatellite / katello-cvmanager

manage Katello content views
GNU General Public License v2.0
49 stars 31 forks source link

`Promote()` should use the version that the user defined in YAML, not just latest #62

Open lvw5264 opened 5 years ago

lvw5264 commented 5 years ago

I had a use case where we don't use composite content views, yet I wanted the promote command to use the content view version I defined. However, for some reason the promote command always uses the latest and does not check for the version the user defined in :cv: under YAML at all.

I suggest a solution in the code below, allowing the user specified version in YAML to be used, as well as latest. since I just hacked this together by combining aspects of the update() function, I will just paste the promote() function I made.

Also when the request in promote() gave a 400 bad request, as the exception was not handled, so I added a rescue statement, which now helpfully tells the command line user that they "Cannot promote environment out of sequence. Use force to bypass restriction.".

The YAML I use, where i have Library.yaml , dev.yaml (always kept up to date with Library), test.yaml, prod.yaml.

# prod.yaml
:settings:
  :user: admin
  :pass: YOURPASSWORD
  :uri: https://localhost
  :timeout: 300
  :org: 1              # always 1 
  :lifecycle: 6        # ID of the lifecycle, not always obvious
  :checkrepos: true    # check if the latest repo packages are already in the curre
nt content view
  :promote_cvs: true   # don't use composite content views for publishing
  :keep: 5
  :wait: false         # should cvmanager wait for tasks to finish, or run them in 
the background
  :sequential: 1       # run only one task at once
:cv:
  rhel7: 10.0   # I specifically want version 10 and not the latest 12
:publish:
  - rhel7
:promote:
  - rhel7

The promote() function:

def promote()
  tasks = []

  ccvs = []
  req = @api.resource(:content_views).call(:index, {:organization_id => @options[:org], :full_results => true})
  ccvs.concat(req['results'])
  while (req['results'].length == req['per_page'].to_i)
    req = @api.resource(:content_views).call(:index, {:organization_id => @options[:org], :full_results => true, :per_page => req['per_page'], :page => req['page'].to_i+1})
    ccvs.concat(req['results'])
  end

  ccvs.each do |ccv|
    next if not ccv['composite'] and not @options[:promote_cvs]
    next if not @yaml[:promote].include?(ccv['label']) and not @yaml[:promote].include?("all")

    puts "Inspecting #{ccv['label']}"

    # get the desired version for this component from the YAML
    # either the version for the component in this CCV is set
    # or it is set globally
    # never touch non-mentioned components
    if @yaml[:cv].is_a?(Hash) and @yaml[:cv].has_key?(ccv['label'])
      users_version = @yaml[:cv][ccv['label']]
      puts_verbose "  Desired version #{users_version} found in CV"

      # instead of hard-coding the versions, the user can also specify "latest"
      # figure out the latest content view version, choose it, unless there is none
      if users_version == 'latest'
        desired_version = ccv['versions'].sort_by { |v| v['version'].to_f }.reverse[0]
        next if ! desired_version
      else
        desired_version = ccv['versions'].select {|v| v["version"].to_f == users_version }[0]
        next if ! desired_version
      end
    else
      puts_verbose "  Desired version not found, skipping"
      next
    end

    if not desired_version['environment_ids'].include?(@options[:lifecycle])
      puts " Promoting version #{desired_version['version']} to lifecycle-environment #{@options[:lifecycle]}"
      if not @options[:noop]
        begin
          req = @api.resource(:content_view_versions).call(:promote, {:id => desired_version['id'], :environment_id => @options[:lifecycle], :force => @options[:force]})
          tasks << req['id']
          wait([req['id']]) if @options[:sequential]
        rescue RestClient::ExceptionWithResponse => e # catch exceptions with more helpful error content
          puts e.response
        end
      else
        puts " [noop] Promoting #{desired_version['version']} to lifecycle-environment #{@options[:lifecycle]}"
      end
    end
  end

  wait(tasks)
end
kallies commented 5 years ago

We ran into the same issue. We divided our systems into patching units and the first unit gets patches from the current month ad the second unit gets patches from last month. There are additional LCEs around (for testing, different states and so on), but I'd say they can be ignored in this case.

We keep two cvmanager configurations:

current month example:

---
:settings:
...
:ccv:
  ccv-lin-7:
    cv-lin-7-os: 150.0
    cv-lin-7-base: 162.0
:promote:
  - ccv-lin-7

last month example:

---
:settings:
...
:ccv:
  ccv-lin-7:
    cv-lin-7-os: 139.0
    cv-lin-7-base: 136.0
:promote:
  - ccv-lin-7

To make your proposal work, this requires an additional cv hash:

...
:cv:
  ccv-lin-7: 12.0
...

This example worked in a test. Nice would be to auto-detect the CCV version ID based on the configured CV version IDs (if not present the CCV it would be necessary to publish a new CCV anyway).

We do incremental CV updates from time to time (if a critical patch needs to find its way to an existing view) which increment the CCVs as well. This is more a topic of updating the config (e.g. ccv-lin-7: 12.1) but should be kept in mind when testing this feature.

@lvw5264 can you create a pull request with your code? This would simplify comments on particular lines of code.