blob: 59edb1755f606c7b3790cf4a9d8a8984649ba2a2 [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001// Copyright 2018 The Kubernetes Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package podmetrics
16
17import (
18 "context"
19 "fmt"
20 "time"
21
22 "github.com/golang/glog"
23
24 "github.com/kubernetes-incubator/metrics-server/pkg/provider"
25 "k8s.io/api/core/v1"
26 "k8s.io/apimachinery/pkg/api/errors"
27 metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 "k8s.io/apimachinery/pkg/labels"
30 "k8s.io/apimachinery/pkg/runtime"
31 "k8s.io/apimachinery/pkg/runtime/schema"
32 apitypes "k8s.io/apimachinery/pkg/types"
33 genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
34 "k8s.io/apiserver/pkg/registry/rest"
35 v1listers "k8s.io/client-go/listers/core/v1"
36 "k8s.io/metrics/pkg/apis/metrics"
37 _ "k8s.io/metrics/pkg/apis/metrics/install"
38)
39
40type MetricStorage struct {
41 groupResource schema.GroupResource
42 prov provider.PodMetricsProvider
43 podLister v1listers.PodLister
44}
45
46var _ rest.KindProvider = &MetricStorage{}
47var _ rest.Storage = &MetricStorage{}
48var _ rest.Getter = &MetricStorage{}
49var _ rest.Lister = &MetricStorage{}
50
51func NewStorage(groupResource schema.GroupResource, prov provider.PodMetricsProvider, podLister v1listers.PodLister) *MetricStorage {
52 return &MetricStorage{
53 groupResource: groupResource,
54 prov: prov,
55 podLister: podLister,
56 }
57}
58
59// Storage interface
60func (m *MetricStorage) New() runtime.Object {
61 return &metrics.PodMetrics{}
62}
63
64// KindProvider interface
65func (m *MetricStorage) Kind() string {
66 return "PodMetrics"
67}
68
69// Lister interface
70func (m *MetricStorage) NewList() runtime.Object {
71 return &metrics.PodMetricsList{}
72}
73
74// Lister interface
75func (m *MetricStorage) List(ctx context.Context, options *metainternalversion.ListOptions) (runtime.Object, error) {
76 labelSelector := labels.Everything()
77 if options != nil && options.LabelSelector != nil {
78 labelSelector = options.LabelSelector
79 }
80 namespace := genericapirequest.NamespaceValue(ctx)
81 pods, err := m.podLister.Pods(namespace).List(labelSelector)
82 if err != nil {
83 errMsg := fmt.Errorf("Error while listing pods for selector %v in namespace %q: %v", labelSelector, namespace, err)
84 glog.Error(errMsg)
85 return &metrics.PodMetricsList{}, errMsg
86 }
87
88 metricsItems, err := m.getPodMetrics(pods...)
89 if err != nil {
90 errMsg := fmt.Errorf("Error while fetching pod metrics for selector %v in namespace %q: %v", labelSelector, namespace, err)
91 glog.Error(errMsg)
92 return &metrics.PodMetricsList{}, errMsg
93 }
94
95 return &metrics.PodMetricsList{Items: metricsItems}, nil
96}
97
98// Getter interface
99func (m *MetricStorage) Get(ctx context.Context, name string, opts *metav1.GetOptions) (runtime.Object, error) {
100 namespace := genericapirequest.NamespaceValue(ctx)
101
102 pod, err := m.podLister.Pods(namespace).Get(name)
103 if err != nil {
104 errMsg := fmt.Errorf("Error while getting pod %v: %v", name, err)
105 glog.Error(errMsg)
106 if errors.IsNotFound(err) {
107 // return not-found errors directly
108 return &metrics.PodMetrics{}, err
109 }
110 return &metrics.PodMetrics{}, errMsg
111 }
112 if pod == nil {
113 return &metrics.PodMetrics{}, errors.NewNotFound(v1.Resource("pods"), fmt.Sprintf("%v/%v", namespace, name))
114 }
115
116 podMetrics, err := m.getPodMetrics(pod)
117 if err == nil && len(podMetrics) == 0 {
118 err = fmt.Errorf("no metrics known for pod \"%s/%s\"", pod.Namespace, pod.Name)
119 }
120 if err != nil {
121 glog.Errorf("unable to fetch pod metrics for pod %s/%s: %v", pod.Namespace, pod.Name, err)
122 return nil, errors.NewNotFound(m.groupResource, fmt.Sprintf("%v/%v", namespace, name))
123 }
124 return &podMetrics[0], nil
125}
126
127func (m *MetricStorage) getPodMetrics(pods ...*v1.Pod) ([]metrics.PodMetrics, error) {
128 namespacedNames := make([]apitypes.NamespacedName, len(pods))
129 for i, pod := range pods {
130 namespacedNames[i] = apitypes.NamespacedName{
131 Name: pod.Name,
132 Namespace: pod.Namespace,
133 }
134 }
135 timestamps, containerMetrics, err := m.prov.GetContainerMetrics(namespacedNames...)
136 if err != nil {
137 return nil, err
138 }
139
140 res := make([]metrics.PodMetrics, 0, len(pods))
141
142 for i, pod := range pods {
143 if pod.Status.Phase != v1.PodRunning {
144 // ignore pod not in Running phase
145 continue
146 }
147 if containerMetrics[i] == nil {
148 glog.Errorf("unable to fetch pod metrics for pod %s/%s: no metrics known for pod", pod.Namespace, pod.Name)
149 continue
150 }
151
152 res = append(res, metrics.PodMetrics{
153 ObjectMeta: metav1.ObjectMeta{
154 Name: pod.Name,
155 Namespace: pod.Namespace,
156 CreationTimestamp: metav1.NewTime(time.Now()),
157 },
158 Timestamp: metav1.NewTime(timestamps[i].Timestamp),
159 Window: metav1.Duration{Duration: timestamps[i].Window},
160 Containers: containerMetrics[i],
161 })
162 }
163 return res, nil
164}
165
166func (m *MetricStorage) NamespaceScoped() bool {
167 return true
168}