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 #22909 - Remove subscriptions from upstream allocation #7241

Merged
merged 1 commit into from Apr 3, 2018

Conversation

jturel
Copy link
Member

@jturel jturel commented Mar 15, 2018

API may be invoked as follows:

DELETE /katello/api/v2/organizations/:id/upstream_subscriptions

With a body:

{
  "pool_ids: ["a", "b", "c"]
}

The pool ids given must have an upstream_entitlement_id

@theforeman-bot
Copy link

Issues: #22909

@jturel
Copy link
Member Author

jturel commented Mar 15, 2018

@akofink this is how things are shaping up from my side, FYI

Copy link
Member

@akofink akofink left a comment

Choose a reason for hiding this comment

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

Looking good!

def plan(organization, entitlement_ids)
sequence do
entitlement_ids.each do |id|
plan_self(organization_id: organization.id, entitlement_id: id)
Copy link
Member

Choose a reason for hiding this comment

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

I think this line could be concurrent, but I don't think we're going to have a bunch of tasks here anyway.

Copy link
Member

Choose a reason for hiding this comment

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

does this actually work? Can you plan_self multiple times? I've not tried it. Normally we would plan some other action like this, rather than plan_self in a loop.

@@ -0,0 +1,30 @@
module Actions
module Katello
module UpstreamSubscriptions
Copy link
Member

Choose a reason for hiding this comment

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

I placed mine in Actions::Candlepin::Consumer, since that's the relevant API endpoint in the Candlepin resource.

def path(id = upstream_consumer_id)
super(id)
def path
join_path(prefix, 'consumers', upstream_consumer_id)
Copy link
Member

Choose a reason for hiding this comment

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

I wouldn't override this. You can use the following to achieve the same result:

self["entitlements/#{entitlement_id}"]

Copy link
Member Author

Choose a reason for hiding this comment

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

Nice

module Katello
class UpstreamConsumer < OpenStruct
def self.remove_entitlement(organization, entitlement_id)
resource_class.organization = organization
Copy link
Member

Choose a reason for hiding this comment

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

I moved this org setting stuff to the Resource (for now).

@@ -27,6 +25,15 @@ def index
respond(collection: collection)
end

api :PUT, "/organizations/:organization_id/upstream_subscriptions",
Copy link
Member

Choose a reason for hiding this comment

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

hrmmm, using PUT here seems weird, what is the update quantity going to look like? We really should have planned out all these apis from the start :)

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, should be a DELETE, huh :)

@waldenraines
Copy link
Contributor

waldenraines commented Mar 16, 2018

@jturel can you please provide examples of the requests including the payload? Want to see how it's supposed to be structured so we can go ahead and start on our end.

@jturel jturel changed the title [WIP] Fixes #22909 - Remove subscriptions from upstream allocation Fixes #22909 - Remove subscriptions from upstream allocation Mar 16, 2018
@jturel
Copy link
Member Author

jturel commented Mar 16, 2018

@waldenraines added example w/ payload

@@ -130,6 +130,13 @@ def resource(url = self.site + self.path, client_cert = self.client_cert, client
)
end

def json_resource(url = self.site + self.path, client_cert = self.client_cert, client_key = self.client_key, ca_file = nil, options = {})
Copy link
Member Author

Choose a reason for hiding this comment

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

Nabbed this from PR7235 - will be rebased out of here once it's merged

Copy link
Contributor

@waldenraines waldenraines left a comment

Choose a reason for hiding this comment

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

@waldenraines added example w/ payload

Thanks. I approve this from a consumer's perspective.

module Katello
module UpstreamSubscriptions
class RemoveEntitlement < Actions::Base
middleware.use ::Actions::Middleware::PropagateCandlepinErrors
Copy link
Member Author

Choose a reason for hiding this comment

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

This action locks the org if we raise an error when, for example, the entitlement was already removed from the allocation

The user doesn't really have a way to work around that, so the locking doesn't make sense. Should the action be cancellable or something similar?

Copy link
Member

Choose a reason for hiding this comment

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

I think this is taken care of with the rescue_strategy: https://github.com/Katello/katello/pull/7235/files#diff-562f4c0f3f476d7dd66af859c597ce58R24

That should keep the task from locking on failure.

Copy link
Member Author

Choose a reason for hiding this comment

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

cool, I think I'll use that

Copy link
Member

Choose a reason for hiding this comment

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

@jturel Still need to follow up here.

Copy link
Member Author

@jturel jturel Mar 19, 2018

Choose a reason for hiding this comment

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

I added it in the 'parent' action ::RemoveEntitlements

Copy link
Member

Choose a reason for hiding this comment

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

Should this middleware.use be in the parent action, or this one, and why?

Copy link
Member Author

