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
apiserver: re-architect storage #1451
Comments
Part of the context maybe. Partition/shard seems like it varies by the context, vs being distinct. |
I've had some further thoughts about this. Work items:
The motivations for this restructuring:
Ideally, when this is done, we can easily serve a REST endpoint that implements our storage interface for component plugins. |
I've started fixing this with #3789 - I'm moving pure API behavior (the set of validation and creation prior to persisting an object into a new package |
Well, ok, we need to do something once we add custom verbs, such as resize, clone, and stop. |
By business logic I mean validation, and decision about what fields can be written during an update. If we have authz at a field level and fix authz to be able to know about the diff, we don't need the latter as much. Is that the biz logic you're concerned with? |
No, I'm primarily concerned with on-the-fly computation of object status in the apiserver, as opposed to in the controller or other responsible component. ResourceLocation doesn't make me happy, either. Pluggable defaulting and validation makes sense. |
K, same page |
To throw a bit of newly-understood information out: Any API object must not ever include an unexported field, unless said If the API decoded into truly dumb structs - never any private values - and On Tue, Jan 27, 2015 at 8:07 PM, Clayton Coleman notifications@github.com
|
As we discussed, the important thing IMO is to group the code that implements the objects -- it doesn't have to be in classes. I really, really, really want to keep the "dumb structs" in a form that can be used by clients and third-party components without complex logic, with fully explicit desired and current state for common use cases, so that it doesn't require complex objects or client libraries to interpret, in general. However, for the apiserver, it would be desirable to group custom code by object for stripping out readonly fields, authz, defaulting, conversion, validation, custom verbs (which should just imperatively update the desired state and not directly trigger other actions), fuzzing, test-object generation, etc. We may also want some of that in kubectl, where it's useful to be able to decode and encode multiple API versions, but we don't want to make it impossible to write clients in other languages, either. The actual CRUD/REST operations should otherwise be generic. Status and derived objects (e.g., endpoints) should be filled in by the relevant object controller. |
Agree. The further we go here, the more the core objects remain dumb structs. ----- Original Message -----
|
Yeah, I get that the API being defined as dumb structs is super-clean. But On Thu, Jan 29, 2015 at 7:15 AM, Clayton Coleman notifications@github.com
|
It's interesting you feel that way. I know we have a couple of hot touch points, but the rest seem not that bad. I.e accessors for lists (we could convert them to maps on conversion) and getting access to object meta and TypeMeta are the two I think of most. What are the ones you rank most highly?
|
Look at Yu-Ju's PR to move defaulting out of the validation code. A If we instead said:
I haven't thought this through fully, it's just something that has been On Thu, Jan 29, 2015 at 5:33 PM, Clayton Coleman notifications@github.com
|
We have a "constructor" that could do that in scheme.New. Or we could expose scheme.Default(obj) and change all the places to use it.
Doesn't this mean validation has to be duplicated across api versions?
Doesn't that mean creating a non trivial object in the versioned object stays equally hard? Agree we do massive refactors in test cases on every trivial change... However, that particular problem wouldn't go away with a smart internal object or a constructor (we exchange go struct init syntax for X lines of setting fields and attributes). I think the biggest pain is we have no smart refactor tool that works on init structures, so no matter what, any non-trivial object has to be changed in a billion places.
|
Whatever "constructor" we use, it needs to basically take whole dumb API structs as input. There's no point in duplicating the API objects with some kind of builder or options object. That would be unmaintainable. It is true that our code is more aspect-oriented than object-oriented currently. As I said before, I think the most important thing is to group the code by API object. Effectively, our "constructor" is DecodeInto: Decoding from versioned structs into the internal rep could leverage this. @smarterclayton I don't think @thockin necessarily meant that we'd apply validation to the versioned structs, but that we'd validate the internal rep before returning it from the "constructor". If our tests used the versioned structs, we wouldn't need to change them when changing the internal rep. |
We should address #2457 when this is done. |
Related: #2977 |
I actually did mean that validation would run against versioned objects. I don't have time to pursue this at the moment, so I'm just watching On Fri, Feb 27, 2015 at 11:43 PM, Brian Grant notifications@github.com
|
I just came across this issue. I am not as familiar with the code as all of you, but from my experience so far, I definitely agree with Tim; I think the code would be simpler/more maintainable (or at least easier to understand) if we had true Go objects corresponding to the API objects. I agree that better grouping the code that manipulates each type would be helpful but I'm not sure it would be as helpful as using objects. I don't think using objects precludes use of "dumb structs" by clients, as the objects/smart structs would just be internal to the API server. |
I think we need to version our client libraries; having it work with the unversioned types makes this hard to get behind. Once the client is versioned, then I think a model where we run the versioned types through a constructor to produce the internal object might be a good idea, but I want to consider it some more. |
/subscribe |
This issue seems to have drifted considerably. It is true now that we have per-resource storage locations. That's a good step. Actually getting alternative storage backends should be easier now, but still challenging. No more will be done on this for 1.2. |
@lavalamp I guess this has progressed since last year. Should we also move it to https://github.com/kubernetes/apiserver if there is still work tbd? |
I think this can be closed and we can open something new to track. |
Currently, each RESTStorage implementation has its own storage object, which eventually ends up being the same etcd instance for all components.
We should consider instead having apiserver pass a storage destination into each RESTStorage method. This would make it much more clear how to write plugins, both those that you compile into the master binary, and those that are external binaries.
This would also let us eventually do fancy things like sharding (or isolating) keys across different etcd clusters without having to make sweeping changes to all the storage objects.
(See #1355, which sparked this)
The text was updated successfully, but these errors were encountered: