1// Copyright 2018 Istio 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 server
16
17import (
18	"context"
19	"strconv"
20
21	"go.opencensus.io/stats"
22	"go.opencensus.io/stats/view"
23	"go.opencensus.io/tag"
24
25	kubeApiAdmission "k8s.io/api/admission/v1beta1"
26)
27
28const (
29	errorStr    = "error"
30	group       = "group"
31	version     = "version"
32	resourceTag = "resource"
33	reason      = "reason"
34	status      = "status"
35)
36
37var (
38	// ErrorTag holds the error for the context.
39	ErrorTag tag.Key
40
41	// GroupTag holds the resource group for the context.
42	GroupTag tag.Key
43
44	// VersionTag holds the resource version for the context.
45	VersionTag tag.Key
46
47	// ResourceTag holds the resource name for the context.
48	ResourceTag tag.Key
49
50	// ReasonTag holds the error reason for the context.
51	ReasonTag tag.Key
52
53	// StatusTag holds the error code for the context.
54	StatusTag tag.Key
55)
56
57var (
58	metricCertKeyUpdate = stats.Int64(
59		"galley/validation/cert_key_updates",
60		"Galley validation webhook certificate updates",
61		stats.UnitDimensionless)
62	metricCertKeyUpdateError = stats.Int64(
63		"galley/validation/cert_key_update_errors",
64		"Galley validation webhook certificate updates errors",
65		stats.UnitDimensionless)
66	metricValidationPassed = stats.Int64(
67		"galley/validation/passed",
68		"Resource is valid",
69		stats.UnitDimensionless)
70	metricValidationFailed = stats.Int64(
71		"galley/validation/failed",
72		"Resource validation failed",
73		stats.UnitDimensionless)
74	metricValidationHTTPError = stats.Int64(
75		"galley/validation/http_error",
76		"Resource validation http serve errors",
77		stats.UnitDimensionless)
78)
79
80func newView(measure stats.Measure, keys []tag.Key, aggregation *view.Aggregation) *view.View {
81	return &view.View{
82		Name:        measure.Name(),
83		Description: measure.Description(),
84		Measure:     measure,
85		TagKeys:     keys,
86		Aggregation: aggregation,
87	}
88}
89
90func init() {
91	var err error
92	if ErrorTag, err = tag.NewKey(errorStr); err != nil {
93		panic(err)
94	}
95	if GroupTag, err = tag.NewKey(group); err != nil {
96		panic(err)
97	}
98	if VersionTag, err = tag.NewKey(version); err != nil {
99		panic(err)
100	}
101	if ResourceTag, err = tag.NewKey(resourceTag); err != nil {
102		panic(err)
103	}
104	if ReasonTag, err = tag.NewKey(reason); err != nil {
105		panic(err)
106	}
107	if StatusTag, err = tag.NewKey(status); err != nil {
108		panic(err)
109	}
110
111	var noKeys []tag.Key
112	errorKey := []tag.Key{ErrorTag}
113	resourceKeys := []tag.Key{GroupTag, VersionTag, ResourceTag}
114	resourceErrorKeys := []tag.Key{GroupTag, VersionTag, ResourceTag, ReasonTag}
115	statusKey := []tag.Key{StatusTag}
116
117	err = view.Register(
118		newView(metricCertKeyUpdate, noKeys, view.Count()),
119		newView(metricCertKeyUpdateError, errorKey, view.Count()),
120		newView(metricValidationPassed, resourceKeys, view.Count()),
121		newView(metricValidationFailed, resourceErrorKeys, view.Count()),
122		newView(metricValidationHTTPError, statusKey, view.Count()),
123	)
124
125	if err != nil {
126		panic(err)
127	}
128}
129
130func reportValidationFailed(request *kubeApiAdmission.AdmissionRequest, reason string) {
131	ctx, err := tag.New(context.Background(),
132		tag.Insert(GroupTag, request.Resource.Group),
133		tag.Insert(VersionTag, request.Resource.Version),
134		tag.Insert(ResourceTag, request.Resource.Resource),
135		tag.Insert(ReasonTag, reason))
136	if err != nil {
137		scope.Errorf("Error creating monitoring context for reportValidationFailed: %v", err)
138	} else {
139		stats.Record(ctx, metricValidationFailed.M(1))
140	}
141}
142
143func reportValidationPass(request *kubeApiAdmission.AdmissionRequest) {
144	ctx, err := tag.New(context.Background(),
145		tag.Insert(GroupTag, request.Resource.Group),
146		tag.Insert(VersionTag, request.Resource.Version),
147		tag.Insert(ResourceTag, request.Resource.Resource))
148	if err != nil {
149		scope.Errorf("Error creating monitoring context for reportValidationPass: %v", err)
150	} else {
151		stats.Record(ctx, metricValidationPassed.M(1))
152	}
153}
154
155func reportValidationHTTPError(status int) {
156	ctx, err := tag.New(context.Background(), tag.Insert(StatusTag, strconv.Itoa(status)))
157	if err != nil {
158		scope.Errorf("Error creating monitoring context for reportValidationHTTPError: %v", err)
159	} else {
160		stats.Record(ctx, metricValidationHTTPError.M(1))
161	}
162}
163
164func reportValidationCertKeyUpdate() {
165	stats.Record(context.Background(), metricCertKeyUpdate.M(1))
166}
167
168func reportValidationCertKeyUpdateError(err error) {
169	ctx, err := tag.New(context.Background(), tag.Insert(ErrorTag, err.Error()))
170	if err != nil {
171		scope.Errorf("Error creating monitoring context for reportValidationCertKeyUpdateError: %v", err)
172	} else {
173		stats.Record(ctx, metricCertKeyUpdateError.M(1))
174	}
175}
176
177const (
178	reasonUnsupportedOperation = "unsupported_operation"
179	reasonYamlDecodeError      = "yaml_decode_error"
180	reasonUnknownType          = "unknown_type"
181	reasonCRDConversionError   = "crd_conversion_error"
182	reasonInvalidConfig        = "invalid_resource"
183)
184