Skip to content
This repository was archived by the owner on May 6, 2022. It is now read-only.

Add List/Relist Catalog Restrictions#1773

Merged
carolynvs merged 53 commits intokubernetes-retired:masterfrom
n3wscott:whitelist_from_labels
Apr 23, 2018
Merged

Add List/Relist Catalog Restrictions#1773
carolynvs merged 53 commits intokubernetes-retired:masterfrom
n3wscott:whitelist_from_labels

Conversation

@n3wscott
Copy link
Contributor

@n3wscott n3wscott commented Feb 28, 2018

In this PR I am wrapping the functionality of label selectors to allow us to apply the label selector query/matching syntax for service classes and service plans. This will allow us to create whitelists or blacklists for either class or plan to give lots of control to the cluster operator on which items end up in the catalog.

As a follow-up I will add the needed namespaced version of the added Cluster* methods to be able to do the same work on the new ns objects.


Seeing if we can use labels.Selector to filter the catalog.

Things to do:
[x] Services that are filtered out need to remove their plans.
[x] Services that have all their plans filtered out should be removed.
[x] Testing needs a fuller catalog to test real filtering.
[x] Add filtering behind a feature flag.
[x] API validation should validate the filter string.
[X] Confirm the storage format is ok for the filter.
[X] ---> Internal representation might need to be the processed labels.Selector.Requirements object?

fixes #1604

@k8s-ci-robot k8s-ci-robot added cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Feb 28, 2018
@k8s-ci-robot k8s-ci-robot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Mar 5, 2018
type ClusterServiceClassRequirements string

type ServiceClassCatalogRestrictions struct {
ServicePlan ClusterServicePlanRequirements
Copy link
Contributor

Choose a reason for hiding this comment

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

It's not clear to me how I would say:

  • From this broker, create resources only for service X
  • From this broker, create resources for all services but Y

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have added some more comments to help you understand.

@staebler
Copy link
Contributor

staebler commented Mar 6, 2018

[] The Admission Controller should validate the filter string.

Is there any reason to do this in an Admission Controller instead of in API validation?

@n3wscott
Copy link
Contributor Author

n3wscott commented Mar 6, 2018

Is there any reason to do this in an Admission Controller instead of in API validation?

whichever. If you think that API validation is better then yeah lets do that. I have not touched that area of the code yet so I am ignorant of the correct way.

Copy link
Contributor

@staebler staebler left a comment

Choose a reason for hiding this comment

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

Looks to me like a reasonably approach to filtering.

// Solution: restrictions := ServiceClassCatalogRestrictions{
// ServiceClass: "externalName in (FooService, BarService)",
// }
// This is an example of a whitelist on service externalName.
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a blacklist not a whitelist, right? It is explicit blocking FooService and BarService.

Copy link
Contributor

@eriknelson eriknelson Mar 9, 2018

Choose a reason for hiding this comment

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

I think this could be interpreted in a couple ways: either "Allow all services that are NOT Foo/BarService", which would be an allowance rule and a whitelist. However, ultimately what you're saying here is "Block FooService and BarService", which sounds much more like a blacklist to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The comment may be confusing, I am staying the previous solution is the whitelist.

Above example is whitelist: "externalName in (FooService, BarService)"

Below example is a blacklist: "externalName notin (FooService, BarService)",

in means the thing must be contained in the following list to pass the predicate.
notin means the thing must not be contained in the following list to pass the predicate.

I hope it is just confusion around the Goal, Solution, and English Description format. I can fix that.

@staebler @eriknelson

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Or you folks are getting caught up on the restrictions name. In that case, I am open to other names that are allowed to house both white and black lists together, example: "externalName in (Demo),name not in (AABBCC)"

Copy link
Contributor

Choose a reason for hiding this comment

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

@n3wscott this is getting kind of pedantic, but I interpret a blacklist as "allow everything but these", compared to a whitelist that is "Allow nothing, but pass these". The distinction being a default allowing everything, or nothing. Given that definition, your examples are exactly correct, and the comment is describing a blacklist.

Copy link
Contributor

@staebler staebler Mar 9, 2018

Choose a reason for hiding this comment

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

@n3wscott OK. The confusion for me was the ordering of the comments. I read it to apply to the following rather than the preceding. An empty line between each example would have made it less confusing for me.

Copy link
Contributor

Choose a reason for hiding this comment

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

@n3wscott And, for what it's worth, I had no problem understanding the examples. I don't think that there is a need to change the restrictions name.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok updated to hopefully clear this up.

this is getting kind of pedantic

:D

@eriknelson we are staying the same thing and we agree. I updated the comment order and added whitespace to clear this up.

Copy link
Contributor

Choose a reason for hiding this comment

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

ack, sounds good.

// }
type ServiceClassCatalogRestrictions struct {
// ServiceClass represents a selector for plans, used to filter catalog re-lists.
ServiceClass ClusterServiceClassRequirements `json:"serviceClass,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

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

This should probably be an array

Copy link
Contributor

Choose a reason for hiding this comment

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

We will need to be able to translate these strings between API versions, so you might consider whether it would be easier to have a structured definition of a restriction than a string that has to be parsed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I could do an array, makes it less easy to fat finger a long string.

As for the structured definition, k8s does both. The object that this turns into is non-trivial because it is pretty powerful and then we either leak the fact we are using label selectors or we have to duplicate the label selector code.

I vote array of strings for now, if that pleases you

Copy link
Contributor

Choose a reason for hiding this comment

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

Array of strings is fine for me.

type ClusterServicePlanRequirements string

// ServiceClassCatalogRestrictions contains the restrictions used to on catalog re-list.
// Some examples of this object are as follows:
Copy link
Contributor

Choose a reason for hiding this comment

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

It needs to be more clear that the restriction expressions are in terms of the k8s API resources. It would also be more correct to say spec.externalName than externalName.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, good call. I think we should make that clear. I had this with field selectors and it got lost when I was attempting label selectors. I can add it back. Then free = true makes a lot more sense when it is next to spec.externalName

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@k8s-ci-robot k8s-ci-robot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Mar 12, 2018

// CatalogRestrictions allows adding restrictions onto Class/Plans on relist.
// +optional
CatalogRestrictions *ServiceClassCatalogRestrictions `json:"catalogRestrictions,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not just make these arrays instead of pointer to typedef?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ServiceClassCatalogRestrictions is a struct that has arrays for class and plan. So this is a pointer to a struct, not a pointer to a typedef'ed array.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now looking at it, I did not mean for this to contain "Class", this type is suppose to be ServiceCatalogRestrictions

I will update.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated.

Copy link
Contributor

@pmorie pmorie left a comment

Choose a reason for hiding this comment

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

Looking really good, just a few things

return serviceClasses, servicePlans, nil
}

func convertAndFilterCatalog(in *osb.CatalogResponse, restrictions *v1beta1.CatalogRestrictions) ([]*v1beta1.ClusterServiceClass, []*v1beta1.ClusterServicePlan, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

In the future, we could make this just work with CommonService(Class|Plan)Spec, and have wrappers that built the right types for cluster and ns-scoped. WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this currently uses the parent service class name in part of the plan query to aid in the filtering. We are going to need a general strategy on how to deal with this all over the place in the codebase.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

also why I did not just copy/paste yet, I wanted to look into seeing if I can make it work as you asked.

// alpha: v0.1.6
PodPreset utilfeature.Feature = "PodPreset"

// CatalogRestrictions controls whether the ClusterServiceBroker will use
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we change the verbiage here around "control which services and plans have k8s resources created for them" ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated comment

)

// Want to filter the allowed labels.
// want to use only
Copy link
Contributor

Choose a reason for hiding this comment

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

Clarify comment

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did not mean to leave that in... removed.

// NotIn Operator = "notin"
//)

// Predicate is used to test if this rule accepts the properties given.
Copy link
Contributor

Choose a reason for hiding this comment

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

if a rule

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated.

Copy link
Contributor

@pmorie pmorie left a comment

Choose a reason for hiding this comment

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

Just a couple nits on godoc stuff, but would you also add a simple integration test?

// +optional
RelistRequests int64 `json:"relistRequests"`

// CatalogRestrictions allows adding restrictions onto Class/Plans on relist.
Copy link
Contributor

Choose a reason for hiding this comment

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

bump - can we change the wording here?

// can be manually incremented by a user to manually trigger a relist.
RelistRequests int64

// CatalogRestrictions is a set of restrictions on which of a broker's services
Copy link
Contributor

Choose a reason for hiding this comment

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

Need to put the same godoc as v1beta1 here.

Copy link
Contributor Author

@n3wscott n3wscott Apr 4, 2018

Choose a reason for hiding this comment

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

ok updated. too much codez... (will push soon)

spec.CatalogRestrictions.ServiceClass, err.Error()))
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