Choose a reason for hiding this comment

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

This lower-level action is the one that's hitting candlepin, so I want it to propagate the error - and it appears that it bubbles up through the parent action too.

@jturel
Copy link
Member Author

jturel commented Mar 19, 2018

@jlsherrill @akofink I think this is complete and is not dependent on the "add subs" PR. please take a look & let me know if you'd like any more changes

@@ -233,6 +240,9 @@ def update(url, client_cert, client_key, ca_file, attributes)
ensure
RestClient.proxy = ""
end

delegate :[], to: :json_resource
delegate :put, to: :json_resource
Copy link
Member

Choose a reason for hiding this comment

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

Unnecessary line.

api_resources :upstream_subscriptions, only: :index
api_resources :upstream_subscriptions, only: [:index] do
collection do
delete :destroy
Copy link
Member

Choose a reason for hiding this comment

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

why not use the shorthand? DELETE is the default method for a destroy action.

Copy link
Member Author

@jturel jturel Mar 19, 2018

Choose a reason for hiding this comment

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

I'm not destroying a single resource - but a collection of them (potentially) - so there won't be an "upstream subscription ID" in the URL. I think this is the correct way...

Copy link
Member

Choose a reason for hiding this comment

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

Ah yeah, right. :)

@akofink
Copy link
Member

akofink commented Mar 19, 2018

It looks good. There's still some vestigial pieces of old implementation hanging around that I commented on.

@jlsherrill
Copy link
Member

[test katello]

@@ -214,6 +214,12 @@ def path(id = upstream_consumer_id)
super(id)
end

def remove_entitlement(entitlement_id)
raise ArgumentError.new("No entitlement ID given to remove.") if entitlement_id.blank?

Choose a reason for hiding this comment

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

Provide an exception class and message as arguments to raise.
Use fail instead of raise to signal exceptions.

@jturel
Copy link
Member Author

jturel commented Mar 26, 2018

@akofink @jlsherrill updated to use the new KeepCurrentTaxonomies action middleware

middleware.use Actions::Middleware::PropagateCandlepinErrors

input_format do
param :org_id
Copy link
Member

Choose a reason for hiding this comment

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

org_id is no longer needed

@jturel
Copy link
Member Author

jturel commented Mar 26, 2018

@jlsherrill removed the org_id input param

@akofink
Copy link
Member

akofink commented Mar 26, 2018

Unrelated bug when refreshing manifest, which is caused by the audit work:

Validation failed: Locations expecting locations used by hosts or inherited (check mismatches report).

Same thing happens for the add API, of course. :)

Copy link
Member

@akofink akofink left a comment

Choose a reason for hiding this comment

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

Code looks good, and my entitlements are successfully removed from my allocation (or whatever ;). One thing that's a bit weird: if you remove a bunch (~20) entitlements, the progress bar quickly hits ~95% and then sits there forever while the manifest refreshes. I wonder if there's a way to weight the progress, since we know the manifest refresh is going to take way longer than the actual remove. I think @johnpmitsch did something like this a long time ago...

@@ -191,6 +191,10 @@ def set_user(user = nil)
User.current = user
end

def set_organization(org)
Organization.current = org
Copy link
Member

Choose a reason for hiding this comment

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

Hmm... I guess this is fine. I just stubbed it because I was scared that the current org would be kept for other tests, but it's clearly copying the set_user pattern above, so I guess it's fine? :)

Copy link
Member Author

Choose a reason for hiding this comment

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

Cool, I think it's good to abstract the impl details from the testing as much as we can!

Copy link
Member

Choose a reason for hiding this comment

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

That should depend on what kind of test you're writing. :)

@johnpmitsch
Copy link
Contributor

Here is the PR that I used to weight the capsule sync progress bar

@jturel
Copy link
Member Author

jturel commented Mar 26, 2018

Thanks @johnpmitsch

@akofink - added some weighting, give it a try - it does seem better with it.

end

def run_progress
done? ? 1 : 0.1
Copy link
Contributor

Choose a reason for hiding this comment

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

I should have clarified, the example I showed you isn't the typical way to weight a task. For that task, it would show as half done very quickly, as the first "half" of the task took a short amount of time, but the second "half" could take hours. This meant the progress bar would just jump to 50% and stay there a long time, which made me use this override.

