mirror of https://github.com/knative/caching.git
Auto-update dependencies (#48)
Produced via: `dep ensure -update github.com/knative/test-infra knative.dev/pkg` /assign @mattmoor
This commit is contained in:
parent
c4acbc0a45
commit
77d253c889
|
@ -261,7 +261,7 @@
|
||||||
"tools/dep-collector",
|
"tools/dep-collector",
|
||||||
]
|
]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "2b0eeafd5300d91d0c96a719bd230fb3b3dd96ce"
|
revision = "f7f13f8e3a8af17943e1a44102607f1d3fc4751d"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:5985ef4caf91ece5d54817c11ea25f182697534f8ae6521eadcd628c142ac4b6"
|
digest = "1:5985ef4caf91ece5d54817c11ea25f182697534f8ae6521eadcd628c142ac4b6"
|
||||||
|
@ -930,7 +930,7 @@
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:c1c7cfe29d74a22c0fbc3ab02907361f8f5cd7e4a6757b9452f64d21682b040b"
|
digest = "1:ed15fad369d1d253ebcae3defd279859d26e54d83e1e601fb3cf40d192377404"
|
||||||
name = "knative.dev/pkg"
|
name = "knative.dev/pkg"
|
||||||
packages = [
|
packages = [
|
||||||
"apis",
|
"apis",
|
||||||
|
@ -949,7 +949,7 @@
|
||||||
"metrics/metricskey",
|
"metrics/metricskey",
|
||||||
]
|
]
|
||||||
pruneopts = "T"
|
pruneopts = "T"
|
||||||
revision = "c9bcf7e03fb535c28eae2b5ec1cf70e9363e32a7"
|
revision = "84d3910c565e397fa044f246398f94712da53303"
|
||||||
|
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
|
|
|
@ -300,14 +300,14 @@
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:bba4889bf4bf0a2f132ed8a2bbc4725c2877bc7b7dee37cdc551351b862a3559"
|
digest = "1:1bc2182db9fca862cd2b3a028aad73e473705be1c3f2454429a37b8626e16eda"
|
||||||
name = "github.com/knative/test-infra"
|
name = "github.com/knative/test-infra"
|
||||||
packages = [
|
packages = [
|
||||||
"scripts",
|
"scripts",
|
||||||
"tools/dep-collector",
|
"tools/dep-collector",
|
||||||
]
|
]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "91d37e4abc3047fc32ff6c0adcdea4fd29d47602"
|
revision = "2b0eeafd5300d91d0c96a719bd230fb3b3dd96ce"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:56dbf15e091bf7926cb33a57cb6bdfc658fc6d3498d2f76f10a97ce7856f1fde"
|
digest = "1:56dbf15e091bf7926cb33a57cb6bdfc658fc6d3498d2f76f10a97ce7856f1fde"
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
/*
|
||||||
|
Copyright 2019 The Knative 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 metricstest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.opencensus.io/stats/view"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckStatsReported checks that there is a view registered with the given name for each string in names,
|
||||||
|
// and that each view has at least one record.
|
||||||
|
func CheckStatsReported(t *testing.T, names ...string) {
|
||||||
|
t.Helper()
|
||||||
|
for _, name := range names {
|
||||||
|
d, err := view.RetrieveData(name)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("For metric %s: Reporter.Report() error = %v", name, err)
|
||||||
|
}
|
||||||
|
if len(d) < 1 {
|
||||||
|
t.Errorf("For metric %s: No data reported when data was expected, view data is empty.", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckStatsNotReported checks that there are no records for any views that a name matching a string in names.
|
||||||
|
// Names that do not match registered views are considered not reported.
|
||||||
|
func CheckStatsNotReported(t *testing.T, names ...string) {
|
||||||
|
t.Helper()
|
||||||
|
for _, name := range names {
|
||||||
|
d, err := view.RetrieveData(name)
|
||||||
|
// err == nil means a valid stat exists matching "name"
|
||||||
|
// len(d) > 0 means a component recorded metrics for that stat
|
||||||
|
if err == nil && len(d) > 0 {
|
||||||
|
t.Errorf("For metric %s: Unexpected data reported when no data was expected. Reporter len(d) = %d", name, len(d))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckCountData checks the view with a name matching string name to verify that the CountData stats
|
||||||
|
// reported are tagged with the tags in wantTags and that wantValue matches reported count.
|
||||||
|
func CheckCountData(t *testing.T, name string, wantTags map[string]string, wantValue int64) {
|
||||||
|
t.Helper()
|
||||||
|
if row := checkExactlyOneRow(t, name, wantTags); row != nil {
|
||||||
|
checkRowTags(t, row, name, wantTags)
|
||||||
|
|
||||||
|
if s, ok := row.Data.(*view.CountData); !ok {
|
||||||
|
t.Errorf("For metric %s: Reporter expected a CountData type", name)
|
||||||
|
} else if s.Value != wantValue {
|
||||||
|
t.Errorf("For metric %s: value = %v, want: %d", name, s.Value, wantValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckDistributionData checks the view with a name matching string name to verify that the DistributionData stats reported
|
||||||
|
// are tagged with the tags in wantTags and that expectedCount number of records were reported.
|
||||||
|
// It also checks that expectedMin and expectedMax match the minimum and maximum reported values, respectively.
|
||||||
|
func CheckDistributionData(t *testing.T, name string, wantTags map[string]string, expectedCount int64, expectedMin float64, expectedMax float64) {
|
||||||
|
t.Helper()
|
||||||
|
if row := checkExactlyOneRow(t, name, wantTags); row != nil {
|
||||||
|
checkRowTags(t, row, name, wantTags)
|
||||||
|
|
||||||
|
if s, ok := row.Data.(*view.DistributionData); !ok {
|
||||||
|
t.Errorf("For metric %s: Reporter expected a DistributionData type", name)
|
||||||
|
} else {
|
||||||
|
if s.Count != expectedCount {
|
||||||
|
t.Errorf("For metric %s: reporter count = %d, want = %d", name, s.Count, expectedCount)
|
||||||
|
}
|
||||||
|
if s.Min != expectedMin {
|
||||||
|
t.Errorf("For metric %s: reporter count = %f, want = %f", name, s.Min, expectedMin)
|
||||||
|
}
|
||||||
|
if s.Max != expectedMax {
|
||||||
|
t.Errorf("For metric %s: reporter count = %f, want = %f", name, s.Max, expectedMax)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckLastValueData checks the view with a name matching string name to verify that the LastValueData stats
|
||||||
|
// reported are tagged with the tags in wantTags and that wantValue matches reported last value.
|
||||||
|
func CheckLastValueData(t *testing.T, name string, wantTags map[string]string, wantValue float64) {
|
||||||
|
t.Helper()
|
||||||
|
if row := checkExactlyOneRow(t, name, wantTags); row != nil {
|
||||||
|
checkRowTags(t, row, name, wantTags)
|
||||||
|
|
||||||
|
if s, ok := row.Data.(*view.LastValueData); !ok {
|
||||||
|
t.Errorf("For metric %s: Reporter.Report() expected a LastValueData type", name)
|
||||||
|
} else if s.Value != wantValue {
|
||||||
|
t.Errorf("For metric %s: Reporter.Report() expected %v got %v", name, s.Value, wantValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckSumData checks the view with a name matching string name to verify that the SumData stats
|
||||||
|
// reported are tagged with the tags in wantTags and that wantValue matches the reported sum.
|
||||||
|
func CheckSumData(t *testing.T, name string, wantTags map[string]string, wantValue float64) {
|
||||||
|
t.Helper()
|
||||||
|
if row := checkExactlyOneRow(t, name, wantTags); row != nil {
|
||||||
|
checkRowTags(t, row, name, wantTags)
|
||||||
|
|
||||||
|
if s, ok := row.Data.(*view.SumData); !ok {
|
||||||
|
t.Errorf("For metric %s: Reporter expected a SumData type", name)
|
||||||
|
} else if s.Value != wantValue {
|
||||||
|
t.Errorf("For metric %s: value = %v, want: %v", name, s.Value, wantValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unregister unregisters the metrics that were registered.
|
||||||
|
// This is useful for testing since golang execute test iterations within the same process and
|
||||||
|
// opencensus views maintain global state. At the beginning of each test, tests should
|
||||||
|
// unregister for all metrics and then re-register for the same metrics. This effectively clears
|
||||||
|
// out any existing data and avoids a panic due to re-registering a metric.
|
||||||
|
//
|
||||||
|
// In normal process shutdown, metrics do not need to be unregistered.
|
||||||
|
func Unregister(names ...string) {
|
||||||
|
for _, n := range names {
|
||||||
|
if v := view.Find(n); v != nil {
|
||||||
|
view.Unregister(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkExactlyOneRow(t *testing.T, name string, wantTags map[string]string) *view.Row {
|
||||||
|
t.Helper()
|
||||||
|
d, err := view.RetrieveData(name)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("For metric %s: Reporter.Report() error = %v", name, err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(d) != 1 {
|
||||||
|
t.Errorf("For metric %s: Reporter.Report() len(d)=%v, want 1", name, len(d))
|
||||||
|
}
|
||||||
|
|
||||||
|
return d[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkRowTags(t *testing.T, row *view.Row, name string, wantTags map[string]string) {
|
||||||
|
t.Helper()
|
||||||
|
for _, got := range row.Tags {
|
||||||
|
n := got.Key.Name()
|
||||||
|
if want, ok := wantTags[n]; !ok {
|
||||||
|
t.Errorf("For metric %s: Reporter got an extra tag %v: %v", name, n, got.Value)
|
||||||
|
} else if got.Value != want {
|
||||||
|
t.Errorf("For metric %s: Reporter expected a different tag value for key: %s, got: %s, want: %s", name, n, got.Value, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -54,3 +54,14 @@ func Record(ctx context.Context, ms stats.Measurement) {
|
||||||
stats.Record(ctx, ms)
|
stats.Record(ctx, ms)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Buckets125 generates an array of buckets with approximate powers-of-two
|
||||||
|
// buckets that also aligns with powers of 10 on every 3rd step. This can
|
||||||
|
// be used to create a view.Distribution.
|
||||||
|
func Buckets125(low, high float64) []float64 {
|
||||||
|
buckets := []float64{low}
|
||||||
|
for last := low; last < high; last = last * 10 {
|
||||||
|
buckets = append(buckets, 2*last, 5*last, 10*last)
|
||||||
|
}
|
||||||
|
return buckets
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,151 @@
|
||||||
|
/*
|
||||||
|
Copyright 2019 The Knative 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 webhook
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.opencensus.io/stats"
|
||||||
|
"go.opencensus.io/stats/view"
|
||||||
|
"go.opencensus.io/tag"
|
||||||
|
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||||
|
"knative.dev/pkg/metrics"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
requestCountName = "request_count"
|
||||||
|
requestLatenciesName = "request_latencies"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
requestCountM = stats.Int64(
|
||||||
|
requestCountName,
|
||||||
|
"The number of requests that are routed to webhook",
|
||||||
|
stats.UnitDimensionless)
|
||||||
|
responseTimeInMsecM = stats.Float64(
|
||||||
|
requestLatenciesName,
|
||||||
|
"The response time in milliseconds",
|
||||||
|
stats.UnitMilliseconds)
|
||||||
|
|
||||||
|
// Create the tag keys that will be used to add tags to our measurements.
|
||||||
|
// Tag keys must conform to the restrictions described in
|
||||||
|
// go.opencensus.io/tag/validate.go. Currently those restrictions are:
|
||||||
|
// - length between 1 and 255 inclusive
|
||||||
|
// - characters are printable US-ASCII
|
||||||
|
requestOperationKey = mustNewTagKey("request_operation")
|
||||||
|
kindGroupKey = mustNewTagKey("kind_group")
|
||||||
|
kindVersionKey = mustNewTagKey("kind_version")
|
||||||
|
kindKindKey = mustNewTagKey("kind_kind")
|
||||||
|
resourceGroupKey = mustNewTagKey("resource_group")
|
||||||
|
resourceVersionKey = mustNewTagKey("resource_version")
|
||||||
|
resourceResourceKey = mustNewTagKey("resource_resource")
|
||||||
|
resourceNameKey = mustNewTagKey("resource_name")
|
||||||
|
resourceNamespaceKey = mustNewTagKey("resource_namespace")
|
||||||
|
admissionAllowedKey = mustNewTagKey("admission_allowed")
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
register()
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatsReporter reports webhook metrics
|
||||||
|
type StatsReporter interface {
|
||||||
|
ReportRequest(request *admissionv1beta1.AdmissionRequest, response *admissionv1beta1.AdmissionResponse, d time.Duration) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// reporter implements StatsReporter interface
|
||||||
|
type reporter struct {
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStatsReporter creaters a reporter for webhook metrics
|
||||||
|
func NewStatsReporter() (StatsReporter, error) {
|
||||||
|
ctx, err := tag.New(
|
||||||
|
context.Background(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &reporter{ctx: ctx}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Captures req count metric, recording the count and the duration
|
||||||
|
func (r *reporter) ReportRequest(req *admissionv1beta1.AdmissionRequest, resp *admissionv1beta1.AdmissionResponse, d time.Duration) error {
|
||||||
|
ctx, err := tag.New(
|
||||||
|
r.ctx,
|
||||||
|
tag.Insert(requestOperationKey, string(req.Operation)),
|
||||||
|
tag.Insert(kindGroupKey, req.Kind.Group),
|
||||||
|
tag.Insert(kindVersionKey, req.Kind.Version),
|
||||||
|
tag.Insert(kindKindKey, req.Kind.Kind),
|
||||||
|
tag.Insert(resourceGroupKey, req.Resource.Group),
|
||||||
|
tag.Insert(resourceVersionKey, req.Resource.Version),
|
||||||
|
tag.Insert(resourceResourceKey, req.Resource.Resource),
|
||||||
|
tag.Insert(resourceNameKey, req.Name),
|
||||||
|
tag.Insert(resourceNamespaceKey, req.Namespace),
|
||||||
|
tag.Insert(admissionAllowedKey, strconv.FormatBool(resp.Allowed)),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
metrics.Record(ctx, requestCountM.M(1))
|
||||||
|
// Convert time.Duration in nanoseconds to milliseconds
|
||||||
|
metrics.Record(ctx, responseTimeInMsecM.M(float64(d/time.Millisecond)))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func register() {
|
||||||
|
tagKeys := []tag.Key{
|
||||||
|
requestOperationKey,
|
||||||
|
kindGroupKey,
|
||||||
|
kindVersionKey,
|
||||||
|
kindKindKey,
|
||||||
|
resourceGroupKey,
|
||||||
|
resourceVersionKey,
|
||||||
|
resourceResourceKey,
|
||||||
|
resourceNamespaceKey,
|
||||||
|
resourceNameKey,
|
||||||
|
admissionAllowedKey}
|
||||||
|
|
||||||
|
if err := view.Register(
|
||||||
|
&view.View{
|
||||||
|
Description: requestCountM.Description(),
|
||||||
|
Measure: requestCountM,
|
||||||
|
Aggregation: view.Count(),
|
||||||
|
TagKeys: tagKeys,
|
||||||
|
},
|
||||||
|
&view.View{
|
||||||
|
Description: responseTimeInMsecM.Description(),
|
||||||
|
Measure: responseTimeInMsecM,
|
||||||
|
Aggregation: view.Distribution(metrics.Buckets125(1, 100000)...), // [1 2 5 10 20 50 100 200 500 1000 2000 5000 10000 20000 50000 100000]ms
|
||||||
|
TagKeys: tagKeys,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustNewTagKey(s string) tag.Key {
|
||||||
|
tagKey, err := tag.NewKey(s)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return tagKey
|
||||||
|
}
|
|
@ -114,10 +114,11 @@ type ResourceDefaulter func(patches *[]jsonpatch.JsonPatchOperation, crd Generic
|
||||||
// AdmissionController implements the external admission webhook for validation of
|
// AdmissionController implements the external admission webhook for validation of
|
||||||
// pilot configuration.
|
// pilot configuration.
|
||||||
type AdmissionController struct {
|
type AdmissionController struct {
|
||||||
Client kubernetes.Interface
|
Client kubernetes.Interface
|
||||||
Options ControllerOptions
|
Options ControllerOptions
|
||||||
Handlers map[schema.GroupVersionKind]GenericCRD
|
Handlers map[schema.GroupVersionKind]GenericCRD
|
||||||
Logger *zap.SugaredLogger
|
Logger *zap.SugaredLogger
|
||||||
|
StatsReporter StatsReporter
|
||||||
|
|
||||||
WithContext func(context.Context) context.Context
|
WithContext func(context.Context) context.Context
|
||||||
DisallowUnknownFields bool
|
DisallowUnknownFields bool
|
||||||
|
@ -408,6 +409,7 @@ func (ac *AdmissionController) register(
|
||||||
// ServeHTTP implements the external admission webhook for mutating
|
// ServeHTTP implements the external admission webhook for mutating
|
||||||
// serving resources.
|
// serving resources.
|
||||||
func (ac *AdmissionController) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (ac *AdmissionController) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var ttStart = time.Now()
|
||||||
logger := ac.Logger
|
logger := ac.Logger
|
||||||
logger.Infof("Webhook ServeHTTP request=%#v", r)
|
logger.Infof("Webhook ServeHTTP request=%#v", r)
|
||||||
|
|
||||||
|
@ -452,6 +454,9 @@ func (ac *AdmissionController) ServeHTTP(w http.ResponseWriter, r *http.Request)
|
||||||
http.Error(w, fmt.Sprintf("could encode response: %v", err), http.StatusInternalServerError)
|
http.Error(w, fmt.Sprintf("could encode response: %v", err), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only report valid requests
|
||||||
|
ac.StatsReporter.ReportRequest(review.Request, response.Response, time.Since(ttStart))
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeErrorStatus(reason string, args ...interface{}) *admissionv1beta1.AdmissionResponse {
|
func makeErrorStatus(reason string, args ...interface{}) *admissionv1beta1.AdmissionResponse {
|
||||||
|
|
Loading…
Reference in New Issue