From cf3553c8f20efcd86085a8490410a40c9385b098 Mon Sep 17 00:00:00 2001 From: zyy17 Date: Wed, 6 Nov 2024 16:52:47 +0800 Subject: [PATCH] feat: support to get pod metrics in operator apiserver --- cmd/operator/app/command.go | 16 +++- cmd/operator/app/options/options.go | 11 ++- go.mod | 11 +-- go.sum | 32 ++++--- pkg/apiserver/apiserver.go | 126 +++++++++++++++++++++------- 5 files changed, 136 insertions(+), 60 deletions(-) diff --git a/cmd/operator/app/command.go b/cmd/operator/app/command.go index d41035e..15df11b 100644 --- a/cmd/operator/app/command.go +++ b/cmd/operator/app/command.go @@ -26,6 +26,7 @@ import ( clientgoscheme "k8s.io/client-go/kubernetes/scheme" "k8s.io/klog/v2" "k8s.io/klog/v2/klogr" + podmetricsv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/healthz" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" @@ -47,10 +48,20 @@ var ( ) func init() { + // Add Kubernetes client-go scheme. utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + + // Add Kubernetes API extensions. + utilruntime.Must(apiextensionsv1.AddToScheme(scheme)) + + // Add GreptimeDB CRD. utilruntime.Must(v1alpha1.AddToScheme(scheme)) + + // Add prometheus-operator's CRDs for monitoring(PodMonitor and ServiceMonitor). utilruntime.Must(monitoringv1.AddToScheme(scheme)) - utilruntime.Must(apiextensionsv1.AddToScheme(scheme)) + + // Add [PodMetrics](https://github.com/kubernetes/metrics/blob/master/pkg/apis/metrics/v1beta1/types.go) for fetching PodMetrics from metrics-server. + utilruntime.Must(podmetricsv1beta1.AddToScheme(scheme)) // +kubebuilder:scaffold:scheme } @@ -102,7 +113,8 @@ func NewOperatorCommand() *cobra.Command { if o.EnableAPIServer { server := apiserver.NewServer(mgr.GetClient(), &apiserver.Options{ - Port: o.APIServerPort, + Port: o.APIServerPort, + EnablePodMetrics: o.EnablePodMetrics, }) go func() { diff --git a/cmd/operator/app/options/options.go b/cmd/operator/app/options/options.go index d0dbf05..a4e155c 100644 --- a/cmd/operator/app/options/options.go +++ b/cmd/operator/app/options/options.go @@ -30,14 +30,16 @@ type Options struct { EnableLeaderElection bool EnableAPIServer bool APIServerPort int32 + EnablePodMetrics bool } func NewDefaultOptions() *Options { return &Options{ - MetricsAddr: defaultMetricsAddr, - HealthProbeAddr: defaultHealthProbeAddr, - APIServerPort: defaultAPIServerPort, - EnableAPIServer: false, + MetricsAddr: defaultMetricsAddr, + HealthProbeAddr: defaultHealthProbeAddr, + APIServerPort: defaultAPIServerPort, + EnableAPIServer: false, + EnablePodMetrics: false, } } @@ -47,4 +49,5 @@ func (o *Options) AddFlags(fs *pflag.FlagSet) { fs.BoolVar(&o.EnableLeaderElection, "enable-leader-election", o.EnableLeaderElection, "Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.") fs.BoolVar(&o.EnableAPIServer, "enable-apiserver", o.EnableAPIServer, "Enable API server for GreptimeDB operator.") fs.Int32Var(&o.APIServerPort, "apiserver-port", o.APIServerPort, "The port the API server binds to.") + fs.BoolVar(&o.EnablePodMetrics, "enable-pod-metrics", o.EnablePodMetrics, "Enable fetching PodMetrics from metrics-server.") } diff --git a/go.mod b/go.mod index 422d8d7..4139977 100644 --- a/go.mod +++ b/go.mod @@ -17,11 +17,12 @@ require ( github.com/spf13/pflag v1.0.5 go.etcd.io/etcd/client/v3 v3.5.9 google.golang.org/grpc v1.56.3 - k8s.io/api v0.28.3 + k8s.io/api v0.28.13 k8s.io/apiextensions-apiserver v0.28.3 - k8s.io/apimachinery v0.28.3 - k8s.io/client-go v0.28.3 + k8s.io/apimachinery v0.28.13 + k8s.io/client-go v0.28.13 k8s.io/klog/v2 v2.100.1 + k8s.io/metrics v0.28.13 k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 sigs.k8s.io/controller-runtime v0.16.4 sigs.k8s.io/yaml v1.3.0 @@ -54,7 +55,7 @@ require ( github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect @@ -95,7 +96,7 @@ require ( golang.org/x/term v0.20.0 // indirect golang.org/x/text v0.15.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.9.3 // indirect + golang.org/x/tools v0.16.1 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 // indirect diff --git a/go.sum b/go.sum index e89c8ae..69f223d 100644 --- a/go.sum +++ b/go.sum @@ -73,12 +73,10 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -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/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 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.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -218,8 +216,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= @@ -236,8 +232,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -266,8 +262,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= -golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= +golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -284,8 +280,6 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1: google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -303,20 +297,22 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM= -k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc= +k8s.io/api v0.28.13 h1:0Sw8MjdkmrJAF/uVv09HXSZ3cQauVyZHQWKt8hiiKo4= +k8s.io/api v0.28.13/go.mod h1:7hlRF5wArzXf0qbRRT2TMtHRa5SHBEVJhA02JpTxj9Q= k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08= k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc= -k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A= -k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8= -k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4= -k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo= +k8s.io/apimachinery v0.28.13 h1:0O2mk2i0Yi+xkron0lK//biI21F1eGXb4eXECLU5v7g= +k8s.io/apimachinery v0.28.13/go.mod h1:zUG757HaKs6Dc3iGtKjzIpBfqTM4yiRsEe3/E7NX15o= +k8s.io/client-go v0.28.13 h1:kHgFOxWwAsa8VxL6Oylo10V6euobub9Jo0wyEWrhrWk= +k8s.io/client-go v0.28.13/go.mod h1:IudvInbWfd+6WLreEVnBnZJCGFaSROCFbny9jFTkk7g= k8s.io/component-base v0.28.3 h1:rDy68eHKxq/80RiMb2Ld/tbH8uAE75JdCqJyi6lXMzI= k8s.io/component-base v0.28.3/go.mod h1:fDJ6vpVNSk6cRo5wmDa6eKIG7UlIQkaFmZN2fYgIUD8= k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= +k8s.io/metrics v0.28.13 h1:q3SbR/2b+drO1pFhQvNMWo9+VVzNkXx9zjn40RuQbXc= +k8s.io/metrics v0.28.13/go.mod h1:UKcfHy+KvTkjrUL4mz0NS7Hb872Q/ponmJw3hQoGNa8= k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index f5eab36..8155b5d 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -24,6 +24,7 @@ import ( "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/klog/v2" + podmetricsv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1" "sigs.k8s.io/controller-runtime/pkg/client" greptimev1alpha1 "github.com/GreptimeTeam/greptimedb-operator/apis/v1alpha1" @@ -40,13 +41,17 @@ const ( type Server struct { client.Client - port int32 + port int32 + enablePodMetrics bool } // Options represents the options for the Server. type Options struct { // Port is the port that the API service will listen on. Port int32 + + // EnablePodMetrics indicates whether to enable fetching PodMetrics from metrics-server. + EnablePodMetrics bool } // GreptimeDBCluster represents a GreptimeDBCluster resource that is returned by the API. @@ -97,8 +102,8 @@ type Pod struct { // Node is the name of the node where the Pod is running. Node string `json:"node"` - // Resource is the resource requirements of the Pod. - Resource corev1.ResourceRequirements `json:"resource,omitempty"` + // Resource is the resources of all containers in the Pod. + Resources []*Resource `json:"resources,omitempty"` // Status is the status of the Pod. Status string `json:"status"` @@ -107,6 +112,21 @@ type Pod struct { StartTime *metav1.Time `json:"startTime,omitempty"` } +// Resource represents the resource of a container. +type Resource struct { + // Name is the name of the container. + Name string `json:"name"` + + // Request is the resource request of the container. + Request corev1.ResourceList `json:"request,omitempty"` + + // Limit is the resource limit of the container. + Limit corev1.ResourceList `json:"limit,omitempty"` + + // Usage is the resource usage of the container. + Usage corev1.ResourceList `json:"usage,omitempty"` +} + // Response represents a response returned by the API. type Response struct { // Success indicates whether the request was successful. @@ -125,8 +145,9 @@ type Response struct { // NewServer creates a new Server with the given client and options. func NewServer(client client.Client, opts *Options) *Server { return &Server{ - Client: client, - port: opts.Port, + Client: client, + port: opts.Port, + enablePodMetrics: opts.EnablePodMetrics, } } @@ -288,46 +309,89 @@ func (s *Server) getTopology(ctx context.Context, namespace, name string) (*Grep } func (s *Server) getPods(ctx context.Context, namespace, name string, kind greptimev1alpha1.ComponentKind, topology *GreptimeDBClusterTopology) error { - var pods corev1.PodList - if err := s.List(ctx, &pods, client.InNamespace(namespace), + var internalPods corev1.PodList + if err := s.List(ctx, &internalPods, client.InNamespace(namespace), client.MatchingLabels{constant.GreptimeDBComponentName: common.ResourceName(name, kind)}); err != nil { return err } + var metrics []*podmetricsv1beta1.PodMetrics + if s.enablePodMetrics { + for _, pod := range internalPods.Items { + podMetrics, err := s.getPodMetrics(ctx, namespace, pod.Name) + if err != nil { + return err + } + metrics = append(metrics, podMetrics) + } + + if len(metrics) != len(internalPods.Items) { + return fmt.Errorf("the number of metrics is not equal to the number of pods") + } + } + + var pods []*Pod + // zip internalPods and metrics. + for i, pod := range internalPods.Items { + if s.enablePodMetrics { + pods = append(pods, s.buildPod(&pod, metrics[i])) + } else { + pods = append(pods, s.buildPod(&pod, nil)) + } + } + switch kind { case greptimev1alpha1.MetaComponentKind: - for _, pod := range pods.Items { - topology.Meta = append(topology.Meta, s.convertToPod(&pod)) - } + topology.Meta = pods case greptimev1alpha1.DatanodeComponentKind: - for _, pod := range pods.Items { - topology.Datanode = append(topology.Datanode, s.convertToPod(&pod)) - } + topology.Datanode = pods case greptimev1alpha1.FrontendComponentKind: - for _, pod := range pods.Items { - topology.Frontend = append(topology.Frontend, s.convertToPod(&pod)) - } + topology.Frontend = pods case greptimev1alpha1.FlownodeComponentKind: - for _, pod := range pods.Items { - topology.Flownode = append(topology.Flownode, s.convertToPod(&pod)) - } + topology.Flownode = pods } return nil } -func (s *Server) convertToPod(pod *corev1.Pod) *Pod { - var resources corev1.ResourceRequirements - if len(pod.Spec.Containers) > constant.MainContainerIndex { - resources = pod.Spec.Containers[constant.MainContainerIndex].Resources +func (s *Server) buildPod(internalPod *corev1.Pod, podMetrics *podmetricsv1beta1.PodMetrics) *Pod { + pod := &Pod{ + Name: internalPod.Name, + Namespace: internalPod.Namespace, + IP: internalPod.Status.PodIP, + Status: string(internalPod.Status.Phase), + Node: internalPod.Spec.NodeName, + } + + var resources []*Resource + + for _, container := range internalPod.Spec.Containers { + resources = append(resources, &Resource{ + Name: container.Name, + Request: container.Resources.Requests, + Limit: container.Resources.Limits, + }) } - return &Pod{ - Name: pod.Name, - Namespace: pod.Namespace, - IP: pod.Status.PodIP, - Status: string(pod.Status.Phase), - Node: pod.Spec.NodeName, - Resource: resources, - StartTime: pod.Status.StartTime, + + if s.enablePodMetrics && podMetrics != nil { + for _, container := range podMetrics.Containers { + for _, resource := range resources { + if resource.Name == container.Name { + resource.Usage = container.Usage + } + } + } + } + + pod.Resources = resources + + return pod +} + +func (s *Server) getPodMetrics(ctx context.Context, namespace, name string) (*podmetricsv1beta1.PodMetrics, error) { + var podMetrics podmetricsv1beta1.PodMetrics + if err := s.Get(ctx, client.ObjectKey{Namespace: namespace, Name: name}, &podMetrics); err != nil { + return nil, err } + return &podMetrics, nil }