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.

    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +apiVersion +string + + +kwok.x-k8s.io/v1alpha1 + +
    +kind +string +ClusterResourceUsage
    +metadata + + +Kubernetes meta/v1.ObjectMeta + + + +

    Standard list metadata. +More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

    +Refer to the Kubernetes API documentation for the fields of the +metadata field. +
    +spec + + +ClusterResourceUsageSpec + + + +

    Spec holds spec for cluster resource usage.

    + + + + + + + + + +
    +selector + + +ObjectSelector + + + +

    Selector is a selector to filter pods to configure.

    +
    +usages + + +[]ResourceUsageContainer + + + +

    Usages is a list of resource usage for the pod.

    +
    +
    +status + + +ClusterResourceUsageStatus + + + +

    Status holds status for cluster resource usage

    +

    Exec # @@ -1200,6 +1311,98 @@ PortForwardStatus +

    +ResourceUsage + # +

    +

    +

    ResourceUsage provides resource usage for a single pod.

    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +apiVersion +string + + +kwok.x-k8s.io/v1alpha1 + +
    +kind +string +ResourceUsage
    +metadata + + +Kubernetes meta/v1.ObjectMeta + + + +

    Standard list metadata. +More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

    +Refer to the Kubernetes API documentation for the fields of the +metadata field. +
    +spec + + +ResourceUsageSpec + + + +

    Spec holds spec for resource usage.

    + + + + + +
    +usages + + +[]ResourceUsageContainer + + + +

    Usages is a list of resource usage for the pod.

    +
    +
    +status + + +ResourceUsageStatus + + + +

    Status holds status for resource usage

    +

    Stage # @@ -3716,6 +3919,87 @@ ClusterPortForwardStatus +

    +ClusterResourceUsageSpec + # +

    +

    +Appears on: +ClusterResourceUsage +

    +

    +

    ClusterResourceUsageSpec holds spec for cluster resource usage.

    +

    + + + + + + + + + + + + + + + + + +
    FieldDescription
    +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

    +

    + + + + + + + + + + + + + +
    FieldDescription
    +conditions + + +[]Condition + + + +

    Conditions holds conditions for cluster resource usage

    +

    Condition # @@ -3732,6 +4016,8 @@ Condition , ClusterPortForwardStatus , +ClusterResourceUsageStatus +, ExecStatus , LogsStatus @@ -3740,6 +4026,8 @@ Condition , PortForwardStatus , +ResourceUsageStatus +, StageStatus

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

    +

    + + + + + + + + + + + + + + + + + +
    FieldDescription
    +containers + +[]string + + +

    Containers is list of container names.

    +
    +usage + + +map[string]sigs.k8s.io/kwok/pkg/apis/v1alpha1.ResourceUsageValue + + + +

    Usage is a list of resource usage for the container.

    +
    +

    +ResourceUsageSpec + # +

    +

    +Appears on: +ResourceUsage +

    +

    +

    ResourceUsageSpec holds spec for resource usage.

    +

    + + + + + + + + + + + + + +
    FieldDescription
    +usages + + +[]ResourceUsageContainer + + + +

    Usages is a list of resource usage for the pod.

    +
    +

    +ResourceUsageStatus + # +

    +

    +Appears on: +ResourceUsage +

    +

    +

    ResourceUsageStatus holds status for resource usage

    +

    + + + + + + + + + + + + + +
    FieldDescription
    +conditions + + +[]Condition + + + +

    Conditions holds conditions for resource usage

    +
    +

    +ResourceUsageValue + # +

    +

    +Appears on: +ResourceUsageContainer +

    +

    +

    ResourceUsageValue holds value for resource usage.

    +

    + + + + + + + + + + + + + + + + + +
    FieldDescription
    +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")'