From 48022ce04383beede66e752564d0c099da0968f4 Mon Sep 17 00:00:00 2001
From: Shiming Zhang
Date: Thu, 27 Jul 2023 17:04:57 +0800
Subject: [PATCH] Add Resource Usage API
---
go.mod | 4 +-
go.sum | 8 +-
.../kwok.x-k8s.io_clusterresourceusages.yaml | 148 ++++++
.../bases/kwok.x-k8s.io_resourceusages.yaml | 132 ++++++
kustomize/crd/embed.go | 8 +
kustomize/kwokctl/resource/pod.yaml | 37 ++
kustomize/rbac/role.yaml | 24 +
.../cluster_resource_usage_types.go | 38 ++
pkg/apis/internalversion/conversion.go | 44 ++
.../internalversion/resource_usage_types.go | 53 +++
.../zz_generated.conversion.go | 203 ++++++++
.../internalversion/zz_generated.deepcopy.go | 141 ++++++
.../v1alpha1/cluster_resource_usage_types.go | 79 +++
pkg/apis/v1alpha1/resource_usage_types.go | 92 ++++
pkg/apis/v1alpha1/zz_generated.deepcopy.go | 273 +++++++++++
.../typed/apis/v1alpha1/apis_client.go | 10 +
.../apis/v1alpha1/clusterresourceusage.go | 184 +++++++
.../apis/v1alpha1/fake/fake_apis_client.go | 8 +
.../fake/fake_clusterresourceusage.go | 132 ++++++
.../apis/v1alpha1/fake/fake_resourceusage.go | 141 ++++++
.../apis/v1alpha1/generated_expansion.go | 4 +
.../typed/apis/v1alpha1/resourceusage.go | 195 ++++++++
pkg/config/config.go | 12 +
pkg/kwok/cmd/root.go | 64 ++-
pkg/kwok/metrics/cel/evaluate.go | 62 +++
pkg/kwok/metrics/cel/evaluate_test.go | 53 +++
pkg/kwok/metrics/cel/quantity.go | 201 ++++++++
pkg/kwok/metrics/cel/resource_list.go | 122 +++++
pkg/kwok/server/metrics.go | 37 +-
pkg/kwok/server/metrics_resource_usage.go | 261 ++++++++++
pkg/kwok/server/server.go | 119 +++--
pkg/kwokctl/runtime/cluster.go | 22 +-
site/content/en/docs/generated/apis.md | 448 ++++++++++++++++++
test/kwokctl/metric-node-resource.yaml | 42 +-
34 files changed, 3316 insertions(+), 85 deletions(-)
create mode 100644 kustomize/crd/bases/kwok.x-k8s.io_clusterresourceusages.yaml
create mode 100644 kustomize/crd/bases/kwok.x-k8s.io_resourceusages.yaml
create mode 100644 pkg/apis/internalversion/cluster_resource_usage_types.go
create mode 100644 pkg/apis/internalversion/resource_usage_types.go
create mode 100644 pkg/apis/v1alpha1/cluster_resource_usage_types.go
create mode 100644 pkg/apis/v1alpha1/resource_usage_types.go
create mode 100644 pkg/client/clientset/versioned/typed/apis/v1alpha1/clusterresourceusage.go
create mode 100644 pkg/client/clientset/versioned/typed/apis/v1alpha1/fake/fake_clusterresourceusage.go
create mode 100644 pkg/client/clientset/versioned/typed/apis/v1alpha1/fake/fake_resourceusage.go
create mode 100644 pkg/client/clientset/versioned/typed/apis/v1alpha1/resourceusage.go
create mode 100644 pkg/kwok/metrics/cel/quantity.go
create mode 100644 pkg/kwok/metrics/cel/resource_list.go
create mode 100644 pkg/kwok/server/metrics_resource_usage.go
diff --git a/go.mod b/go.mod
index d72a92ba6..179e6ebe7 100644
--- a/go.mod
+++ b/go.mod
@@ -10,7 +10,7 @@ require (
github.com/creack/pty v1.1.18
github.com/emicklei/go-restful/v3 v3.10.2
github.com/fsnotify/fsnotify v1.6.0
- github.com/google/cel-go v0.16.0
+ github.com/google/cel-go v0.17.6
github.com/google/go-cmp v0.5.9
github.com/itchyny/gojq v0.12.13
github.com/nxadm/tail v1.4.8
@@ -20,7 +20,7 @@ require (
github.com/spf13/pflag v1.0.5
github.com/wzshiming/cmux v0.3.2
github.com/wzshiming/ctc v1.2.3
- github.com/wzshiming/easycel v0.4.0
+ github.com/wzshiming/easycel v0.5.0
go.uber.org/atomic v1.11.0
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb
golang.org/x/sync v0.3.0
diff --git a/go.sum b/go.sum
index 54dae3457..c346afa3d 100644
--- a/go.sum
+++ b/go.sum
@@ -75,8 +75,8 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/google/cel-go v0.16.0 h1:DG9YQ8nFCFXAs/FDDwBxmL1tpKNrdlGUM9U3537bX/Y=
-github.com/google/cel-go v0.16.0/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY=
+github.com/google/cel-go v0.17.6 h1:QDvHTIJunIsbgN8yVukx0HGnsqVLSY6xGqo+17IjIyM=
+github.com/google/cel-go v0.17.6/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -198,8 +198,8 @@ github.com/wzshiming/cmux v0.3.2 h1:lBEWbfbRqUDdXB6Mro/g35kvCuUEmAgIdpGEuER3bis=
github.com/wzshiming/cmux v0.3.2/go.mod h1:lPhqJN2E3frzkxrPdjesxL09z7nTcuZ6i8Is+2G/Xw4=
github.com/wzshiming/ctc v1.2.3 h1:q+hW3IQNsjIlOFBTGZZZeIXTElFM4grF4spW/errh/c=
github.com/wzshiming/ctc v1.2.3/go.mod h1:2tVAtIY7SUyraSk0JxvwmONNPFL4ARavPuEsg5+KA28=
-github.com/wzshiming/easycel v0.4.0 h1:flPpJYS2bXQj1Jgk2yOp5oB6FvflvPM0VCHXx/qOJDk=
-github.com/wzshiming/easycel v0.4.0/go.mod h1:qg3oAkmPOLJEUFlFsxBxYbp0cXHgbq2sZEHVq7SmUp8=
+github.com/wzshiming/easycel v0.5.0 h1:pnMBpOuEfr9EYLNpKD52NfpAhUrOQ3uAHFQT7bF76ZM=
+github.com/wzshiming/easycel v0.5.0/go.mod h1:4Jue5gr86TomO6urajHxWCNbEKwwYnQVsyjKOWT/V/U=
github.com/wzshiming/trie v0.1.1 h1:02AaBSZGhs6Aqljp8fz4xq/Mg8omFBPIlrUS0pJ11ks=
github.com/wzshiming/trie v0.1.1/go.mod h1:c9thxXTh4KcGkejt4sUsO4c5GUmWpxeWzOJ7AZJaI+8=
github.com/wzshiming/winseq v0.0.0-20200112104235-db357dc107ae h1:tpXvBXC3hpQBDCc9OojJZCQMVRAbT3TTdUMP8WguXkY=
diff --git a/kustomize/crd/bases/kwok.x-k8s.io_clusterresourceusages.yaml b/kustomize/crd/bases/kwok.x-k8s.io_clusterresourceusages.yaml
new file mode 100644
index 000000000..4b977de19
--- /dev/null
+++ b/kustomize/crd/bases/kwok.x-k8s.io_clusterresourceusages.yaml
@@ -0,0 +1,148 @@
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.12.1
+ name: clusterresourceusages.kwok.x-k8s.io
+spec:
+ group: kwok.x-k8s.io
+ names:
+ kind: ClusterResourceUsage
+ listKind: ClusterResourceUsageList
+ plural: clusterresourceusages
+ singular: clusterresourceusage
+ scope: Cluster
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ description: ClusterResourceUsage provides cluster-wide resource usage.
+ properties:
+ apiVersion:
+ description: 'APIVersion defines the versioned schema of this representation
+ of an object. Servers should convert recognized schemas to the latest
+ internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+ type: string
+ kind:
+ description: 'Kind is a string value representing the REST resource this
+ object represents. Servers may infer this from the endpoint the client
+ submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+ type: string
+ metadata:
+ type: object
+ spec:
+ description: Spec holds spec for cluster resource usage.
+ properties:
+ selector:
+ description: Selector is a selector to filter pods to configure.
+ properties:
+ matchNames:
+ description: MatchNames is a list of names to match. if not set,
+ all names will be matched.
+ items:
+ type: string
+ type: array
+ matchNamespaces:
+ description: MatchNamespaces is a list of namespaces to match.
+ if not set, all namespaces will be matched.
+ items:
+ type: string
+ type: array
+ type: object
+ usages:
+ description: Usages is a list of resource usage for the pod.
+ items:
+ description: ResourceUsageContainer holds spec for resource usage
+ container.
+ properties:
+ containers:
+ description: Containers is list of container names.
+ items:
+ type: string
+ type: array
+ usage:
+ additionalProperties:
+ description: ResourceUsageValue holds value for resource usage.
+ properties:
+ expression:
+ description: Expression is the expression for resource
+ usage.
+ type: string
+ value:
+ anyOf:
+ - type: integer
+ - type: string
+ description: Value is the value for resource usage.
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ description: Usage is a list of resource usage for the container.
+ type: object
+ type: object
+ type: array
+ type: object
+ status:
+ description: Status holds status for cluster resource usage
+ properties:
+ conditions:
+ description: Conditions holds conditions for cluster resource usage
+ items:
+ description: Condition contains details for one aspect of the current
+ state of this API Resource.
+ properties:
+ lastTransitionTime:
+ description: LastTransitionTime is the last time the condition
+ transitioned from one status to another. This should be when
+ the underlying condition changed. If that is not known, then
+ using the time when the API field changed is acceptable.
+ format: date-time
+ type: string
+ message:
+ description: Message is a human readable message indicating
+ details about the transition. This may be an empty string.
+ maxLength: 32768
+ type: string
+ reason:
+ description: Reason contains a programmatic identifier indicating
+ the reason for the condition's last transition. Producers
+ of specific condition types may define expected values and
+ meanings for this field, and whether the values are considered
+ a guaranteed API. The value should be a CamelCase string.
+ This field may not be empty.
+ maxLength: 1024
+ minLength: 1
+ pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+ type: string
+ status:
+ description: Status of the condition
+ type: string
+ type:
+ description: Type of condition in CamelCase or in foo.example.com/CamelCase.
+ Many .condition.type values are consistent across resources
+ like Available, but because arbitrary conditions can be useful
+ (see .node.status.conditions), the ability to deconflict is
+ important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+ maxLength: 316
+ pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+ type: string
+ required:
+ - lastTransitionTime
+ - message
+ - reason
+ - status
+ - type
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - type
+ x-kubernetes-list-type: map
+ type: object
+ required:
+ - metadata
+ - spec
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
diff --git a/kustomize/crd/bases/kwok.x-k8s.io_resourceusages.yaml b/kustomize/crd/bases/kwok.x-k8s.io_resourceusages.yaml
new file mode 100644
index 000000000..1268625c9
--- /dev/null
+++ b/kustomize/crd/bases/kwok.x-k8s.io_resourceusages.yaml
@@ -0,0 +1,132 @@
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.12.1
+ name: resourceusages.kwok.x-k8s.io
+spec:
+ group: kwok.x-k8s.io
+ names:
+ kind: ResourceUsage
+ listKind: ResourceUsageList
+ plural: resourceusages
+ singular: resourceusage
+ scope: Namespaced
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ description: ResourceUsage provides resource usage for a single pod.
+ properties:
+ apiVersion:
+ description: 'APIVersion defines the versioned schema of this representation
+ of an object. Servers should convert recognized schemas to the latest
+ internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+ type: string
+ kind:
+ description: 'Kind is a string value representing the REST resource this
+ object represents. Servers may infer this from the endpoint the client
+ submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+ type: string
+ metadata:
+ type: object
+ spec:
+ description: Spec holds spec for resource usage.
+ properties:
+ usages:
+ description: Usages is a list of resource usage for the pod.
+ items:
+ description: ResourceUsageContainer holds spec for resource usage
+ container.
+ properties:
+ containers:
+ description: Containers is list of container names.
+ items:
+ type: string
+ type: array
+ usage:
+ additionalProperties:
+ description: ResourceUsageValue holds value for resource usage.
+ properties:
+ expression:
+ description: Expression is the expression for resource
+ usage.
+ type: string
+ value:
+ anyOf:
+ - type: integer
+ - type: string
+ description: Value is the value for resource usage.
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ description: Usage is a list of resource usage for the container.
+ type: object
+ type: object
+ type: array
+ type: object
+ status:
+ description: Status holds status for resource usage
+ properties:
+ conditions:
+ description: Conditions holds conditions for resource usage
+ items:
+ description: Condition contains details for one aspect of the current
+ state of this API Resource.
+ properties:
+ lastTransitionTime:
+ description: LastTransitionTime is the last time the condition
+ transitioned from one status to another. This should be when
+ the underlying condition changed. If that is not known, then
+ using the time when the API field changed is acceptable.
+ format: date-time
+ type: string
+ message:
+ description: Message is a human readable message indicating
+ details about the transition. This may be an empty string.
+ maxLength: 32768
+ type: string
+ reason:
+ description: Reason contains a programmatic identifier indicating
+ the reason for the condition's last transition. Producers
+ of specific condition types may define expected values and
+ meanings for this field, and whether the values are considered
+ a guaranteed API. The value should be a CamelCase string.
+ This field may not be empty.
+ maxLength: 1024
+ minLength: 1
+ pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+ type: string
+ status:
+ description: Status of the condition
+ type: string
+ type:
+ description: Type of condition in CamelCase or in foo.example.com/CamelCase.
+ Many .condition.type values are consistent across resources
+ like Available, but because arbitrary conditions can be useful
+ (see .node.status.conditions), the ability to deconflict is
+ important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+ maxLength: 316
+ pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+ type: string
+ required:
+ - lastTransitionTime
+ - message
+ - reason
+ - status
+ - type
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - type
+ x-kubernetes-list-type: map
+ type: object
+ required:
+ - metadata
+ - spec
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
diff --git a/kustomize/crd/embed.go b/kustomize/crd/embed.go
index d82b613d8..61dbcc134 100644
--- a/kustomize/crd/embed.go
+++ b/kustomize/crd/embed.go
@@ -58,6 +58,14 @@ var (
//go:embed bases/kwok.x-k8s.io_clusterportforwards.yaml
ClusterPortForward []byte
+ // ResourceUsage is the custom resource definition for resource usage.
+ //go:embed bases/kwok.x-k8s.io_resourceusages.yaml
+ ResourceUsage []byte
+
+ // ClusterResourceUsage is the custom resource definition for cluster resource usage.
+ //go:embed bases/kwok.x-k8s.io_clusterresourceusages.yaml
+ ClusterResourceUsage []byte
+
// Metric is the custom resource definition for metrics.
//go:embed bases/kwok.x-k8s.io_metrics.yaml
Metric []byte
diff --git a/kustomize/kwokctl/resource/pod.yaml b/kustomize/kwokctl/resource/pod.yaml
index e8eb8f964..2fa3d7b74 100644
--- a/kustomize/kwokctl/resource/pod.yaml
+++ b/kustomize/kwokctl/resource/pod.yaml
@@ -20,11 +20,48 @@ template: |-
{{ range $index, $container := .containers }}
- name: {{ $container.name }}
image: {{ $container.image }}
+ {{ if and $container $container.resources }}
+ {{ $resources := $container.resources }}
+ resources:
+ requests:
+ {{ range $key, $value := $resources.requests }}
+ {{ $key }}: {{ $value }}
+ {{ end }}
+ limits:
+ {{ if $resources.limits }}
+ {{ range $key, $value := $resources.requests }}
+ {{ $key }}: {{ or ( index $resources.limits $key ) $value }}
+ {{ end }}
+ {{ else }}
+ {{ range $key, $value := $resources.requests }}
+ {{ $key }}: {{ $value }}
+ {{ end }}
+ {{ end }}
+ {{ end }}
{{ end }}
+
initContainers:
{{ range $index, $container := .initContainers }}
- name: {{ $container.name }}
image: {{ $container.image }}
+ {{ if and $container $container.resources }}
+ {{ $resources := $container.resources }}
+ resources:
+ requests:
+ {{ range $key, $value := $resources.requests }}
+ {{ $key }}: {{ $value }}
+ {{ end }}
+ limits:
+ {{ if $resources.limits }}
+ {{ range $key, $value := $resources.requests }}
+ {{ $key }}: {{ or ( index $resources.limits $key ) $value }}
+ {{ end }}
+ {{ else }}
+ {{ range $key, $value := $resources.requests }}
+ {{ $key }}: {{ $value }}
+ {{ end }}
+ {{ end }}
+ {{ end }}
{{ end }}
hostNetwork: {{ .hostNetwork }}
nodeName: {{ .nodeName }}
diff --git a/kustomize/rbac/role.yaml b/kustomize/rbac/role.yaml
index 21925c3fa..62c971462 100644
--- a/kustomize/rbac/role.yaml
+++ b/kustomize/rbac/role.yaml
@@ -120,6 +120,18 @@ rules:
- patch
- update
- watch
+- apiGroups:
+ - kwok.x-k8s.io
+ resources:
+ - clusterresourceusages
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
- apiGroups:
- kwok.x-k8s.io
resources:
@@ -168,6 +180,18 @@ rules:
- patch
- update
- watch
+- apiGroups:
+ - kwok.x-k8s.io
+ resources:
+ - resourceusages
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
- apiGroups:
- kwok.x-k8s.io
resources:
diff --git a/pkg/apis/internalversion/cluster_resource_usage_types.go b/pkg/apis/internalversion/cluster_resource_usage_types.go
new file mode 100644
index 000000000..10bb25951
--- /dev/null
+++ b/pkg/apis/internalversion/cluster_resource_usage_types.go
@@ -0,0 +1,38 @@
+/*
+Copyright 2023 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package internalversion
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// ClusterResourceUsage provides cluster-wide resource usage.
+type ClusterResourceUsage struct {
+ // Standard list metadata.
+ // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
+ metav1.ObjectMeta
+ // Spec holds spec for cluster resource usage.
+ Spec ClusterResourceUsageSpec
+}
+
+// ClusterResourceUsageSpec holds spec for cluster resource usage.
+type ClusterResourceUsageSpec struct {
+ // Selector is a selector to filter pods to configure.
+ Selector *ObjectSelector
+ // Usages is a list of resource usage for the pod.
+ Usages []ResourceUsageContainer
+}
diff --git a/pkg/apis/internalversion/conversion.go b/pkg/apis/internalversion/conversion.go
index c4b5db997..f507354c9 100644
--- a/pkg/apis/internalversion/conversion.go
+++ b/pkg/apis/internalversion/conversion.go
@@ -322,6 +322,50 @@ func ConvertToInternalAttach(in *v1alpha1.Attach) (*Attach, error) {
return &out, nil
}
+// ConvertToV1Alpha1ResourceUsage converts an internal version ResourceUsage to a v1alpha1.ResourceUsage.
+func ConvertToV1Alpha1ResourceUsage(in *ResourceUsage) (*v1alpha1.ResourceUsage, error) {
+ var out v1alpha1.ResourceUsage
+ out.APIVersion = v1alpha1.GroupVersion.String()
+ out.Kind = v1alpha1.ResourceUsageKind
+ err := Convert_internalversion_ResourceUsage_To_v1alpha1_ResourceUsage(in, &out, nil)
+ if err != nil {
+ return nil, err
+ }
+ return &out, nil
+}
+
+// ConvertToInternalResourceUsage converts a v1alpha1.ResourceUsage to an internal version.
+func ConvertToInternalResourceUsage(in *v1alpha1.ResourceUsage) (*ResourceUsage, error) {
+ var out ResourceUsage
+ err := Convert_v1alpha1_ResourceUsage_To_internalversion_ResourceUsage(in, &out, nil)
+ if err != nil {
+ return nil, err
+ }
+ return &out, nil
+}
+
+// ConvertToV1Alpha1ClusterResourceUsage converts an internal version ClusterResourceUsage to a v1alpha1.ClusterResourceUsage.
+func ConvertToV1Alpha1ClusterResourceUsage(in *ClusterResourceUsage) (*v1alpha1.ClusterResourceUsage, error) {
+ var out v1alpha1.ClusterResourceUsage
+ out.APIVersion = v1alpha1.GroupVersion.String()
+ out.Kind = v1alpha1.ClusterResourceUsageKind
+ err := Convert_internalversion_ClusterResourceUsage_To_v1alpha1_ClusterResourceUsage(in, &out, nil)
+ if err != nil {
+ return nil, err
+ }
+ return &out, nil
+}
+
+// ConvertToInternalClusterResourceUsage converts a v1alpha1.ClusterResourceUsage to an internal version.
+func ConvertToInternalClusterResourceUsage(in *v1alpha1.ClusterResourceUsage) (*ClusterResourceUsage, error) {
+ var out ClusterResourceUsage
+ err := Convert_v1alpha1_ClusterResourceUsage_To_internalversion_ClusterResourceUsage(in, &out, nil)
+ if err != nil {
+ return nil, err
+ }
+ return &out, nil
+}
+
// ConvertToV1Alpha1Metric converts an internal version Metric to a v1alpha1.Metric.
func ConvertToV1Alpha1Metric(in *Metric) (*v1alpha1.Metric, error) {
var out v1alpha1.Metric
diff --git a/pkg/apis/internalversion/resource_usage_types.go b/pkg/apis/internalversion/resource_usage_types.go
new file mode 100644
index 000000000..a978c890c
--- /dev/null
+++ b/pkg/apis/internalversion/resource_usage_types.go
@@ -0,0 +1,53 @@
+/*
+Copyright 2023 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package internalversion
+
+import (
+ "k8s.io/apimachinery/pkg/api/resource"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// ResourceUsage provides resource usage for a single pod.
+type ResourceUsage struct {
+ // Standard list metadata.
+ // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
+ metav1.ObjectMeta
+ // Spec holds spec for resource usage.
+ Spec ResourceUsageSpec
+}
+
+// ResourceUsageSpec holds spec for resource usage.
+type ResourceUsageSpec struct {
+ // Usages is a list of resource usage for the pod.
+ Usages []ResourceUsageContainer
+}
+
+// ResourceUsageContainer holds spec for resource usage container.
+type ResourceUsageContainer struct {
+ // Containers is list of container names.
+ Containers []string
+ // Usage is a list of resource usage for the container.
+ Usage map[string]ResourceUsageValue
+}
+
+// ResourceUsageValue holds value for resource usage.
+type ResourceUsageValue struct {
+ // Value is the value for resource usage.
+ Value *resource.Quantity
+ // Expression is the expression for resource usage.
+ Expression *string
+}
diff --git a/pkg/apis/internalversion/zz_generated.conversion.go b/pkg/apis/internalversion/zz_generated.conversion.go
index 50ccded57..00a95ca02 100644
--- a/pkg/apis/internalversion/zz_generated.conversion.go
+++ b/pkg/apis/internalversion/zz_generated.conversion.go
@@ -25,6 +25,7 @@ import (
json "encoding/json"
unsafe "unsafe"
+ resource "k8s.io/apimachinery/pkg/api/resource"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime"
@@ -149,6 +150,26 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
+ if err := s.AddGeneratedConversionFunc((*ClusterResourceUsage)(nil), (*v1alpha1.ClusterResourceUsage)(nil), func(a, b interface{}, scope conversion.Scope) error {
+ return Convert_internalversion_ClusterResourceUsage_To_v1alpha1_ClusterResourceUsage(a.(*ClusterResourceUsage), b.(*v1alpha1.ClusterResourceUsage), scope)
+ }); err != nil {
+ return err
+ }
+ if err := s.AddGeneratedConversionFunc((*v1alpha1.ClusterResourceUsage)(nil), (*ClusterResourceUsage)(nil), func(a, b interface{}, scope conversion.Scope) error {
+ return Convert_v1alpha1_ClusterResourceUsage_To_internalversion_ClusterResourceUsage(a.(*v1alpha1.ClusterResourceUsage), b.(*ClusterResourceUsage), scope)
+ }); err != nil {
+ return err
+ }
+ if err := s.AddGeneratedConversionFunc((*ClusterResourceUsageSpec)(nil), (*v1alpha1.ClusterResourceUsageSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
+ return Convert_internalversion_ClusterResourceUsageSpec_To_v1alpha1_ClusterResourceUsageSpec(a.(*ClusterResourceUsageSpec), b.(*v1alpha1.ClusterResourceUsageSpec), scope)
+ }); err != nil {
+ return err
+ }
+ if err := s.AddGeneratedConversionFunc((*v1alpha1.ClusterResourceUsageSpec)(nil), (*ClusterResourceUsageSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
+ return Convert_v1alpha1_ClusterResourceUsageSpec_To_internalversion_ClusterResourceUsageSpec(a.(*v1alpha1.ClusterResourceUsageSpec), b.(*ClusterResourceUsageSpec), scope)
+ }); err != nil {
+ return err
+ }
if err := s.AddGeneratedConversionFunc((*Component)(nil), (*configv1alpha1.Component)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_internalversion_Component_To_v1alpha1_Component(a.(*Component), b.(*configv1alpha1.Component), scope)
}); err != nil {
@@ -459,6 +480,46 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
+ if err := s.AddGeneratedConversionFunc((*ResourceUsage)(nil), (*v1alpha1.ResourceUsage)(nil), func(a, b interface{}, scope conversion.Scope) error {
+ return Convert_internalversion_ResourceUsage_To_v1alpha1_ResourceUsage(a.(*ResourceUsage), b.(*v1alpha1.ResourceUsage), scope)
+ }); err != nil {
+ return err
+ }
+ if err := s.AddGeneratedConversionFunc((*v1alpha1.ResourceUsage)(nil), (*ResourceUsage)(nil), func(a, b interface{}, scope conversion.Scope) error {
+ return Convert_v1alpha1_ResourceUsage_To_internalversion_ResourceUsage(a.(*v1alpha1.ResourceUsage), b.(*ResourceUsage), scope)
+ }); err != nil {
+ return err
+ }
+ if err := s.AddGeneratedConversionFunc((*ResourceUsageContainer)(nil), (*v1alpha1.ResourceUsageContainer)(nil), func(a, b interface{}, scope conversion.Scope) error {
+ return Convert_internalversion_ResourceUsageContainer_To_v1alpha1_ResourceUsageContainer(a.(*ResourceUsageContainer), b.(*v1alpha1.ResourceUsageContainer), scope)
+ }); err != nil {
+ return err
+ }
+ if err := s.AddGeneratedConversionFunc((*v1alpha1.ResourceUsageContainer)(nil), (*ResourceUsageContainer)(nil), func(a, b interface{}, scope conversion.Scope) error {
+ return Convert_v1alpha1_ResourceUsageContainer_To_internalversion_ResourceUsageContainer(a.(*v1alpha1.ResourceUsageContainer), b.(*ResourceUsageContainer), scope)
+ }); err != nil {
+ return err
+ }
+ if err := s.AddGeneratedConversionFunc((*ResourceUsageSpec)(nil), (*v1alpha1.ResourceUsageSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
+ return Convert_internalversion_ResourceUsageSpec_To_v1alpha1_ResourceUsageSpec(a.(*ResourceUsageSpec), b.(*v1alpha1.ResourceUsageSpec), scope)
+ }); err != nil {
+ return err
+ }
+ if err := s.AddGeneratedConversionFunc((*v1alpha1.ResourceUsageSpec)(nil), (*ResourceUsageSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
+ return Convert_v1alpha1_ResourceUsageSpec_To_internalversion_ResourceUsageSpec(a.(*v1alpha1.ResourceUsageSpec), b.(*ResourceUsageSpec), scope)
+ }); err != nil {
+ return err
+ }
+ if err := s.AddGeneratedConversionFunc((*ResourceUsageValue)(nil), (*v1alpha1.ResourceUsageValue)(nil), func(a, b interface{}, scope conversion.Scope) error {
+ return Convert_internalversion_ResourceUsageValue_To_v1alpha1_ResourceUsageValue(a.(*ResourceUsageValue), b.(*v1alpha1.ResourceUsageValue), scope)
+ }); err != nil {
+ return err
+ }
+ if err := s.AddGeneratedConversionFunc((*v1alpha1.ResourceUsageValue)(nil), (*ResourceUsageValue)(nil), func(a, b interface{}, scope conversion.Scope) error {
+ return Convert_v1alpha1_ResourceUsageValue_To_internalversion_ResourceUsageValue(a.(*v1alpha1.ResourceUsageValue), b.(*ResourceUsageValue), scope)
+ }); err != nil {
+ return err
+ }
if err := s.AddGeneratedConversionFunc((*SecurityContext)(nil), (*v1alpha1.SecurityContext)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_internalversion_SecurityContext_To_v1alpha1_SecurityContext(a.(*SecurityContext), b.(*v1alpha1.SecurityContext), scope)
}); err != nil {
@@ -906,6 +967,56 @@ func Convert_v1alpha1_ClusterPortForwardSpec_To_internalversion_ClusterPortForwa
return autoConvert_v1alpha1_ClusterPortForwardSpec_To_internalversion_ClusterPortForwardSpec(in, out, s)
}
+func autoConvert_internalversion_ClusterResourceUsage_To_v1alpha1_ClusterResourceUsage(in *ClusterResourceUsage, out *v1alpha1.ClusterResourceUsage, s conversion.Scope) error {
+ out.ObjectMeta = in.ObjectMeta
+ if err := Convert_internalversion_ClusterResourceUsageSpec_To_v1alpha1_ClusterResourceUsageSpec(&in.Spec, &out.Spec, s); err != nil {
+ return err
+ }
+ return nil
+}
+
+// Convert_internalversion_ClusterResourceUsage_To_v1alpha1_ClusterResourceUsage is an autogenerated conversion function.
+func Convert_internalversion_ClusterResourceUsage_To_v1alpha1_ClusterResourceUsage(in *ClusterResourceUsage, out *v1alpha1.ClusterResourceUsage, s conversion.Scope) error {
+ return autoConvert_internalversion_ClusterResourceUsage_To_v1alpha1_ClusterResourceUsage(in, out, s)
+}
+
+func autoConvert_v1alpha1_ClusterResourceUsage_To_internalversion_ClusterResourceUsage(in *v1alpha1.ClusterResourceUsage, out *ClusterResourceUsage, s conversion.Scope) error {
+ // INFO: in.TypeMeta opted out of conversion generation
+ out.ObjectMeta = in.ObjectMeta
+ if err := Convert_v1alpha1_ClusterResourceUsageSpec_To_internalversion_ClusterResourceUsageSpec(&in.Spec, &out.Spec, s); err != nil {
+ return err
+ }
+ // INFO: in.Status opted out of conversion generation
+ return nil
+}
+
+// Convert_v1alpha1_ClusterResourceUsage_To_internalversion_ClusterResourceUsage is an autogenerated conversion function.
+func Convert_v1alpha1_ClusterResourceUsage_To_internalversion_ClusterResourceUsage(in *v1alpha1.ClusterResourceUsage, out *ClusterResourceUsage, s conversion.Scope) error {
+ return autoConvert_v1alpha1_ClusterResourceUsage_To_internalversion_ClusterResourceUsage(in, out, s)
+}
+
+func autoConvert_internalversion_ClusterResourceUsageSpec_To_v1alpha1_ClusterResourceUsageSpec(in *ClusterResourceUsageSpec, out *v1alpha1.ClusterResourceUsageSpec, s conversion.Scope) error {
+ out.Selector = (*v1alpha1.ObjectSelector)(unsafe.Pointer(in.Selector))
+ out.Usages = *(*[]v1alpha1.ResourceUsageContainer)(unsafe.Pointer(&in.Usages))
+ return nil
+}
+
+// Convert_internalversion_ClusterResourceUsageSpec_To_v1alpha1_ClusterResourceUsageSpec is an autogenerated conversion function.
+func Convert_internalversion_ClusterResourceUsageSpec_To_v1alpha1_ClusterResourceUsageSpec(in *ClusterResourceUsageSpec, out *v1alpha1.ClusterResourceUsageSpec, s conversion.Scope) error {
+ return autoConvert_internalversion_ClusterResourceUsageSpec_To_v1alpha1_ClusterResourceUsageSpec(in, out, s)
+}
+
+func autoConvert_v1alpha1_ClusterResourceUsageSpec_To_internalversion_ClusterResourceUsageSpec(in *v1alpha1.ClusterResourceUsageSpec, out *ClusterResourceUsageSpec, s conversion.Scope) error {
+ out.Selector = (*ObjectSelector)(unsafe.Pointer(in.Selector))
+ out.Usages = *(*[]ResourceUsageContainer)(unsafe.Pointer(&in.Usages))
+ return nil
+}
+
+// Convert_v1alpha1_ClusterResourceUsageSpec_To_internalversion_ClusterResourceUsageSpec is an autogenerated conversion function.
+func Convert_v1alpha1_ClusterResourceUsageSpec_To_internalversion_ClusterResourceUsageSpec(in *v1alpha1.ClusterResourceUsageSpec, out *ClusterResourceUsageSpec, s conversion.Scope) error {
+ return autoConvert_v1alpha1_ClusterResourceUsageSpec_To_internalversion_ClusterResourceUsageSpec(in, out, s)
+}
+
func autoConvert_internalversion_Component_To_v1alpha1_Component(in *Component, out *configv1alpha1.Component, s conversion.Scope) error {
out.Name = in.Name
out.Links = *(*[]string)(unsafe.Pointer(&in.Links))
@@ -2017,6 +2128,98 @@ func Convert_v1alpha1_PortForwardSpec_To_internalversion_PortForwardSpec(in *v1a
return autoConvert_v1alpha1_PortForwardSpec_To_internalversion_PortForwardSpec(in, out, s)
}
+func autoConvert_internalversion_ResourceUsage_To_v1alpha1_ResourceUsage(in *ResourceUsage, out *v1alpha1.ResourceUsage, s conversion.Scope) error {
+ out.ObjectMeta = in.ObjectMeta
+ if err := Convert_internalversion_ResourceUsageSpec_To_v1alpha1_ResourceUsageSpec(&in.Spec, &out.Spec, s); err != nil {
+ return err
+ }
+ return nil
+}
+
+// Convert_internalversion_ResourceUsage_To_v1alpha1_ResourceUsage is an autogenerated conversion function.
+func Convert_internalversion_ResourceUsage_To_v1alpha1_ResourceUsage(in *ResourceUsage, out *v1alpha1.ResourceUsage, s conversion.Scope) error {
+ return autoConvert_internalversion_ResourceUsage_To_v1alpha1_ResourceUsage(in, out, s)
+}
+
+func autoConvert_v1alpha1_ResourceUsage_To_internalversion_ResourceUsage(in *v1alpha1.ResourceUsage, out *ResourceUsage, s conversion.Scope) error {
+ // INFO: in.TypeMeta opted out of conversion generation
+ out.ObjectMeta = in.ObjectMeta
+ if err := Convert_v1alpha1_ResourceUsageSpec_To_internalversion_ResourceUsageSpec(&in.Spec, &out.Spec, s); err != nil {
+ return err
+ }
+ // INFO: in.Status opted out of conversion generation
+ return nil
+}
+
+// Convert_v1alpha1_ResourceUsage_To_internalversion_ResourceUsage is an autogenerated conversion function.
+func Convert_v1alpha1_ResourceUsage_To_internalversion_ResourceUsage(in *v1alpha1.ResourceUsage, out *ResourceUsage, s conversion.Scope) error {
+ return autoConvert_v1alpha1_ResourceUsage_To_internalversion_ResourceUsage(in, out, s)
+}
+
+func autoConvert_internalversion_ResourceUsageContainer_To_v1alpha1_ResourceUsageContainer(in *ResourceUsageContainer, out *v1alpha1.ResourceUsageContainer, s conversion.Scope) error {
+ out.Containers = *(*[]string)(unsafe.Pointer(&in.Containers))
+ out.Usage = *(*map[string]v1alpha1.ResourceUsageValue)(unsafe.Pointer(&in.Usage))
+ return nil
+}
+
+// Convert_internalversion_ResourceUsageContainer_To_v1alpha1_ResourceUsageContainer is an autogenerated conversion function.
+func Convert_internalversion_ResourceUsageContainer_To_v1alpha1_ResourceUsageContainer(in *ResourceUsageContainer, out *v1alpha1.ResourceUsageContainer, s conversion.Scope) error {
+ return autoConvert_internalversion_ResourceUsageContainer_To_v1alpha1_ResourceUsageContainer(in, out, s)
+}
+
+func autoConvert_v1alpha1_ResourceUsageContainer_To_internalversion_ResourceUsageContainer(in *v1alpha1.ResourceUsageContainer, out *ResourceUsageContainer, s conversion.Scope) error {
+ out.Containers = *(*[]string)(unsafe.Pointer(&in.Containers))
+ out.Usage = *(*map[string]ResourceUsageValue)(unsafe.Pointer(&in.Usage))
+ return nil
+}
+
+// Convert_v1alpha1_ResourceUsageContainer_To_internalversion_ResourceUsageContainer is an autogenerated conversion function.
+func Convert_v1alpha1_ResourceUsageContainer_To_internalversion_ResourceUsageContainer(in *v1alpha1.ResourceUsageContainer, out *ResourceUsageContainer, s conversion.Scope) error {
+ return autoConvert_v1alpha1_ResourceUsageContainer_To_internalversion_ResourceUsageContainer(in, out, s)
+}
+
+func autoConvert_internalversion_ResourceUsageSpec_To_v1alpha1_ResourceUsageSpec(in *ResourceUsageSpec, out *v1alpha1.ResourceUsageSpec, s conversion.Scope) error {
+ out.Usages = *(*[]v1alpha1.ResourceUsageContainer)(unsafe.Pointer(&in.Usages))
+ return nil
+}
+
+// Convert_internalversion_ResourceUsageSpec_To_v1alpha1_ResourceUsageSpec is an autogenerated conversion function.
+func Convert_internalversion_ResourceUsageSpec_To_v1alpha1_ResourceUsageSpec(in *ResourceUsageSpec, out *v1alpha1.ResourceUsageSpec, s conversion.Scope) error {
+ return autoConvert_internalversion_ResourceUsageSpec_To_v1alpha1_ResourceUsageSpec(in, out, s)
+}
+
+func autoConvert_v1alpha1_ResourceUsageSpec_To_internalversion_ResourceUsageSpec(in *v1alpha1.ResourceUsageSpec, out *ResourceUsageSpec, s conversion.Scope) error {
+ out.Usages = *(*[]ResourceUsageContainer)(unsafe.Pointer(&in.Usages))
+ return nil
+}
+
+// Convert_v1alpha1_ResourceUsageSpec_To_internalversion_ResourceUsageSpec is an autogenerated conversion function.
+func Convert_v1alpha1_ResourceUsageSpec_To_internalversion_ResourceUsageSpec(in *v1alpha1.ResourceUsageSpec, out *ResourceUsageSpec, s conversion.Scope) error {
+ return autoConvert_v1alpha1_ResourceUsageSpec_To_internalversion_ResourceUsageSpec(in, out, s)
+}
+
+func autoConvert_internalversion_ResourceUsageValue_To_v1alpha1_ResourceUsageValue(in *ResourceUsageValue, out *v1alpha1.ResourceUsageValue, s conversion.Scope) error {
+ out.Value = (*resource.Quantity)(unsafe.Pointer(in.Value))
+ out.Expression = (*string)(unsafe.Pointer(in.Expression))
+ return nil
+}
+
+// Convert_internalversion_ResourceUsageValue_To_v1alpha1_ResourceUsageValue is an autogenerated conversion function.
+func Convert_internalversion_ResourceUsageValue_To_v1alpha1_ResourceUsageValue(in *ResourceUsageValue, out *v1alpha1.ResourceUsageValue, s conversion.Scope) error {
+ return autoConvert_internalversion_ResourceUsageValue_To_v1alpha1_ResourceUsageValue(in, out, s)
+}
+
+func autoConvert_v1alpha1_ResourceUsageValue_To_internalversion_ResourceUsageValue(in *v1alpha1.ResourceUsageValue, out *ResourceUsageValue, s conversion.Scope) error {
+ out.Value = (*resource.Quantity)(unsafe.Pointer(in.Value))
+ out.Expression = (*string)(unsafe.Pointer(in.Expression))
+ return nil
+}
+
+// Convert_v1alpha1_ResourceUsageValue_To_internalversion_ResourceUsageValue is an autogenerated conversion function.
+func Convert_v1alpha1_ResourceUsageValue_To_internalversion_ResourceUsageValue(in *v1alpha1.ResourceUsageValue, out *ResourceUsageValue, s conversion.Scope) error {
+ return autoConvert_v1alpha1_ResourceUsageValue_To_internalversion_ResourceUsageValue(in, out, s)
+}
+
func autoConvert_internalversion_SecurityContext_To_v1alpha1_SecurityContext(in *SecurityContext, out *v1alpha1.SecurityContext, s conversion.Scope) error {
out.RunAsUser = (*int64)(unsafe.Pointer(in.RunAsUser))
out.RunAsGroup = (*int64)(unsafe.Pointer(in.RunAsGroup))
diff --git a/pkg/apis/internalversion/zz_generated.deepcopy.go b/pkg/apis/internalversion/zz_generated.deepcopy.go
index 2c03bf13f..2eaca0be3 100644
--- a/pkg/apis/internalversion/zz_generated.deepcopy.go
+++ b/pkg/apis/internalversion/zz_generated.deepcopy.go
@@ -271,6 +271,52 @@ func (in *ClusterPortForwardSpec) DeepCopy() *ClusterPortForwardSpec {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterResourceUsage) DeepCopyInto(out *ClusterResourceUsage) {
+ *out = *in
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ in.Spec.DeepCopyInto(&out.Spec)
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterResourceUsage.
+func (in *ClusterResourceUsage) DeepCopy() *ClusterResourceUsage {
+ if in == nil {
+ return nil
+ }
+ out := new(ClusterResourceUsage)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterResourceUsageSpec) DeepCopyInto(out *ClusterResourceUsageSpec) {
+ *out = *in
+ if in.Selector != nil {
+ in, out := &in.Selector, &out.Selector
+ *out = new(ObjectSelector)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.Usages != nil {
+ in, out := &in.Usages, &out.Usages
+ *out = make([]ResourceUsageContainer, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterResourceUsageSpec.
+func (in *ClusterResourceUsageSpec) DeepCopy() *ClusterResourceUsageSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(ClusterResourceUsageSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Component) DeepCopyInto(out *Component) {
*out = *in
@@ -953,6 +999,101 @@ func (in *PortForwardSpec) DeepCopy() *PortForwardSpec {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ResourceUsage) DeepCopyInto(out *ResourceUsage) {
+ *out = *in
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ in.Spec.DeepCopyInto(&out.Spec)
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceUsage.
+func (in *ResourceUsage) DeepCopy() *ResourceUsage {
+ if in == nil {
+ return nil
+ }
+ out := new(ResourceUsage)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ResourceUsageContainer) DeepCopyInto(out *ResourceUsageContainer) {
+ *out = *in
+ if in.Containers != nil {
+ in, out := &in.Containers, &out.Containers
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
+ if in.Usage != nil {
+ in, out := &in.Usage, &out.Usage
+ *out = make(map[string]ResourceUsageValue, len(*in))
+ for key, val := range *in {
+ (*out)[key] = *val.DeepCopy()
+ }
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceUsageContainer.
+func (in *ResourceUsageContainer) DeepCopy() *ResourceUsageContainer {
+ if in == nil {
+ return nil
+ }
+ out := new(ResourceUsageContainer)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ResourceUsageSpec) DeepCopyInto(out *ResourceUsageSpec) {
+ *out = *in
+ if in.Usages != nil {
+ in, out := &in.Usages, &out.Usages
+ *out = make([]ResourceUsageContainer, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceUsageSpec.
+func (in *ResourceUsageSpec) DeepCopy() *ResourceUsageSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(ResourceUsageSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ResourceUsageValue) DeepCopyInto(out *ResourceUsageValue) {
+ *out = *in
+ if in.Value != nil {
+ in, out := &in.Value, &out.Value
+ x := (*in).DeepCopy()
+ *out = &x
+ }
+ if in.Expression != nil {
+ in, out := &in.Expression, &out.Expression
+ *out = new(string)
+ **out = **in
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceUsageValue.
+func (in *ResourceUsageValue) DeepCopy() *ResourceUsageValue {
+ if in == nil {
+ return nil
+ }
+ out := new(ResourceUsageValue)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SecurityContext) DeepCopyInto(out *SecurityContext) {
*out = *in
diff --git a/pkg/apis/v1alpha1/cluster_resource_usage_types.go b/pkg/apis/v1alpha1/cluster_resource_usage_types.go
new file mode 100644
index 000000000..811e11909
--- /dev/null
+++ b/pkg/apis/v1alpha1/cluster_resource_usage_types.go
@@ -0,0 +1,79 @@
+/*
+Copyright 2023 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+const (
+ // ClusterResourceUsageKind is the kind for ClusterResourceUsage.
+ ClusterResourceUsageKind = "ClusterResourceUsage"
+)
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+// +genclient
+// +genclient:nonNamespaced
+// +kubebuilder:subresource:status
+// +kubebuilder:resource:scope=Cluster
+// +kubebuilder:rbac:groups=kwok.x-k8s.io,resources=clusterresourceusages,verbs=create;delete;get;list;patch;update;watch
+
+// ClusterResourceUsage provides cluster-wide resource usage.
+type ClusterResourceUsage struct {
+ //+k8s:conversion-gen=false
+ metav1.TypeMeta `json:",inline"`
+ // Standard list metadata.
+ // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
+ metav1.ObjectMeta `json:"metadata"`
+ // Spec holds spec for cluster resource usage.
+ Spec ClusterResourceUsageSpec `json:"spec"`
+ // Status holds status for cluster resource usage
+ //+k8s:conversion-gen=false
+ Status ClusterResourceUsageStatus `json:"status,omitempty"`
+}
+
+// ClusterResourceUsageStatus holds status for cluster resource usage
+type ClusterResourceUsageStatus struct {
+ // Conditions holds conditions for cluster resource usage
+ // +patchMergeKey=type
+ // +patchStrategy=merge
+ // +listType=map
+ // +listMapKey=type
+ Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
+}
+
+// ClusterResourceUsageSpec holds spec for cluster resource usage.
+type ClusterResourceUsageSpec struct {
+ // Selector is a selector to filter pods to configure.
+ Selector *ObjectSelector `json:"selector,omitempty"`
+ // Usages is a list of resource usage for the pod.
+ Usages []ResourceUsageContainer `json:"usages,omitempty"`
+}
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+// +kubebuilder:object:root=true
+
+// ClusterResourceUsageList is a list of ClusterResourceUsage.
+type ClusterResourceUsageList struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ListMeta `json:"metadata"`
+ Items []ClusterResourceUsage `json:"items"`
+}
+
+func init() {
+ SchemeBuilder.Register(&ClusterResourceUsage{}, &ClusterResourceUsageList{})
+}
diff --git a/pkg/apis/v1alpha1/resource_usage_types.go b/pkg/apis/v1alpha1/resource_usage_types.go
new file mode 100644
index 000000000..f6937c1ee
--- /dev/null
+++ b/pkg/apis/v1alpha1/resource_usage_types.go
@@ -0,0 +1,92 @@
+/*
+Copyright 2023 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+ "k8s.io/apimachinery/pkg/api/resource"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+const (
+ // ResourceUsageKind is the kind for resource usage.
+ ResourceUsageKind = "ResourceUsage"
+)
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+// +genclient
+// +kubebuilder:subresource:status
+// +kubebuilder:rbac:groups=kwok.x-k8s.io,resources=resourceusages,verbs=create;delete;get;list;patch;update;watch
+
+// ResourceUsage provides resource usage for a single pod.
+type ResourceUsage struct {
+ //+k8s:conversion-gen=false
+ metav1.TypeMeta `json:",inline"`
+ // Standard list metadata.
+ // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
+ metav1.ObjectMeta `json:"metadata"`
+ // Spec holds spec for resource usage.
+ Spec ResourceUsageSpec `json:"spec"`
+ // Status holds status for resource usage
+ //+k8s:conversion-gen=false
+ Status ResourceUsageStatus `json:"status,omitempty"`
+}
+
+// ResourceUsageStatus holds status for resource usage
+type ResourceUsageStatus struct {
+ // Conditions holds conditions for resource usage
+ // +patchMergeKey=type
+ // +patchStrategy=merge
+ // +listType=map
+ // +listMapKey=type
+ Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
+}
+
+// ResourceUsageSpec holds spec for resource usage.
+type ResourceUsageSpec struct {
+ // Usages is a list of resource usage for the pod.
+ Usages []ResourceUsageContainer `json:"usages,omitempty"`
+}
+
+// ResourceUsageContainer holds spec for resource usage container.
+type ResourceUsageContainer struct {
+ // Containers is list of container names.
+ Containers []string `json:"containers,omitempty"`
+ // Usage is a list of resource usage for the container.
+ Usage map[string]ResourceUsageValue `json:"usage,omitempty"`
+}
+
+// ResourceUsageValue holds value for resource usage.
+type ResourceUsageValue struct {
+ // Value is the value for resource usage.
+ Value *resource.Quantity `json:"value,omitempty"`
+ // Expression is the expression for resource usage.
+ Expression *string `json:"expression,omitempty"`
+}
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+// +kubebuilder:object:root=true
+
+// ResourceUsageList is a list of ResourceUsage.
+type ResourceUsageList struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ListMeta `json:"metadata"`
+ Items []ResourceUsage `json:"items"`
+}
+
+func init() {
+ SchemeBuilder.Register(&ResourceUsage{}, &ResourceUsageList{})
+}
diff --git a/pkg/apis/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/v1alpha1/zz_generated.deepcopy.go
index ded7eca0f..987e2137e 100644
--- a/pkg/apis/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/v1alpha1/zz_generated.deepcopy.go
@@ -606,6 +606,118 @@ func (in *ClusterPortForwardStatus) DeepCopy() *ClusterPortForwardStatus {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterResourceUsage) DeepCopyInto(out *ClusterResourceUsage) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ in.Spec.DeepCopyInto(&out.Spec)
+ in.Status.DeepCopyInto(&out.Status)
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterResourceUsage.
+func (in *ClusterResourceUsage) DeepCopy() *ClusterResourceUsage {
+ if in == nil {
+ return nil
+ }
+ out := new(ClusterResourceUsage)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *ClusterResourceUsage) 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 *ClusterResourceUsageList) DeepCopyInto(out *ClusterResourceUsageList) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ListMeta.DeepCopyInto(&out.ListMeta)
+ if in.Items != nil {
+ in, out := &in.Items, &out.Items
+ *out = make([]ClusterResourceUsage, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterResourceUsageList.
+func (in *ClusterResourceUsageList) DeepCopy() *ClusterResourceUsageList {
+ if in == nil {
+ return nil
+ }
+ out := new(ClusterResourceUsageList)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *ClusterResourceUsageList) 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 *ClusterResourceUsageSpec) DeepCopyInto(out *ClusterResourceUsageSpec) {
+ *out = *in
+ if in.Selector != nil {
+ in, out := &in.Selector, &out.Selector
+ *out = new(ObjectSelector)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.Usages != nil {
+ in, out := &in.Usages, &out.Usages
+ *out = make([]ResourceUsageContainer, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterResourceUsageSpec.
+func (in *ClusterResourceUsageSpec) DeepCopy() *ClusterResourceUsageSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(ClusterResourceUsageSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterResourceUsageStatus) DeepCopyInto(out *ClusterResourceUsageStatus) {
+ *out = *in
+ if in.Conditions != nil {
+ in, out := &in.Conditions, &out.Conditions
+ *out = make([]Condition, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterResourceUsageStatus.
+func (in *ClusterResourceUsageStatus) DeepCopy() *ClusterResourceUsageStatus {
+ if in == nil {
+ return nil
+ }
+ out := new(ClusterResourceUsageStatus)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Condition) DeepCopyInto(out *Condition) {
*out = *in
@@ -1313,6 +1425,167 @@ func (in *PortForwardStatus) DeepCopy() *PortForwardStatus {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ResourceUsage) DeepCopyInto(out *ResourceUsage) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ in.Spec.DeepCopyInto(&out.Spec)
+ in.Status.DeepCopyInto(&out.Status)
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceUsage.
+func (in *ResourceUsage) DeepCopy() *ResourceUsage {
+ if in == nil {
+ return nil
+ }
+ out := new(ResourceUsage)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *ResourceUsage) 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 *ResourceUsageContainer) DeepCopyInto(out *ResourceUsageContainer) {
+ *out = *in
+ if in.Containers != nil {
+ in, out := &in.Containers, &out.Containers
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
+ if in.Usage != nil {
+ in, out := &in.Usage, &out.Usage
+ *out = make(map[string]ResourceUsageValue, len(*in))
+ for key, val := range *in {
+ (*out)[key] = *val.DeepCopy()
+ }
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceUsageContainer.
+func (in *ResourceUsageContainer) DeepCopy() *ResourceUsageContainer {
+ if in == nil {
+ return nil
+ }
+ out := new(ResourceUsageContainer)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ResourceUsageList) DeepCopyInto(out *ResourceUsageList) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ListMeta.DeepCopyInto(&out.ListMeta)
+ if in.Items != nil {
+ in, out := &in.Items, &out.Items
+ *out = make([]ResourceUsage, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceUsageList.
+func (in *ResourceUsageList) DeepCopy() *ResourceUsageList {
+ if in == nil {
+ return nil
+ }
+ out := new(ResourceUsageList)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *ResourceUsageList) 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 *ResourceUsageSpec) DeepCopyInto(out *ResourceUsageSpec) {
+ *out = *in
+ if in.Usages != nil {
+ in, out := &in.Usages, &out.Usages
+ *out = make([]ResourceUsageContainer, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceUsageSpec.
+func (in *ResourceUsageSpec) DeepCopy() *ResourceUsageSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(ResourceUsageSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ResourceUsageStatus) DeepCopyInto(out *ResourceUsageStatus) {
+ *out = *in
+ if in.Conditions != nil {
+ in, out := &in.Conditions, &out.Conditions
+ *out = make([]Condition, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceUsageStatus.
+func (in *ResourceUsageStatus) DeepCopy() *ResourceUsageStatus {
+ if in == nil {
+ return nil
+ }
+ out := new(ResourceUsageStatus)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ResourceUsageValue) DeepCopyInto(out *ResourceUsageValue) {
+ *out = *in
+ if in.Value != nil {
+ in, out := &in.Value, &out.Value
+ x := (*in).DeepCopy()
+ *out = &x
+ }
+ if in.Expression != nil {
+ in, out := &in.Expression, &out.Expression
+ *out = new(string)
+ **out = **in
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceUsageValue.
+func (in *ResourceUsageValue) DeepCopy() *ResourceUsageValue {
+ if in == nil {
+ return nil
+ }
+ out := new(ResourceUsageValue)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SecurityContext) DeepCopyInto(out *SecurityContext) {
*out = *in
diff --git a/pkg/client/clientset/versioned/typed/apis/v1alpha1/apis_client.go b/pkg/client/clientset/versioned/typed/apis/v1alpha1/apis_client.go
index e9726e4a9..b9558dd83 100644
--- a/pkg/client/clientset/versioned/typed/apis/v1alpha1/apis_client.go
+++ b/pkg/client/clientset/versioned/typed/apis/v1alpha1/apis_client.go
@@ -33,10 +33,12 @@ type KwokV1alpha1Interface interface {
ClusterExecsGetter
ClusterLogsGetter
ClusterPortForwardsGetter
+ ClusterResourceUsagesGetter
ExecsGetter
LogsGetter
MetricsGetter
PortForwardsGetter
+ ResourceUsagesGetter
StagesGetter
}
@@ -65,6 +67,10 @@ func (c *KwokV1alpha1Client) ClusterPortForwards() ClusterPortForwardInterface {
return newClusterPortForwards(c)
}
+func (c *KwokV1alpha1Client) ClusterResourceUsages() ClusterResourceUsageInterface {
+ return newClusterResourceUsages(c)
+}
+
func (c *KwokV1alpha1Client) Execs(namespace string) ExecInterface {
return newExecs(c, namespace)
}
@@ -81,6 +87,10 @@ func (c *KwokV1alpha1Client) PortForwards(namespace string) PortForwardInterface
return newPortForwards(c, namespace)
}
+func (c *KwokV1alpha1Client) ResourceUsages(namespace string) ResourceUsageInterface {
+ return newResourceUsages(c, namespace)
+}
+
func (c *KwokV1alpha1Client) Stages() StageInterface {
return newStages(c)
}
diff --git a/pkg/client/clientset/versioned/typed/apis/v1alpha1/clusterresourceusage.go b/pkg/client/clientset/versioned/typed/apis/v1alpha1/clusterresourceusage.go
new file mode 100644
index 000000000..b180d0254
--- /dev/null
+++ b/pkg/client/clientset/versioned/typed/apis/v1alpha1/clusterresourceusage.go
@@ -0,0 +1,184 @@
+/*
+Copyright 2023 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+ "context"
+ "time"
+
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ types "k8s.io/apimachinery/pkg/types"
+ watch "k8s.io/apimachinery/pkg/watch"
+ rest "k8s.io/client-go/rest"
+ v1alpha1 "sigs.k8s.io/kwok/pkg/apis/v1alpha1"
+ scheme "sigs.k8s.io/kwok/pkg/client/clientset/versioned/scheme"
+)
+
+// ClusterResourceUsagesGetter has a method to return a ClusterResourceUsageInterface.
+// A group's client should implement this interface.
+type ClusterResourceUsagesGetter interface {
+ ClusterResourceUsages() ClusterResourceUsageInterface
+}
+
+// ClusterResourceUsageInterface has methods to work with ClusterResourceUsage resources.
+type ClusterResourceUsageInterface interface {
+ Create(ctx context.Context, clusterResourceUsage *v1alpha1.ClusterResourceUsage, opts v1.CreateOptions) (*v1alpha1.ClusterResourceUsage, error)
+ Update(ctx context.Context, clusterResourceUsage *v1alpha1.ClusterResourceUsage, opts v1.UpdateOptions) (*v1alpha1.ClusterResourceUsage, error)
+ UpdateStatus(ctx context.Context, clusterResourceUsage *v1alpha1.ClusterResourceUsage, opts v1.UpdateOptions) (*v1alpha1.ClusterResourceUsage, error)
+ Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
+ DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
+ Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ClusterResourceUsage, error)
+ List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ClusterResourceUsageList, error)
+ Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
+ Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterResourceUsage, err error)
+ ClusterResourceUsageExpansion
+}
+
+// clusterResourceUsages implements ClusterResourceUsageInterface
+type clusterResourceUsages struct {
+ client rest.Interface
+}
+
+// newClusterResourceUsages returns a ClusterResourceUsages
+func newClusterResourceUsages(c *KwokV1alpha1Client) *clusterResourceUsages {
+ return &clusterResourceUsages{
+ client: c.RESTClient(),
+ }
+}
+
+// Get takes name of the clusterResourceUsage, and returns the corresponding clusterResourceUsage object, and an error if there is any.
+func (c *clusterResourceUsages) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ClusterResourceUsage, err error) {
+ result = &v1alpha1.ClusterResourceUsage{}
+ err = c.client.Get().
+ Resource("clusterresourceusages").
+ Name(name).
+ VersionedParams(&options, scheme.ParameterCodec).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// List takes label and field selectors, and returns the list of ClusterResourceUsages that match those selectors.
+func (c *clusterResourceUsages) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ClusterResourceUsageList, err error) {
+ var timeout time.Duration
+ if opts.TimeoutSeconds != nil {
+ timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+ }
+ result = &v1alpha1.ClusterResourceUsageList{}
+ err = c.client.Get().
+ Resource("clusterresourceusages").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Watch returns a watch.Interface that watches the requested clusterResourceUsages.
+func (c *clusterResourceUsages) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
+ var timeout time.Duration
+ if opts.TimeoutSeconds != nil {
+ timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+ }
+ opts.Watch = true
+ return c.client.Get().
+ Resource("clusterresourceusages").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Watch(ctx)
+}
+
+// Create takes the representation of a clusterResourceUsage and creates it. Returns the server's representation of the clusterResourceUsage, and an error, if there is any.
+func (c *clusterResourceUsages) Create(ctx context.Context, clusterResourceUsage *v1alpha1.ClusterResourceUsage, opts v1.CreateOptions) (result *v1alpha1.ClusterResourceUsage, err error) {
+ result = &v1alpha1.ClusterResourceUsage{}
+ err = c.client.Post().
+ Resource("clusterresourceusages").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(clusterResourceUsage).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Update takes the representation of a clusterResourceUsage and updates it. Returns the server's representation of the clusterResourceUsage, and an error, if there is any.
+func (c *clusterResourceUsages) Update(ctx context.Context, clusterResourceUsage *v1alpha1.ClusterResourceUsage, opts v1.UpdateOptions) (result *v1alpha1.ClusterResourceUsage, err error) {
+ result = &v1alpha1.ClusterResourceUsage{}
+ err = c.client.Put().
+ Resource("clusterresourceusages").
+ Name(clusterResourceUsage.Name).
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(clusterResourceUsage).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+func (c *clusterResourceUsages) UpdateStatus(ctx context.Context, clusterResourceUsage *v1alpha1.ClusterResourceUsage, opts v1.UpdateOptions) (result *v1alpha1.ClusterResourceUsage, err error) {
+ result = &v1alpha1.ClusterResourceUsage{}
+ err = c.client.Put().
+ Resource("clusterresourceusages").
+ Name(clusterResourceUsage.Name).
+ SubResource("status").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(clusterResourceUsage).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Delete takes name of the clusterResourceUsage and deletes it. Returns an error if one occurs.
+func (c *clusterResourceUsages) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
+ return c.client.Delete().
+ Resource("clusterresourceusages").
+ Name(name).
+ Body(&opts).
+ Do(ctx).
+ Error()
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *clusterResourceUsages) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
+ var timeout time.Duration
+ if listOpts.TimeoutSeconds != nil {
+ timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
+ }
+ return c.client.Delete().
+ Resource("clusterresourceusages").
+ VersionedParams(&listOpts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Body(&opts).
+ Do(ctx).
+ Error()
+}
+
+// Patch applies the patch and returns the patched clusterResourceUsage.
+func (c *clusterResourceUsages) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterResourceUsage, err error) {
+ result = &v1alpha1.ClusterResourceUsage{}
+ err = c.client.Patch(pt).
+ Resource("clusterresourceusages").
+ Name(name).
+ SubResource(subresources...).
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(data).
+ Do(ctx).
+ Into(result)
+ return
+}
diff --git a/pkg/client/clientset/versioned/typed/apis/v1alpha1/fake/fake_apis_client.go b/pkg/client/clientset/versioned/typed/apis/v1alpha1/fake/fake_apis_client.go
index cb30c320f..648e517c7 100644
--- a/pkg/client/clientset/versioned/typed/apis/v1alpha1/fake/fake_apis_client.go
+++ b/pkg/client/clientset/versioned/typed/apis/v1alpha1/fake/fake_apis_client.go
@@ -48,6 +48,10 @@ func (c *FakeKwokV1alpha1) ClusterPortForwards() v1alpha1.ClusterPortForwardInte
return &FakeClusterPortForwards{c}
}
+func (c *FakeKwokV1alpha1) ClusterResourceUsages() v1alpha1.ClusterResourceUsageInterface {
+ return &FakeClusterResourceUsages{c}
+}
+
func (c *FakeKwokV1alpha1) Execs(namespace string) v1alpha1.ExecInterface {
return &FakeExecs{c, namespace}
}
@@ -64,6 +68,10 @@ func (c *FakeKwokV1alpha1) PortForwards(namespace string) v1alpha1.PortForwardIn
return &FakePortForwards{c, namespace}
}
+func (c *FakeKwokV1alpha1) ResourceUsages(namespace string) v1alpha1.ResourceUsageInterface {
+ return &FakeResourceUsages{c, namespace}
+}
+
func (c *FakeKwokV1alpha1) Stages() v1alpha1.StageInterface {
return &FakeStages{c}
}
diff --git a/pkg/client/clientset/versioned/typed/apis/v1alpha1/fake/fake_clusterresourceusage.go b/pkg/client/clientset/versioned/typed/apis/v1alpha1/fake/fake_clusterresourceusage.go
new file mode 100644
index 000000000..705676e64
--- /dev/null
+++ b/pkg/client/clientset/versioned/typed/apis/v1alpha1/fake/fake_clusterresourceusage.go
@@ -0,0 +1,132 @@
+/*
+Copyright 2023 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+ "context"
+
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ labels "k8s.io/apimachinery/pkg/labels"
+ types "k8s.io/apimachinery/pkg/types"
+ watch "k8s.io/apimachinery/pkg/watch"
+ testing "k8s.io/client-go/testing"
+ v1alpha1 "sigs.k8s.io/kwok/pkg/apis/v1alpha1"
+)
+
+// FakeClusterResourceUsages implements ClusterResourceUsageInterface
+type FakeClusterResourceUsages struct {
+ Fake *FakeKwokV1alpha1
+}
+
+var clusterresourceusagesResource = v1alpha1.SchemeGroupVersion.WithResource("clusterresourceusages")
+
+var clusterresourceusagesKind = v1alpha1.SchemeGroupVersion.WithKind("ClusterResourceUsage")
+
+// Get takes name of the clusterResourceUsage, and returns the corresponding clusterResourceUsage object, and an error if there is any.
+func (c *FakeClusterResourceUsages) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ClusterResourceUsage, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewRootGetAction(clusterresourceusagesResource, name), &v1alpha1.ClusterResourceUsage{})
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*v1alpha1.ClusterResourceUsage), err
+}
+
+// List takes label and field selectors, and returns the list of ClusterResourceUsages that match those selectors.
+func (c *FakeClusterResourceUsages) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ClusterResourceUsageList, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewRootListAction(clusterresourceusagesResource, clusterresourceusagesKind, opts), &v1alpha1.ClusterResourceUsageList{})
+ if obj == nil {
+ return nil, err
+ }
+
+ label, _, _ := testing.ExtractFromListOptions(opts)
+ if label == nil {
+ label = labels.Everything()
+ }
+ list := &v1alpha1.ClusterResourceUsageList{ListMeta: obj.(*v1alpha1.ClusterResourceUsageList).ListMeta}
+ for _, item := range obj.(*v1alpha1.ClusterResourceUsageList).Items {
+ if label.Matches(labels.Set(item.Labels)) {
+ list.Items = append(list.Items, item)
+ }
+ }
+ return list, err
+}
+
+// Watch returns a watch.Interface that watches the requested clusterResourceUsages.
+func (c *FakeClusterResourceUsages) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
+ return c.Fake.
+ InvokesWatch(testing.NewRootWatchAction(clusterresourceusagesResource, opts))
+}
+
+// Create takes the representation of a clusterResourceUsage and creates it. Returns the server's representation of the clusterResourceUsage, and an error, if there is any.
+func (c *FakeClusterResourceUsages) Create(ctx context.Context, clusterResourceUsage *v1alpha1.ClusterResourceUsage, opts v1.CreateOptions) (result *v1alpha1.ClusterResourceUsage, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewRootCreateAction(clusterresourceusagesResource, clusterResourceUsage), &v1alpha1.ClusterResourceUsage{})
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*v1alpha1.ClusterResourceUsage), err
+}
+
+// Update takes the representation of a clusterResourceUsage and updates it. Returns the server's representation of the clusterResourceUsage, and an error, if there is any.
+func (c *FakeClusterResourceUsages) Update(ctx context.Context, clusterResourceUsage *v1alpha1.ClusterResourceUsage, opts v1.UpdateOptions) (result *v1alpha1.ClusterResourceUsage, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewRootUpdateAction(clusterresourceusagesResource, clusterResourceUsage), &v1alpha1.ClusterResourceUsage{})
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*v1alpha1.ClusterResourceUsage), err
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+func (c *FakeClusterResourceUsages) UpdateStatus(ctx context.Context, clusterResourceUsage *v1alpha1.ClusterResourceUsage, opts v1.UpdateOptions) (*v1alpha1.ClusterResourceUsage, error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewRootUpdateSubresourceAction(clusterresourceusagesResource, "status", clusterResourceUsage), &v1alpha1.ClusterResourceUsage{})
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*v1alpha1.ClusterResourceUsage), err
+}
+
+// Delete takes name of the clusterResourceUsage and deletes it. Returns an error if one occurs.
+func (c *FakeClusterResourceUsages) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
+ _, err := c.Fake.
+ Invokes(testing.NewRootDeleteActionWithOptions(clusterresourceusagesResource, name, opts), &v1alpha1.ClusterResourceUsage{})
+ return err
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *FakeClusterResourceUsages) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
+ action := testing.NewRootDeleteCollectionAction(clusterresourceusagesResource, listOpts)
+
+ _, err := c.Fake.Invokes(action, &v1alpha1.ClusterResourceUsageList{})
+ return err
+}
+
+// Patch applies the patch and returns the patched clusterResourceUsage.
+func (c *FakeClusterResourceUsages) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterResourceUsage, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewRootPatchSubresourceAction(clusterresourceusagesResource, name, pt, data, subresources...), &v1alpha1.ClusterResourceUsage{})
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*v1alpha1.ClusterResourceUsage), err
+}
diff --git a/pkg/client/clientset/versioned/typed/apis/v1alpha1/fake/fake_resourceusage.go b/pkg/client/clientset/versioned/typed/apis/v1alpha1/fake/fake_resourceusage.go
new file mode 100644
index 000000000..a4327d5dc
--- /dev/null
+++ b/pkg/client/clientset/versioned/typed/apis/v1alpha1/fake/fake_resourceusage.go
@@ -0,0 +1,141 @@
+/*
+Copyright 2023 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+ "context"
+
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ labels "k8s.io/apimachinery/pkg/labels"
+ types "k8s.io/apimachinery/pkg/types"
+ watch "k8s.io/apimachinery/pkg/watch"
+ testing "k8s.io/client-go/testing"
+ v1alpha1 "sigs.k8s.io/kwok/pkg/apis/v1alpha1"
+)
+
+// FakeResourceUsages implements ResourceUsageInterface
+type FakeResourceUsages struct {
+ Fake *FakeKwokV1alpha1
+ ns string
+}
+
+var resourceusagesResource = v1alpha1.SchemeGroupVersion.WithResource("resourceusages")
+
+var resourceusagesKind = v1alpha1.SchemeGroupVersion.WithKind("ResourceUsage")
+
+// Get takes name of the resourceUsage, and returns the corresponding resourceUsage object, and an error if there is any.
+func (c *FakeResourceUsages) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ResourceUsage, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewGetAction(resourceusagesResource, c.ns, name), &v1alpha1.ResourceUsage{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*v1alpha1.ResourceUsage), err
+}
+
+// List takes label and field selectors, and returns the list of ResourceUsages that match those selectors.
+func (c *FakeResourceUsages) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ResourceUsageList, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewListAction(resourceusagesResource, resourceusagesKind, c.ns, opts), &v1alpha1.ResourceUsageList{})
+
+ if obj == nil {
+ return nil, err
+ }
+
+ label, _, _ := testing.ExtractFromListOptions(opts)
+ if label == nil {
+ label = labels.Everything()
+ }
+ list := &v1alpha1.ResourceUsageList{ListMeta: obj.(*v1alpha1.ResourceUsageList).ListMeta}
+ for _, item := range obj.(*v1alpha1.ResourceUsageList).Items {
+ if label.Matches(labels.Set(item.Labels)) {
+ list.Items = append(list.Items, item)
+ }
+ }
+ return list, err
+}
+
+// Watch returns a watch.Interface that watches the requested resourceUsages.
+func (c *FakeResourceUsages) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
+ return c.Fake.
+ InvokesWatch(testing.NewWatchAction(resourceusagesResource, c.ns, opts))
+
+}
+
+// Create takes the representation of a resourceUsage and creates it. Returns the server's representation of the resourceUsage, and an error, if there is any.
+func (c *FakeResourceUsages) Create(ctx context.Context, resourceUsage *v1alpha1.ResourceUsage, opts v1.CreateOptions) (result *v1alpha1.ResourceUsage, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewCreateAction(resourceusagesResource, c.ns, resourceUsage), &v1alpha1.ResourceUsage{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*v1alpha1.ResourceUsage), err
+}
+
+// Update takes the representation of a resourceUsage and updates it. Returns the server's representation of the resourceUsage, and an error, if there is any.
+func (c *FakeResourceUsages) Update(ctx context.Context, resourceUsage *v1alpha1.ResourceUsage, opts v1.UpdateOptions) (result *v1alpha1.ResourceUsage, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewUpdateAction(resourceusagesResource, c.ns, resourceUsage), &v1alpha1.ResourceUsage{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*v1alpha1.ResourceUsage), err
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+func (c *FakeResourceUsages) UpdateStatus(ctx context.Context, resourceUsage *v1alpha1.ResourceUsage, opts v1.UpdateOptions) (*v1alpha1.ResourceUsage, error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewUpdateSubresourceAction(resourceusagesResource, "status", c.ns, resourceUsage), &v1alpha1.ResourceUsage{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*v1alpha1.ResourceUsage), err
+}
+
+// Delete takes name of the resourceUsage and deletes it. Returns an error if one occurs.
+func (c *FakeResourceUsages) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
+ _, err := c.Fake.
+ Invokes(testing.NewDeleteActionWithOptions(resourceusagesResource, c.ns, name, opts), &v1alpha1.ResourceUsage{})
+
+ return err
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *FakeResourceUsages) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
+ action := testing.NewDeleteCollectionAction(resourceusagesResource, c.ns, listOpts)
+
+ _, err := c.Fake.Invokes(action, &v1alpha1.ResourceUsageList{})
+ return err
+}
+
+// Patch applies the patch and returns the patched resourceUsage.
+func (c *FakeResourceUsages) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ResourceUsage, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewPatchSubresourceAction(resourceusagesResource, c.ns, name, pt, data, subresources...), &v1alpha1.ResourceUsage{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*v1alpha1.ResourceUsage), err
+}
diff --git a/pkg/client/clientset/versioned/typed/apis/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/apis/v1alpha1/generated_expansion.go
index 8ef525def..a1b96b65f 100644
--- a/pkg/client/clientset/versioned/typed/apis/v1alpha1/generated_expansion.go
+++ b/pkg/client/clientset/versioned/typed/apis/v1alpha1/generated_expansion.go
@@ -28,6 +28,8 @@ type ClusterLogsExpansion interface{}
type ClusterPortForwardExpansion interface{}
+type ClusterResourceUsageExpansion interface{}
+
type ExecExpansion interface{}
type LogsExpansion interface{}
@@ -36,4 +38,6 @@ type MetricExpansion interface{}
type PortForwardExpansion interface{}
+type ResourceUsageExpansion interface{}
+
type StageExpansion interface{}
diff --git a/pkg/client/clientset/versioned/typed/apis/v1alpha1/resourceusage.go b/pkg/client/clientset/versioned/typed/apis/v1alpha1/resourceusage.go
new file mode 100644
index 000000000..7cfe177a9
--- /dev/null
+++ b/pkg/client/clientset/versioned/typed/apis/v1alpha1/resourceusage.go
@@ -0,0 +1,195 @@
+/*
+Copyright 2023 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+ "context"
+ "time"
+
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ types "k8s.io/apimachinery/pkg/types"
+ watch "k8s.io/apimachinery/pkg/watch"
+ rest "k8s.io/client-go/rest"
+ v1alpha1 "sigs.k8s.io/kwok/pkg/apis/v1alpha1"
+ scheme "sigs.k8s.io/kwok/pkg/client/clientset/versioned/scheme"
+)
+
+// ResourceUsagesGetter has a method to return a ResourceUsageInterface.
+// A group's client should implement this interface.
+type ResourceUsagesGetter interface {
+ ResourceUsages(namespace string) ResourceUsageInterface
+}
+
+// ResourceUsageInterface has methods to work with ResourceUsage resources.
+type ResourceUsageInterface interface {
+ Create(ctx context.Context, resourceUsage *v1alpha1.ResourceUsage, opts v1.CreateOptions) (*v1alpha1.ResourceUsage, error)
+ Update(ctx context.Context, resourceUsage *v1alpha1.ResourceUsage, opts v1.UpdateOptions) (*v1alpha1.ResourceUsage, error)
+ UpdateStatus(ctx context.Context, resourceUsage *v1alpha1.ResourceUsage, opts v1.UpdateOptions) (*v1alpha1.ResourceUsage, error)
+ Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
+ DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
+ Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ResourceUsage, error)
+ List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ResourceUsageList, error)
+ Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
+ Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ResourceUsage, err error)
+ ResourceUsageExpansion
+}
+
+// resourceUsages implements ResourceUsageInterface
+type resourceUsages struct {
+ client rest.Interface
+ ns string
+}
+
+// newResourceUsages returns a ResourceUsages
+func newResourceUsages(c *KwokV1alpha1Client, namespace string) *resourceUsages {
+ return &resourceUsages{
+ client: c.RESTClient(),
+ ns: namespace,
+ }
+}
+
+// Get takes name of the resourceUsage, and returns the corresponding resourceUsage object, and an error if there is any.
+func (c *resourceUsages) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ResourceUsage, err error) {
+ result = &v1alpha1.ResourceUsage{}
+ err = c.client.Get().
+ Namespace(c.ns).
+ Resource("resourceusages").
+ Name(name).
+ VersionedParams(&options, scheme.ParameterCodec).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// List takes label and field selectors, and returns the list of ResourceUsages that match those selectors.
+func (c *resourceUsages) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ResourceUsageList, err error) {
+ var timeout time.Duration
+ if opts.TimeoutSeconds != nil {
+ timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+ }
+ result = &v1alpha1.ResourceUsageList{}
+ err = c.client.Get().
+ Namespace(c.ns).
+ Resource("resourceusages").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Watch returns a watch.Interface that watches the requested resourceUsages.
+func (c *resourceUsages) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
+ var timeout time.Duration
+ if opts.TimeoutSeconds != nil {
+ timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+ }
+ opts.Watch = true
+ return c.client.Get().
+ Namespace(c.ns).
+ Resource("resourceusages").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Watch(ctx)
+}
+
+// Create takes the representation of a resourceUsage and creates it. Returns the server's representation of the resourceUsage, and an error, if there is any.
+func (c *resourceUsages) Create(ctx context.Context, resourceUsage *v1alpha1.ResourceUsage, opts v1.CreateOptions) (result *v1alpha1.ResourceUsage, err error) {
+ result = &v1alpha1.ResourceUsage{}
+ err = c.client.Post().
+ Namespace(c.ns).
+ Resource("resourceusages").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(resourceUsage).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Update takes the representation of a resourceUsage and updates it. Returns the server's representation of the resourceUsage, and an error, if there is any.
+func (c *resourceUsages) Update(ctx context.Context, resourceUsage *v1alpha1.ResourceUsage, opts v1.UpdateOptions) (result *v1alpha1.ResourceUsage, err error) {
+ result = &v1alpha1.ResourceUsage{}
+ err = c.client.Put().
+ Namespace(c.ns).
+ Resource("resourceusages").
+ Name(resourceUsage.Name).
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(resourceUsage).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+func (c *resourceUsages) UpdateStatus(ctx context.Context, resourceUsage *v1alpha1.ResourceUsage, opts v1.UpdateOptions) (result *v1alpha1.ResourceUsage, err error) {
+ result = &v1alpha1.ResourceUsage{}
+ err = c.client.Put().
+ Namespace(c.ns).
+ Resource("resourceusages").
+ Name(resourceUsage.Name).
+ SubResource("status").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(resourceUsage).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Delete takes name of the resourceUsage and deletes it. Returns an error if one occurs.
+func (c *resourceUsages) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
+ return c.client.Delete().
+ Namespace(c.ns).
+ Resource("resourceusages").
+ Name(name).
+ Body(&opts).
+ Do(ctx).
+ Error()
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *resourceUsages) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
+ var timeout time.Duration
+ if listOpts.TimeoutSeconds != nil {
+ timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
+ }
+ return c.client.Delete().
+ Namespace(c.ns).
+ Resource("resourceusages").
+ VersionedParams(&listOpts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Body(&opts).
+ Do(ctx).
+ Error()
+}
+
+// Patch applies the patch and returns the patched resourceUsage.
+func (c *resourceUsages) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ResourceUsage, err error) {
+ result = &v1alpha1.ResourceUsage{}
+ err = c.client.Patch(pt).
+ Namespace(c.ns).
+ Resource("resourceusages").
+ Name(name).
+ SubResource(subresources...).
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(data).
+ Do(ctx).
+ Into(result)
+ return
+}
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 835a177b8..df4ac0c53 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -161,6 +161,18 @@ var configHandlers = map[string]configHandler{
MutateToInternal: mutateToInternalConfig(internalversion.ConvertToInternalClusterAttach),
MutateToVersiond: mutateToVersiondConfig(internalversion.ConvertToV1Alpha1ClusterAttach),
},
+ v1alpha1.ResourceUsageKind: {
+ Unmarshal: unmarshalConfig[*v1alpha1.ResourceUsage],
+ Marshal: marshalConfig,
+ MutateToInternal: mutateToInternalConfig(internalversion.ConvertToInternalResourceUsage),
+ MutateToVersiond: mutateToVersiondConfig(internalversion.ConvertToV1Alpha1ResourceUsage),
+ },
+ v1alpha1.ClusterResourceUsageKind: {
+ Unmarshal: unmarshalConfig[*v1alpha1.ClusterResourceUsage],
+ Marshal: marshalConfig,
+ MutateToInternal: mutateToInternalConfig(internalversion.ConvertToInternalClusterResourceUsage),
+ MutateToVersiond: mutateToVersiondConfig(internalversion.ConvertToV1Alpha1ClusterResourceUsage),
+ },
v1alpha1.MetricKind: {
Unmarshal: unmarshalConfig[*v1alpha1.Metric],
Marshal: marshalConfig,
diff --git a/pkg/kwok/cmd/root.go b/pkg/kwok/cmd/root.go
index a4d039672..96df494bc 100644
--- a/pkg/kwok/cmd/root.go
+++ b/pkg/kwok/cmd/root.go
@@ -100,16 +100,18 @@ func NewCommand(ctx context.Context) *cobra.Command {
}
var crdDefines = map[string]struct{}{
- v1alpha1.StageKind: {},
- v1alpha1.AttachKind: {},
- v1alpha1.ClusterAttachKind: {},
- v1alpha1.ExecKind: {},
- v1alpha1.ClusterExecKind: {},
- v1alpha1.PortForwardKind: {},
- v1alpha1.ClusterPortForwardKind: {},
- v1alpha1.LogsKind: {},
- v1alpha1.ClusterLogsKind: {},
- v1alpha1.MetricKind: {},
+ v1alpha1.StageKind: {},
+ v1alpha1.AttachKind: {},
+ v1alpha1.ClusterAttachKind: {},
+ v1alpha1.ExecKind: {},
+ v1alpha1.ClusterExecKind: {},
+ v1alpha1.PortForwardKind: {},
+ v1alpha1.ClusterPortForwardKind: {},
+ v1alpha1.LogsKind: {},
+ v1alpha1.ClusterLogsKind: {},
+ v1alpha1.ResourceUsageKind: {},
+ v1alpha1.ClusterResourceUsageKind: {},
+ v1alpha1.MetricKind: {},
}
func runE(ctx context.Context, flags *flagpole) error {
@@ -207,6 +209,18 @@ func runE(ctx context.Context, flags *flagpole) error {
return err
}
+ clusterResourceUsages := config.FilterWithTypeFromContext[*internalversion.ClusterResourceUsage](ctx)
+ err = checkConfigOrCRD(flags.Options.EnableCRDs, v1alpha1.ClusterResourceUsageKind, clusterResourceUsages)
+ if err != nil {
+ return err
+ }
+
+ resourceUsages := config.FilterWithTypeFromContext[*internalversion.ResourceUsage](ctx)
+ err = checkConfigOrCRD(flags.Options.EnableCRDs, v1alpha1.ResourceUsageKind, resourceUsages)
+ if err != nil {
+ return err
+ }
+
metrics := config.FilterWithTypeFromContext[*internalversion.Metric](ctx)
err = checkConfigOrCRD(flags.Options.EnableCRDs, v1alpha1.MetricKind, metrics)
if err != nil {
@@ -298,20 +312,22 @@ func runE(ctx context.Context, flags *flagpole) error {
if serverAddress != "" {
conf := server.Config{
- TypedKwokClient: typedKwokClient,
- EnableCRDs: flags.Options.EnableCRDs,
- ClusterPortForwards: clusterPortForwards,
- PortForwards: portForwards,
- ClusterExecs: clusterExecs,
- Execs: execs,
- ClusterLogs: clusterLogs,
- Logs: logs,
- ClusterAttaches: clusterAttaches,
- Attaches: attaches,
- Metrics: metrics,
- DataSource: ctr,
- NodeCacheGetter: ctr.GetNodeCache(),
- PodCacheGetter: ctr.GetPodCache(),
+ TypedKwokClient: typedKwokClient,
+ EnableCRDs: flags.Options.EnableCRDs,
+ ClusterPortForwards: clusterPortForwards,
+ PortForwards: portForwards,
+ ClusterExecs: clusterExecs,
+ Execs: execs,
+ ClusterLogs: clusterLogs,
+ Logs: logs,
+ ClusterAttaches: clusterAttaches,
+ Attaches: attaches,
+ ClusterResourceUsages: clusterResourceUsages,
+ ResourceUsages: resourceUsages,
+ Metrics: metrics,
+ DataSource: ctr,
+ NodeCacheGetter: ctr.GetNodeCache(),
+ PodCacheGetter: ctr.GetPodCache(),
}
svc, err := server.NewServer(conf)
if err != nil {
diff --git a/pkg/kwok/metrics/cel/evaluate.go b/pkg/kwok/metrics/cel/evaluate.go
index 631783868..5d026b3b9 100644
--- a/pkg/kwok/metrics/cel/evaluate.go
+++ b/pkg/kwok/metrics/cel/evaluate.go
@@ -28,6 +28,7 @@ import (
"github.com/google/cel-go/common/types/ref"
"github.com/wzshiming/easycel"
corev1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@@ -38,6 +39,14 @@ type NodeEvaluatorConfig struct {
Now func() time.Time
StartedContainersTotal func(nodeName string) int64
+
+ ContainerResourceUsage func(resourceName, podNamespace, podName, containerName string) float64
+ PodResourceUsage func(resourceName, podNamespace, podName string) float64
+ NodeResourceUsage func(resourceName, nodeName string) float64
+
+ ContainerResourceCumulativeUsage func(resourceName, podNamespace, podName, containerName string) float64
+ PodResourceCumulativeUsage func(resourceName, podNamespace, podName string) float64
+ NodeResourceCumulativeUsage func(resourceName, nodeName string) float64
}
// NewEnvironment returns a MetricEvaluator that is able to evaluate node metrics
@@ -103,16 +112,24 @@ func (e *Environment) init() error {
}
return types.Duration{Duration: t.Duration}
},
+ func(t resource.Quantity) Quantity {
+ return NewQuantity(&t)
+ },
+ NewResourceList,
}
+
types := []any{
corev1.Node{},
corev1.NodeSpec{},
corev1.NodeStatus{},
corev1.Pod{},
corev1.PodSpec{},
+ corev1.ResourceRequirements{},
corev1.PodStatus{},
corev1.Container{},
metav1.ObjectMeta{},
+ Quantity{},
+ ResourceList{},
}
vars := map[string]any{
@@ -134,6 +151,11 @@ func (e *Environment) init() error {
mathRandName = "Rand"
sinceSecondName = "SinceSecond"
unixSecondName = "UnixSecond"
+
+ quantityName = "Quantity"
+
+ usageName = "Usage"
+ cumulativeUsageName = "CumulativeUsage"
)
if e.conf.Now != nil {
funcs[nowOldName] = append(funcs[nowOldName], e.conf.Now)
@@ -151,6 +173,44 @@ func (e *Environment) init() error {
methods[unixSecondName] = append(methods[unixSecondName], unixSecond)
funcs[unixSecondName] = append(funcs[unixSecondName], unixSecond)
+ funcs[quantityName] = append(funcs[quantityName], NewQuantityFromString)
+
+ if e.conf.ContainerResourceUsage != nil {
+ methods[usageName] = append(methods[usageName], func(pod corev1.Pod, resourceName string, containerName string) float64 {
+ return e.conf.ContainerResourceUsage(resourceName, pod.Namespace, pod.Name, containerName)
+ })
+ }
+
+ if e.conf.PodResourceUsage != nil {
+ methods[usageName] = append(methods[usageName], func(pod corev1.Pod, resourceName string) float64 {
+ return e.conf.PodResourceUsage(resourceName, pod.Namespace, pod.Name)
+ })
+ }
+
+ if e.conf.NodeResourceUsage != nil {
+ methods[usageName] = append(methods[usageName], func(node corev1.Node, resourceName string) float64 {
+ return e.conf.NodeResourceUsage(resourceName, node.Name)
+ })
+ }
+
+ if e.conf.ContainerResourceCumulativeUsage != nil {
+ methods[cumulativeUsageName] = append(methods[cumulativeUsageName], func(pod corev1.Pod, resourceName string, containerName string) float64 {
+ return e.conf.ContainerResourceCumulativeUsage(resourceName, pod.Namespace, pod.Name, containerName)
+ })
+ }
+
+ if e.conf.PodResourceCumulativeUsage != nil {
+ methods[cumulativeUsageName] = append(methods[cumulativeUsageName], func(pod corev1.Pod, resourceName string) float64 {
+ return e.conf.PodResourceCumulativeUsage(resourceName, pod.Namespace, pod.Name)
+ })
+ }
+
+ if e.conf.NodeResourceCumulativeUsage != nil {
+ methods[cumulativeUsageName] = append(methods[cumulativeUsageName], func(node corev1.Node, resourceName string) float64 {
+ return e.conf.NodeResourceCumulativeUsage(resourceName, node.Name)
+ })
+ }
+
if e.conf.StartedContainersTotal != nil {
startedContainersTotal := e.conf.StartedContainersTotal
startedContainersTotalByNode := func(node corev1.Node) float64 {
@@ -309,6 +369,8 @@ func (e *Evaluator) EvaluateFloat64(data Data) (float64, error) {
return 1, nil
}
return 0, nil
+ case Quantity:
+ return v.Quantity.AsApproximateFloat64(), nil
default:
return 0, fmt.Errorf("unsupported metric value type: %T", v)
}
diff --git a/pkg/kwok/metrics/cel/evaluate_test.go b/pkg/kwok/metrics/cel/evaluate_test.go
index aaaaace84..8ce09abb0 100644
--- a/pkg/kwok/metrics/cel/evaluate_test.go
+++ b/pkg/kwok/metrics/cel/evaluate_test.go
@@ -21,6 +21,7 @@ import (
"time"
corev1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@@ -62,3 +63,55 @@ func TestNodeEvaluation(t *testing.T) {
t.Errorf("expected %v, got %v", 17280, actual)
}
}
+
+func TestResourceEvaluation(t *testing.T) {
+ n := &corev1.Node{
+ Status: corev1.NodeStatus{
+ Allocatable: map[corev1.ResourceName]resource.Quantity{
+ corev1.ResourceCPU: resource.MustParse("90m"),
+ },
+ },
+ }
+ p := &corev1.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Annotations: map[string]string{
+ "cpu_usage": "10m",
+ },
+ },
+ Spec: corev1.PodSpec{
+ Containers: []corev1.Container{
+ {
+ Resources: corev1.ResourceRequirements{
+ Requests: map[corev1.ResourceName]resource.Quantity{
+ corev1.ResourceCPU: resource.MustParse("10m"),
+ },
+ },
+ },
+ },
+ },
+ }
+
+ exp := `( Quantity("10m") + Quantity(pod.metadata.annotations["cpu_usage"]) + node.status.allocatable["cpu"] + pod.spec.containers[0].resources.requests["cpu"] ) * 1.5 * 10`
+
+ env, err := NewEnvironment(NodeEvaluatorConfig{})
+ if err != nil {
+ t.Fatalf("failed to instantiate node Evaluator: %v", err)
+ }
+
+ eval, err := env.Compile(exp)
+ if err != nil {
+ t.Fatalf("failed to compile expression: %v", err)
+ }
+
+ actual, err := eval.EvaluateFloat64(Data{
+ Node: n,
+ Pod: p,
+ })
+ if err != nil {
+ t.Fatalf("evaluation failed: %v", err)
+ }
+
+ if actual != 18 {
+ t.Errorf("expected %v, got %v", 18, actual)
+ }
+}
diff --git a/pkg/kwok/metrics/cel/quantity.go b/pkg/kwok/metrics/cel/quantity.go
new file mode 100644
index 000000000..4cd32cc69
--- /dev/null
+++ b/pkg/kwok/metrics/cel/quantity.go
@@ -0,0 +1,201 @@
+/*
+Copyright 2023 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cel
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/google/cel-go/cel"
+ "github.com/google/cel-go/common/types"
+ "github.com/google/cel-go/common/types/ref"
+ "github.com/google/cel-go/common/types/traits"
+ "k8s.io/apimachinery/pkg/api/resource"
+)
+
+var (
+ // QuantityType singleton.
+ QuantityType = cel.ObjectType("kubernetes.Quantity",
+ traits.AdderType,
+ traits.ComparerType,
+ traits.DividerType,
+ traits.MultiplierType,
+ traits.NegatorType,
+ traits.SubtractorType,
+ )
+)
+
+// Quantity is a wrapper around k8s.io/apimachinery/pkg/api/resource.Quantity
+type Quantity struct {
+ Quantity *resource.Quantity
+}
+
+// NewQuantity creates a new Quantity
+func NewQuantity(q *resource.Quantity) Quantity {
+ if q == nil {
+ q = resource.NewScaledQuantity(0, resource.Nano)
+ }
+ return Quantity{Quantity: q}
+}
+
+// NewQuantityFromString creates a new Quantity from a string
+func NewQuantityFromString(s string) (Quantity, error) {
+ r, err := resource.ParseQuantity(s)
+ if err != nil {
+ return Quantity{}, err
+ }
+ return NewQuantity(&r), nil
+}
+
+func newQuantityFromNanoInt64(v int64) Quantity {
+ r := resource.NewScaledQuantity(v, resource.Nano)
+ return NewQuantity(r)
+}
+
+func newQuantityFromFloat64(v float64) Quantity {
+ r := resource.NewScaledQuantity(int64(v*10e9), resource.Nano)
+ return NewQuantity(r)
+}
+
+func (q Quantity) nano() int64 {
+ return q.Quantity.ScaledValue(resource.Nano)
+}
+
+func (q Quantity) float() float64 {
+ return q.Quantity.AsApproximateFloat64()
+}
+
+// ConvertToNative implements the ref.Val interface.
+func (q Quantity) ConvertToNative(typeDesc reflect.Type) (any, error) {
+ switch typeDesc.Kind() {
+ case reflect.Float32, reflect.Float64:
+ v := q.Quantity.AsApproximateFloat64()
+ return reflect.ValueOf(v).Convert(typeDesc).Interface(), nil
+ }
+ return nil, fmt.Errorf("type conversion error from Quantity to '%v'", typeDesc)
+}
+
+// ConvertToType implements the ref.Val interface.
+func (q Quantity) ConvertToType(typeVal ref.Type) ref.Val {
+ switch typeVal {
+ case types.DoubleType:
+ v := q.Quantity.AsApproximateFloat64()
+ return types.Double(v)
+ case types.StringType:
+ v := q.Quantity.String()
+ return types.String(v)
+ case types.TypeType:
+ return QuantityType
+ }
+ return types.NewErr("type conversion error from '%s' to '%s'", QuantityType, typeVal)
+}
+
+// Equal implements the ref.Val interface.
+func (q Quantity) Equal(other ref.Val) ref.Val {
+ otherQuantity, ok := other.(Quantity)
+ if !ok {
+ return types.MaybeNoSuchOverloadErr(other)
+ }
+ return types.Bool(q.Quantity.Equal(*otherQuantity.Quantity))
+}
+
+// Type implements the ref.Val interface.
+func (q Quantity) Type() ref.Type {
+ return QuantityType
+}
+
+// Value implements the ref.Val interface.
+func (q Quantity) Value() any {
+ return q.Quantity
+}
+
+// Add implements the traits.Adder interface.
+func (q Quantity) Add(other ref.Val) ref.Val {
+ otherQuantity, ok := other.(Quantity)
+ if !ok {
+ return types.MaybeNoSuchOverloadErr(other)
+ }
+ r := q.Quantity.DeepCopy()
+ r.Add(*otherQuantity.Quantity)
+ return NewQuantity(&r)
+}
+
+// Subtract implements the traits.Subtractor interface.
+func (q Quantity) Subtract(subtrahend ref.Val) ref.Val {
+ otherQuantity, ok := subtrahend.(Quantity)
+ if !ok {
+ return types.MaybeNoSuchOverloadErr(subtrahend)
+ }
+ r := q.Quantity.DeepCopy()
+ r.Sub(*otherQuantity.Quantity)
+ return NewQuantity(&r)
+}
+
+// Negate implements the traits.Negater interface.
+func (q Quantity) Negate() ref.Val {
+ r := q.Quantity.DeepCopy()
+ r.Neg()
+ return NewQuantity(&r)
+}
+
+// Divide implements the traits.Divider interface.
+func (q Quantity) Divide(other ref.Val) ref.Val {
+ switch other.Type() {
+ case types.IntType:
+ otherInt := other.(types.Int)
+ return newQuantityFromNanoInt64(q.nano() / int64(otherInt))
+ case types.UintType:
+ otherUint := other.(types.Uint)
+ return newQuantityFromNanoInt64(q.nano() / int64(otherUint))
+ case types.DoubleType:
+ otherDouble := other.(types.Double)
+ return newQuantityFromFloat64(q.float() / float64(otherDouble))
+ }
+
+ return types.MaybeNoSuchOverloadErr(other)
+}
+
+// Multiply implements the traits.Multiplier interface.
+func (q Quantity) Multiply(other ref.Val) ref.Val {
+ switch other.Type() {
+ case types.IntType:
+ otherInt := other.(types.Int)
+ return newQuantityFromNanoInt64(q.nano() * int64(otherInt))
+ case types.UintType:
+ otherUint := other.(types.Uint)
+ return newQuantityFromNanoInt64(q.nano() * int64(otherUint))
+ case types.DoubleType:
+ otherDouble := other.(types.Double)
+ return newQuantityFromFloat64(q.float() * float64(otherDouble))
+ }
+
+ return types.MaybeNoSuchOverloadErr(other)
+}
+
+// Compare implements the traits.Comparer interface.
+func (q Quantity) Compare(other ref.Val) ref.Val {
+ otherQuantity, ok := other.(Quantity)
+ if !ok {
+ return types.MaybeNoSuchOverloadErr(other)
+ }
+ return types.Int(q.Quantity.Cmp(*otherQuantity.Quantity))
+}
+
+// IsZeroValue implements the traits.Zeroer interface.
+func (q Quantity) IsZeroValue() bool {
+ return q.Quantity.IsZero()
+}
diff --git a/pkg/kwok/metrics/cel/resource_list.go b/pkg/kwok/metrics/cel/resource_list.go
new file mode 100644
index 000000000..6885fad8c
--- /dev/null
+++ b/pkg/kwok/metrics/cel/resource_list.go
@@ -0,0 +1,122 @@
+/*
+Copyright 2023 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cel
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/google/cel-go/cel"
+ "github.com/google/cel-go/common/types"
+ "github.com/google/cel-go/common/types/ref"
+ "github.com/google/cel-go/common/types/traits"
+ corev1 "k8s.io/api/core/v1"
+)
+
+var (
+ // ResourceListType singleton.
+ ResourceListType = cel.ObjectType("kubernetes.ResourceList",
+ traits.ContainerType,
+ traits.IndexerType,
+ traits.SizerType,
+ )
+)
+
+// ResourceList is a wrapper around k8s.io/api/core/v1.ResourceList
+type ResourceList struct {
+ List corev1.ResourceList
+}
+
+// NewResourceList creates a new ResourceList
+func NewResourceList(list corev1.ResourceList) ResourceList {
+ return ResourceList{List: list}
+}
+
+// ConvertToNative implements the ref.Val interface.
+func (r ResourceList) ConvertToNative(typeDesc reflect.Type) (any, error) {
+ return nil, fmt.Errorf("unsupported conversion from '%s' to '%v'", ResourceListType, typeDesc)
+}
+
+// ConvertToType implements the ref.Val interface.
+func (r ResourceList) ConvertToType(typeValue ref.Type) ref.Val {
+ return types.NewErr("type conversion error from '%s' to '%s'", ResourceListType, typeValue)
+}
+
+// Equal implements the ref.Val interface.
+func (r ResourceList) Equal(other ref.Val) ref.Val {
+ otherResourceList, ok := other.(ResourceList)
+ if !ok {
+ return types.MaybeNoSuchOverloadErr(other)
+ }
+ if len(r.List) != len(otherResourceList.List) {
+ return types.False
+ }
+ for k, v := range r.List {
+ otherVal, ok := otherResourceList.List[k]
+ if !ok {
+ return types.False
+ }
+ if !v.Equal(otherVal) {
+ return types.False
+ }
+ }
+ return types.True
+}
+
+// Type implements the ref.Val interface.
+func (r ResourceList) Type() ref.Type {
+ return ResourceListType
+}
+
+// Value implements the ref.Val interface.
+func (r ResourceList) Value() any {
+ return r.List
+}
+
+// Contains implements the traits.Container interface.
+func (r ResourceList) Contains(index ref.Val) ref.Val {
+ key, ok := index.(types.String)
+ if !ok {
+ return types.MaybeNoSuchOverloadErr(index)
+ }
+ _, found := r.List[corev1.ResourceName(key)]
+ return types.Bool(found)
+}
+
+// Get implements the traits.Indexer interface.
+func (r ResourceList) Get(index ref.Val) ref.Val {
+ key, ok := index.(types.String)
+ if !ok {
+ return types.MaybeNoSuchOverloadErr(index)
+ }
+
+ val, found := r.List[corev1.ResourceName(key)]
+ if !found {
+ return NewQuantity(nil)
+ }
+ return NewQuantity(&val)
+}
+
+// IsZeroValue implements the traits.Zeroer interface.
+func (r ResourceList) IsZeroValue() bool {
+ return len(r.List) == 0
+}
+
+// Size implements the traits.Sizer interface.
+func (r ResourceList) Size() ref.Val {
+ return types.Int(len(r.List))
+}
diff --git a/pkg/kwok/server/metrics.go b/pkg/kwok/server/metrics.go
index 069c83c1f..cc6f62c0c 100644
--- a/pkg/kwok/server/metrics.go
+++ b/pkg/kwok/server/metrics.go
@@ -32,22 +32,43 @@ import (
"sigs.k8s.io/kwok/pkg/log"
)
-// InstallMetrics registers the metrics handler on the given mux.
-func (s *Server) InstallMetrics(ctx context.Context) error {
- promHandler := promhttp.Handler()
-
- selfMetric := func(req *restful.Request, resp *restful.Response) {
- promHandler.ServeHTTP(resp.ResponseWriter, req.Request)
+func (s *Server) initCEL() error {
+ if s.env != nil {
+ return fmt.Errorf("CEL environment already initialized")
}
env, err := cel.NewEnvironment(cel.NodeEvaluatorConfig{
EnableEvaluatorCache: true,
EnableResultCache: true,
StartedContainersTotal: s.dataSource.StartedContainersTotal,
+
+ ContainerResourceUsage: s.containerResourceUsage,
+ PodResourceUsage: s.podResourceUsage,
+ NodeResourceUsage: s.nodeResourceUsage,
+
+ ContainerResourceCumulativeUsage: s.containerResourceCumulativeUsage,
+ PodResourceCumulativeUsage: s.podResourceCumulativeUsage,
+ NodeResourceCumulativeUsage: s.nodeResourceCumulativeUsage,
})
if err != nil {
return fmt.Errorf("failed to create CEL environment: %w", err)
}
+ s.env = env
+ return nil
+}
+
+// InstallMetrics registers the metrics handler on the given mux.
+func (s *Server) InstallMetrics(ctx context.Context) error {
+ err := s.initCEL()
+ if err != nil {
+ return err
+ }
+
+ promHandler := promhttp.Handler()
+
+ selfMetric := func(req *restful.Request, resp *restful.Response) {
+ promHandler.ServeHTTP(resp.ResponseWriter, req.Request)
+ }
const rootPath = "/metrics"
ws := new(restful.WebService)
@@ -77,7 +98,7 @@ func (s *Server) InstallMetrics(ctx context.Context) error {
}
}
ws.Route(ws.GET(path).
- To(s.getMetrics(m, env)))
+ To(s.getMetrics(m, s.env)))
}
for path := range hasPaths {
@@ -98,7 +119,7 @@ func (s *Server) InstallMetrics(ctx context.Context) error {
return fmt.Errorf("metric path %q does not start with %q", m.Spec.Path, rootPath)
}
ws.Route(ws.GET(strings.TrimPrefix(m.Spec.Path, rootPath)).
- To(s.getMetrics(m, env)))
+ To(s.getMetrics(m, s.env)))
}
}
diff --git a/pkg/kwok/server/metrics_resource_usage.go b/pkg/kwok/server/metrics_resource_usage.go
new file mode 100644
index 000000000..680be541b
--- /dev/null
+++ b/pkg/kwok/server/metrics_resource_usage.go
@@ -0,0 +1,261 @@
+/*
+Copyright 2023 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package server
+
+import (
+ "fmt"
+ "time"
+
+ corev1 "k8s.io/api/core/v1"
+
+ "sigs.k8s.io/kwok/pkg/apis/internalversion"
+ "sigs.k8s.io/kwok/pkg/kwok/metrics/cel"
+ "sigs.k8s.io/kwok/pkg/log"
+ "sigs.k8s.io/kwok/pkg/utils/slices"
+)
+
+type cumulative struct {
+ time time.Time
+ value float64
+}
+
+func (s *Server) containerResourceCumulativeUsage(resourceName, podNamespace, podName, containerName string) float64 {
+ key := fmt.Sprintf("%s/%s/%s/%s", resourceName, podNamespace, podName, containerName)
+ v := s.containerResourceUsage(resourceName, podNamespace, podName, containerName)
+
+ now := time.Now()
+ s.cumulativesMut.Lock()
+ defer s.cumulativesMut.Unlock()
+ c, ok := s.cumulatives[key]
+ if ok {
+ c.value += now.Sub(c.time).Seconds() * v
+ }
+
+ c.time = now
+ s.cumulatives[key] = c
+
+ return c.value
+}
+
+func (s *Server) podResourceCumulativeUsage(resourceName, podNamespace, podName string) float64 {
+ pod, ok := s.podCacheGetter.GetWithNamespace(podName, podNamespace)
+ if !ok {
+ return 0
+ }
+
+ sum := 0.0
+ for _, c := range pod.Spec.Containers {
+ sum += s.containerResourceCumulativeUsage(resourceName, podNamespace, podName, c.Name)
+ }
+ return sum
+}
+
+func (s *Server) nodeResourceCumulativeUsage(resourceName, nodeName string) float64 {
+ node, ok := s.nodeCacheGetter.Get(nodeName)
+ if !ok {
+ return 0
+ }
+
+ pods, ok := s.dataSource.ListPods(nodeName)
+ if !ok {
+ return 0
+ }
+
+ data := cel.Data{
+ Node: node,
+ }
+
+ sum := 0.0
+ for _, pi := range pods {
+ pod, ok := s.podCacheGetter.GetWithNamespace(pi.Name, pi.Namespace)
+ if !ok {
+ continue
+ }
+ data.Pod = pod
+ for _, c := range pod.Spec.Containers {
+ data.Container = &c
+ sum += s.evaluateContainerResourceUsage(resourceName, data)
+ }
+ }
+
+ now := time.Now()
+ key := nodeName
+ s.cumulativesMut.Lock()
+ defer s.cumulativesMut.Unlock()
+ c, ok := s.cumulatives[key]
+ if ok {
+ c.value += now.Sub(c.time).Seconds() * sum
+ }
+
+ c.time = now
+ s.cumulatives[key] = c
+
+ return c.value
+}
+
+func (s *Server) containerResourceUsage(resourceName, podNamespace, podName, containerName string) float64 {
+ pod, ok := s.podCacheGetter.GetWithNamespace(podName, podNamespace)
+ if !ok {
+ return 0
+ }
+
+ node, ok := s.nodeCacheGetter.Get(pod.Spec.NodeName)
+ if !ok {
+ return 0
+ }
+
+ c, ok := slices.Find(pod.Spec.Containers, func(c corev1.Container) bool {
+ return c.Name == containerName
+ })
+ if !ok {
+ return 0
+ }
+ data := cel.Data{
+ Node: node,
+ Pod: pod,
+ Container: &c,
+ }
+ return s.evaluateContainerResourceUsage(resourceName, data)
+}
+
+func (s *Server) evaluateContainerResourceUsage(resourceName string, data cel.Data) float64 {
+ u, err := s.getResourceUsage(data.Pod.Name, data.Pod.Namespace, data.Container.Name)
+ if err != nil {
+ logger := log.FromContext(s.ctx)
+ logger.Error("failed to get resource usage", err, "pod", log.KRef(data.Pod.Namespace, data.Pod.Name), "container", data.Container.Name)
+ return 0
+ }
+ if u.Usage == nil {
+ return 0
+ }
+ r := u.Usage[resourceName]
+ if r.Value != nil {
+ return r.Value.AsApproximateFloat64()
+ }
+
+ if r.Expression != nil {
+ eval, err := s.env.Compile(*r.Expression)
+ if err != nil {
+ logger := log.FromContext(s.ctx)
+ logger.Error("failed to compile expression", err, "expression", r)
+ return 0
+ }
+
+ out, err := eval.EvaluateFloat64(data)
+ if err != nil {
+ logger := log.FromContext(s.ctx)
+ logger.Error("failed to evaluate expression", err, "expression", r)
+ return 0
+ }
+ return out
+ }
+ return 0
+}
+
+func (s *Server) podResourceUsage(resourceName, podNamespace, podName string) float64 {
+ pod, ok := s.podCacheGetter.GetWithNamespace(podName, podNamespace)
+ if !ok {
+ return 0
+ }
+
+ node, ok := s.nodeCacheGetter.Get(pod.Spec.NodeName)
+ if !ok {
+ return 0
+ }
+
+ data := cel.Data{
+ Node: node,
+ Pod: pod,
+ }
+
+ sum := 0.0
+ for _, c := range pod.Spec.Containers {
+ data.Container = &c
+ sum += s.evaluateContainerResourceUsage(resourceName, data)
+ }
+ return sum
+}
+
+func (s *Server) nodeResourceUsage(resourceName, nodeName string) float64 {
+ node, ok := s.nodeCacheGetter.Get(nodeName)
+ if !ok {
+ return 0
+ }
+
+ pods, ok := s.dataSource.ListPods(nodeName)
+ if !ok {
+ return 0
+ }
+
+ data := cel.Data{
+ Node: node,
+ }
+
+ sum := 0.0
+ for _, pi := range pods {
+ pod, ok := s.podCacheGetter.GetWithNamespace(pi.Name, pi.Namespace)
+ if !ok {
+ continue
+ }
+ data.Pod = pod
+ for _, c := range pod.Spec.Containers {
+ data.Container = &c
+ sum += s.evaluateContainerResourceUsage(resourceName, data)
+ }
+ }
+ return sum
+}
+
+func (s *Server) getResourceUsage(podName, podNamespace, containerName string) (*internalversion.ResourceUsageContainer, error) {
+ u, has := slices.Find(s.resourceUsages.Get(), func(a *internalversion.ResourceUsage) bool {
+ return a.Name == podName && a.Namespace == podNamespace
+ })
+ if has {
+ u, found := findUsageInUsages(containerName, u.Spec.Usages)
+ if found {
+ return u, nil
+ }
+ return nil, fmt.Errorf("no resource usage found for container %q in pod %q", containerName, log.KRef(podNamespace, podName))
+ }
+
+ for _, cl := range s.clusterResourceUsages.Get() {
+ if !cl.Spec.Selector.Match(podName, podNamespace) {
+ continue
+ }
+
+ u, found := findUsageInUsages(containerName, cl.Spec.Usages)
+ if found {
+ return u, nil
+ }
+ }
+
+ return nil, fmt.Errorf("no resource usage found for container %q in pod %q", containerName, log.KRef(podNamespace, podName))
+}
+
+func findUsageInUsages(containerName string, usages []internalversion.ResourceUsageContainer) (*internalversion.ResourceUsageContainer, bool) {
+ var defaultUsage *internalversion.ResourceUsageContainer
+ for i, u := range usages {
+ if len(u.Containers) == 0 && defaultUsage == nil {
+ defaultUsage = &usages[i]
+ continue
+ }
+ if slices.Contains(u.Containers, containerName) {
+ return &u, true
+ }
+ }
+ return defaultUsage, defaultUsage != nil
+}
diff --git a/pkg/kwok/server/server.go b/pkg/kwok/server/server.go
index cc8dc4d20..e88b9ec25 100644
--- a/pkg/kwok/server/server.go
+++ b/pkg/kwok/server/server.go
@@ -21,6 +21,7 @@ import (
"fmt"
"net"
"net/http"
+ "sync"
"time"
"github.com/emicklei/go-restful/v3"
@@ -34,6 +35,7 @@ import (
"sigs.k8s.io/kwok/pkg/client/clientset/versioned"
"sigs.k8s.io/kwok/pkg/config/resources"
"sigs.k8s.io/kwok/pkg/kwok/metrics"
+ "sigs.k8s.io/kwok/pkg/kwok/metrics/cel"
"sigs.k8s.io/kwok/pkg/log"
"sigs.k8s.io/kwok/pkg/utils/informer"
"sigs.k8s.io/kwok/pkg/utils/maps"
@@ -47,6 +49,8 @@ const (
// Server is a server that can serve HTTP/HTTPS requests.
type Server struct {
+ ctx context.Context
+
typedKwokClient versioned.Interface
enableCRDs []string
@@ -57,18 +61,25 @@ type Server struct {
streamCreationTimeout time.Duration
bufPool *pools.Pool[[]byte]
- clusterPortForwards resources.Getter[[]*internalversion.ClusterPortForward]
- portForwards resources.Getter[[]*internalversion.PortForward]
- clusterExecs resources.Getter[[]*internalversion.ClusterExec]
- execs resources.Getter[[]*internalversion.Exec]
- clusterLogs resources.Getter[[]*internalversion.ClusterLogs]
- logs resources.Getter[[]*internalversion.Logs]
- clusterAttaches resources.Getter[[]*internalversion.ClusterAttach]
- attaches resources.Getter[[]*internalversion.Attach]
- metrics resources.Getter[[]*internalversion.Metric]
+ clusterPortForwards resources.Getter[[]*internalversion.ClusterPortForward]
+ portForwards resources.Getter[[]*internalversion.PortForward]
+ clusterExecs resources.Getter[[]*internalversion.ClusterExec]
+ execs resources.Getter[[]*internalversion.Exec]
+ clusterLogs resources.Getter[[]*internalversion.ClusterLogs]
+ logs resources.Getter[[]*internalversion.Logs]
+ clusterAttaches resources.Getter[[]*internalversion.ClusterAttach]
+ attaches resources.Getter[[]*internalversion.Attach]
+ clusterResourceUsages resources.Getter[[]*internalversion.ClusterResourceUsage]
+ resourceUsages resources.Getter[[]*internalversion.ResourceUsage]
+ metrics resources.Getter[[]*internalversion.Metric]
metricsUpdateHandler maps.SyncMap[string, *metrics.UpdateHandler]
+ cumulatives map[string]cumulative
+ cumulativesMut sync.Mutex
+
+ env *cel.Environment
+
dataSource DataSource
nodeCacheGetter informer.Getter[*corev1.Node]
podCacheGetter informer.Getter[*corev1.Pod]
@@ -86,15 +97,17 @@ type Config struct {
TypedKwokClient versioned.Interface
EnableCRDs []string
- ClusterPortForwards []*internalversion.ClusterPortForward
- PortForwards []*internalversion.PortForward
- ClusterExecs []*internalversion.ClusterExec
- Execs []*internalversion.Exec
- ClusterLogs []*internalversion.ClusterLogs
- Logs []*internalversion.Logs
- ClusterAttaches []*internalversion.ClusterAttach
- Attaches []*internalversion.Attach
- Metrics []*internalversion.Metric
+ ClusterPortForwards []*internalversion.ClusterPortForward
+ PortForwards []*internalversion.PortForward
+ ClusterExecs []*internalversion.ClusterExec
+ Execs []*internalversion.Exec
+ ClusterLogs []*internalversion.ClusterLogs
+ Logs []*internalversion.Logs
+ ClusterAttaches []*internalversion.ClusterAttach
+ Attaches []*internalversion.Attach
+ ClusterResourceUsages []*internalversion.ClusterResourceUsage
+ ResourceUsages []*internalversion.ResourceUsage
+ Metrics []*internalversion.Metric
DataSource DataSource
NodeCacheGetter informer.Getter[*corev1.Node]
@@ -112,15 +125,19 @@ func NewServer(conf Config) (*Server, error) {
idleTimeout: 1 * time.Hour,
streamCreationTimeout: remotecommandconsts.DefaultStreamCreationTimeout,
- clusterPortForwards: resources.NewStaticGetter(conf.ClusterPortForwards),
- portForwards: resources.NewStaticGetter(conf.PortForwards),
- clusterExecs: resources.NewStaticGetter(conf.ClusterExecs),
- execs: resources.NewStaticGetter(conf.Execs),
- clusterLogs: resources.NewStaticGetter(conf.ClusterLogs),
- logs: resources.NewStaticGetter(conf.Logs),
- clusterAttaches: resources.NewStaticGetter(conf.ClusterAttaches),
- attaches: resources.NewStaticGetter(conf.Attaches),
- metrics: resources.NewStaticGetter(conf.Metrics),
+ clusterPortForwards: resources.NewStaticGetter(conf.ClusterPortForwards),
+ portForwards: resources.NewStaticGetter(conf.PortForwards),
+ clusterExecs: resources.NewStaticGetter(conf.ClusterExecs),
+ execs: resources.NewStaticGetter(conf.Execs),
+ clusterLogs: resources.NewStaticGetter(conf.ClusterLogs),
+ logs: resources.NewStaticGetter(conf.Logs),
+ clusterAttaches: resources.NewStaticGetter(conf.ClusterAttaches),
+ attaches: resources.NewStaticGetter(conf.Attaches),
+ clusterResourceUsages: resources.NewStaticGetter(conf.ClusterResourceUsages),
+ resourceUsages: resources.NewStaticGetter(conf.ResourceUsages),
+ metrics: resources.NewStaticGetter(conf.Metrics),
+
+ cumulatives: map[string]cumulative{},
dataSource: conf.DataSource,
podCacheGetter: conf.PodCacheGetter,
@@ -327,6 +344,52 @@ func (s *Server) initWatchCRD(ctx context.Context) ([]resources.Starter, error)
)
starters = append(starters, attaches)
s.attaches = attaches
+ case v1alpha1.ClusterResourceUsageKind:
+ if len(s.clusterResourceUsages.Get()) != 0 {
+ return nil, fmt.Errorf("cluster resource usage already exists, cannot watch CRD")
+ }
+ clusterResourceUsages := resources.NewDynamicGetter[
+ []*internalversion.ClusterResourceUsage,
+ *v1alpha1.ClusterResourceUsage,
+ *v1alpha1.ClusterResourceUsageList,
+ ](
+ cli.KwokV1alpha1().ClusterResourceUsages(),
+ func(objs []*v1alpha1.ClusterResourceUsage) []*internalversion.ClusterResourceUsage {
+ return slices.FilterAndMap(objs, func(obj *v1alpha1.ClusterResourceUsage) (*internalversion.ClusterResourceUsage, bool) {
+ r, err := internalversion.ConvertToInternalClusterResourceUsage(obj)
+ if err != nil {
+ logger.Error("failed to convert to internal cluster resource usage", err, "obj", obj)
+ return nil, false
+ }
+ return r, true
+ })
+ },
+ )
+ starters = append(starters, clusterResourceUsages)
+ s.clusterResourceUsages = clusterResourceUsages
+ case v1alpha1.ResourceUsageKind:
+ if len(s.resourceUsages.Get()) != 0 {
+ return nil, fmt.Errorf("resource usage already exists, cannot watch CRD")
+ }
+ resourceUsages := resources.NewDynamicGetter[
+ []*internalversion.ResourceUsage,
+ *v1alpha1.ResourceUsage,
+ *v1alpha1.ResourceUsageList,
+ ](
+ cli.KwokV1alpha1().ResourceUsages(""),
+ func(objs []*v1alpha1.ResourceUsage) []*internalversion.ResourceUsage {
+ return slices.FilterAndMap(objs, func(obj *v1alpha1.ResourceUsage) (*internalversion.ResourceUsage, bool) {
+ r, err := internalversion.ConvertToInternalResourceUsage(obj)
+ if err != nil {
+ logger.Error("failed to convert to internal resource usage", err, "obj", obj)
+ return nil, false
+ }
+ return r, true
+ })
+ },
+ )
+ starters = append(starters, resourceUsages)
+ s.resourceUsages = resourceUsages
case v1alpha1.MetricKind:
if len(s.metrics.Get()) != 0 {
return nil, fmt.Errorf("metrics already exists, cannot watch CRD")
@@ -400,6 +463,8 @@ func (s *Server) Run(ctx context.Context, address string, certFile, privateKeyFi
ctx, cancel := context.WithCancel(ctx)
defer cancel()
+ s.ctx = ctx
+
errCh := make(chan error, 1)
if certFile != "" && privateKeyFile != "" {
diff --git a/pkg/kwokctl/runtime/cluster.go b/pkg/kwokctl/runtime/cluster.go
index ddef0f708..79f338fb5 100644
--- a/pkg/kwokctl/runtime/cluster.go
+++ b/pkg/kwokctl/runtime/cluster.go
@@ -571,14 +571,16 @@ func (c *Cluster) InitCRDs(ctx context.Context) error {
}
var crdDefines = map[string][]byte{
- v1alpha1.StageKind: crd.Stage,
- v1alpha1.AttachKind: crd.Attach,
- v1alpha1.ClusterAttachKind: crd.ClusterAttach,
- v1alpha1.ExecKind: crd.Exec,
- v1alpha1.ClusterExecKind: crd.ClusterExec,
- v1alpha1.PortForwardKind: crd.PortForward,
- v1alpha1.ClusterPortForwardKind: crd.ClusterPortForward,
- v1alpha1.LogsKind: crd.Logs,
- v1alpha1.ClusterLogsKind: crd.ClusterLogs,
- v1alpha1.MetricKind: crd.Metric,
+ v1alpha1.StageKind: crd.Stage,
+ v1alpha1.AttachKind: crd.Attach,
+ v1alpha1.ClusterAttachKind: crd.ClusterAttach,
+ v1alpha1.ExecKind: crd.Exec,
+ v1alpha1.ClusterExecKind: crd.ClusterExec,
+ v1alpha1.PortForwardKind: crd.PortForward,
+ v1alpha1.ClusterPortForwardKind: crd.ClusterPortForward,
+ v1alpha1.LogsKind: crd.Logs,
+ v1alpha1.ClusterLogsKind: crd.ClusterLogs,
+ v1alpha1.ResourceUsageKind: crd.ResourceUsage,
+ v1alpha1.ClusterResourceUsageKind: crd.ClusterResourceUsage,
+ v1alpha1.MetricKind: crd.Metric,
}
diff --git a/site/content/en/docs/generated/apis.md b/site/content/en/docs/generated/apis.md
index 5b76527e2..d61581891 100644
--- a/site/content/en/docs/generated/apis.md
+++ b/site/content/en/docs/generated/apis.md
@@ -295,6 +295,9 @@ Resource Types:
ClusterPortForward
+ClusterResourceUsage
+
+
Exec
@@ -307,6 +310,9 @@ Resource Types:
PortForward
+ResourceUsage
+
+
Stage
@@ -821,6 +827,111 @@ ClusterPortForwardStatus
+
+ClusterResourceUsage
+ #
+
+
+
ClusterResourceUsage provides cluster-wide resource usage.
+
+
Exec
#
@@ -1200,6 +1311,98 @@ PortForwardStatus
+
+ResourceUsage
+ #
+
+
+
ResourceUsage provides resource usage for a single pod.
+
+
Stage
#
@@ -3716,6 +3919,87 @@ ClusterPortForwardStatus
+
+ClusterResourceUsageSpec
+ #
+
+
+Appears on:
+ClusterResourceUsage
+
+
+
ClusterResourceUsageSpec holds spec for cluster resource usage.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+selector
+
+
+ObjectSelector
+
+
+ |
+
+ Selector is a selector to filter pods to configure.
+ |
+
+
+
+usages
+
+
+[]ResourceUsageContainer
+
+
+ |
+
+ Usages is a list of resource usage for the pod.
+ |
+
+
+
+
+ClusterResourceUsageStatus
+ #
+
+
+Appears on:
+ClusterResourceUsage
+
+
+
ClusterResourceUsageStatus holds status for cluster resource usage
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+conditions
+
+
+[]Condition
+
+
+ |
+
+ Conditions holds conditions for cluster resource usage
+ |
+
+
+
@@ -4738,6 +5026,8 @@ ObjectSelector
ClusterLogsSpec
,
ClusterPortForwardSpec
+,
+ClusterResourceUsageSpec
ObjectSelector holds information how to match based on namespace and name.
@@ -4844,6 +5134,164 @@ PortForwardStatus
+
+ResourceUsageContainer
+ #
+
+
+Appears on:
+ClusterResourceUsageSpec
+,
+ResourceUsageSpec
+
+
+
ResourceUsageContainer holds spec for resource usage container.
+
+
+
+ResourceUsageSpec
+ #
+
+
+Appears on:
+ResourceUsage
+
+
+
ResourceUsageSpec holds spec for resource usage.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+usages
+
+
+[]ResourceUsageContainer
+
+
+ |
+
+ Usages is a list of resource usage for the pod.
+ |
+
+
+
+
+ResourceUsageStatus
+ #
+
+
+Appears on:
+ResourceUsage
+
+
+
ResourceUsageStatus holds status for resource usage
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+conditions
+
+
+[]Condition
+
+
+ |
+
+ Conditions holds conditions for resource usage
+ |
+
+
+
+
+ResourceUsageValue
+ #
+
+
+Appears on:
+ResourceUsageContainer
+
+
+
ResourceUsageValue holds value for resource usage.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+value
+
+k8s.io/apimachinery/pkg/api/resource.Quantity
+
+ |
+
+ Value is the value for resource usage.
+ |
+
+
+
+expression
+
+string
+
+ |
+
+ Expression is the expression for resource usage.
+ |
+
+
+
SecurityContext
#
diff --git a/test/kwokctl/metric-node-resource.yaml b/test/kwokctl/metric-node-resource.yaml
index c3df72b97..86750747b 100644
--- a/test/kwokctl/metric-node-resource.yaml
+++ b/test/kwokctl/metric-node-resource.yaml
@@ -35,7 +35,7 @@ spec:
value: 'pod.metadata.namespace'
- name: pod
value: 'pod.metadata.name'
- value: 'pod.SinceSecond() / 10.0'
+ value: 'pod.CumulativeUsage("cpu", container.name)'
- name: pod_cpu_usage_seconds_total
dimension: pod
help: "[ALPHA] Cumulative cpu time consumed by the pod in core-seconds"
@@ -45,14 +45,12 @@ spec:
value: 'pod.metadata.namespace'
- name: pod
value: 'pod.metadata.name'
- # TODO: sum(container_cpu_usage_seconds_total) by (pod)
- value: 'pod.SinceSecond()'
+ value: 'pod.CumulativeUsage("cpu")'
- name: node_cpu_usage_seconds_total
dimension: node
help: "[ALPHA] Cumulative cpu time consumed by the node in core-seconds"
kind: counter
- # TODO: sum(pod_cpu_usage_seconds_total) by (node)
- value: '10.0 * node.SinceSecond()'
+ value: 'node.CumulativeUsage("cpu")'
# Memory
- name: container_memory_working_set_bytes
@@ -66,7 +64,7 @@ spec:
value: 'pod.metadata.namespace'
- name: pod
value: 'pod.metadata.name'
- value: '1024.0 * 1024.0 * 1024.0'
+ value: 'pod.Usage("memory", container.name)'
- name: pod_memory_working_set_bytes
dimension: pod
help: "[ALPHA] Current working set of the pod in bytes"
@@ -76,11 +74,35 @@ spec:
value: 'pod.metadata.namespace'
- name: pod
value: 'pod.metadata.name'
- # TODO: sum(container_memory_working_set_bytes) by (pod)
- value: '10.0 * 1024.0 * 1024.0 * 1024.0'
+ value: 'pod.Usage("memory")'
- name: node_memory_working_set_bytes
dimension: node
help: "[ALPHA] Current working set of the node in bytes"
kind: gauge
- # TODO: sum(pod_memory_working_set_bytes) by (node)
- value: '100.0 * 1024.0 * 1024.0 * 1024.0'
+ value: 'node.Usage("memory")'
+
+---
+kind: ClusterResourceUsage
+apiVersion: kwok.x-k8s.io/v1alpha1
+metadata:
+ name: cluster-resource-usage
+spec:
+ usages:
+ - usage:
+ cpu:
+ expression: '"cpu" in container.resources.requests ? container.resources.requests["cpu"] : Quantity("10m")'
+ memory:
+ expression: '"memory" in container.resources.requests ? container.resources.requests["memory"] : Quantity("10Mi")'
+---
+kind: ResourceUsage
+apiVersion: kwok.x-k8s.io/v1alpha1
+metadata:
+ name: pod-000000
+ namespace: default
+spec:
+ usages:
+ - usage:
+ cpu:
+ expression: '"cpu" in container.resources.requests ? container.resources.requests["cpu"] * 2 : Quantity("10m")'
+ memory:
+ expression: '"memory" in container.resources.requests ? container.resources.requests["memory"] * 2 : Quantity("10Mi")'