Documentation ¶
Overview ¶
Package forest defines the Forest type.
Index ¶
- type Forest
- func (f *Forest) AddListener(l NamespaceListener)
- func (f *Forest) AddTypeSyncer(nss TypeSyncer)
- func (f *Forest) Get(nm string) *Namespace
- func (f *Forest) GetNamespaceNames() []string
- func (f *Forest) GetRoots() []*Namespace
- func (f *Forest) GetTypeSyncer(gvk schema.GroupVersionKind) TypeSyncer
- func (f *Forest) GetTypeSyncerFromGroupKind(gk schema.GroupKind) TypeSyncer
- func (f *Forest) GetTypeSyncers() []TypeSyncer
- func (f *Forest) Lock()
- func (f *Forest) OnChangeNamespace(log logr.Logger, ns *Namespace)
- func (f *Forest) RectifySubtreeUsages(log logr.Logger) []types.NamespacedName
- func (f *Forest) Unlock()
- type Namespace
- func (ns *Namespace) AllowsCascadingDeletion() bool
- func (ns *Namespace) AncestryNames() []string
- func (ns *Namespace) CanSetParent(p *Namespace) string
- func (ns *Namespace) ChildNames() []string
- func (ns *Namespace) ClearConditions()
- func (ns *Namespace) Conditions() []metav1.Condition
- func (ns *Namespace) CycleNames() []string
- func (ns *Namespace) DeleteSourceObject(gvk schema.GroupVersionKind, nm string)
- func (ns *Namespace) DescendantNames() []string
- func (ns *Namespace) Exists() bool
- func (ns *Namespace) FullDescendantNames() []string
- func (ns *Namespace) GetAncestorSourceNames(gvk schema.GroupVersionKind, name string) []types.NamespacedName
- func (ns *Namespace) GetHaltedRoot() string
- func (ns *Namespace) GetLabels() labels.Set
- func (n *Namespace) GetLocalUsages() v1.ResourceList
- func (ns *Namespace) GetNumSourceObjects(gvk schema.GroupVersionKind) int
- func (ns *Namespace) GetSourceNames(gvk schema.GroupVersionKind) []types.NamespacedName
- func (ns *Namespace) GetSourceObject(gvk schema.GroupVersionKind, nm string) *unstructured.Unstructured
- func (n *Namespace) GetSubtreeUsages() v1.ResourceList
- func (ns *Namespace) GetTreeLabels() map[string]int
- func (n *Namespace) HRQNames() []string
- func (ns *Namespace) HasAnchor(n string) bool
- func (ns *Namespace) HasSourceObject(gvk schema.GroupVersionKind, oo string) bool
- func (ns *Namespace) IsAncestor(other *Namespace) bool
- func (ns *Namespace) IsExternal() bool
- func (ns *Namespace) IsHalted() bool
- func (n *Namespace) Limits() v1.ResourceList
- func (ns *Namespace) Name() string
- func (ns *Namespace) Parent() *Namespace
- func (ns *Namespace) RelativesNames() []string
- func (n *Namespace) RemoveLimits(nm string)
- func (ns *Namespace) SetAnchors(anchors []string) (diff []string)
- func (ns *Namespace) SetCondition(tp, reason, msg string)
- func (ns *Namespace) SetExists() bool
- func (ns *Namespace) SetLabels(labels map[string]string) bool
- func (ns *Namespace) SetParent(p *Namespace)
- func (ns *Namespace) SetSourceObject(obj *unstructured.Unstructured)
- func (n *Namespace) TestOnlySetSubtreeUsage(rl v1.ResourceList)
- func (n *Namespace) TryUseResources(rl v1.ResourceList) error
- func (ns *Namespace) UnsetExists() bool
- func (ns *Namespace) UpdateAllowCascadingDeletion(acd bool) bool
- func (n *Namespace) UpdateLimits(nm string, l v1.ResourceList) bool
- func (n *Namespace) UseResources(newUsage v1.ResourceList)
- type NamespaceListener
- type TypeSyncer
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Forest ¶
type Forest struct {
// contains filtered or unexported fields
}
Forest defines a forest of namespaces - that is, a set of trees. It includes methods to mutate the forest and track problems such as cycles.
The forest should always be locked/unlocked (via the `Lock` and `Unlock` methods) while it's being mutated to avoid different controllers from making inconsistent changes.
func (*Forest) AddListener ¶ added in v1.1.0
func (f *Forest) AddListener(l NamespaceListener)
func (*Forest) AddTypeSyncer ¶
func (f *Forest) AddTypeSyncer(nss TypeSyncer)
AddTypeSyncer adds a reconciler to the types list.
func (*Forest) GetNamespaceNames ¶
GetNamespaceNames returns names of all namespaces in the cluster.
func (*Forest) GetRoots ¶
GetRoots returns all the root namespaces in the cluster. Any possible cycles are omitted since we look for namespaces with no parent and cycles must always be at roots.
func (*Forest) GetTypeSyncer ¶
func (f *Forest) GetTypeSyncer(gvk schema.GroupVersionKind) TypeSyncer
GetTypeSyncer returns the reconciler for the given GVK or nil if the reconciler does not exist.
func (*Forest) GetTypeSyncerFromGroupKind ¶
func (f *Forest) GetTypeSyncerFromGroupKind(gk schema.GroupKind) TypeSyncer
GetTypeSyncerFromGroupKind returns the reconciler for the given GK or nil if the reconciler does not exist.
func (*Forest) GetTypeSyncers ¶
func (f *Forest) GetTypeSyncers() []TypeSyncer
GetTypeSyncers returns the types list. Retuns a copy here so that the caller does not need to hold the mutex while accessing the returned value and can modify the returned value without fear of corrupting the original types list.
func (*Forest) OnChangeNamespace ¶ added in v1.1.0
func (*Forest) RectifySubtreeUsages ¶ added in v1.1.0
func (f *Forest) RectifySubtreeUsages(log logr.Logger) []types.NamespacedName
RectifySubtreeUsages ensures that the subtree usages of every namespaces is in sync with all of its descendants. This should be ensured by the logic in UseResources, but bugs happen, so this is an added level of safety. If any discrepancies are found, this function logs an error, updates the corrected usages in-memory, and returns a list of affected HRQ objects so that they can be re-reconciled to show the corrected usages.
The forest lock must be held when calling this function.
type Namespace ¶
type Namespace struct { // ManagedLabels are all managed labels explicitly set on this namespace (i.e., excluding anything // set by ancestors). ManagedLabels map[string]string // ManagedAnnotations are all managed annotations explicitly set on this namespace (i.e., // excluding anything set by ancestors). ManagedAnnotations map[string]string // IsSub indicates that this namespace is being or was created solely to live as a // subnamespace of the specified parent. IsSub bool // Anchors store a list of anchors in the namespace. Anchors []string // Manager stores the manager of the namespace. The default value of "hnc.x-k8s.io" means it's // managed by HNC. Any other value means that the namespace is an "external" namespace, whose // metadata (e.g. labels) are set outside of HNC. Manager string // contains filtered or unexported fields }
Namespace represents a namespace in a forest. Other than its structure, it contains some properties useful to the reconcilers.
func (*Namespace) AllowsCascadingDeletion ¶
AllowsCascadingDeletion returns true if the namespace's or any of the ancestors' allowCascadingDeletion field is set to true.
func (*Namespace) AncestryNames ¶
AncestryNames returns all ancestors of this namespace. The namespace itself is the last element of the returned slice, with the root at the beginning of the list.
This method is cycle-safe, and can be used to detect and recover from cycles. If there's a cycle, the first ancestor that's a member of the cycle we encounter is repeated at the beginning of the list.
func (*Namespace) CanSetParent ¶
CanSetParent returns the empty string if the assignment is currently legal, or a non-empty string indicating the reason if it cannot be done.
func (*Namespace) ChildNames ¶
ChildNames returns a sorted list of names or nil if there are no children.
func (*Namespace) ClearConditions ¶
func (ns *Namespace) ClearConditions()
ClearConditions set conditions to nil.
func (*Namespace) Conditions ¶
Conditions returns a full list of the conditions in the namespace.
func (*Namespace) CycleNames ¶
CycleNames returns nil if the namespace is not in a cycle, or a list of names in the cycle if it is. All namespaces in the cycle return the same list, which is the same as calling ns.AncestryNames() on the namespaces with the lexicographically smallest name.
func (*Namespace) DeleteSourceObject ¶
func (ns *Namespace) DeleteSourceObject(gvk schema.GroupVersionKind, nm string)
DeleteSourceObject deletes a source object by name.
func (*Namespace) DescendantNames ¶
DescendantNames returns a slice of strings like ["achild", "agrandchild", "bchild", ...] of names of all namespaces in its subtree, or nil if the namespace has no descendents. The names are returned in alphabetical order (as defined by `sort.Strings()`), *not* depth-first, breadth-first, etc.
This method is cycle-safe. If there are cycles, each namespace is only listed once.
func (*Namespace) FullDescendantNames ¶
FullDescendantNames returns a sorted list of descendant namespaces that are full namespaces.
func (*Namespace) GetAncestorSourceNames ¶ added in v1.1.0
func (ns *Namespace) GetAncestorSourceNames(gvk schema.GroupVersionKind, name string) []types.NamespacedName
GetAncestorSourceNames returns all source objects with the specified name in the ancestors (including itself) from top down. If the name is not specified, all the source objects in the ancestors will be returned.
func (*Namespace) GetHaltedRoot ¶ added in v1.0.0
GetHaltedRoot returns the name of the lowest subtree that's halted for any reason *other* than a higher ancestor being halted. This can be the name of the current namespace, or the empty string if there are no halted ancestors.
func (*Namespace) GetLocalUsages ¶ added in v1.1.0
func (n *Namespace) GetLocalUsages() v1.ResourceList
GetLocalUsages returns a copy of local resource usages.
func (*Namespace) GetNumSourceObjects ¶
func (ns *Namespace) GetNumSourceObjects(gvk schema.GroupVersionKind) int
GetNumSourceObjects returns the total number of source objects of a specific GVK in the namespace.
func (*Namespace) GetSourceNames ¶ added in v1.1.0
func (ns *Namespace) GetSourceNames(gvk schema.GroupVersionKind) []types.NamespacedName
GetSourceNames returns all source objects in the namespace.
func (*Namespace) GetSourceObject ¶
func (ns *Namespace) GetSourceObject(gvk schema.GroupVersionKind, nm string) *unstructured.Unstructured
GetSourceObject gets a source object by name. If it doesn't exist, return nil.
func (*Namespace) GetSubtreeUsages ¶ added in v1.1.0
func (n *Namespace) GetSubtreeUsages() v1.ResourceList
GetSubtreeUsages returns a copy of subtree resource usages.
func (*Namespace) GetTreeLabels ¶ added in v1.0.0
GetTreeLabels returns all the tree labels with the values converted into integers for easier manipulation.
func (*Namespace) HRQNames ¶ added in v1.1.0
HRQNames returns the names of every HRQ object in this namespace
func (*Namespace) HasSourceObject ¶
func (ns *Namespace) HasSourceObject(gvk schema.GroupVersionKind, oo string) bool
HasSourceObject returns if the namespace has a source object.
func (*Namespace) IsAncestor ¶
IsAncestor is *not* cycle-safe, so should only be called from namespace trees that are known not to have cycles.
func (*Namespace) IsExternal ¶
IsExternal returns true if the namespace is not managed by HNC.
func (*Namespace) IsHalted ¶ added in v1.0.0
IsHalted returns true if this namespace has an ActivitiesHalted condition for any reason *other* than one of its ancestors also being halted.
func (*Namespace) Limits ¶ added in v1.1.0
func (n *Namespace) Limits() v1.ResourceList
Limits returns limits limits specified in quotas.limits of the current namespace and its ancestors. If there are more than one limits for a resource type, the most strictest limit will be returned.
func (*Namespace) Name ¶
Name returns the name of the namespace, of "<none>" if the namespace is nil.
func (*Namespace) RelativesNames ¶
RelativesNames returns the children and parent.
func (*Namespace) RemoveLimits ¶ added in v1.1.0
RemoveLimits removes limits specified by the HierarchicalResourceQuota object of the given name.
func (*Namespace) SetAnchors ¶
SetAnchors updates the anchors and returns a difference between the new/old list.
func (*Namespace) SetCondition ¶
SetCondition adds a new condition to the current condition list.
Any condition with ReasonAncestor is ignored; this is added dynamically when calling Conditions().
func (*Namespace) SetExists ¶
SetExists marks this namespace as existing, returning true if didn't previously exist.
func (*Namespace) SetLabels ¶
Deep copy the input labels so that it'll not be changed after. It returns true if the labels are updated; returns false if there's no change.
func (*Namespace) SetParent ¶
SetParent modifies the namespace's parent, including updating the list of children and updating the old and new subtree usage. It may result in a cycle being created; this can be prevented by calling CanSetParent before, or seeing if it happened by calling CycleNames afterwards.
func (*Namespace) SetSourceObject ¶
func (ns *Namespace) SetSourceObject(obj *unstructured.Unstructured)
SetSourceObject updates or creates the source object in forest.namespace.
func (*Namespace) TestOnlySetSubtreeUsage ¶ added in v1.1.0
func (n *Namespace) TestOnlySetSubtreeUsage(rl v1.ResourceList)
TestOnlySetSubtreeUsage overwrites the actual, calculated subtree usages and replaces them with arbitrary garbage. Needless to say, you should never call this, unless you're testing HNC's ability to recover from arbitrary garbage.
The passed-in arg is used as-is, not copied. This is test code, so deal with it 😎
func (*Namespace) TryUseResources ¶ added in v1.1.0
func (n *Namespace) TryUseResources(rl v1.ResourceList) error
TryUseResources checks resource limits in the namespace and its ancestors when given proposed absolute (not delta) resource usages in the namespace. If there are any changes in the usages, we only check to see if any proposed increases take us over any limits. If any of them exceed resource limits, it returns an error suitable to display to end users; otherwise, it updates the in-memory usages of both this namespace as well as all its ancestors. Callers of this method are responsible for updating resource usage status of the HierarchicalResourceQuota objects.
TryUseResources is called by the HRQ admission controller to decide if a ResourceQuota.Status update issued by the K8s ResourceQuota admission controller is allowed. Since UseResources() modifies resource usages in the in-memory forest, the forest lock should be held while calling the method.
Normally, admission controllers shouldn't assume that if they allow a change, that this change will actually be performed, since another admission controller can be called later and deny it. Uniquely for Resource Quotas, this isn't true - the K8s apiserver only attempts to update the RQ status when _all_ other admission controllers have passed and the resources are about to be consumed. In rare cases, of course, the resources may not be consumed (e.g. due to an error in etcd) but the apiserver runs a cleanup process that occasionally syncs up actual usage with the usage recorded in RQs. When the RQs are changed, we'll be updated too.
Based on observations, the K8s ResourceQuota admission controller is called only when a resource is consumed, not when a resource is released. Therefore, in most cases, the proposed resource usages that the HRQ admission controller received should be larger than in-memory resource usages. However, this function is robust to (that is, always allows) decreases as well, mainly because it's easier to test - plus, who knows, the K8s behaviour may change in the future.
This may allow one weird case where a user may be allowed to use something they weren't supposed to. Let's say you're well over your limit, and then in quick succession, some resources are deleted, and some _fewer_ are added, but enough to still go over the limit. In that case, there's a race condition between this function being called, and the RQ reconciler updating the baseline resource usage. If this function wins, it will look like resource usage is decreasing, and will be incorrectly allowed. If the RQ reconciler runs first, we'll see that the usage is incorrectly _increasing_ and it will be disallowed. However, I think the simplicity of not trying to prevent this (hopefully very unlikely) corner case is more valuable than trying to catch it.
func (*Namespace) UnsetExists ¶
UnsetExists marks this namespace as missing, returning true if it previously existed. It also removes it from its parent, if any, since a nonexistent namespace can't have a parent.
func (*Namespace) UpdateAllowCascadingDeletion ¶
UpdateAllowCascadingDeletion updates if this namespace allows cascading deletion. It returns true if the value has changed, false otherwise.
func (*Namespace) UpdateLimits ¶ added in v1.1.0
func (n *Namespace) UpdateLimits(nm string, l v1.ResourceList) bool
UpdateLimits updates in-memory limits of the HierarchicalResourceQuota object of the given name. Returns true if there's a difference.
func (*Namespace) UseResources ¶ added in v1.1.0
func (n *Namespace) UseResources(newUsage v1.ResourceList)
UseResources sets the absolute resource usage in this namespace, and should be called when we're being informed of a new set of resource usage. It also updates the subtree usage in this namespace and all its ancestors.
The callers will typically then enqueue all ancestor HRQs to update their usages with apiserver.
UseResources can be called in the following scenarios:
- Called by the HRQ admission controller when a request is allowed
- Called by the HRQ ResourceQuota reconciler when it observes `local` usages are different from ResourceQuota.Status.Used
- Called by the HRQ Namespace reconciler to remove `local` usages of a namespace from the subtree usages of the previous ancestors of the namespace.
- Called by the SetParent to remove `local` usages of a namespace from the subtree usages of the previous ancestors of the namespace and add the usages to the new ancestors following a parent update
type NamespaceListener ¶ added in v1.1.0
type NamespaceListener interface { // OnChangeNamespace is called whenever a namespace changes. OnChangeNamespace(logr.Logger, *Namespace) }
NamespaceListener has methods that get called whenever a namespace changes.
type TypeSyncer ¶
type TypeSyncer interface { // Provides the GVK that is handled by the reconciler who implements the interface. GetGVK() schema.GroupVersionKind // SetMode sets the propagation mode of objects that are handled by the reconciler who implements // the interface. The method also syncs objects in the cluster for the type handled by the // reconciler if necessary. SetMode(context.Context, logr.Logger, api.SynchronizationMode) error // GetMode gets the propagation mode of objects that are handled by the reconciler who implements the interface. GetMode() api.SynchronizationMode // CanPropagate returns true if Propagate mode or AllowPropagate mode is set CanPropagate() bool // GetNumPropagatedObjects returns the number of propagated objects on the apiserver. GetNumPropagatedObjects() int }
TypeSyncer syncs objects of a specific type. Reconcilers implement the interface so that they can be called by the HierarchyReconciler if the hierarchy changes.