Skip to content

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

Merged
merged 1 commit into from
Mar 31, 2023
Merged

Add to README #38

merged 1 commit into from
Mar 31, 2023

Conversation

rmweir
Copy link
Contributor

@rmweir rmweir commented Feb 25, 2023

No description provided.

@rmweir rmweir force-pushed the add-docs branch 5 times, most recently from 534111b to 0b84cea Compare February 27, 2023 18:13
@rmweir rmweir requested a review from a team February 27, 2023 20:44
Copy link
Contributor

@cmurphy cmurphy left a 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.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
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.

Copy link
Contributor Author

@rmweir rmweir Mar 6, 2023

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.

Copy link
Contributor

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

Choose a reason for hiding this comment

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

Suggested change
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?

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

@rmweir rmweir requested a review from cmurphy March 6, 2023 19:03
@rmweir
Copy link
Contributor Author

rmweir commented Mar 6, 2023

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

@rmweir rmweir requested a review from a team March 6, 2023 21:17
Copy link
Contributor

@moio moio left a 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!
Copy link
Contributor

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.

Copy link
Contributor Author

@rmweir rmweir Mar 17, 2023

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

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor Author

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.

Copy link
Contributor Author

@rmweir rmweir Mar 17, 2023

Choose a reason for hiding this comment

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

Copy link
Contributor

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

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

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added.

Copy link
Contributor

@moio moio left a 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.
Copy link
Contributor

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.

Copy link
Contributor

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 Indexers are caches actually - indeed the Indexer interface extends Store. Now Stores (and, by extension, Indexers) 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 Indexers 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

Copy link
Contributor

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 Indexers are caches actually

I agree Indexers 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
Comment on lines 58 to 60
Informer
EventHandler
Indexers
Copy link
Contributor

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.

Suggested change
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.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
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.

@rmweir
Copy link
Contributor Author

rmweir commented Mar 28, 2023

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

@moio
Copy link
Contributor

moio commented Mar 31, 2023

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

Copy link
Contributor

@KevinJoiner KevinJoiner left a 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.

@rmweir rmweir merged commit f2977a6 into rancher:master Mar 31, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants