Move pkg/kubectl/metricsutil to staging
Kubernetes-commit: a1de0e93315dd56269a859b916ad5689ad4017b4
This commit is contained in:
parent
8f21c5b72f
commit
6f46e8d063
20
go.mod
20
go.mod
|
@ -25,12 +25,13 @@ require (
|
|||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f
|
||||
gopkg.in/yaml.v2 v2.2.2
|
||||
gotest.tools v2.2.0+incompatible // indirect
|
||||
k8s.io/api v0.0.0-20190726022912-69e1bce1dad5
|
||||
k8s.io/apimachinery v0.0.0-20190727130956-f97a4e5b4abc
|
||||
k8s.io/cli-runtime v0.0.0-20190726024606-74a61cd71909
|
||||
k8s.io/client-go v0.0.0-20190726023111-a9c895e7f2ac
|
||||
k8s.io/api v0.0.0
|
||||
k8s.io/apimachinery v0.0.0
|
||||
k8s.io/cli-runtime v0.0.0
|
||||
k8s.io/client-go v0.0.0
|
||||
k8s.io/klog v0.3.1
|
||||
k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058
|
||||
k8s.io/metrics v0.0.0
|
||||
k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a
|
||||
sigs.k8s.io/yaml v1.1.0
|
||||
)
|
||||
|
@ -42,8 +43,11 @@ replace (
|
|||
golang.org/x/sys => golang.org/x/sys v0.0.0-20190209173611-3b5209105503
|
||||
golang.org/x/text => golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db
|
||||
golang.org/x/tools => golang.org/x/tools v0.0.0-20190313210603-aa82965741a9
|
||||
k8s.io/api => k8s.io/api v0.0.0-20190726022912-69e1bce1dad5
|
||||
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20190727130956-f97a4e5b4abc
|
||||
k8s.io/cli-runtime => k8s.io/cli-runtime v0.0.0-20190726024606-74a61cd71909
|
||||
k8s.io/client-go => k8s.io/client-go v0.0.0-20190725230141-579ad46bdcb9
|
||||
k8s.io/api => ../api
|
||||
k8s.io/apimachinery => ../apimachinery
|
||||
k8s.io/cli-runtime => ../cli-runtime
|
||||
k8s.io/client-go => ../client-go
|
||||
k8s.io/code-generator => ../code-generator
|
||||
k8s.io/kubectl => ../kubectl
|
||||
k8s.io/metrics => ../metrics
|
||||
)
|
||||
|
|
18
go.sum
18
go.sum
|
@ -3,6 +3,7 @@ github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7O
|
|||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU=
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
|
@ -129,6 +130,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
|
@ -154,6 +156,10 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT
|
|||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
golang.org/x/crypto v0.0.0-20181025213731-e84da0312774 h1:a4tQYYYuK9QdeO/+kEvNYyuR21S+7ve5EANok6hABhI=
|
||||
golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/net v0.0.0-20190206173232-65e2d4e15006 h1:bfLnR+k0tq5Lqt6dflRLcZiz6UaXCMt3vhYJ1l4FQ80=
|
||||
golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA=
|
||||
|
@ -167,6 +173,9 @@ golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5f
|
|||
golang.org/x/time v0.0.0-20161028155119-f51c12702a4d h1:TnM+PKb3ylGmZvyPXmo9m/wktg7Jn/a/fNmr33HSj8g=
|
||||
golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20190313210603-aa82965741a9/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
|
||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
|
||||
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
|
@ -185,10 +194,6 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
|||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
k8s.io/api v0.0.0-20190726022912-69e1bce1dad5/go.mod h1:V6cpJ9D7WqSy0wqcE096gcbj+W//rshgQgmj1Shdwi8=
|
||||
k8s.io/apimachinery v0.0.0-20190727130956-f97a4e5b4abc/go.mod h1:eXR4ljjmbwK6Ng0PKsXRySPXnTUy/qBUa6kPDeckhQ0=
|
||||
k8s.io/cli-runtime v0.0.0-20190726024606-74a61cd71909/go.mod h1:bk/fSEmINmKG2jHCCbqbXmwEJgE6kHVMkOC1U9dclzo=
|
||||
k8s.io/client-go v0.0.0-20190725230141-579ad46bdcb9/go.mod h1:ncT9fCvHnM5BUiZs0RCf9vAEqRrRoJtR2sZ2evompEU=
|
||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
|
@ -198,6 +203,11 @@ k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058 h1:di3XCwddOR9cWBNpfgXask
|
|||
k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4=
|
||||
k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a h1:2jUDc9gJja832Ftp+QbDV0tVhQHMISFn01els+2ZAcw=
|
||||
k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
|
||||
modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
|
||||
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
|
||||
modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
|
||||
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
|
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
Copyright 2016 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 metricsutil
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/validation"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
metricsapi "k8s.io/metrics/pkg/apis/metrics"
|
||||
metricsv1alpha1api "k8s.io/metrics/pkg/apis/metrics/v1alpha1"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultHeapsterNamespace = "kube-system"
|
||||
DefaultHeapsterScheme = "http"
|
||||
DefaultHeapsterService = "heapster"
|
||||
DefaultHeapsterPort = "" // use the first exposed port on the service
|
||||
)
|
||||
|
||||
var (
|
||||
prefix = "/apis"
|
||||
groupVersion = fmt.Sprintf("%s/%s", metricsGv.Group, metricsGv.Version)
|
||||
metricsRoot = fmt.Sprintf("%s/%s", prefix, groupVersion)
|
||||
|
||||
// TODO: get this from metrics api once it's finished
|
||||
metricsGv = schema.GroupVersion{Group: "metrics", Version: "v1alpha1"}
|
||||
)
|
||||
|
||||
type HeapsterMetricsClient struct {
|
||||
SVCClient corev1client.ServicesGetter
|
||||
HeapsterNamespace string
|
||||
HeapsterScheme string
|
||||
HeapsterService string
|
||||
HeapsterPort string
|
||||
}
|
||||
|
||||
func NewHeapsterMetricsClient(svcClient corev1client.ServicesGetter, namespace, scheme, service, port string) *HeapsterMetricsClient {
|
||||
return &HeapsterMetricsClient{
|
||||
SVCClient: svcClient,
|
||||
HeapsterNamespace: namespace,
|
||||
HeapsterScheme: scheme,
|
||||
HeapsterService: service,
|
||||
HeapsterPort: port,
|
||||
}
|
||||
}
|
||||
|
||||
func podMetricsURL(namespace string, name string) (string, error) {
|
||||
if namespace == metav1.NamespaceAll {
|
||||
return fmt.Sprintf("%s/pods", metricsRoot), nil
|
||||
}
|
||||
errs := validation.ValidateNamespaceName(namespace, false)
|
||||
if len(errs) > 0 {
|
||||
message := fmt.Sprintf("invalid namespace: %s - %v", namespace, errs)
|
||||
return "", errors.New(message)
|
||||
}
|
||||
if len(name) > 0 {
|
||||
errs = validation.NameIsDNSSubdomain(name, false)
|
||||
if len(errs) > 0 {
|
||||
message := fmt.Sprintf("invalid pod name: %s - %v", name, errs)
|
||||
return "", errors.New(message)
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%s/namespaces/%s/pods/%s", metricsRoot, namespace, name), nil
|
||||
}
|
||||
|
||||
func nodeMetricsURL(name string) (string, error) {
|
||||
if len(name) > 0 {
|
||||
errs := validation.NameIsDNSSubdomain(name, false)
|
||||
if len(errs) > 0 {
|
||||
message := fmt.Sprintf("invalid node name: %s - %v", name, errs)
|
||||
return "", errors.New(message)
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%s/nodes/%s", metricsRoot, name), nil
|
||||
}
|
||||
|
||||
func (cli *HeapsterMetricsClient) GetNodeMetrics(nodeName string, selector string) (*metricsapi.NodeMetricsList, error) {
|
||||
params := map[string]string{"labelSelector": selector}
|
||||
path, err := nodeMetricsURL(nodeName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resultRaw, err := GetHeapsterMetrics(cli, path, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
versionedMetrics := metricsv1alpha1api.NodeMetricsList{}
|
||||
if len(nodeName) == 0 {
|
||||
err = json.Unmarshal(resultRaw, &versionedMetrics)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshall heapster response: %v", err)
|
||||
}
|
||||
} else {
|
||||
var singleMetric metricsv1alpha1api.NodeMetrics
|
||||
err = json.Unmarshal(resultRaw, &singleMetric)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshall heapster response: %v", err)
|
||||
}
|
||||
versionedMetrics.Items = []metricsv1alpha1api.NodeMetrics{singleMetric}
|
||||
}
|
||||
metrics := &metricsapi.NodeMetricsList{}
|
||||
err = metricsv1alpha1api.Convert_v1alpha1_NodeMetricsList_To_metrics_NodeMetricsList(&versionedMetrics, metrics, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return metrics, nil
|
||||
}
|
||||
|
||||
func (cli *HeapsterMetricsClient) GetPodMetrics(namespace string, podName string, allNamespaces bool, selector labels.Selector) (*metricsapi.PodMetricsList, error) {
|
||||
if allNamespaces {
|
||||
namespace = metav1.NamespaceAll
|
||||
}
|
||||
path, err := podMetricsURL(namespace, podName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params := map[string]string{"labelSelector": selector.String()}
|
||||
versionedMetrics := metricsv1alpha1api.PodMetricsList{}
|
||||
|
||||
resultRaw, err := GetHeapsterMetrics(cli, path, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(podName) == 0 {
|
||||
err = json.Unmarshal(resultRaw, &versionedMetrics)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshall heapster response: %v", err)
|
||||
}
|
||||
} else {
|
||||
var singleMetric metricsv1alpha1api.PodMetrics
|
||||
err = json.Unmarshal(resultRaw, &singleMetric)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshall heapster response: %v", err)
|
||||
}
|
||||
versionedMetrics.Items = []metricsv1alpha1api.PodMetrics{singleMetric}
|
||||
}
|
||||
metrics := &metricsapi.PodMetricsList{}
|
||||
err = metricsv1alpha1api.Convert_v1alpha1_PodMetricsList_To_metrics_PodMetricsList(&versionedMetrics, metrics, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return metrics, nil
|
||||
}
|
||||
|
||||
func GetHeapsterMetrics(cli *HeapsterMetricsClient, path string, params map[string]string) ([]byte, error) {
|
||||
return cli.SVCClient.Services(cli.HeapsterNamespace).
|
||||
ProxyGet(cli.HeapsterScheme, cli.HeapsterService, cli.HeapsterPort, path, params).
|
||||
DoRaw()
|
||||
}
|
|
@ -0,0 +1,323 @@
|
|||
/*
|
||||
Copyright 2016 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 metricsutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/kubectl/pkg/util/printers"
|
||||
metricsapi "k8s.io/metrics/pkg/apis/metrics"
|
||||
)
|
||||
|
||||
var (
|
||||
MeasuredResources = []v1.ResourceName{
|
||||
v1.ResourceCPU,
|
||||
v1.ResourceMemory,
|
||||
}
|
||||
NodeColumns = []string{"NAME", "CPU(cores)", "CPU%", "MEMORY(bytes)", "MEMORY%"}
|
||||
PodColumns = []string{"NAME", "CPU(cores)", "MEMORY(bytes)"}
|
||||
NamespaceColumn = "NAMESPACE"
|
||||
PodColumn = "POD"
|
||||
)
|
||||
|
||||
type ResourceMetricsInfo struct {
|
||||
Name string
|
||||
Metrics v1.ResourceList
|
||||
Available v1.ResourceList
|
||||
}
|
||||
|
||||
type TopCmdPrinter struct {
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
func NewTopCmdPrinter(out io.Writer) *TopCmdPrinter {
|
||||
return &TopCmdPrinter{out: out}
|
||||
}
|
||||
|
||||
type NodeMetricsSorter struct {
|
||||
metrics []metricsapi.NodeMetrics
|
||||
sortBy string
|
||||
usages []v1.ResourceList
|
||||
}
|
||||
|
||||
func (n *NodeMetricsSorter) Len() int {
|
||||
return len(n.metrics)
|
||||
}
|
||||
|
||||
func (n *NodeMetricsSorter) Swap(i, j int) {
|
||||
n.metrics[i], n.metrics[j] = n.metrics[j], n.metrics[i]
|
||||
}
|
||||
|
||||
func (n *NodeMetricsSorter) Less(i, j int) bool {
|
||||
switch n.sortBy {
|
||||
case "cpu":
|
||||
qi := n.usages[i][v1.ResourceCPU]
|
||||
qj := n.usages[j][v1.ResourceCPU]
|
||||
return qi.Value() > qj.Value()
|
||||
case "memory":
|
||||
qi := n.usages[i][v1.ResourceMemory]
|
||||
qj := n.usages[j][v1.ResourceMemory]
|
||||
return qi.Value() > qj.Value()
|
||||
default:
|
||||
return n.metrics[i].Name < n.metrics[j].Name
|
||||
}
|
||||
}
|
||||
|
||||
func NewNodeMetricsSorter(metrics []metricsapi.NodeMetrics, sortBy string) (*NodeMetricsSorter, error) {
|
||||
var usages = make([]v1.ResourceList, len(metrics))
|
||||
if len(sortBy) > 0 {
|
||||
for i, v := range metrics {
|
||||
if err := scheme.Scheme.Convert(&v.Usage, &usages[i], nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &NodeMetricsSorter{
|
||||
metrics: metrics,
|
||||
sortBy: sortBy,
|
||||
usages: usages,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type PodMetricsSorter struct {
|
||||
metrics []metricsapi.PodMetrics
|
||||
sortBy string
|
||||
withNamespace bool
|
||||
podMetrics []v1.ResourceList
|
||||
}
|
||||
|
||||
func (p *PodMetricsSorter) Len() int {
|
||||
return len(p.metrics)
|
||||
}
|
||||
|
||||
func (p *PodMetricsSorter) Swap(i, j int) {
|
||||
p.metrics[i], p.metrics[j] = p.metrics[j], p.metrics[i]
|
||||
}
|
||||
|
||||
func (p *PodMetricsSorter) Less(i, j int) bool {
|
||||
switch p.sortBy {
|
||||
case "cpu":
|
||||
qi := p.podMetrics[i][v1.ResourceCPU]
|
||||
qj := p.podMetrics[j][v1.ResourceCPU]
|
||||
return qi.Value() > qj.Value()
|
||||
case "memory":
|
||||
qi := p.podMetrics[i][v1.ResourceMemory]
|
||||
qj := p.podMetrics[j][v1.ResourceMemory]
|
||||
return qi.Value() > qj.Value()
|
||||
default:
|
||||
if p.withNamespace && p.metrics[i].Namespace != p.metrics[j].Namespace {
|
||||
return p.metrics[i].Namespace < p.metrics[j].Namespace
|
||||
}
|
||||
return p.metrics[i].Name < p.metrics[j].Name
|
||||
}
|
||||
}
|
||||
|
||||
func NewPodMetricsSorter(metrics []metricsapi.PodMetrics, printContainers bool, withNamespace bool, sortBy string) (*PodMetricsSorter, error) {
|
||||
var podMetrics = make([]v1.ResourceList, len(metrics))
|
||||
if len(sortBy) > 0 {
|
||||
for i, v := range metrics {
|
||||
podMetrics[i], _, _ = getPodMetrics(&v, printContainers)
|
||||
}
|
||||
}
|
||||
|
||||
return &PodMetricsSorter{
|
||||
metrics: metrics,
|
||||
sortBy: sortBy,
|
||||
withNamespace: withNamespace,
|
||||
podMetrics: podMetrics,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (printer *TopCmdPrinter) PrintNodeMetrics(metrics []metricsapi.NodeMetrics, availableResources map[string]v1.ResourceList, noHeaders bool, sortBy string) error {
|
||||
if len(metrics) == 0 {
|
||||
return nil
|
||||
}
|
||||
w := printers.GetNewTabWriter(printer.out)
|
||||
defer w.Flush()
|
||||
|
||||
n, err := NewNodeMetricsSorter(metrics, sortBy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sort.Sort(n)
|
||||
|
||||
if !noHeaders {
|
||||
printColumnNames(w, NodeColumns)
|
||||
}
|
||||
var usage v1.ResourceList
|
||||
for _, m := range metrics {
|
||||
err := scheme.Scheme.Convert(&m.Usage, &usage, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printMetricsLine(w, &ResourceMetricsInfo{
|
||||
Name: m.Name,
|
||||
Metrics: usage,
|
||||
Available: availableResources[m.Name],
|
||||
})
|
||||
delete(availableResources, m.Name)
|
||||
}
|
||||
|
||||
// print lines for nodes of which the metrics is unreachable.
|
||||
for nodeName := range availableResources {
|
||||
printMissingMetricsNodeLine(w, nodeName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (printer *TopCmdPrinter) PrintPodMetrics(metrics []metricsapi.PodMetrics, printContainers bool, withNamespace bool, noHeaders bool, sortBy string) error {
|
||||
if len(metrics) == 0 {
|
||||
return nil
|
||||
}
|
||||
w := printers.GetNewTabWriter(printer.out)
|
||||
defer w.Flush()
|
||||
if !noHeaders {
|
||||
if withNamespace {
|
||||
printValue(w, NamespaceColumn)
|
||||
}
|
||||
if printContainers {
|
||||
printValue(w, PodColumn)
|
||||
}
|
||||
printColumnNames(w, PodColumns)
|
||||
}
|
||||
|
||||
p, err := NewPodMetricsSorter(metrics, printContainers, withNamespace, sortBy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sort.Sort(p)
|
||||
|
||||
for _, m := range metrics {
|
||||
err := printSinglePodMetrics(w, &m, printContainers, withNamespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func printColumnNames(out io.Writer, names []string) {
|
||||
for _, name := range names {
|
||||
printValue(out, name)
|
||||
}
|
||||
fmt.Fprint(out, "\n")
|
||||
}
|
||||
|
||||
func printSinglePodMetrics(out io.Writer, m *metricsapi.PodMetrics, printContainersOnly bool, withNamespace bool) error {
|
||||
podMetrics, containers, err := getPodMetrics(m, printContainersOnly)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if printContainersOnly {
|
||||
for contName := range containers {
|
||||
if withNamespace {
|
||||
printValue(out, m.Namespace)
|
||||
}
|
||||
printValue(out, m.Name)
|
||||
printMetricsLine(out, &ResourceMetricsInfo{
|
||||
Name: contName,
|
||||
Metrics: containers[contName],
|
||||
Available: v1.ResourceList{},
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if withNamespace {
|
||||
printValue(out, m.Namespace)
|
||||
}
|
||||
printMetricsLine(out, &ResourceMetricsInfo{
|
||||
Name: m.Name,
|
||||
Metrics: podMetrics,
|
||||
Available: v1.ResourceList{},
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getPodMetrics(m *metricsapi.PodMetrics, printContainersOnly bool) (v1.ResourceList, map[string]v1.ResourceList, error) {
|
||||
containers := make(map[string]v1.ResourceList)
|
||||
podMetrics := make(v1.ResourceList)
|
||||
for _, res := range MeasuredResources {
|
||||
podMetrics[res], _ = resource.ParseQuantity("0")
|
||||
}
|
||||
|
||||
for _, c := range m.Containers {
|
||||
var usage v1.ResourceList
|
||||
if err := scheme.Scheme.Convert(&c.Usage, &usage, nil); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
containers[c.Name] = usage
|
||||
if !printContainersOnly {
|
||||
for _, res := range MeasuredResources {
|
||||
quantity := podMetrics[res]
|
||||
quantity.Add(usage[res])
|
||||
podMetrics[res] = quantity
|
||||
}
|
||||
}
|
||||
}
|
||||
return podMetrics, containers, nil
|
||||
}
|
||||
|
||||
func printMetricsLine(out io.Writer, metrics *ResourceMetricsInfo) {
|
||||
printValue(out, metrics.Name)
|
||||
printAllResourceUsages(out, metrics)
|
||||
fmt.Fprint(out, "\n")
|
||||
}
|
||||
|
||||
func printMissingMetricsNodeLine(out io.Writer, nodeName string) {
|
||||
printValue(out, nodeName)
|
||||
unknownMetricsStatus := "<unknown>"
|
||||
for i := 0; i < len(MeasuredResources); i++ {
|
||||
printValue(out, unknownMetricsStatus)
|
||||
printValue(out, "\t")
|
||||
printValue(out, unknownMetricsStatus)
|
||||
printValue(out, "\t")
|
||||
}
|
||||
fmt.Fprint(out, "\n")
|
||||
}
|
||||
|
||||
func printValue(out io.Writer, value interface{}) {
|
||||
fmt.Fprintf(out, "%v\t", value)
|
||||
}
|
||||
|
||||
func printAllResourceUsages(out io.Writer, metrics *ResourceMetricsInfo) {
|
||||
for _, res := range MeasuredResources {
|
||||
quantity := metrics.Metrics[res]
|
||||
printSingleResourceUsage(out, res, quantity)
|
||||
fmt.Fprint(out, "\t")
|
||||
if available, found := metrics.Available[res]; found {
|
||||
fraction := float64(quantity.MilliValue()) / float64(available.MilliValue()) * 100
|
||||
fmt.Fprintf(out, "%d%%\t", int64(fraction))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printSingleResourceUsage(out io.Writer, resourceType v1.ResourceName, quantity resource.Quantity) {
|
||||
switch resourceType {
|
||||
case v1.ResourceCPU:
|
||||
fmt.Fprintf(out, "%vm", quantity.MilliValue())
|
||||
case v1.ResourceMemory:
|
||||
fmt.Fprintf(out, "%vMi", quantity.Value()/(1024*1024))
|
||||
default:
|
||||
fmt.Fprintf(out, "%v", quantity.Value())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue