-
Notifications
You must be signed in to change notification settings - Fork 365
Isolation segments <-> Organizations #685
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
Conversation
Hey DanLavine! Thanks for submitting this pull request! I'm here to inform the recipients of the pull request that you and the commit authors have already signed the CLA. |
We have created an issue in Pivotal Tracker to manage this: https://www.pivotaltracker.com/story/show/130958055 The labels on this github issue will be updated when the story is started. |
dataset = IsolationSegmentModel.dataset.where(organizations: org) | ||
message = IsolationSegmentsListMessage.from_params(@params) | ||
|
||
[HTTP::OK, MultiJson.dump(Presenters::V3::PaginatedListPresenter.new( |
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.
Using the v3 presenter will mean that this will look like a v3 endpoint, even though it is in the v2 api.
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.
My understanding from @zrob was that using the v3 presenter was okay, since these are v3-only objects - for example, GET /v3/isolation_segments will use this same presenter, so we'd like to keep the same look/feel. There is no v2 isolation segment API. Zach, did we misunderstand the outcome of our last conversation on this?
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've talked some with @SocalNick about this.
By exposing this resource on the v2 API, it is not longer a v3-only object. Using V3:PaginatedListPresenter
means that you will get v3 pagination even though you are hitting a URL that begins with "v2." If I'm building an API client, this means that I can no longer key off of the api version number to determine what pagination scheme will be returned.
Given that, the two options are:
- Don't expose isolation segments through the v2 api
- Build v2 presenters for isolation segments
Since these are still experimental endpoints, this change doesn't have to block merging this PR, but we need to change it before going GA.
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.
@Gerg Thanks for checking with Nick on this. If you can let me know when you've finished merging, I'll see about a follow-on PR to implement a v2 presenter for Isolation Segments.
-Sandy
'guid' => isolation_segment_model.guid, | ||
'created_at' => iso8601, | ||
'updated_at' => nil, | ||
'links' => { |
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.
Related to my other comment: the pagination and resource links are v3-style 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.
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.
@Gerg @ScarletTanager I remember that we considered the body the same, but we never expected to see links or v3 pagination on the v2 api
- has a code-constant guid - name can be changed via manifest - cannot be deleted or updated via API Signed-off-by: Dan Lavine <[email protected]>
Signed-off-by: Dan Lavine <[email protected]>
- IS<->org is many:many - Allows for creation and reading of IS<->org assignments - Organization has a default_isolation_segment which is the first isolation segment assigned to the org - Setup logic for deletion of orgs with isolation segments. This also respects space associations with Isolation Segments - Organizations list the isolation segments associated with them - Isolation Segments have relationship endpoints to list Organizations and spaces - Isolation Segments can add organizations through a bulk api - Note: There are no query parameters for isolation segmetns on Organizations [finishes #129611053] Signed-off-by: Dan Lavine <[email protected]>
…rganizations' Signed-off-by: Sandy Cash <[email protected]>
…:"guid"}' - This is an OrgManager/Admin only endpoint Signed-off-by: Sandy Cash <[email protected]>
[finishes #129612885] Signed-off-by: Dan Lavine <[email protected]>
[finishes #129612925] Signed-off-by: Dan Lavine <[email protected]>
[#130698053] Signed-off-by: Dan Lavine <[email protected]>
Signed-off-by: Dan Lavine <[email protected]>
- Improve error messages - Add 'relationships' for get urls - Move isolation segments <-> organizations to end of migration list Signed-off-by: Dan Lavine <[email protected]>
Since we have been so slow reviewing/merging this, there were a bunch of merge issues. I rebased and pushed to the isolation_segments branch |
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 didn't have time to go through most of the specs yet.
isolation_segment = IsolationSegmentModel[guid: request_attrs['isolation_segment_guid']] | ||
raise CloudController::Errors::ApiError.new_from_details('InvalidRelation', | ||
"Could not find Isolation Segment with guid: #{request_attrs['isolation_segment_guid']}") unless isolation_segment | ||
isolation_segment_guids = obj.organization.isolation_segment_models.map(&:guid) |
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.
Law of Demeter. Maybe organizations should have an accessor for isolation segments?
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.
Not sure what you mean - organization.isolation_segment_models is the accessor for ISes from orgs...? The space needs to navigate to the ISes to which the owning org is entitled, I don't really see a shorter path to get there?
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.
Sorry about the confusion. This was a note to myself that I forgot to edit.
I'm talking about obj.organization.isolation_segment_models.map(&:guid)
vs something like obj.organization.get_isolation_segment_guids
. That way you define a public interface for organizations instead of reaching into their internal state. We do the former a lot in CC anyway, so this isn't blocking the PR.
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.
Okay, thanks for the clarification.
roles.admin? || | ||
isolation_segment_model.spaces.any? { |space| can_read?(space.guid, space.organization.guid) } || | ||
isolation_segment_model.organizations.any? { |org| can_read_from_org?(org.guid) } | ||
) |
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 logic is starting to get pretty hairy, might be nice to break out helper methods.
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.
Sure, I can do 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.
Fixed. (Coming soon to a commit near you)
@@ -56,6 +60,9 @@ def destroy | |||
isolation_segment_model = IsolationSegmentModel.where(guid: params[:guid]).first | |||
resource_not_found!(:isolation_segment) unless isolation_segment_model | |||
|
|||
method_not_allowed!('DELETE', "the #{isolation_segment_model.name} Isolation Segment") if |
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 is the first time we are returning a 405 from the CC API, and I don't think this is the correct usage. DELETE is a supported action for IsolationSegments, just not this particular isolation segment. I'm not sure what the current idiom is for these (maybe just 400?) @SocalNick
Relevant docs: https://httpstatuses.com/405
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 a 405 is exactly the correct response here - from the spec: "The method specified in the Request-Line is not allowed for the resource identified by the Request-URI." A 405 doesn't mean you can't DELETE any IS, just this one IS.
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 409 seem more appropriate? https://httpstatuses.com/409
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.
@SocalNick I don't think so - a 409 implies that you could tweak the request - but same method, same resource - and have it succeed. That isn't the case here, there is literally no way to delete (or update) this particular segment.
isolation_segment_model = IsolationSegmentModel.where(guid: params[:guid]).first | ||
resource_not_found!(:isolation_segment) unless isolation_segment_model | ||
|
||
method_not_allowed!('PUT', "the #{isolation_segment_model.name} Isolation Segment") if |
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.
Same 405 issue as above
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.
Same 405 response as above
@@ -16,6 +16,14 @@ def unprocessable!(message) | |||
def unauthorized! | |||
raise CloudController::Errors::ApiError.new_from_details('NotAuthorized') | |||
end | |||
|
|||
def method_not_allowed!(method, obj) |
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.
See my other comments about if we should ever be returning 405s
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.
lIRC we had at least one legit case for a 405 - but I'll look at each of the cases you pointed out and be sure we're staying on the reservation.
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 both cases where we return 405s are legit, so I think this should stay.
end | ||
|
||
down do | ||
alter_table :organizations 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.
Do you also need to remove the default_isolation_segment_guid
column? Or does Sequel magically do that when dropping the foreign key?
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'll check.
@@ -19,6 +19,7 @@ | |||
field :status, 'Status of the organization' | |||
field :quota_definition_guid, 'The guid of quota to associate with this organization', example_values: ['org-quota-def-guid'] | |||
field :billing_enabled, 'If billing is enabled for this organization', deprecated: true | |||
field :default_isolation_segment_guid, 'The guid for the default isolation segment', experimental: 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.
We are no longer automatically updating the v2 api docs via these tests. In order for this change to be reflected in the docs, you will have to manually edit them.
|
||
it 'returns the seeded isolation segment' 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.
This seems more appropriate as a controller spec
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 I disagree. We want to show that the API will always return the seeded segment, even if no others have been created - this seems like a basic happy path behavior to me and appropriate for a request spec.
expect(parsed_response['pagination']).to eq(expected_pagination) | ||
end | ||
|
||
context 'when the user is not an admin' 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.
This would also be more appropriate as a controller spec. Generally, we want the request specs to only cover super happy path cases since they are relatively slow.
Correct me if I'm wrong @utako
@@ -12,6 +12,8 @@ | |||
require 'timecop' | |||
require 'awesome_print' | |||
|
|||
require 'pry' |
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.
Unrelated to PR.
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.
Whoops, this leaked in. I'll remove it. As a side note, is there any good reason not to include this in the spec_helper?
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.
Fixed.
Signed-off-by: Dan Lavine <[email protected]>
Signed-off-by: Dan Lavine <[email protected]>
Signed-off-by: Dan Lavine <[email protected]>
post '/v2/organizations', MultiJson.dump({ name: 'my-org-name' }) | ||
|
||
expect(last_response.status).to eq(201) | ||
org = Organization.find(name: 'my-org-name') | ||
expect(org.managers.count).to eq(0) | ||
end | ||
|
||
it 'does not allow creation with a default isolation segment set' 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.
Reads weird (as it's not preventing creation), when the contents indicate a 201 - Created.
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.
Fixed. Spec description was incorrect, code is correct.
organization.add_manager(user) | ||
end | ||
|
||
# it 'fails with a 403' 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.
Extra comment.
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.
Fixed, also later in the same file.
@@ -5,6 +5,7 @@ module VCAP::CloudController | |||
let(:user) { User.make } | |||
let!(:space) { Space.make(organization: organization) } | |||
let(:organization) { Organization.make } | |||
let(:organization2) { Organization.make } |
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.
Unused variable.
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.
Fixed.
end | ||
|
||
context 'when setting the default isolation segment' do | ||
it 'must be in the allowed list' 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.
Empty test.
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.
Ugh. This behavior is partially handled (where it should be) in the organization model spec, but there are empty tests there as well. Fixing.
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.
Fixed.
it 'must be in the allowed list' do | ||
end | ||
|
||
it 'can be updated' 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.
Empty test.
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.
See previous.
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.
Fixed.
Signed-off-by: Dan Lavine <[email protected]>
Signed-off-by: Dan Lavine <[email protected]>
2173fb1
to
d3450b0
Compare
@Gerg @SocalNick @zrob @aashah I fixed most of the issues, but I think the following remain:
|
Regarding all the discussion around HTTP status codes: One of our objectives for v3 was to avoid the status code pedantry that I've been gleefully engaging in. Zach reminded me that in our v3 style guide we have a small list of response codes: https://github.com/cloudfoundry/cc-api-v3-style-guide#response-codes. The idea is to use the response codes somewhat generically, but have descriptive error messages, since those are what most people care about anyway. Given that, it seems like 422 is appropriate for the 501/405 cases. Thoughts? |
@Gerg - I don't think so - I actually think that a 405 is correct for the two cases where I'm defending it (there is an entity identifiable by the URI path for which the verb used will not be accepted ever). It's not because there is some semantic error (I realize that the scope of what could qualify for a 422 is pretty broad, which is one of the reasons to be careful about using it). In the case where we had a 501, but you argued for a 405, I think you are probably correct - I think that should be a 405. Since I'm X-team pairing with CAPI today, we can probably work this out in real time. |
@Gerg @SocalNick any news on when we might get this merged? It's blocking a bunch of stuff for us. |
Our pipeline went green Friday EOD, so we should be able to push today. On Monday, October 24, 2016, Sandy Cash [email protected] wrote:
|
@Gerg @SocalNick @zrob - it appears that all the commits from this PR are now in master. Can we close this? |
Created a "shared" Isolation Segment that gets seeded in the database.
Organizations have a list of "allowed Isolation Segments"
Organizations can set a 'default' from the allowed list.
A number of endpoints to manage Isolation Segments: