-
Notifications
You must be signed in to change notification settings - Fork 38.7k
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
Enforce namespace unique constraints #1564
Enforce namespace unique constraints #1564
Conversation
0994cbf
to
197c7ac
Compare
Not sure why Go 1.2.2 is showing an error, but Go 1.3 is building and passing Travis without issue. |
Probably using a Go 1.3 specific feature |
Travis flaked, all things pass now. |
197c7ac
to
f9c3e19
Compare
Have a test that sets and gets a non-default namespace? Is hack/test-cmd.sh the right place to add that? |
I can see three possible "features" that require some notion of a "default namespace":
Do you see "default" being used for all of those? Not something that has to be determined in this PR, but something to think about. |
sync := req.URL.Query().Get("sync") == "true" | ||
timeout := parseTimeout(req.URL.Query().Get("timeout")) | ||
// if a namespace if specified, it's always used. | ||
// for list/watch operations, a namespace is not required if omitted. |
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 it will seem odd for the "average" user that normal GET and POST operate only on one namespace, but list and watch can operate on all namespaces. The things that need to list/watch all objects are special cases like controllers. I wonder if we should put the burden on those special cases to explicitly say they want to see all namespaces.
To that end, what if namespace is required to be specified for list and watch, but you can optionally specify "__ALL" in the namespace parameter to match all namespaces? What do you think?
Also thinking about the three different types of "default" namespaces and whether those should have their own special identifiers.
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 take on this is its less of a concern when we move away from the query parameter and instead namespace is in the resource path.
one minor addition to a test is requested. LGTM after that. |
f4330d0
to
998a549
Compare
Made following updates:
Thanks for feedback. |
998a549
to
7fdc31f
Compare
LGTM. One tiny suggestion. And please squash for merge. |
7fdc31f
to
1764a03
Compare
@erictune - squashed commits at appropriate boundaries. made last requested change. |
Hold on merge until we close the discussion about this requiring downtime and recreation of all resources. |
My preference is to not migrate existing resources into the new etcd key-paths, but I do think we need to start the discussion on how we can do both on and offline migration of etcd content in the future in case we move resources again. In reviewing the following from @thockin, it appears that services v2 is taking a similar statement here: https://groups.google.com/forum/#!topic/kubernetes-announce/oO7Bs_c16lI In a future PR, I think its imperative we can ask a registry object what schema version it is running [ which is different than api version ], and that we can ask a registry object to go from schema version N to N+1. |
1764a03
to
e7ceb34
Compare
If a live update is doable without crippling the feature (which services v2 But at this point it sounds like we have a few such things which together On Tue, Oct 7, 2014 at 8:25 AM, Derek Carr notifications@github.com wrote:
|
The migration process for this would require the following: Resources Affected:
To perform a migration, we would need to do the following:
Move resources Update each resource in above and ensure:
|
e7ceb34
to
f2ca39a
Compare
Rebase completed to update changes to api.JSONBase becoming api.TypeMeta |
if err != nil { | ||
return err | ||
if node.Dir { | ||
h.decodeNodeList(node.Nodes, slicePtr) |
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.
Can you return here to avoid the deep else?
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.
@smarterclayton No. It's possible in the future that we will have a node structure in etcd where a directory and a node exist at the same depth in the hierarchy, so we need to process all non-directory sibling nodes.
For example:
/key/registry/foo/bar <- Node.Dir=false
/key/registry/foo/abc <- Node.Dir=true
/key/registry/foo/abc/def <- Node.Dir = false
calling List nodes recursively on /key/registry/foo would need to return {bar,def}
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 meant continue
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 thing - update complete.
----- Original Message -----
From: "Clayton Coleman" notifications@github.com
To: "GoogleCloudPlatform/kubernetes" kubernetes@noreply.github.com
Cc: "Derek Carr" decarr@redhat.com
Sent: Wednesday, October 8, 2014 1:36:47 PM
Subject: Re: [kubernetes] Enforce namespace unique constraints (#1564)
@@ -139,16 +145,20 @@ func (h *EtcdHelper) ExtractList(key string, slicePtr interface{}, resourceVersi
}
v := pv.Elem()
for _, node := range nodes {
obj := reflect.New(v.Type().Elem())
err = h.Codec.DecodeInto([]byte(node.Value), obj.Interface().(runtime.Object))
if h.ResourceVersioner != nil {
_ = h.ResourceVersioner.SetResourceVersion(obj.Interface().(runtime.Object), node.ModifiedIndex)
// being unable to set the version does not prevent the object from being extracted
}
if err != nil {
return err
if node.Dir {
h.decodeNodeList(node.Nodes, slicePtr)
I meant continue
Reply to this email directly or view it on GitHub:
https://github.com/GoogleCloudPlatform/kubernetes/pull/1564/files#r18598677
One last comment, then LGTM |
f2ca39a
to
e72bf13
Compare
4147679
to
567bb1f
Compare
rebased again. |
567bb1f
to
2132c11
Compare
rebased again. |
I would like to look at this change, please hold for a bit. ETA 4 hours |
@@ -74,6 +74,15 @@ func (r *Request) Sync(sync bool) *Request { | |||
return r | |||
} | |||
|
|||
// Namespace sets a namespace parameter on a request based on the value in the context | |||
func (r *Request) Namespace(ctx api.Context) *Request { |
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.
Is there a reason why this function takes an api.Context? That is for tracking client requests server side. There is not much sense forcing clients to use it, no? I think this should just take a string. I also think it's something you should set on the client object, not the request object-- if you're doing namespaced operations, you probably want many in a row with the same namespace.
Very sorry for not noticing this earlier, I was out all last week and have just been catching up this week.
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 had the same argument earlier - derek had something that half convinced me (convenience for client code writers), although that may be solvable. Derek, sway me back??? :)
On Oct 10, 2014, at 2:17 PM, Daniel Smith notifications@github.com wrote:
In pkg/client/request.go:
@@ -74,6 +74,15 @@ func (r *Request) Sync(sync bool) *Request {
return r
}+// Namespace sets a namespace parameter on a request based on the value in the context
+func (r *Request) Namespace(ctx api.Context) *Request {
Is there a reason why this function takes an api.Context? That is for tracking client requests server side. There is not much sense forcing clients to use it, no? I think this should just take a string. I also think it's something you should set on the client object, not the request object-- if you're doing namespaced operations, you probably want many in a row with the same namespace.Very sorry for not noticing this earlier, I was out all last week and have just been catching up this week.
—
Reply to this email directly or view it on GitHub.
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.
Is there a reason why this function takes an api.Context? That is for tracking client requests server side. There is not much sense forcing clients to use it, no?
If you just think about the kubecfg client or the end-user of kubernetes, then sure, you tend to work in a single namespace, and this argument holds. In fact, its how I first wrote the code, but if you take a step back, you realize the majority of our existing clients are server-side and need to operate across namespace boundaries to perform their operation. It then becomes a matter of wanting to make it easier for those clients to operate.
The replication-controller and scheduler both follow a similar pattern:
- initialize the daemon with an interface X that is subset of kubeclient
- run a sync loop
- List/Watch over all name-spaces using X to resource type Y
- In response, do some series of CRUD operations in Y.Namespace
In OpenShift Origin, we have a similar pattern for build-controllers and deployment-controllers. I suspect others may choose to write plug-ins that follow the same convention to meet a particular need.
If we kept context on Client as a means of passing namespace around, you are left with the following when looking at the above style-use case.
A. Create new client objects when you change namespace boundary
B. Require the ability to mutate a context on a client interface
(A) seems like an anti-pattern when its an obvious control loop in our internal usage of Kubernetes to require so many throw-away clients when we have callers that work in multiple namespace boundaries.
(B) has issues with concurrency because we share the same client, and so we would need locking, and things get ugly quick.
That left me with (C) pass context as argument - which lets me reuse the client object, and keep clean enough reading code after actually writing all the other options for callers that work on resources across namespace boundaries.
As for this particular function taking a api.Context object instead of a string, it actually keeps the LIST/WATCH operations clean when working in the no namespace-boundary versus passing the empty string as input around.
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.
It should be possible to override the namespace on a particular request, if necessary. Just like we have defaults for other things, the client has a namespace setting which requests get by default, but if you need to change the namespace for a particular request, you can.
The common client functions should be able to get the namespace out of the object, anyway, you shouldn't have to pass it again in a context. I can't think of a reason why server components should even need to reference namespaces, other than perhaps copying it from a {replicationController,pod} to a {pod,binding}.
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.
As long as we agree with override on a particular request, then we get what we both want, and then its a manner of finding the best way to provide that override into the operation. Can we evolve this in a subsequent PR and not hold up this support? I know there are many things waiting on this feature making it in from our end, and it seems like this type of thing can get further discussion after the fact.
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.
The thing I want is for this to not take an api.Context. There's no reason not to take a string, is there?
(also, all other client methods shouldn't take api.Context, but I won't block this PR on 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.
Ok, I can update Namespace to take a string, and callers will do things like the following for now:
err = c.Put().Namespace(api.Namespace(ctx)).Path("services").Path(svc.ID).Body(svc).Do().Into(result)
Will open a separate PR to discuss alternate approaches to providing an override to client context in a thread-safe manner.
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 can live with that for the moment, thanks.
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.
Actually we can use #1708 to discuss possible alternative solutions now that you understand the current requirements I worked against.
2132c11
to
5e8d0bc
Compare
Updates requested by @lavalamp completed. |
LGTM |
Still LGTM. Not merging because my understanding is that this is to be merged on the "big breakage day", which is not chosen yet. Assigning to @thockin who I think will do the merges on that day. |
@@ -71,3 +77,12 @@ func ValidNamespace(ctx Context, resource *TypeMeta) bool { | |||
} | |||
return ns == resource.Namespace && ok | |||
} | |||
|
|||
// WithNamespaceDefaultIfNone returns a context whose namespace is the default if and only if the parent context has no namespace value |
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.
What is the difference between the default and not being set?
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.
The default Namespace is literally, Namespace="default".
It is the assumed namespace context when calling the legacy API patterns where namespace was not in the resource path.
A namespace not being set means the operation context has no namespace scope at all. It is useful for LIST and WATCH operations when working across namespace boundaries.
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 find this really confusing. Is it too late to ask for a system where "" == "default", and if you want to span namespaces, you have to explicitly say namespace "*"?
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.
From a REST perspective, GET /pods returning all pods seems more correct than GET /pods/ns/* returning all pods.
----- Original Message -----
@@ -71,3 +77,12 @@ func ValidNamespace(ctx Context, resource *TypeMeta)
bool {
}
return ns == resource.Namespace && ok
}
+
+// WithNamespaceDefaultIfNone returns a context whose namespace is the
default if and only if the parent context has no namespace valueI find this really confusing. Is it too late to ask for a system where "" ==
"default", and if you want to span namespaces, you have to explicitly say
namespace "*"?
Reply to this email directly or view it on GitHub:
https://github.com/GoogleCloudPlatform/kubernetes/pull/1564/files#r18857208
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, I'm not arguing about the REST path, I'm arguing about not using the go type's default value of "" to mean the default. Perhaps we need to make a custom type to represent namespaces.
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.
Fine with me, we can merge and fix up later.
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.
Final answer: is this PR done enough to commit tomorrow?
@lavalamp @derekwaynecarr @smarterclayton
On Wed, Oct 15, 2014 at 10:54 AM, Daniel Smith notifications@github.com
wrote:
In pkg/api/context.go:
@@ -71,3 +77,12 @@ func ValidNamespace(ctx Context, resource *TypeMeta) bool {
}
return ns == resource.Namespace && ok
}
+
+// WithNamespaceDefaultIfNone returns a context whose namespace is the default if and only if the parent context has no namespace valueFine with me, we can merge and fix up later.
Reply to this email directly or view it on GitHub
https://github.com/GoogleCloudPlatform/kubernetes/pull/1564/files#r18910297
.
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.
Yes.
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 vote yes
Sent from my iPhone
On Oct 15, 2014, at 4:29 PM, Tim Hockin notifications@github.com wrote:
In pkg/api/context.go:
@@ -71,3 +77,12 @@ func ValidNamespace(ctx Context, resource *TypeMeta) bool {
}
return ns == resource.Namespace && ok
}
+
+// WithNamespaceDefaultIfNone returns a context whose namespace is the default if and only if the parent context has no namespace value
Final answer: is this PR done enough to commit tomorrow? @lavalamp @derekwaynecarr @smarterclayton
…
—
Reply to this email directly or view it on GitHub.
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.
yes
On Wed, Oct 15, 2014 at 1:34 PM, Derek Carr notifications@github.com
wrote:
In pkg/api/context.go:
@@ -71,3 +77,12 @@ func ValidNamespace(ctx Context, resource *TypeMeta) bool {
}
return ns == resource.Namespace && ok
}
+
+// WithNamespaceDefaultIfNone returns a context whose namespace is the default if and only if the parent context has no namespace valueI vote yes
… <#149158515c230ada_>
Sent from my iPhone
On Oct 15, 2014, at 4:29 PM, Tim Hockin notifications@github.com
wrote: In pkg/api/context.go: > @@ -71,3 +77,12 @@ func ValidNamespace(ctx
Context, resource *TypeMeta) bool { > } > return ns == resource.Namespace
&& ok > } > + > +// WithNamespaceDefaultIfNone returns a context whose
namespace is the default if and only if the parent context has no namespace
value Final answer: is this PR done enough to commit tomorrow? @lavalamp
https://github.com/lavalamp @derekwaynecarr
https://github.com/derekwaynecarr @smarterclayton
https://github.com/smarterclayton … — Reply to this email directly or
view it on GitHub.—
Reply to this email directly or view it on GitHub
https://github.com/GoogleCloudPlatform/kubernetes/pull/1564/files#r18920554
.
5e8d0bc
to
bd85baa
Compare
Rebased branch to latest. |
a28f129
to
f4bf740
Compare
Forced a push to try to fix Travis flake on go 1.2, where go 1.3 worked. Go 1.2 was reporting an error reaching etcd in integration tests. |
I will kick travis until i get a green or get tired. On Thu, Oct 16, 2014 at 8:46 AM, Derek Carr notifications@github.com
|
f4bf740
to
085ca40
Compare
Thar she blows! |
…_constraints Enforce namespace unique constraints
This is the update to actually enable namespace support.
At the moment, I pass a namespace as a query parameter in order to support namespace scoping on the v1beta urls. Once v1beta3 lands, I will modify to pass namespace in the path. Note the client libraries were modified to make this easy to do, so should be a trivial change once ready.
I broke the commits into logical sections to improve readability.