Skip to content

Conversation

ghost
Copy link

@ghost ghost commented Sep 23, 2016

Created a "shared" Isolation Segment that gets seeded in the database.

Organizations have a list of "allowed Isolation Segments"

  • Only Admins can manage the allowed list
  • Space's Isolation Segments must belong to an Organization's allowed list
  • Org managers can manage Space's Isolation Segment

Organizations can set a 'default' from the allowed list.

A number of endpoints to manage Isolation Segments:

  • post '/isolation_segments/:guid/relationships/organizations'
  • delete '/isolation_segments/:guid/relationships/organizations'
  • get '/isolation_segments/:guid/relationships/organizations'
  • get '/isolation_segments/:guid/relationships/spaces'
  • put '/v2/organizations/:guid' -d '{"default_isolation_segment_guid":"guid"}'
  • get /v2/organizations/:guid/isolation_segments

@cfdreddbot
Copy link

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.

@cf-gitbot
Copy link

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(
Copy link
Member

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.

Copy link
Contributor

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?

Copy link
Member

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:

  1. Don't expose isolation segments through the v2 api
  2. 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.

Copy link
Contributor

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' => {
Copy link
Member

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.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yep, please see my response above. Need @zrob or @utako to weigh in with they remember us having agreed on.

Copy link
Contributor

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

ScarletTanager and others added 10 commits October 12, 2016 10:29
- 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]>
- 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]>
…:"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]>
- Improve error messages
- Add 'relationships' for get urls
- Move isolation segments <-> organizations to end of migration list

Signed-off-by: Dan Lavine <[email protected]>
@Gerg
Copy link
Member

Gerg commented Oct 12, 2016

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

Copy link
Member

@Gerg Gerg left a 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)
Copy link
Member

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?

Copy link
Contributor

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?

Copy link
Member

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.

Copy link
Contributor

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) }
)
Copy link
Member

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.

Copy link
Contributor

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.

Copy link
Contributor

@ScarletTanager ScarletTanager Oct 13, 2016

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
Copy link
Member

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

Copy link
Contributor

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.

Copy link
Contributor

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

Copy link
Contributor

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
Copy link
Member

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

Copy link
Contributor

@ScarletTanager ScarletTanager Oct 13, 2016

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)
Copy link
Member

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

Copy link
Contributor

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.

Copy link
Contributor

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
Copy link
Member

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?

Copy link
Contributor

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
Copy link
Member

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
Copy link
Member

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

Copy link
Contributor

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
Copy link
Member

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'
Copy link
Member

Choose a reason for hiding this comment

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

Unrelated to PR.

Copy link
Contributor

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?

Copy link
Contributor

Choose a reason for hiding this comment

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

Fixed.

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
Copy link
Contributor

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.

Copy link
Contributor

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
Copy link
Contributor

Choose a reason for hiding this comment

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

Extra comment.

Copy link
Contributor

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 }
Copy link
Contributor

Choose a reason for hiding this comment

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

Unused variable.

Copy link
Contributor

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
Copy link
Contributor

Choose a reason for hiding this comment

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

Empty test.

Copy link
Contributor

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.

Copy link
Contributor

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
Copy link
Contributor

Choose a reason for hiding this comment

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

Empty test.

Copy link
Contributor

Choose a reason for hiding this comment

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

See previous.

Copy link
Contributor

Choose a reason for hiding this comment

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

Fixed.

@ScarletTanager
Copy link
Contributor

ScarletTanager commented Oct 14, 2016

@Gerg @SocalNick @zrob @aashah I fixed most of the issues, but I think the following remain:

  1. Moving the IS model hooks into v3 action(s)
  2. Adding a v2 presenter for ISes (does not block PR, but needs to happen before GA)
  3. Moving one negative spec from IS request spec into controller spec
  4. Need to come to some agreement on response codes - for the most part, I think the 405s are correct. The one that still sticks is the 501 case, which may need to become something else (405 or 409, possibly?). Need a verdict on that. Does this block the PR?

@Gerg
Copy link
Member

Gerg commented Oct 17, 2016

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?

@ScarletTanager
Copy link
Contributor

@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.

@ScarletTanager
Copy link
Contributor

@Gerg @SocalNick any news on when we might get this merged? It's blocking a bunch of stuff for us.

@Gerg
Copy link
Member

Gerg commented Oct 25, 2016

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 https://github.com/gerg @SocalNick https://github.com/SocalNick
any news on when we might get this merged? It's blocking a bunch of stuff
for us.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#685 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AA1ddM-ZyIbV5Mgu7h-oEaabP1nH6GWSks5q3K3XgaJpZM4KEhoS
.

@ScarletTanager
Copy link
Contributor

@Gerg @SocalNick @zrob - it appears that all the commits from this PR are now in master. Can we close this?

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

Successfully merging this pull request may close these issues.

7 participants