forest

package
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 12, 2023 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package forest defines the Forest type.

Index

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 NewForest

func NewForest() *Forest

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) Get

func (f *Forest) Get(nm string) *Namespace

Get returns a `Namespace` object representing a namespace in K8s.

func (*Forest) GetNamespaceNames

func (f *Forest) GetNamespaceNames() []string

GetNamespaceNames returns names of all namespaces in the cluster.

func (*Forest) GetRoots

func (f *Forest) GetRoots() []*Namespace

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) Lock

func (f *Forest) Lock()

func (*Forest) OnChangeNamespace added in v1.1.0

func (f *Forest) OnChangeNamespace(log logr.Logger, ns *Namespace)

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.

func (*Forest) Unlock

func (f *Forest) Unlock()

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

func (ns *Namespace) AllowsCascadingDeletion() bool

AllowsCascadingDeletion returns true if the namespace's or any of the ancestors' allowCascadingDeletion field is set to true.

func (*Namespace) AncestryNames

func (ns *Namespace) AncestryNames() []string

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

func (ns *Namespace) CanSetParent(p *Namespace) string

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

func (ns *Namespace) ChildNames() []string

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

func (ns *Namespace) Conditions() []metav1.Condition

Conditions returns a full list of the conditions in the namespace.

func (*Namespace) CycleNames

func (ns *Namespace) CycleNames() []string

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

func (ns *Namespace) DescendantNames() []string

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) Exists

func (ns *Namespace) Exists() bool

Exists returns true if the namespace exists.

func (*Namespace) FullDescendantNames

func (ns *Namespace) FullDescendantNames() []string

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

func (ns *Namespace) GetHaltedRoot() string

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) GetLabels

func (ns *Namespace) GetLabels() labels.Set

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

func (ns *Namespace) GetTreeLabels() map[string]int

GetTreeLabels returns all the tree labels with the values converted into integers for easier manipulation.

func (*Namespace) HRQNames added in v1.1.0

func (n *Namespace) HRQNames() []string

HRQNames returns the names of every HRQ object in this namespace

func (*Namespace) HasAnchor

func (ns *Namespace) HasAnchor(n string) bool

HasAnchor returns true if the name exists in the anchor list.

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

func (ns *Namespace) IsAncestor(other *Namespace) bool

IsAncestor is *not* cycle-safe, so should only be called from namespace trees that are known not to have cycles.

func (*Namespace) IsExternal

func (ns *Namespace) IsExternal() bool

IsExternal returns true if the namespace is not managed by HNC.

func (*Namespace) IsHalted added in v1.0.0

func (ns *Namespace) IsHalted() bool

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

func (ns *Namespace) Name() string

Name returns the name of the namespace, of "<none>" if the namespace is nil.

func (*Namespace) Parent

func (ns *Namespace) Parent() *Namespace

Parent returns a pointer to the parent namespace.

func (*Namespace) RelativesNames

func (ns *Namespace) RelativesNames() []string

RelativesNames returns the children and parent.

func (*Namespace) RemoveLimits added in v1.1.0

func (n *Namespace) RemoveLimits(nm string)

RemoveLimits removes limits specified by the HierarchicalResourceQuota object of the given name.

func (*Namespace) SetAnchors

func (ns *Namespace) SetAnchors(anchors []string) (diff []string)

SetAnchors updates the anchors and returns a difference between the new/old list.

func (*Namespace) SetCondition

func (ns *Namespace) SetCondition(tp, reason, msg string)

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

func (ns *Namespace) SetExists() bool

SetExists marks this namespace as existing, returning true if didn't previously exist.

func (*Namespace) SetLabels

func (ns *Namespace) SetLabels(labels map[string]string) bool

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

func (ns *Namespace) SetParent(p *Namespace)

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

func (ns *Namespace) UnsetExists() bool

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

func (ns *Namespace) UpdateAllowCascadingDeletion(acd bool) bool

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL