monitoring.go 11.4 KB
Newer Older
G
Guangzhe Huang 已提交
1
/*
H
hongming 已提交
2
Copyright 2019 The KubeSphere Authors.
G
Guangzhe Huang 已提交
3

H
hongming 已提交
4 5 6
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
G
Guangzhe Huang 已提交
7

H
hongming 已提交
8
    http://www.apache.org/licenses/LICENSE-2.0
G
Guangzhe Huang 已提交
9

H
hongming 已提交
10 11 12 13 14
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.
G
Guangzhe Huang 已提交
15 16 17 18 19
*/

package monitoring

import (
H
hongming 已提交
20
	"context"
H
huanggze 已提交
21 22 23
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/client-go/kubernetes"
H
huanggze 已提交
24
	"k8s.io/klog"
H
huanggze 已提交
25 26 27
	ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
	"kubesphere.io/kubesphere/pkg/constants"
	"kubesphere.io/kubesphere/pkg/informers"
H
huanggze 已提交
28
	"kubesphere.io/kubesphere/pkg/models/monitoring/expressions"
H
huanggze 已提交
29 30
	"kubesphere.io/kubesphere/pkg/models/openpitrix"
	"kubesphere.io/kubesphere/pkg/server/params"
G
Guangzhe Huang 已提交
31
	"kubesphere.io/kubesphere/pkg/simple/client/monitoring"
H
huanggze 已提交
32
	opclient "kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
G
Guangzhe Huang 已提交
33 34 35 36
	"time"
)

type MonitoringOperator interface {
H
huanggze 已提交
37 38
	GetMetric(expr, namespace string, time time.Time) (monitoring.Metric, error)
	GetMetricOverTime(expr, namespace string, start, end time.Time, step time.Duration) (monitoring.Metric, error)
Z
zryfish 已提交
39 40
	GetNamedMetrics(metrics []string, time time.Time, opt monitoring.QueryOption) Metrics
	GetNamedMetricsOverTime(metrics []string, start, end time.Time, step time.Duration, opt monitoring.QueryOption) Metrics
H
huanggze 已提交
41
	GetMetadata(namespace string) Metadata
H
huanggze 已提交
42
	GetMetricLabelSet(metric, namespace string, start, end time.Time) MetricLabelSet
H
huanggze 已提交
43

H
huanggze 已提交
44
	// TODO: expose KubeSphere self metrics in Prometheus format
H
huanggze 已提交
45 46
	GetKubeSphereStats() Metrics
	GetWorkspaceStats(workspace string) Metrics
G
Guangzhe Huang 已提交
47 48 49
}

type monitoringOperator struct {
R
root 已提交
50 51 52 53 54
	prometheus    monitoring.Interface
	metricsserver monitoring.Interface
	k8s           kubernetes.Interface
	ks            ksinformers.SharedInformerFactory
	op            openpitrix.Interface
G
Guangzhe Huang 已提交
55 56
}

57
func NewMonitoringOperator(monitoringClient monitoring.Interface, metricsClient monitoring.Interface, k8s kubernetes.Interface, factory informers.InformerFactory, opClient opclient.Client) MonitoringOperator {
H
huanggze 已提交
58
	return &monitoringOperator{
59
		prometheus:    monitoringClient,
R
root 已提交
60 61 62 63
		metricsserver: metricsClient,
		k8s:           k8s,
		ks:            factory.KubeSphereSharedInformerFactory(),
		op:            openpitrix.NewOpenpitrixOperator(factory.KubernetesSharedInformerFactory(), opClient),
H
huanggze 已提交
64
	}
G
Guangzhe Huang 已提交
65 66
}

H
huanggze 已提交
67
func (mo monitoringOperator) GetMetric(expr, namespace string, time time.Time) (monitoring.Metric, error) {
J
junotx 已提交
68 69 70 71 72 73 74 75 76 77
	if namespace != "" {
		// Different monitoring backend implementations have different ways to enforce namespace isolation.
		// Each implementation should register itself to `ReplaceNamespaceFns` during init().
		// We hard code "prometheus" here because we only support this datasource so far.
		// In the future, maybe the value should be returned from a method like `mo.c.GetMonitoringServiceName()`.
		var err error
		expr, err = expressions.ReplaceNamespaceFns["prometheus"](expr, namespace)
		if err != nil {
			return monitoring.Metric{}, err
		}
H
huanggze 已提交
78
	}
R
root 已提交
79
	return mo.prometheus.GetMetric(expr, time), nil
G
Guangzhe Huang 已提交
80 81
}

H
huanggze 已提交
82
func (mo monitoringOperator) GetMetricOverTime(expr, namespace string, start, end time.Time, step time.Duration) (monitoring.Metric, error) {
J
junotx 已提交
83 84 85 86 87 88 89 90 91 92
	if namespace != "" {
		// Different monitoring backend implementations have different ways to enforce namespace isolation.
		// Each implementation should register itself to `ReplaceNamespaceFns` during init().
		// We hard code "prometheus" here because we only support this datasource so far.
		// In the future, maybe the value should be returned from a method like `mo.c.GetMonitoringServiceName()`.
		var err error
		expr, err = expressions.ReplaceNamespaceFns["prometheus"](expr, namespace)
		if err != nil {
			return monitoring.Metric{}, err
		}
H
huanggze 已提交
93
	}
R
root 已提交
94
	return mo.prometheus.GetMetricOverTime(expr, start, end, step), nil
G
Guangzhe Huang 已提交
95 96
}

Z
zryfish 已提交
97
func (mo monitoringOperator) GetNamedMetrics(metrics []string, time time.Time, opt monitoring.QueryOption) Metrics {
R
root 已提交
98 99
	ress := mo.prometheus.GetNamedMetrics(metrics, time, opt)

100 101 102 103 104 105 106 107
	if mo.metricsserver != nil {
		mr := mo.metricsserver.GetNamedMetrics(metrics, time, opt)

		//Merge edge node metrics data
		edgeMetrics := make(map[string]monitoring.MetricData)
		for _, metric := range mr {
			edgeMetrics[metric.MetricName] = metric.MetricData
		}
R
root 已提交
108

109 110 111 112
		for i, metric := range ress {
			if val, ok := edgeMetrics[metric.MetricName]; ok {
				ress[i].MetricData.MetricValues = append(ress[i].MetricData.MetricValues, val.MetricValues...)
			}
R
root 已提交
113 114 115
		}
	}

Z
zryfish 已提交
116
	return Metrics{Results: ress}
G
Guangzhe Huang 已提交
117 118
}

Z
zryfish 已提交
119
func (mo monitoringOperator) GetNamedMetricsOverTime(metrics []string, start, end time.Time, step time.Duration, opt monitoring.QueryOption) Metrics {
R
root 已提交
120 121
	ress := mo.prometheus.GetNamedMetricsOverTime(metrics, start, end, step, opt)

122 123 124 125 126 127 128 129
	if mo.metricsserver != nil {
		mr := mo.metricsserver.GetNamedMetricsOverTime(metrics, start, end, step, opt)

		//Merge edge node metrics data
		edgeMetrics := make(map[string]monitoring.MetricData)
		for _, metric := range mr {
			edgeMetrics[metric.MetricName] = metric.MetricData
		}
R
root 已提交
130

131 132 133 134
		for i, metric := range ress {
			if val, ok := edgeMetrics[metric.MetricName]; ok {
				ress[i].MetricData.MetricValues = append(ress[i].MetricData.MetricValues, val.MetricValues...)
			}
R
root 已提交
135 136 137
		}
	}

Z
zryfish 已提交
138
	return Metrics{Results: ress}
G
Guangzhe Huang 已提交
139
}
H
huanggze 已提交
140 141

func (mo monitoringOperator) GetMetadata(namespace string) Metadata {
R
root 已提交
142
	data := mo.prometheus.GetMetadata(namespace)
H
huanggze 已提交
143 144
	return Metadata{Data: data}
}
H
huanggze 已提交
145 146

func (mo monitoringOperator) GetMetricLabelSet(metric, namespace string, start, end time.Time) MetricLabelSet {
J
junotx 已提交
147 148 149 150 151 152 153 154 155 156 157 158
	var expr = metric
	var err error
	if namespace != "" {
		// Different monitoring backend implementations have different ways to enforce namespace isolation.
		// Each implementation should register itself to `ReplaceNamespaceFns` during init().
		// We hard code "prometheus" here because we only support this datasource so far.
		// In the future, maybe the value should be returned from a method like `mo.c.GetMonitoringServiceName()`.
		expr, err = expressions.ReplaceNamespaceFns["prometheus"](metric, namespace)
		if err != nil {
			klog.Error(err)
			return MetricLabelSet{}
		}
H
huanggze 已提交
159
	}
R
root 已提交
160
	data := mo.prometheus.GetMetricLabelSet(expr, start, end)
H
huanggze 已提交
161 162
	return MetricLabelSet{Data: data}
}
H
huanggze 已提交
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191

func (mo monitoringOperator) GetKubeSphereStats() Metrics {
	var res Metrics
	now := float64(time.Now().Unix())

	clusterList, err := mo.ks.Cluster().V1alpha1().Clusters().Lister().List(labels.Everything())
	clusterTotal := len(clusterList)
	if clusterTotal == 0 {
		clusterTotal = 1
	}
	if err != nil {
		res.Results = append(res.Results, monitoring.Metric{
			MetricName: KubeSphereClusterCount,
			Error:      err.Error(),
		})
	} else {
		res.Results = append(res.Results, monitoring.Metric{
			MetricName: KubeSphereClusterCount,
			MetricData: monitoring.MetricData{
				MetricType: monitoring.MetricTypeVector,
				MetricValues: []monitoring.MetricValue{
					{
						Sample: &monitoring.Point{now, float64(clusterTotal)},
					},
				},
			},
		})
	}

192
	wkList, err := mo.ks.Tenant().V1alpha2().WorkspaceTemplates().Lister().List(labels.Everything())
H
huanggze 已提交
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
	if err != nil {
		res.Results = append(res.Results, monitoring.Metric{
			MetricName: KubeSphereWorkspaceCount,
			Error:      err.Error(),
		})
	} else {
		res.Results = append(res.Results, monitoring.Metric{
			MetricName: KubeSphereWorkspaceCount,
			MetricData: monitoring.MetricData{
				MetricType: monitoring.MetricTypeVector,
				MetricValues: []monitoring.MetricValue{
					{
						Sample: &monitoring.Point{now, float64(len(wkList))},
					},
				},
			},
		})
	}

	usrList, err := mo.ks.Iam().V1alpha2().Users().Lister().List(labels.Everything())
	if err != nil {
		res.Results = append(res.Results, monitoring.Metric{
			MetricName: KubeSphereUserCount,
			Error:      err.Error(),
		})
	} else {
		res.Results = append(res.Results, monitoring.Metric{
			MetricName: KubeSphereUserCount,
			MetricData: monitoring.MetricData{
				MetricType: monitoring.MetricTypeVector,
				MetricValues: []monitoring.MetricValue{
					{
						Sample: &monitoring.Point{now, float64(len(usrList))},
					},
				},
			},
		})
	}

232 233 234 235 236 237
	cond := &params.Conditions{
		Match: map[string]string{
			openpitrix.Status: openpitrix.StatusActive,
			openpitrix.RepoId: openpitrix.BuiltinRepoId,
		},
	}
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
	if mo.op != nil {
		tmpl, err := mo.op.ListApps(cond, "", false, 0, 0)
		if err != nil {
			res.Results = append(res.Results, monitoring.Metric{
				MetricName: KubeSphereAppTmplCount,
				Error:      err.Error(),
			})
		} else {
			res.Results = append(res.Results, monitoring.Metric{
				MetricName: KubeSphereAppTmplCount,
				MetricData: monitoring.MetricData{
					MetricType: monitoring.MetricTypeVector,
					MetricValues: []monitoring.MetricValue{
						{
							Sample: &monitoring.Point{now, float64(tmpl.TotalCount)},
						},
H
huanggze 已提交
254 255
					},
				},
256 257
			})
		}
H
huanggze 已提交
258 259 260 261 262 263 264 265 266 267 268 269
	}

	return res
}

func (mo monitoringOperator) GetWorkspaceStats(workspace string) Metrics {
	var res Metrics
	now := float64(time.Now().Unix())

	selector := labels.SelectorFromSet(labels.Set{constants.WorkspaceLabelKey: workspace})
	opt := metav1.ListOptions{LabelSelector: selector.String()}

H
hongming 已提交
270
	nsList, err := mo.k8s.CoreV1().Namespaces().List(context.Background(), opt)
H
huanggze 已提交
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
	if err != nil {
		res.Results = append(res.Results, monitoring.Metric{
			MetricName: WorkspaceNamespaceCount,
			Error:      err.Error(),
		})
	} else {
		res.Results = append(res.Results, monitoring.Metric{
			MetricName: WorkspaceNamespaceCount,
			MetricData: monitoring.MetricData{
				MetricType: monitoring.MetricTypeVector,
				MetricValues: []monitoring.MetricValue{
					{
						Sample: &monitoring.Point{now, float64(len(nsList.Items))},
					},
				},
			},
		})
	}

	devopsList, err := mo.ks.Devops().V1alpha3().DevOpsProjects().Lister().List(selector)
	if err != nil {
		res.Results = append(res.Results, monitoring.Metric{
			MetricName: WorkspaceDevopsCount,
			Error:      err.Error(),
		})
	} else {
		res.Results = append(res.Results, monitoring.Metric{
			MetricName: WorkspaceDevopsCount,
			MetricData: monitoring.MetricData{
				MetricType: monitoring.MetricTypeVector,
				MetricValues: []monitoring.MetricValue{
					{
						Sample: &monitoring.Point{now, float64(len(devopsList))},
					},
				},
			},
		})
	}

	memberList, err := mo.ks.Iam().V1alpha2().WorkspaceRoleBindings().Lister().List(selector)
	if err != nil {
		res.Results = append(res.Results, monitoring.Metric{
			MetricName: WorkspaceMemberCount,
			Error:      err.Error(),
		})
	} else {
		res.Results = append(res.Results, monitoring.Metric{
			MetricName: WorkspaceMemberCount,
			MetricData: monitoring.MetricData{
				MetricType: monitoring.MetricTypeVector,
				MetricValues: []monitoring.MetricValue{
					{
						Sample: &monitoring.Point{now, float64(len(memberList))},
					},
				},
			},
		})
	}

	roleList, err := mo.ks.Iam().V1alpha2().WorkspaceRoles().Lister().List(selector)
	if err != nil {
		res.Results = append(res.Results, monitoring.Metric{
			MetricName: WorkspaceRoleCount,
			Error:      err.Error(),
		})
	} else {
		res.Results = append(res.Results, monitoring.Metric{
			MetricName: WorkspaceRoleCount,
			MetricData: monitoring.MetricData{
				MetricType: monitoring.MetricTypeVector,
				MetricValues: []monitoring.MetricValue{
					{
						Sample: &monitoring.Point{now, float64(len(roleList))},
					},
				},
			},
		})
	}

	return res
}