-
Notifications
You must be signed in to change notification settings - Fork 55
Add to README #38
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
Add to README #38
Conversation
534111b
to
0b84cea
Compare
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.
Awesome work 👍
README.md
Outdated
### Lasso Controller | ||
Lasso standardizes what a controller is with its controller package. Lasso introduces factories for managing caches, controllers, and clients within a project. This is imperative for rancher which manages many instances of many types. | ||
|
||
The lasso controller uses the client-go work-queue. Lasso registers an event handler that processes the work-queue. The work-queue oriented handler makes it so only one event handler needs to be added for each Kubernetes type. Now registering a handler just adds it to the type's controller's list of handlers. Enqueueing an objects adds it to the work-queue. Each object in the work-queue will then be processed by workers running in go routines. The controller's event handler will re-enqueue any object that causes one or more of the controller's handlers to return an error that is not of type `ErrIgnore`. This work-queue can also have objects enqueued from other sources. This functionality is exposed through the controller's Enqueue function. This transforms controllers from only being reactive to Kubernetes resources and cache re-syncs, to also being invokable by code. |
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 lasso controller uses the client-go work-queue. Lasso registers an event handler that processes the work-queue. The work-queue oriented handler makes it so only one event handler needs to be added for each Kubernetes type. Now registering a handler just adds it to the type's controller's list of handlers. Enqueueing an objects adds it to the work-queue. Each object in the work-queue will then be processed by workers running in go routines. The controller's event handler will re-enqueue any object that causes one or more of the controller's handlers to return an error that is not of type `ErrIgnore`. This work-queue can also have objects enqueued from other sources. This functionality is exposed through the controller's Enqueue function. This transforms controllers from only being reactive to Kubernetes resources and cache re-syncs, to also being invokable by code. | |
The lasso controller uses the client-go work-queue. Lasso registers an event handler that processes the work queue. The work queue oriented handler makes it so only one event handler needs to be added for each Kubernetes type. Now registering a handler just adds it to the type's controller's list of handlers. Enqueuing an objects adds it to the work queue. Each object in the work queue will then be processed by workers running in goroutines. The controller's event handler will re-enqueue any object that causes one or more of the controller's handlers to return an error that is not of type `ErrIgnore`. This work queue can also have objects enqueued from other sources. This functionality is exposed through the controller's Enqueue function. This transforms controllers from only being reactive to Kubernetes resources and cache re-syncs, to also being invokable by code. |
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 looks wrong but I believe enqueueing is the proper spelling.
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 guess it's both, my bad https://en.wiktionary.org/wiki/enqueuing
README.md
Outdated
The sharedController can create its underlying controller. It will only do so once a handler is registered to the sharedController. | ||
|
||
### Lasso Client | ||
Wraps Kubernetes client. Contains GVK fields for managing the client in a parent struct such as the sharedClientyFactory. |
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.
Wraps Kubernetes client. Contains GVK fields for managing the client in a parent struct such as the sharedClientyFactory. | |
Wraps Kubernetes client. Contains GVK fields for managing the client in a parent struct such as the sharedClientFactory. |
What's the value add for using the lasso client vs the k8s client?
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 mostly exists to facilitate shareClientFactory by having a GVK field. It also does have some additional options that are not exposed by the Kubernetes client, such as WithAgent and soon impersonation but this is secondary to its intent. I can add a note though.
@cmurphy have addressed most of your feedback and otherwise replied to anything else. I have left my update in a separate commit in case that helps with reviewing. Will squash when ready for approval. |
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.
Left some totally optional comments - intention being to be even more friendly to absolute beginners 😇
README.md
Outdated
The `Controller` is a simple approach that wraps the standard pattern of worker goroutines and workqueue. The package | ||
depends on the `cache` and `client` removing the need for generated clientsets, informers, and listers. | ||
# Kubernetes Controllers | ||
_Note:_ This will be a very brief crash course. We encourage you to look up other resources regarding the topic, there are many! |
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.
Optional: offer few links to high-quality material.
Rationale: beginners struggle when there is abundance of low quality material.
Personally https://github.com/aiyengar2/k8s-docs/blob/main/docs/controllers/00_introduction.md and the https://web.microsoftstream.com/video/967a4311-fb11-4637-90c9-a4be4c180dad series of videos helped - but I was not starting completely from scratch.
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 second link is not open to everyone. I will link the sample controller project instead of that one.
README.md
Outdated
|
||
There are many frameworks to help achieve the above goals. Lasso is one of them. What most of these frameworks have in common, including lasso, is that they use functionality from client-go to achieve these goals. You could achieve these goals by using just client-go. For the remainder of this section, we will fly through how a controller is put together just using client-go concepts. | ||
|
||
An informer is used to achieve the above goals needed for a Kubernetes Controller. An informer possesses an event handler and a set of indexers. The underlying structs are different but a pseudo struct is shown below to illustrate 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.
Before introducing how an informer is composed, it would help highlighting its role/function/goal.
Actually, since a lot of concepts are introduced, it would help adding definitions for each new jargon term, with examples eg.:
A resource is anything Kubernetes manages - such as pods, ingresses, jobs and so on.
An event is a change to a resource - such as an addition, an update or a deletion.
An informer is a Go object that observes a set of resources through the Kubernetes API and reacts to events pertaining to them. It also keeps a local cache of its objects, to avoid to load the API excessively. You could have your own informer to react to changes in basic Kubernetes resources, or your own.
An indexer is a component inside of informers. Its job is to maintain copies of resources temporarily - for example as caches or work queues. As the name implies, indexers keep indices on their resources for fast access.
...and so on
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 feel like indexers are further explained here https://github.com/rancher/lasso/pull/38/files#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5R63 and informers are better explained here https://github.com/rancher/lasso/pull/38/files#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5R65. The linked image should also help clarify here https://github.com/rancher/lasso/pull/38/files#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5R69.
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.
Otherwise, I want to keep this doc more on the lean side. I want to give just enough info if you haven't been exposed to controllers but I don't think this is the place for a general k8s 101.
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.
OK, so, I did not write that comment to suggest broadening the scope of the content, but rather to give an example on how adding a bit more structure to the prose could help making it easier to scan for keywords.
A beginner would read the paragraph and extract all the important information. Then go forward with the reading (and perhaps the coding), inevitably forget part of that and at some later point ask "what was an indexer again?" - having a succint, isolated paragraph with indexer in bold somewhere helps more than plain prose in answering that question.
That said I completely trust you in knowing readers better and picking the most appropriate final form 👍
|
||
# Lasso's Value Proposition | ||
### Lasso Controller | ||
Lasso standardizes what a controller is with its controller package. Lasso introduces factories for managing caches, controllers, and clients within a project. This is imperative for rancher which manages many instances of many types. |
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 remains important to highlight that, in Kubernetes jargon, a controller is a pattern, while in Lasso it is an object.
The main pain point Lasso strives to solve is DX in writing controllers: make it as easy as implementing few interfaces, like normal practice in Golang.
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 went ahead and updated the opening definition of controller to clarify that it is lasso which uses it to refer to its controller structure(s).
Wraps Kubernetes client. Contains GVK fields for managing the client in a parent struct such as the sharedClientFactory. Exposes some additional functionality like configuring the User-Agent header for the underlying REST client. | ||
|
||
### Lasso Cache | ||
The lasso Cache is a light wrapper around a [SharedIndexInformer from client-go](https://pkg.go.dev/k8s.io/client-go/tools/cache#SharedIndexInformer). |
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.
Why should I use it instead of SharedIndexInformer?
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.
Added.
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.
Great work
README.md
Outdated
|
||
The EventHandler can have different logic for create, update, and delete events. The event handler is where the controller comes in. You use it to configure the create, update, and delete event handlers. | ||
|
||
The indexers are a type of computational cache. They are used to map a key to an object, given a function, then the result is saved. Here it is easiest to think of them simply- they receive some metadata such as the name and namespace of the an object and return that object. |
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 am unsure how to reword this, but I wouldn't want people to think each indexer has its own cache. Maybe something like an indexer is a function that creates a key for each object, allowing for faster lookups. An indexer does this by moving some of the computational load from when the data is accessed to the moment the data is stored in the cache. All Caches by default have a namespace indexer called 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.
I wouldn't want people to think each indexer has its own cache
Hmm, it is my understanding that Indexer
s are caches actually - indeed the Indexer
interface extends Store
. Now Store
s (and, by extension, Indexer
s) can in principle be used for anything - but in practice in client-go they are always used as caches of k8s resource objects (with various usage patterns and deletion policies).
One thing that confused me a lot is that Indexer
s are not Indexers
! Indexers
(with the plural in the interface name!) is, indeed, a completely different concept - a map from string
to IndexFunc
- the latter being a function from any
to a string slice.
Please see:
https://github.com/kubernetes/client-go/blob/1517ffb8d37c99e6a3a2842bcdee0aa271f0332b/tools/cache/index.go#L35-L36
vs
https://github.com/kubernetes/client-go/blob/master/tools/cache/index.go#L97-L98
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.
Hmm, it is my understanding that
Indexer
s are caches actually
I agree Indexer
s non-plural is essentially a cache.
But the above struct was using plural Indexers
which is not a cache.
Informer
EventHandler
Indexers
We are probably better off explaining that there are two concepts Indexers
and Indexer
and distinguishing them separately to avoid conflation of the two. Especially because anyone using this package will most likely be working with theIndexers
interface
README.md
Outdated
Informer | ||
EventHandler | ||
Indexers |
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 feel like the role of Indexers can get confusing without including the Store at a high level. We do not have to go into the different Store implementations but simply use Stores to help outline the relationship between Informers and Indexers. Such as, an Informer passes objects through indexers to be placed in a Store, and Indexers create indexes on those objects for efficient reads from the store.
Informer | |
EventHandler | |
Indexers | |
Informer | |
EventHandler | |
Indexers | |
Store |
README.md
Outdated
|
||
The indexers are a type of computational cache. They are used to map a key to an object, given a function, then the result is saved. Here it is easiest to think of them simply- they receive some metadata such as the name and namespace of the an object and return that object. | ||
|
||
The informer first populates the indexer by listing it from a client. Then the informer watches the Kubernetes resource it is assigned to. When an event comes through, it uses that event to update its indexer (by remapping the object associated with the event) and to execute its event handlers. |
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 informer first populates the indexer by listing it from a client. Then the informer watches the Kubernetes resource it is assigned to. When an event comes through, it uses that event to update its indexer (by remapping the object associated with the event) and to execute its event handlers. | |
The informer first populates the indexers and the store by listing all resources returned from a client. Then the informer watches the Kubernetes resource it is assigned to. When an event comes through, it uses that event to update its indexer and execute its event handlers. It does this by remapping the object associated with the event. |
@KevinJoiner @moio I have addressed feedback including changing the informer section in this commit 4dfb4ec. Any comments that are unresolved are because I did not do exactly what was requested and instead implemented a compromise, so it is up to you to resolve. I think along with the disclaimer on this being a simplification of the actual inner workings of these components, this should give enough information for understanding how lasso is working while not misguiding anyone that will go on to learn more. |
@rmweir I think the current text is good, it's an improvement on the earlier revision, and at this point so much better than the original that it should be merged. Of course refinements by anyone are always possible via PR - I just wouldn't wait for perfection while we have a substantial improvement already 👍 👍 👍 |
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 agree with @moio, I think this captures a lot of good information, that was previously undocumented.
No description provided.