mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-02-14 09:59:57 +00:00
feat(api): add resourcepools and claims (#1333)
* feat: functional appsets * feat(api): add resourcepools api Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: fix gomod Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: correct webhooks Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: fix harpoon image Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: improve e2e Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: add labels to e2e test Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: fix status handling Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: fix racing conditions Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: make values compatible Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: fix custom resources test Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: correct metrics Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> --------- Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
This commit is contained in:
276
api/v1beta2/resourcepool_func.go
Normal file
276
api/v1beta2/resourcepool_func.go
Normal file
@@ -0,0 +1,276 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
func (r *ResourcePool) AssignNamespaces(namespaces []corev1.Namespace) {
|
||||
var l []string
|
||||
|
||||
for _, ns := range namespaces {
|
||||
if ns.Status.Phase == corev1.NamespaceActive && ns.DeletionTimestamp == nil {
|
||||
l = append(l, ns.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(l)
|
||||
|
||||
r.Status.NamespaceSize = uint(len(l))
|
||||
r.Status.Namespaces = l
|
||||
}
|
||||
|
||||
func (r *ResourcePool) AssignClaims() {
|
||||
var size uint
|
||||
|
||||
for _, claims := range r.Status.Claims {
|
||||
for range claims {
|
||||
size++
|
||||
}
|
||||
}
|
||||
|
||||
r.Status.ClaimSize = size
|
||||
}
|
||||
|
||||
func (r *ResourcePool) GetClaimFromStatus(cl *ResourcePoolClaim) *ResourcePoolClaimsItem {
|
||||
ns := cl.Namespace
|
||||
|
||||
claims := r.Status.Claims[ns]
|
||||
if claims == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, claim := range claims {
|
||||
if claim.UID == cl.UID {
|
||||
return claim
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ResourcePool) AddClaimToStatus(claim *ResourcePoolClaim) {
|
||||
ns := claim.Namespace
|
||||
|
||||
if r.Status.Claims == nil {
|
||||
r.Status.Claims = ResourcePoolNamespaceClaimsStatus{}
|
||||
}
|
||||
|
||||
if r.Status.Allocation.Claimed == nil {
|
||||
r.Status.Allocation.Claimed = corev1.ResourceList{}
|
||||
}
|
||||
|
||||
claims := r.Status.Claims[ns]
|
||||
if claims == nil {
|
||||
claims = ResourcePoolClaimsList{}
|
||||
}
|
||||
|
||||
scl := &ResourcePoolClaimsItem{
|
||||
StatusNameUID: api.StatusNameUID{
|
||||
UID: claim.UID,
|
||||
Name: api.Name(claim.Name),
|
||||
},
|
||||
Claims: claim.Spec.ResourceClaims,
|
||||
}
|
||||
|
||||
// Try to update existing entry if UID matches
|
||||
exists := false
|
||||
|
||||
for i, cl := range claims {
|
||||
if cl.UID == claim.UID {
|
||||
claims[i] = scl
|
||||
|
||||
exists = true
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !exists {
|
||||
claims = append(claims, scl)
|
||||
}
|
||||
|
||||
r.Status.Claims[ns] = claims
|
||||
|
||||
r.CalculateClaimedResources()
|
||||
}
|
||||
|
||||
func (r *ResourcePool) RemoveClaimFromStatus(claim *ResourcePoolClaim) {
|
||||
newClaims := ResourcePoolClaimsList{}
|
||||
|
||||
claims, ok := r.Status.Claims[claim.Namespace]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
for _, cl := range claims {
|
||||
if cl.UID != claim.UID {
|
||||
newClaims = append(newClaims, cl)
|
||||
}
|
||||
}
|
||||
|
||||
r.Status.Claims[claim.Namespace] = newClaims
|
||||
|
||||
if len(newClaims) == 0 {
|
||||
delete(r.Status.Claims, claim.Namespace)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ResourcePool) CalculateClaimedResources() {
|
||||
usage := corev1.ResourceList{}
|
||||
|
||||
for res := range r.Status.Allocation.Hard {
|
||||
usage[res] = resource.MustParse("0")
|
||||
}
|
||||
|
||||
for _, claims := range r.Status.Claims {
|
||||
for _, claim := range claims {
|
||||
for resourceName, qt := range claim.Claims {
|
||||
amount, exists := usage[resourceName]
|
||||
if !exists {
|
||||
amount = resource.MustParse("0")
|
||||
}
|
||||
|
||||
amount.Add(qt)
|
||||
usage[resourceName] = amount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.Status.Allocation.Claimed = usage
|
||||
|
||||
r.CalculateAvailableResources()
|
||||
}
|
||||
|
||||
func (r *ResourcePool) CalculateAvailableResources() {
|
||||
available := corev1.ResourceList{}
|
||||
|
||||
for res, qt := range r.Status.Allocation.Hard {
|
||||
amount, exists := r.Status.Allocation.Claimed[res]
|
||||
if exists {
|
||||
qt.Sub(amount)
|
||||
}
|
||||
|
||||
available[res] = qt
|
||||
}
|
||||
|
||||
r.Status.Allocation.Available = available
|
||||
}
|
||||
|
||||
func (r *ResourcePool) CanClaimFromPool(claim corev1.ResourceList) []error {
|
||||
claimable := r.GetAvailableClaimableResources()
|
||||
errs := []error{}
|
||||
|
||||
for resourceName, req := range claim {
|
||||
available, exists := claimable[resourceName]
|
||||
if !exists || available.IsZero() || available.Cmp(req) < 0 {
|
||||
errs = append(errs, errors.New("not enough resources"+string(resourceName)+"available"))
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (r *ResourcePool) GetAvailableClaimableResources() corev1.ResourceList {
|
||||
hard := r.Status.Allocation.Hard.DeepCopy()
|
||||
|
||||
for resourceName, qt := range hard {
|
||||
claimed, exists := r.Status.Allocation.Claimed[resourceName]
|
||||
if !exists {
|
||||
claimed = resource.MustParse("0")
|
||||
}
|
||||
|
||||
qt.Sub(claimed)
|
||||
|
||||
hard[resourceName] = qt
|
||||
}
|
||||
|
||||
return hard
|
||||
}
|
||||
|
||||
// Gets the Hard specification for the resourcequotas
|
||||
// This takes into account the default resources being used. However they don't count towards the claim usage
|
||||
// This can be changed in the future, the default is not calculated as usage because this might interrupt the namespace management
|
||||
// As we would need to verify if a new namespace with it's defaults still has place in the Pool. Same with attempting to join existing namespaces.
|
||||
func (r *ResourcePool) GetResourceQuotaHardResources(namespace string) corev1.ResourceList {
|
||||
_, claimed := r.GetNamespaceClaims(namespace)
|
||||
|
||||
for resourceName, amount := range claimed {
|
||||
if amount.IsZero() {
|
||||
delete(claimed, resourceName)
|
||||
}
|
||||
}
|
||||
|
||||
// Only Consider Default, when enabled
|
||||
for resourceName, amount := range r.Spec.Defaults {
|
||||
usedValue := claimed[resourceName]
|
||||
usedValue.Add(amount)
|
||||
|
||||
claimed[resourceName] = usedValue
|
||||
}
|
||||
|
||||
return claimed
|
||||
}
|
||||
|
||||
// Gets the total amount of claimed resources for a namespace.
|
||||
func (r *ResourcePool) GetNamespaceClaims(namespace string) (claims map[string]*ResourcePoolClaimsItem, claimedResources corev1.ResourceList) {
|
||||
claimedResources = corev1.ResourceList{}
|
||||
claims = map[string]*ResourcePoolClaimsItem{}
|
||||
|
||||
// First, check if quota exists in the status
|
||||
for ns, cl := range r.Status.Claims {
|
||||
if ns != namespace {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, claim := range cl {
|
||||
for resourceName, claimed := range claim.Claims {
|
||||
usedValue, usedExists := claimedResources[resourceName]
|
||||
if !usedExists {
|
||||
usedValue = resource.MustParse("0") // Default to zero if no used value is found
|
||||
}
|
||||
|
||||
// Combine with claim
|
||||
usedValue.Add(claimed)
|
||||
claimedResources[resourceName] = usedValue
|
||||
}
|
||||
|
||||
claims[string(claim.UID)] = claim
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Calculate usage for each namespace.
|
||||
func (r *ResourcePool) GetClaimedByNamespaceClaims() (claims map[string]corev1.ResourceList) {
|
||||
claims = map[string]corev1.ResourceList{}
|
||||
|
||||
// First, check if quota exists in the status
|
||||
for ns, cl := range r.Status.Claims {
|
||||
claims[ns] = corev1.ResourceList{}
|
||||
nsScope := claims[ns]
|
||||
|
||||
for _, claim := range cl {
|
||||
for resourceName, claimed := range claim.Claims {
|
||||
usedValue, usedExists := nsScope[resourceName]
|
||||
if !usedExists {
|
||||
usedValue = resource.MustParse("0")
|
||||
}
|
||||
|
||||
usedValue.Add(claimed)
|
||||
nsScope[resourceName] = usedValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
292
api/v1beta2/resourcepool_func_test.go
Normal file
292
api/v1beta2/resourcepool_func_test.go
Normal file
@@ -0,0 +1,292 @@
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/meta"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetClaimFromStatus(t *testing.T) {
|
||||
ns := "test-namespace"
|
||||
testUID := types.UID("test-uid")
|
||||
otherUID := types.UID("wrong-uid")
|
||||
|
||||
claim := &ResourcePoolClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "claim-a",
|
||||
Namespace: ns,
|
||||
UID: testUID,
|
||||
},
|
||||
}
|
||||
|
||||
pool := &ResourcePool{
|
||||
Status: ResourcePoolStatus{
|
||||
Claims: ResourcePoolNamespaceClaimsStatus{
|
||||
ns: {
|
||||
&ResourcePoolClaimsItem{
|
||||
StatusNameUID: api.StatusNameUID{
|
||||
UID: testUID,
|
||||
},
|
||||
Claims: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("500m"),
|
||||
corev1.ResourceMemory: resource.MustParse("256Mi"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("returns matching claim", func(t *testing.T) {
|
||||
found := pool.GetClaimFromStatus(claim)
|
||||
assert.NotNil(t, found)
|
||||
assert.Equal(t, testUID, found.UID)
|
||||
})
|
||||
|
||||
t.Run("returns nil if UID doesn't match", func(t *testing.T) {
|
||||
claimWrongUID := *claim
|
||||
claimWrongUID.UID = otherUID
|
||||
|
||||
found := pool.GetClaimFromStatus(&claimWrongUID)
|
||||
assert.Nil(t, found)
|
||||
})
|
||||
|
||||
t.Run("returns nil if namespace has no claims", func(t *testing.T) {
|
||||
claimWrongNS := *claim
|
||||
claimWrongNS.Namespace = "other-ns"
|
||||
|
||||
found := pool.GetClaimFromStatus(&claimWrongNS)
|
||||
assert.Nil(t, found)
|
||||
})
|
||||
}
|
||||
|
||||
func makeResourceList(cpu, memory string) corev1.ResourceList {
|
||||
return corev1.ResourceList{
|
||||
corev1.ResourceLimitsCPU: resource.MustParse(cpu),
|
||||
corev1.ResourceLimitsMemory: resource.MustParse(memory),
|
||||
}
|
||||
}
|
||||
|
||||
func makeClaim(name, ns string, uid types.UID, res corev1.ResourceList) *ResourcePoolClaim {
|
||||
return &ResourcePoolClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: ns,
|
||||
UID: uid,
|
||||
},
|
||||
Spec: ResourcePoolClaimSpec{
|
||||
ResourceClaims: res,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestAssignNamespaces(t *testing.T) {
|
||||
pool := &ResourcePool{}
|
||||
|
||||
namespaces := []corev1.Namespace{
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "active-ns"}, Status: corev1.NamespaceStatus{Phase: corev1.NamespaceActive}},
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "terminating-ns", DeletionTimestamp: &metav1.Time{}}, Status: corev1.NamespaceStatus{Phase: corev1.NamespaceTerminating}},
|
||||
}
|
||||
|
||||
pool.AssignNamespaces(namespaces)
|
||||
|
||||
assert.Equal(t, uint(1), pool.Status.NamespaceSize)
|
||||
assert.Equal(t, []string{"active-ns"}, pool.Status.Namespaces)
|
||||
}
|
||||
|
||||
func TestAssignClaims(t *testing.T) {
|
||||
pool := &ResourcePool{
|
||||
Status: ResourcePoolStatus{
|
||||
Claims: ResourcePoolNamespaceClaimsStatus{
|
||||
"ns": {
|
||||
&ResourcePoolClaimsItem{},
|
||||
&ResourcePoolClaimsItem{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
pool.AssignClaims()
|
||||
|
||||
assert.Equal(t, uint(2), pool.Status.ClaimSize)
|
||||
}
|
||||
|
||||
func TestAddRemoveClaimToStatus(t *testing.T) {
|
||||
pool := &ResourcePool{}
|
||||
|
||||
claim := makeClaim("claim-1", "ns", "uid-1", makeResourceList("1", "1Gi"))
|
||||
pool.AddClaimToStatus(claim)
|
||||
|
||||
stored := pool.GetClaimFromStatus(claim)
|
||||
assert.NotNil(t, stored)
|
||||
assert.Equal(t, api.Name("claim-1"), stored.Name)
|
||||
|
||||
pool.RemoveClaimFromStatus(claim)
|
||||
assert.Nil(t, pool.GetClaimFromStatus(claim))
|
||||
}
|
||||
|
||||
func TestCalculateResources(t *testing.T) {
|
||||
pool := &ResourcePool{
|
||||
Status: ResourcePoolStatus{
|
||||
Allocation: ResourcePoolQuotaStatus{
|
||||
Hard: corev1.ResourceList{
|
||||
corev1.ResourceLimitsCPU: resource.MustParse("2"),
|
||||
},
|
||||
},
|
||||
Claims: ResourcePoolNamespaceClaimsStatus{
|
||||
"ns": {
|
||||
&ResourcePoolClaimsItem{
|
||||
Claims: corev1.ResourceList{
|
||||
corev1.ResourceLimitsCPU: resource.MustParse("1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
pool.CalculateClaimedResources()
|
||||
|
||||
actualClaimed := pool.Status.Allocation.Claimed[corev1.ResourceLimitsCPU]
|
||||
actualAvailable := pool.Status.Allocation.Available[corev1.ResourceLimitsCPU]
|
||||
|
||||
assert.Equal(t, 0, (&actualClaimed).Cmp(resource.MustParse("1")))
|
||||
assert.Equal(t, 0, (&actualAvailable).Cmp(resource.MustParse("1")))
|
||||
}
|
||||
|
||||
func TestCanClaimFromPool(t *testing.T) {
|
||||
pool := &ResourcePool{
|
||||
Status: ResourcePoolStatus{
|
||||
Allocation: ResourcePoolQuotaStatus{
|
||||
Hard: corev1.ResourceList{
|
||||
corev1.ResourceLimitsMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
Claimed: corev1.ResourceList{
|
||||
corev1.ResourceLimitsMemory: resource.MustParse("512Mi"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
errs := pool.CanClaimFromPool(corev1.ResourceList{
|
||||
corev1.ResourceLimitsMemory: resource.MustParse("1Gi"),
|
||||
})
|
||||
assert.Len(t, errs, 1)
|
||||
|
||||
errs = pool.CanClaimFromPool(corev1.ResourceList{
|
||||
corev1.ResourceLimitsMemory: resource.MustParse("500Mi"),
|
||||
})
|
||||
assert.Len(t, errs, 0)
|
||||
}
|
||||
|
||||
func TestGetResourceQuotaHardResources(t *testing.T) {
|
||||
pool := &ResourcePool{
|
||||
Spec: ResourcePoolSpec{
|
||||
Defaults: corev1.ResourceList{
|
||||
corev1.ResourceLimitsCPU: resource.MustParse("1"),
|
||||
},
|
||||
},
|
||||
Status: ResourcePoolStatus{
|
||||
Claims: ResourcePoolNamespaceClaimsStatus{
|
||||
"ns": {
|
||||
&ResourcePoolClaimsItem{
|
||||
Claims: corev1.ResourceList{
|
||||
corev1.ResourceLimitsCPU: resource.MustParse("1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
res := pool.GetResourceQuotaHardResources("ns")
|
||||
actual := res[corev1.ResourceLimitsCPU]
|
||||
assert.Equal(t, 0, (&actual).Cmp(resource.MustParse("2")))
|
||||
}
|
||||
|
||||
func TestGetNamespaceClaims(t *testing.T) {
|
||||
pool := &ResourcePool{
|
||||
Status: ResourcePoolStatus{
|
||||
Claims: ResourcePoolNamespaceClaimsStatus{
|
||||
"ns": {
|
||||
&ResourcePoolClaimsItem{
|
||||
StatusNameUID: api.StatusNameUID{UID: "uid1"},
|
||||
Claims: corev1.ResourceList{
|
||||
corev1.ResourceLimitsCPU: resource.MustParse("1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
claims, res := pool.GetNamespaceClaims("ns")
|
||||
assert.Contains(t, claims, "uid1")
|
||||
actual := res[corev1.ResourceLimitsCPU]
|
||||
assert.Equal(t, 0, (&actual).Cmp(resource.MustParse("1")))
|
||||
}
|
||||
|
||||
func TestGetClaimedByNamespaceClaims(t *testing.T) {
|
||||
pool := &ResourcePool{
|
||||
Status: ResourcePoolStatus{
|
||||
Claims: ResourcePoolNamespaceClaimsStatus{
|
||||
"ns1": {
|
||||
&ResourcePoolClaimsItem{
|
||||
Claims: makeResourceList("1", "1Gi"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
result := pool.GetClaimedByNamespaceClaims()
|
||||
actualCPU := result["ns1"][corev1.ResourceLimitsCPU]
|
||||
actualMem := result["ns1"][corev1.ResourceLimitsMemory]
|
||||
|
||||
assert.Equal(t, 0, (&actualCPU).Cmp(resource.MustParse("1")))
|
||||
assert.Equal(t, 0, (&actualMem).Cmp(resource.MustParse("1Gi")))
|
||||
}
|
||||
|
||||
func TestIsBoundToResourcePool_2(t *testing.T) {
|
||||
t.Run("bound to resource pool (Assigned=True)", func(t *testing.T) {
|
||||
claim := &ResourcePoolClaim{
|
||||
Status: ResourcePoolClaimStatus{
|
||||
Condition: metav1.Condition{
|
||||
Type: meta.BoundCondition,
|
||||
Status: metav1.ConditionTrue,
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.Equal(t, true, claim.IsBoundToResourcePool())
|
||||
})
|
||||
|
||||
t.Run("not bound - wrong condition type", func(t *testing.T) {
|
||||
claim := &ResourcePoolClaim{
|
||||
Status: ResourcePoolClaimStatus{
|
||||
Condition: metav1.Condition{
|
||||
Type: "Other",
|
||||
Status: metav1.ConditionTrue,
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.Equal(t, false, claim.IsBoundToResourcePool())
|
||||
})
|
||||
|
||||
t.Run("not bound - condition not true", func(t *testing.T) {
|
||||
claim := &ResourcePoolClaim{
|
||||
Status: ResourcePoolClaimStatus{
|
||||
Condition: metav1.Condition{
|
||||
Type: meta.BoundCondition,
|
||||
Status: metav1.ConditionFalse,
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.Equal(t, false, claim.IsBoundToResourcePool())
|
||||
})
|
||||
}
|
||||
62
api/v1beta2/resourcepool_status.go
Normal file
62
api/v1beta2/resourcepool_status.go
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
// GlobalResourceQuotaStatus defines the observed state of GlobalResourceQuota.
|
||||
type ResourcePoolStatus struct {
|
||||
// How many namespaces are considered
|
||||
// +kubebuilder:default=0
|
||||
NamespaceSize uint `json:"namespaceCount,omitempty"`
|
||||
// Amount of claims
|
||||
// +kubebuilder:default=0
|
||||
ClaimSize uint `json:"claimCount,omitempty"`
|
||||
// Namespaces which are considered for claims
|
||||
Namespaces []string `json:"namespaces,omitempty"`
|
||||
// Tracks the quotas for the Resource.
|
||||
Claims ResourcePoolNamespaceClaimsStatus `json:"claims,omitempty"`
|
||||
// Tracks the Usage from Claimed against what has been granted from the pool
|
||||
Allocation ResourcePoolQuotaStatus `json:"allocation,omitempty"`
|
||||
}
|
||||
|
||||
type ResourcePoolNamespaceClaimsStatus map[string]ResourcePoolClaimsList
|
||||
|
||||
type ResourcePoolQuotaStatus struct {
|
||||
// Hard is the set of enforced hard limits for each named resource.
|
||||
// More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/
|
||||
// +optional
|
||||
Hard corev1.ResourceList `json:"hard,omitempty" protobuf:"bytes,1,rep,name=hard,casttype=ResourceList,castkey=ResourceName"`
|
||||
// Used is the current observed total usage of the resource in the namespace.
|
||||
// +optional
|
||||
Claimed corev1.ResourceList `json:"used,omitempty" protobuf:"bytes,2,rep,name=used,casttype=ResourceList,castkey=ResourceName"`
|
||||
// Used to track the usage of the resource in the pool (diff hard - claimed). May be used for further automation
|
||||
// +optional
|
||||
Available corev1.ResourceList `json:"available,omitempty" protobuf:"bytes,2,rep,name=available,casttype=ResourceList,castkey=ResourceName"`
|
||||
}
|
||||
|
||||
type ResourcePoolClaimsList []*ResourcePoolClaimsItem
|
||||
|
||||
func (r *ResourcePoolClaimsList) GetClaimByUID(uid types.UID) *ResourcePoolClaimsItem {
|
||||
for _, claim := range *r {
|
||||
if claim.UID == uid {
|
||||
return claim
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResourceQuotaClaimStatus defines the observed state of ResourceQuotaClaim.
|
||||
type ResourcePoolClaimsItem struct {
|
||||
// Reference to the GlobalQuota being claimed from
|
||||
api.StatusNameUID `json:",inline"`
|
||||
// Claimed resources
|
||||
Claims corev1.ResourceList `json:"claims,omitempty"`
|
||||
}
|
||||
76
api/v1beta2/resourcepool_types.go
Normal file
76
api/v1beta2/resourcepool_types.go
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
// ResourcePoolSpec.
|
||||
type ResourcePoolSpec struct {
|
||||
// Selector to match the namespaces that should be managed by the GlobalResourceQuota
|
||||
Selectors []api.NamespaceSelector `json:"selectors,omitempty"`
|
||||
// Define the resourcequota served by this resourcepool.
|
||||
Quota corev1.ResourceQuotaSpec `json:"quota"`
|
||||
// The Defaults given for each namespace, the default is not counted towards the total allocation
|
||||
// When you use claims it's recommended to provision Defaults as the prevent the scheduling of any resources
|
||||
Defaults corev1.ResourceList `json:"defaults,omitempty"`
|
||||
// Additional Configuration
|
||||
//+kubebuilder:default:={}
|
||||
Config ResourcePoolSpecConfiguration `json:"config,omitempty"`
|
||||
}
|
||||
|
||||
type ResourcePoolSpecConfiguration struct {
|
||||
// With this option all resources which can be allocated are set to 0 for the resourcequota defaults.
|
||||
// +kubebuilder:default=false
|
||||
DefaultsAssignZero *bool `json:"defaultsZero,omitempty"`
|
||||
// Claims are queued whenever they are allocated to a pool. A pool tries to allocate claims in order based on their
|
||||
// creation date. But no matter their creation time, if a claim is requesting too much resources it's put into the queue
|
||||
// but if a lower priority claim still has enough space in the available resources, it will be able to claim them. Eventough
|
||||
// it's priority was lower
|
||||
// Enabling this option respects to Order. Meaning the Creationtimestamp matters and if a resource is put into the queue, no
|
||||
// other claim can claim the same resources with lower priority.
|
||||
// +kubebuilder:default=false
|
||||
OrderedQueue *bool `json:"orderedQueue,omitempty"`
|
||||
// When a resourcepool is deleted, the resourceclaims bound to it are disassociated from the resourcepool but not deleted.
|
||||
// By Enabling this option, the resourceclaims will be deleted when the resourcepool is deleted, if they are in bound state.
|
||||
// +kubebuilder:default=false
|
||||
DeleteBoundResources *bool `json:"deleteBoundResources,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:resource:scope=Cluster,shortName=quotapool
|
||||
// +kubebuilder:printcolumn:name="Claims",type="integer",JSONPath=".status.claimCount",description="The total amount of Claims bound"
|
||||
// +kubebuilder:printcolumn:name="Namespaces",type="integer",JSONPath=".status.namespaceCount",description="The total amount of Namespaces considered"
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Age"
|
||||
|
||||
// Resourcepools allows you to define a set of resources as known from ResoureQuotas. The Resourcepools are defined at cluster-scope an should
|
||||
// be administrated by cluster-administrators. However they create an interface, where cluster-administrators can define
|
||||
// from which namespaces resources from a Resourcepool can be claimed. The claiming is done via a namespaced CRD called ResourcePoolClaim. Then
|
||||
// it's up the group of users within these namespaces, to manage the resources they consume per namespace. Each Resourcepool provisions a ResourceQuotainto all the selected namespaces. Then essentially the ResourcePoolClaims, when they can be assigned to the ResourcePool stack resources on top of that
|
||||
// ResourceQuota based on the namspace, where the ResourcePoolClaim was made from.
|
||||
type ResourcePool struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec ResourcePoolSpec `json:"spec,omitempty"`
|
||||
Status ResourcePoolStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// ResourcePoolList contains a list of ResourcePool.
|
||||
type ResourcePoolList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []ResourcePool `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&ResourcePool{}, &ResourcePoolList{})
|
||||
}
|
||||
20
api/v1beta2/resourcepoolclaim_func.go
Normal file
20
api/v1beta2/resourcepoolclaim_func.go
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/meta"
|
||||
)
|
||||
|
||||
// Indicate the claim is bound to a resource pool.
|
||||
func (r *ResourcePoolClaim) IsBoundToResourcePool() bool {
|
||||
if r.Status.Condition.Type == meta.BoundCondition &&
|
||||
r.Status.Condition.Status == metav1.ConditionTrue {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
71
api/v1beta2/resourcepoolclaim_func_test.go
Normal file
71
api/v1beta2/resourcepoolclaim_func_test.go
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/meta"
|
||||
"github.com/stretchr/testify/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestIsBoundToResourcePool(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
claim ResourcePoolClaim
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "bound to resource pool (Assigned=True)",
|
||||
claim: ResourcePoolClaim{
|
||||
Status: ResourcePoolClaimStatus{
|
||||
Condition: metav1.Condition{
|
||||
Type: meta.BoundCondition,
|
||||
Status: metav1.ConditionTrue,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "not bound - wrong condition type",
|
||||
claim: ResourcePoolClaim{
|
||||
Status: ResourcePoolClaimStatus{
|
||||
Condition: metav1.Condition{
|
||||
Type: "SomethingElse",
|
||||
Status: metav1.ConditionTrue,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "not bound - status not true",
|
||||
claim: ResourcePoolClaim{
|
||||
Status: ResourcePoolClaimStatus{
|
||||
Condition: metav1.Condition{
|
||||
Type: meta.BoundCondition,
|
||||
Status: metav1.ConditionFalse,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "not bound - empty condition",
|
||||
claim: ResourcePoolClaim{
|
||||
Status: ResourcePoolClaimStatus{},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
actual := tt.claim.IsBoundToResourcePool()
|
||||
assert.Equal(t, tt.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
58
api/v1beta2/resourcepoolclaim_types.go
Normal file
58
api/v1beta2/resourcepoolclaim_types.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
type ResourcePoolClaimSpec struct {
|
||||
// If there's the possability to claim from multiple global Quotas
|
||||
// You must be specific about which one you want to claim resources from
|
||||
// Once bound to a ResourcePool, this field is immutable
|
||||
Pool string `json:"pool"`
|
||||
// Amount which should be claimed for the resourcequota
|
||||
ResourceClaims corev1.ResourceList `json:"claim"`
|
||||
}
|
||||
|
||||
// ResourceQuotaClaimStatus defines the observed state of ResourceQuotaClaim.
|
||||
type ResourcePoolClaimStatus struct {
|
||||
// Reference to the GlobalQuota being claimed from
|
||||
Pool api.StatusNameUID `json:"pool,omitempty"`
|
||||
// Condtion for this resource claim
|
||||
Condition metav1.Condition `json:"condition,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:name="Pool",type="string",JSONPath=".status.pool.name",description="The ResourcePool being claimed from"
|
||||
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.condition.type",description="Status for claim"
|
||||
// +kubebuilder:printcolumn:name="Reason",type="string",JSONPath=".status.condition.reason",description="Reason for status"
|
||||
// +kubebuilder:printcolumn:name="Message",type="string",JSONPath=".status.condition.message",description="Condition Message"
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
|
||||
|
||||
// ResourcePoolClaim is the Schema for the resourcepoolclaims API.
|
||||
type ResourcePoolClaim struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec ResourcePoolClaimSpec `json:"spec,omitempty"`
|
||||
Status ResourcePoolClaimStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// ResourceQuotaClaimList contains a list of ResourceQuotaClaim.
|
||||
type ResourcePoolClaimList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []ResourcePoolClaim `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&ResourcePoolClaim{}, &ResourcePoolClaimList{})
|
||||
}
|
||||
@@ -9,6 +9,7 @@ package v1beta2
|
||||
|
||||
import (
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -517,6 +518,387 @@ func (in *RawExtension) DeepCopy() *RawExtension {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResourcePool) DeepCopyInto(out *ResourcePool) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourcePool.
|
||||
func (in *ResourcePool) DeepCopy() *ResourcePool {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResourcePool)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ResourcePool) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResourcePoolClaim) DeepCopyInto(out *ResourcePoolClaim) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourcePoolClaim.
|
||||
func (in *ResourcePoolClaim) DeepCopy() *ResourcePoolClaim {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResourcePoolClaim)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ResourcePoolClaim) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResourcePoolClaimList) DeepCopyInto(out *ResourcePoolClaimList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]ResourcePoolClaim, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourcePoolClaimList.
|
||||
func (in *ResourcePoolClaimList) DeepCopy() *ResourcePoolClaimList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResourcePoolClaimList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ResourcePoolClaimList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResourcePoolClaimSpec) DeepCopyInto(out *ResourcePoolClaimSpec) {
|
||||
*out = *in
|
||||
if in.ResourceClaims != nil {
|
||||
in, out := &in.ResourceClaims, &out.ResourceClaims
|
||||
*out = make(corev1.ResourceList, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val.DeepCopy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourcePoolClaimSpec.
|
||||
func (in *ResourcePoolClaimSpec) DeepCopy() *ResourcePoolClaimSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResourcePoolClaimSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResourcePoolClaimStatus) DeepCopyInto(out *ResourcePoolClaimStatus) {
|
||||
*out = *in
|
||||
out.Pool = in.Pool
|
||||
in.Condition.DeepCopyInto(&out.Condition)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourcePoolClaimStatus.
|
||||
func (in *ResourcePoolClaimStatus) DeepCopy() *ResourcePoolClaimStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResourcePoolClaimStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResourcePoolClaimsItem) DeepCopyInto(out *ResourcePoolClaimsItem) {
|
||||
*out = *in
|
||||
out.StatusNameUID = in.StatusNameUID
|
||||
if in.Claims != nil {
|
||||
in, out := &in.Claims, &out.Claims
|
||||
*out = make(corev1.ResourceList, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val.DeepCopy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourcePoolClaimsItem.
|
||||
func (in *ResourcePoolClaimsItem) DeepCopy() *ResourcePoolClaimsItem {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResourcePoolClaimsItem)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in ResourcePoolClaimsList) DeepCopyInto(out *ResourcePoolClaimsList) {
|
||||
{
|
||||
in := &in
|
||||
*out = make(ResourcePoolClaimsList, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(ResourcePoolClaimsItem)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourcePoolClaimsList.
|
||||
func (in ResourcePoolClaimsList) DeepCopy() ResourcePoolClaimsList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResourcePoolClaimsList)
|
||||
in.DeepCopyInto(out)
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResourcePoolList) DeepCopyInto(out *ResourcePoolList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]ResourcePool, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourcePoolList.
|
||||
func (in *ResourcePoolList) DeepCopy() *ResourcePoolList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResourcePoolList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ResourcePoolList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in ResourcePoolNamespaceClaimsStatus) DeepCopyInto(out *ResourcePoolNamespaceClaimsStatus) {
|
||||
{
|
||||
in := &in
|
||||
*out = make(ResourcePoolNamespaceClaimsStatus, len(*in))
|
||||
for key, val := range *in {
|
||||
var outVal []*ResourcePoolClaimsItem
|
||||
if val == nil {
|
||||
(*out)[key] = nil
|
||||
} else {
|
||||
inVal := (*in)[key]
|
||||
in, out := &inVal, &outVal
|
||||
*out = make(ResourcePoolClaimsList, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(ResourcePoolClaimsItem)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
(*out)[key] = outVal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourcePoolNamespaceClaimsStatus.
|
||||
func (in ResourcePoolNamespaceClaimsStatus) DeepCopy() ResourcePoolNamespaceClaimsStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResourcePoolNamespaceClaimsStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResourcePoolQuotaStatus) DeepCopyInto(out *ResourcePoolQuotaStatus) {
|
||||
*out = *in
|
||||
if in.Hard != nil {
|
||||
in, out := &in.Hard, &out.Hard
|
||||
*out = make(corev1.ResourceList, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val.DeepCopy()
|
||||
}
|
||||
}
|
||||
if in.Claimed != nil {
|
||||
in, out := &in.Claimed, &out.Claimed
|
||||
*out = make(corev1.ResourceList, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val.DeepCopy()
|
||||
}
|
||||
}
|
||||
if in.Available != nil {
|
||||
in, out := &in.Available, &out.Available
|
||||
*out = make(corev1.ResourceList, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val.DeepCopy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourcePoolQuotaStatus.
|
||||
func (in *ResourcePoolQuotaStatus) DeepCopy() *ResourcePoolQuotaStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResourcePoolQuotaStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResourcePoolSpec) DeepCopyInto(out *ResourcePoolSpec) {
|
||||
*out = *in
|
||||
if in.Selectors != nil {
|
||||
in, out := &in.Selectors, &out.Selectors
|
||||
*out = make([]api.NamespaceSelector, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
in.Quota.DeepCopyInto(&out.Quota)
|
||||
if in.Defaults != nil {
|
||||
in, out := &in.Defaults, &out.Defaults
|
||||
*out = make(corev1.ResourceList, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val.DeepCopy()
|
||||
}
|
||||
}
|
||||
in.Config.DeepCopyInto(&out.Config)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourcePoolSpec.
|
||||
func (in *ResourcePoolSpec) DeepCopy() *ResourcePoolSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResourcePoolSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResourcePoolSpecConfiguration) DeepCopyInto(out *ResourcePoolSpecConfiguration) {
|
||||
*out = *in
|
||||
if in.DefaultsAssignZero != nil {
|
||||
in, out := &in.DefaultsAssignZero, &out.DefaultsAssignZero
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.OrderedQueue != nil {
|
||||
in, out := &in.OrderedQueue, &out.OrderedQueue
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.DeleteBoundResources != nil {
|
||||
in, out := &in.DeleteBoundResources, &out.DeleteBoundResources
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourcePoolSpecConfiguration.
|
||||
func (in *ResourcePoolSpecConfiguration) DeepCopy() *ResourcePoolSpecConfiguration {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResourcePoolSpecConfiguration)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResourcePoolStatus) DeepCopyInto(out *ResourcePoolStatus) {
|
||||
*out = *in
|
||||
if in.Namespaces != nil {
|
||||
in, out := &in.Namespaces, &out.Namespaces
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Claims != nil {
|
||||
in, out := &in.Claims, &out.Claims
|
||||
*out = make(ResourcePoolNamespaceClaimsStatus, len(*in))
|
||||
for key, val := range *in {
|
||||
var outVal []*ResourcePoolClaimsItem
|
||||
if val == nil {
|
||||
(*out)[key] = nil
|
||||
} else {
|
||||
inVal := (*in)[key]
|
||||
in, out := &inVal, &outVal
|
||||
*out = make(ResourcePoolClaimsList, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(ResourcePoolClaimsItem)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
(*out)[key] = outVal
|
||||
}
|
||||
}
|
||||
in.Allocation.DeepCopyInto(&out.Allocation)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourcePoolStatus.
|
||||
func (in *ResourcePoolStatus) DeepCopy() *ResourcePoolStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResourcePoolStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResourceSpec) DeepCopyInto(out *ResourceSpec) {
|
||||
*out = *in
|
||||
|
||||
Reference in New Issue
Block a user