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
Conversation
Issues: #22909 |
@akofink this is how things are shaping up from my side, FYI |
There was a problem hiding this 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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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}"]
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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", |
There was a problem hiding this comment.
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 :)
There was a problem hiding this comment.
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 :)
@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. |
e6be9a9
to
2d494d5
Compare
@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 = {}) |
There was a problem hiding this comment.
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
There was a problem hiding this 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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
2d494d5
to
bf04aa5
Compare
@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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary line.
config/routes/api/v2.rb
Outdated
api_resources :upstream_subscriptions, only: :index | ||
api_resources :upstream_subscriptions, only: [:index] do | ||
collection do | ||
delete :destroy |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yeah, right. :)
It looks good. There's still some vestigial pieces of old implementation hanging around that I commented on. |
bf04aa5
to
5968916
Compare
[test katello] |
5968916
to
a010df5
Compare
@@ -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? |
There was a problem hiding this comment.
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.
a010df5
to
e4b9c6f
Compare
@akofink @jlsherrill updated to use the new KeepCurrentTaxonomies action middleware |
middleware.use Actions::Middleware::PropagateCandlepinErrors | ||
|
||
input_format do | ||
param :org_id |
There was a problem hiding this comment.
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
e4b9c6f
to
0244713
Compare
@jlsherrill removed the org_id input param |
Unrelated bug when refreshing manifest, which is caused by the audit work:
Same thing happens for the add API, of course. :) |
There was a problem hiding this 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 |
There was a problem hiding this comment.
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? :)
There was a problem hiding this comment.
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!
There was a problem hiding this comment.
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. :)
Here is the PR that I used to weight the capsule sync progress bar |
Thanks @johnpmitsch @akofink - added some weighting, give it a try - it does seem better with it. |
end | ||
|
||
def run_progress | ||
done? ? 1 : 0.1 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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
[test] |
[test katello] |
@akofink @jlsherrill anything missing which prevents an ACK (aside from the tests still running!)? |
I still need to retest. The code looks good, though! |
@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 |
1ae0aae
to
17e2920
Compare
@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. |
There was a problem hiding this 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 |
There was a problem hiding this comment.
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"?
I think I'd rather support entitlement_ids when we have the need. Updating the API language.. |
17e2920
to
0e92de8
Compare
def remove_entitlement(entitlement_id) | ||
fail ArgumentError, "No entitlement ID given to remove." if entitlement_id.blank? | ||
|
||
self["entitlements/#{entitlement_id}"].delete |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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!
There was a problem hiding this 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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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).
0e92de8
to
6ebefb0
Compare
There was a problem hiding this 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. 🥇
@akofink done! |
@jturel One more thing I just realized: perhaps we should handle the case of nil |
bfdc666
to
9a12f49
Compare
9a12f49
to
05ac7ff
Compare
@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? |
There was a problem hiding this 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!
[test katello] |
API may be invoked as follows:
DELETE /katello/api/v2/organizations/:id/upstream_subscriptions
With a body:
The pool ids given must have an upstream_entitlement_id