One thing I just thought of: I think we should not allow you to set these fields if the feature gate isn't enabled.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

meh, I am not sure about that. I could see a use for setting this up then turning on or off the feature.

Copy link
Contributor

Choose a reason for hiding this comment

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

It's conventional not to allow fields for a feature that isn't enabled.

Copy link
Contributor

Choose a reason for hiding this comment

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

If you accept the fields, users will expect them to work.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the flag has been removed.

originatingIdentityEnabled: false
# Whether the AsyncBindingOperations alpha feature should be enabled
asyncBindingOperationsEnabled: false
# Whether the CatalogRestrictions alpha feature should be enabled
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we still need this flag, since the feature gate has been removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

killed. thanks!

@pmorie
Copy link
Contributor

pmorie commented Apr 18, 2018

Looks like CI failure was a flake - I bounced it.

@pmorie pmorie added the LGTM1 label Apr 19, 2018
@n3wscott
Copy link
Contributor Author

/retest

@n3wscott
Copy link
Contributor Author

Dear Travis,

You suck.

Sincerely,
-Scott

@n3wscott
Copy link
Contributor Author

Sorted it out, I had old compiled generators locally giving me different things than master expected.

PTAL.

@jeremyrickard
Copy link
Contributor

This is cool! I'm excited to see this make it in! LGTM.

Copy link
Contributor

@carolynvs carolynvs left a comment

Choose a reason for hiding this comment

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

LGTM

if len(errs) != 0 && tc.valid {
t.Errorf("%v: unexpected error: %v", tc.name, errs)
continue
return
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. LGTM1 size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

API surface for which services and plans in a broker's catalog have k8s objects created for them

8 participants