I'm not sure if that fix is what you need (it very well could be), but there is also the run_progress_weight method, which we us other places which gives a task weight among all the other tasks.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for clarifying @johnpmitsch - I think the way I've done it works fine (I did try run_progress too but it actually didn't seem to have an affect).

Copy link
Contributor

Choose a reason for hiding this comment

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

must be a similar situation then! As a long term goal we should look at adding this functionality to dynflow since it seems to be common and we could possibly use in other places

@jturel
Copy link
Member Author

jturel commented Mar 27, 2018

[test]

@jturel
Copy link
Member Author

jturel commented Mar 27, 2018

[test katello]

@jturel
Copy link
Member Author

jturel commented Mar 27, 2018

@akofink @jlsherrill anything missing which prevents an ACK (aside from the tests still running!)?

@akofink
Copy link
Member

akofink commented Mar 27, 2018

I still need to retest. The code looks good, though!

@jturel
Copy link
Member Author

jturel commented Mar 27, 2018

@akofink don't test just yet, this API is going to be changing per recent IRC discussion to accept local pool id rather than upstream entitlement ID so that we can do the lookup on the server side

@jturel jturel changed the title Fixes #22909 - Remove subscriptions from upstream allocation [WIP] Fixes #22909 - Remove subscriptions from upstream allocation Mar 27, 2018
@jturel jturel changed the title [WIP] Fixes #22909 - Remove subscriptions from upstream allocation Fixes #22909 - Remove subscriptions from upstream allocation Mar 28, 2018
@jturel
Copy link
Member Author

jturel commented Mar 28, 2018

@akofink @jlsherrill API changed to accept downstream pool ids where we will now lookup the upstream entitlement ID on the backend. Let me know what you think.

Reminder that the purpose of the change is from the UI/API perspective where the upstream entitlement id is not readily available.

Copy link
Member

@akofink akofink left a comment

Choose a reason for hiding this comment

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

Do you think we should still also accept entitlement_ids? This supports a future refresh_manifest param so users could add/remove subs without refreshing their manifest until the end of their actions. We've spoken a bit about this potential outcome for CLI users.

@@ -26,6 +24,14 @@ def index
respond(collection: collection)
end

api :DELETE, "/upstream_subscriptions",
N_("Remove one or more subscriptions from an upstream subscription allocation")
param :pool_ids, Array, desc: N_("Array of pool IDs. This will only work with upstream (non-custom) pools"), required: true
Copy link
Member

@akofink akofink Mar 28, 2018

Choose a reason for hiding this comment

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

I think we should make it clear that this ID does not come from upstream. I'm not really sure how, maybe:

"Array of local pool IDs. ...", or "Katello pool IDs"?

@jturel
Copy link
Member Author

jturel commented Mar 28, 2018

I think I'd rather support entitlement_ids when we have the need. Updating the API language..

def remove_entitlement(entitlement_id)
fail ArgumentError, "No entitlement ID given to remove." if entitlement_id.blank?

self["entitlements/#{entitlement_id}"].delete
Copy link
Member

Choose a reason for hiding this comment

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

Working well, other than the individual RemoveEntitlement output in Dynflow. You need to return a string to the action. Wrapping this in a JSON.parse would do the trick.

Copy link
Member

Choose a reason for hiding this comment

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

Hmm... nevermind. Candlepin doesn't return anything here on success! Bummer!

Copy link
Member

@akofink akofink left a comment

Choose a reason for hiding this comment

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

Works as expected. Could we have a remove_entitlement_test.rb please?

@@ -0,0 +1,63 @@
require 'katello_test_helper'

describe ::Actions::Katello::UpstreamSubscriptions::RemoveEntitlement do
Copy link
Member Author

Choose a reason for hiding this comment

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

@akofink I'm testing that action here - do you want me to put this in a separate file?

Copy link
Member

Choose a reason for hiding this comment

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

Oh! lol Yeah, that'd be good. :) Just for the reason that people looking for these tests will look there (including myself just now).

Copy link
Member

@akofink akofink left a comment

Choose a reason for hiding this comment

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

Very nice @jturel! Works as expected. 🥇

@jturel
Copy link
Member Author

jturel commented Mar 29, 2018

@akofink done!

@akofink
Copy link
Member

akofink commented Mar 29, 2018

@jturel One more thing I just realized: perhaps we should handle the case of nil Organization.current. Right now, the manifest refresh is called with nil.

@jturel jturel force-pushed the remove_upstream_subs branch 2 times, most recently from bfdc666 to 9a12f49 Compare April 2, 2018 20:26
@jturel
Copy link
Member Author

jturel commented Apr 2, 2018

@akofink this is updated with the latest stuff and I added the nil check for current org. can I get (hopefully!) one last run-through?

Copy link
Member

@akofink akofink left a comment

Choose a reason for hiding this comment

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

Still works great. ACK from me! Thanks @jturel!

@jturel
Copy link
Member Author

jturel commented Apr 3, 2018

[test katello]

@jturel jturel merged commit 59e3a1a into Katello:master Apr 3, 2018
@jturel jturel deleted the remove_upstream_subs branch April 3, 2018 15:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
7 participants