From e3145bc1cbf790a772a33f9f48f5cdb589f03e50 Mon Sep 17 00:00:00 2001 From: mattmoor-sockpuppet Date: Fri, 6 Sep 2019 07:25:05 -0700 Subject: [PATCH] Auto-update dependencies (#93) Produced via: `dep ensure -update knative.dev/test-infra knative.dev/pkg` /assign mattmoor --- Gopkg.lock | 6 +- vendor/knative.dev/pkg/test/clients.go | 4 +- .../pkg/test/logstream/kubelogs.go | 8 +- vendor/knative.dev/pkg/test/request.go | 29 +- vendor/knative.dev/pkg/test/spoof/spoof.go | 99 +++--- .../coveragecalculator/calculator.go | 8 + .../coveragecalculator/coveragedata.go | 10 +- .../test/webhook-apicoverage/tools/tools.go | 14 +- .../view/aggregate_coverage.html | 25 ++ .../webhook-apicoverage/view/html_display.go | 128 ++------ .../view/type_coverage.html | 61 ++++ .../webhook/apicoverage_recorder.go | 12 +- .../webhook/resource_admission_controller.go | 78 ++++- vendor/knative.dev/pkg/webhook/webhook.go | 303 ++++++++---------- .../knative.dev/pkg/websocket/connection.go | 2 +- 15 files changed, 446 insertions(+), 341 deletions(-) create mode 100644 vendor/knative.dev/pkg/test/webhook-apicoverage/view/aggregate_coverage.html create mode 100644 vendor/knative.dev/pkg/test/webhook-apicoverage/view/type_coverage.html diff --git a/Gopkg.lock b/Gopkg.lock index 54aeb0d0..51b0ee19 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -927,7 +927,7 @@ [[projects]] branch = "master" - digest = "1:80ebfb8d1bf38f2d1e3bf52eaa074a62e04c6b506ea5f00dc39596425e00ee5a" + digest = "1:c58bb491fe5be973899a0c744db8a283745641503bed8ddb683da4ad9bff09c5" name = "knative.dev/pkg" packages = [ "apis", @@ -946,7 +946,7 @@ "metrics/metricskey", ] pruneopts = "T" - revision = "e9c7aefce07abe1e9b7246a2f73537a68726f244" + revision = "75da3911c0205df6c1b8acb74662d96a6e37b305" [[projects]] branch = "master" @@ -957,7 +957,7 @@ "tools/dep-collector", ] pruneopts = "UT" - revision = "305dd68b036713ee60a38caab1d8531a20e741d1" + revision = "cfee08011fea83b3a3b4fc0d3dfe436789a1ac24" [solve-meta] analyzer-name = "dep" diff --git a/vendor/knative.dev/pkg/test/clients.go b/vendor/knative.dev/pkg/test/clients.go index c3f43bda..6f9347f2 100644 --- a/vendor/knative.dev/pkg/test/clients.go +++ b/vendor/knative.dev/pkg/test/clients.go @@ -38,8 +38,8 @@ type KubeClient struct { } // NewSpoofingClient returns a spoofing client to make requests -func NewSpoofingClient(client *KubeClient, logf logging.FormatLogger, domain string, resolvable bool) (*spoof.SpoofingClient, error) { - return spoof.New(client.Kube, logf, domain, resolvable, Flags.IngressEndpoint) +func NewSpoofingClient(client *KubeClient, logf logging.FormatLogger, domain string, resolvable bool, opts ...spoof.TransportOption) (*spoof.SpoofingClient, error) { + return spoof.New(client.Kube, logf, domain, resolvable, Flags.IngressEndpoint, opts...) } // NewKubeClient instantiates and returns several clientsets required for making request to the diff --git a/vendor/knative.dev/pkg/test/logstream/kubelogs.go b/vendor/knative.dev/pkg/test/logstream/kubelogs.go index b18a5f14..733e564e 100644 --- a/vendor/knative.dev/pkg/test/logstream/kubelogs.go +++ b/vendor/knative.dev/pkg/test/logstream/kubelogs.go @@ -137,11 +137,17 @@ func (k *kubelogs) handleLine(l string) { continue } + // We also get logs not from controllers (activator, autoscaler). + // So replace controller string in them with their callsite. + site := line.Controller + if site == "" { + site = line.Caller + } // E 15:04:05.000 [route-controller] [default/testroute-xyz] this is my message msg := fmt.Sprintf("%s %s [%s] [%s] %s", strings.ToUpper(string(line.Level[0])), line.Timestamp.Format(timeFormat), - line.Controller, + site, line.Key, line.Message) diff --git a/vendor/knative.dev/pkg/test/request.go b/vendor/knative.dev/pkg/test/request.go index d6d84f35..8b38cb29 100644 --- a/vendor/knative.dev/pkg/test/request.go +++ b/vendor/knative.dev/pkg/test/request.go @@ -134,7 +134,14 @@ func MatchesAllOf(checkers ...spoof.ResponseChecker) spoof.ResponseChecker { // the domain in the request headers, otherwise it will make the request directly to domain. // desc will be used to name the metric that is emitted to track how long it took for the // domain to get into the state checked by inState. Commas in `desc` must be escaped. -func WaitForEndpointState(kubeClient *KubeClient, logf logging.FormatLogger, theURL string, inState spoof.ResponseChecker, desc string, resolvable bool, opts ...RequestOption) (*spoof.Response, error) { +func WaitForEndpointState( + kubeClient *KubeClient, + logf logging.FormatLogger, + theURL string, + inState spoof.ResponseChecker, + desc string, + resolvable bool, + opts ...interface{}) (*spoof.Response, error) { return WaitForEndpointStateWithTimeout(kubeClient, logf, theURL, inState, desc, resolvable, spoof.RequestTimeout, opts...) } @@ -145,8 +152,14 @@ func WaitForEndpointState(kubeClient *KubeClient, logf logging.FormatLogger, the // desc will be used to name the metric that is emitted to track how long it took for the // domain to get into the state checked by inState. Commas in `desc` must be escaped. func WaitForEndpointStateWithTimeout( - kubeClient *KubeClient, logf logging.FormatLogger, theURL string, inState spoof.ResponseChecker, - desc string, resolvable bool, timeout time.Duration, opts ...RequestOption) (*spoof.Response, error) { + kubeClient *KubeClient, + logf logging.FormatLogger, + theURL string, + inState spoof.ResponseChecker, + desc string, + resolvable bool, + timeout time.Duration, + opts ...interface{}) (*spoof.Response, error) { defer logging.GetEmitableSpan(context.Background(), fmt.Sprintf("WaitForEndpointState/%s", desc)).End() // Try parsing the "theURL" with and without a scheme. @@ -163,11 +176,17 @@ func WaitForEndpointStateWithTimeout( return nil, err } + var tOpts []spoof.TransportOption for _, opt := range opts { - opt(req) + rOpt, ok := opt.(RequestOption) + if ok { + rOpt(req) + } else if tOpt, ok := opt.(spoof.TransportOption); ok { + tOpts = append(tOpts, tOpt) + } } - client, err := NewSpoofingClient(kubeClient, logf, asURL.Hostname(), resolvable) + client, err := NewSpoofingClient(kubeClient, logf, asURL.Hostname(), resolvable, tOpts...) if err != nil { return nil, err } diff --git a/vendor/knative.dev/pkg/test/spoof/spoof.go b/vendor/knative.dev/pkg/test/spoof/spoof.go index 125a37a9..a102bd1c 100644 --- a/vendor/knative.dev/pkg/test/spoof/spoof.go +++ b/vendor/knative.dev/pkg/test/spoof/spoof.go @@ -19,9 +19,12 @@ limitations under the License. package spoof import ( + "context" "fmt" "io/ioutil" + "net" "net/http" + "strings" "time" "github.com/pkg/errors" @@ -66,7 +69,10 @@ type Interface interface { } // https://medium.com/stupid-gopher-tricks/ensuring-go-interface-satisfaction-at-compile-time-1ed158e8fa17 -var _ Interface = (*SpoofingClient)(nil) +var ( + _ Interface = (*SpoofingClient)(nil) + dialContext = (&net.Dialer{}).DialContext +) // ResponseChecker is used to determine when SpoofinClient.Poll is done polling. // This allows you to predicate wait.PollImmediate on the request's http.Response. @@ -82,72 +88,81 @@ type SpoofingClient struct { RequestInterval time.Duration RequestTimeout time.Duration - endpoint string - domain string - logf logging.FormatLogger } -// New returns a SpoofingClient that rewrites requests if the target domain is not `resolveable`. +// TransportOption allows callers to customize the http.Transport used by a SpoofingClient +type TransportOption func(transport *http.Transport) *http.Transport + +// New returns a SpoofingClient that rewrites requests if the target domain is not `resolvable`. // It does this by looking up the ingress at construction time, so reusing a client will not // follow the ingress if it moves (or if there are multiple ingresses). // // If that's a problem, see test/request.go#WaitForEndpointState for oneshot spoofing. -func New(kubeClientset *kubernetes.Clientset, logf logging.FormatLogger, domain string, resolvable bool, endpointOverride string) (*SpoofingClient, error) { +func New( + kubeClientset *kubernetes.Clientset, + logf logging.FormatLogger, + domain string, + resolvable bool, + endpointOverride string, + opts ...TransportOption) (*SpoofingClient, error) { + endpoint, err := ResolveEndpoint(kubeClientset, domain, resolvable, endpointOverride) + if err != nil { + fmt.Errorf("failed get the cluster endpoint: %v", err) + } + + // Spoof the hostname at the resolver level + transport := &http.Transport{ + DialContext: func(ctx context.Context, network, addr string) (conn net.Conn, e error) { + spoofed := addr + if i := strings.LastIndex(addr, ":"); i != -1 && domain == addr[:i] { + // The original hostname:port is spoofed by replacing the hostname by the value + // returned by ResolveEndpoint. + spoofed = endpoint + ":" + addr[i+1:] + logf("Spoofing %s -> %s", addr, spoofed) + } + return dialContext(ctx, network, spoofed) + }, + } + + for _, opt := range opts { + transport = opt(transport) + } + + // Enable Zipkin tracing + roundTripper := &ochttp.Transport{ + Base: transport, + Propagation: &b3.HTTPFormat{}, + } + sc := SpoofingClient{ - Client: &http.Client{Transport: &ochttp.Transport{Propagation: &b3.HTTPFormat{}}}, // Using ochttp Transport required for zipkin-tracing + Client: &http.Client{Transport: roundTripper}, RequestInterval: requestInterval, RequestTimeout: RequestTimeout, logf: logf, } - - var err error - if sc.endpoint, err = ResolveEndpoint(kubeClientset, domain, resolvable, endpointOverride); err != nil { - return nil, err - } - - if !resolvable { - sc.domain = domain - } - return &sc, nil } // ResolveEndpoint resolves the endpoint address considering whether the domain is resolvable and taking into // account whether the user overrode the endpoint address externally func ResolveEndpoint(kubeClientset *kubernetes.Clientset, domain string, resolvable bool, endpointOverride string) (string, error) { - // If the domain is resolvable, we can use it directly when we make requests. - endpoint := domain - if !resolvable { - e := endpointOverride - if endpointOverride == "" { - var err error - // If the domain that the Route controller is configured to assign to Route.Status.Domain - // (the domainSuffix) is not resolvable, we need to retrieve the endpoint and spoof - // the Host in our requests. - if e, err = ingress.GetIngressEndpoint(kubeClientset); err != nil { - return "", err - } - } - endpoint = e + // If the domain is resolvable, it can be used directly + if resolvable { + return domain, nil } - return endpoint, nil + // If an override is provided, use it + if endpointOverride != "" { + return endpointOverride, nil + } + // Otherwise, use the actual cluster endpoint + return ingress.GetIngressEndpoint(kubeClientset) } // Do dispatches to the underlying http.Client.Do, spoofing domains as needed // and transforming the http.Response into a spoof.Response. // Each response is augmented with "ZipkinTraceID" header that identifies the zipkin trace corresponding to the request. func (sc *SpoofingClient) Do(req *http.Request) (*Response, error) { - // Controls the Host header, for spoofing. - if sc.domain != "" { - req.Host = sc.domain - } - - // Controls the actual resolution. - if sc.endpoint != "" { - req.URL.Host = sc.endpoint - } - // Starting span to capture zipkin trace. traceContext, span := trace.StartSpan(req.Context(), "SpoofingClient-Trace") defer span.End() diff --git a/vendor/knative.dev/pkg/test/webhook-apicoverage/coveragecalculator/calculator.go b/vendor/knative.dev/pkg/test/webhook-apicoverage/coveragecalculator/calculator.go index 26d97dee..2dbfb233 100644 --- a/vendor/knative.dev/pkg/test/webhook-apicoverage/coveragecalculator/calculator.go +++ b/vendor/knative.dev/pkg/test/webhook-apicoverage/coveragecalculator/calculator.go @@ -21,6 +21,14 @@ type CoverageValues struct { TotalFields int CoveredFields int IgnoredFields int + + PercentCoverage float64 +} + +func (c *CoverageValues) CalculatePercentageValue() { + if c.TotalFields > 0 { + c.PercentCoverage = (float64(c.CoveredFields) / float64(c.TotalFields-c.IgnoredFields)) * 100 + } } // CalculateTypeCoverage calculates aggregate coverage values based on provided []TypeCoverage diff --git a/vendor/knative.dev/pkg/test/webhook-apicoverage/coveragecalculator/coveragedata.go b/vendor/knative.dev/pkg/test/webhook-apicoverage/coveragecalculator/coveragedata.go index bba92002..0012ebf8 100644 --- a/vendor/knative.dev/pkg/test/webhook-apicoverage/coveragecalculator/coveragedata.go +++ b/vendor/knative.dev/pkg/test/webhook-apicoverage/coveragecalculator/coveragedata.go @@ -16,7 +16,10 @@ limitations under the License. package coveragecalculator -import "k8s.io/apimachinery/pkg/util/sets" +import ( + "strings" + "k8s.io/apimachinery/pkg/util/sets" +) // FieldCoverage represents coverage data for a field. type FieldCoverage struct { @@ -43,6 +46,11 @@ func (f *FieldCoverage) GetValues() []string { return values } +// GetValuesForDisplay returns value strings as comma separated string. +func (f *FieldCoverage) GetValuesForDisplay() string { + return strings.Join(f.GetValues(), ",") +} + // TypeCoverage encapsulates type information and field coverage. type TypeCoverage struct { Package string `json:"Package"` diff --git a/vendor/knative.dev/pkg/test/webhook-apicoverage/tools/tools.go b/vendor/knative.dev/pkg/test/webhook-apicoverage/tools/tools.go index fec5c5a0..b8207b34 100644 --- a/vendor/knative.dev/pkg/test/webhook-apicoverage/tools/tools.go +++ b/vendor/knative.dev/pkg/test/webhook-apicoverage/tools/tools.go @@ -24,7 +24,6 @@ import ( "net/http" "os/user" "path" - "strings" "knative.dev/pkg/test/webhook-apicoverage/coveragecalculator" "knative.dev/pkg/test/webhook-apicoverage/view" @@ -177,13 +176,12 @@ func GetAndWriteTotalCoverage(webhookIP string, outputFile string) error { return err } - var buffer strings.Builder - buffer.WriteString(view.HTMLHeader) - totalCoverageDisplay := view.GetHTMLCoverageValuesDisplay(totalCoverage) - buffer.WriteString(totalCoverageDisplay) - buffer.WriteString(view.HTMLFooter) - if err = ioutil.WriteFile(outputFile, []byte(buffer.String()), 0400); err != nil { - return fmt.Errorf("error writing total coverage to output file: %s, error: %v coverage: %s", outputFile, err, totalCoverageDisplay) + if htmlData, err := view.GetHTMLCoverageValuesDisplay(totalCoverage); err != nil { + return fmt.Errorf("error building html file from total coverage. error: %v", err) + } else { + if err = ioutil.WriteFile(outputFile, []byte(htmlData), 0400); err != nil { + return fmt.Errorf("error writing total coverage to output file: %s, error: %v", outputFile, err) + } } return nil diff --git a/vendor/knative.dev/pkg/test/webhook-apicoverage/view/aggregate_coverage.html b/vendor/knative.dev/pkg/test/webhook-apicoverage/view/aggregate_coverage.html new file mode 100644 index 00000000..98b64764 --- /dev/null +++ b/vendor/knative.dev/pkg/test/webhook-apicoverage/view/aggregate_coverage.html @@ -0,0 +1,25 @@ + + + + + + + + + +
Total Fields{{ .TotalFields }}
Covered Fields{{ .CoveredFields }}
Ignored Fields{{ .IgnoredFields }}
Coverage Percentage{{ .PercentCoverage }}
+ + diff --git a/vendor/knative.dev/pkg/test/webhook-apicoverage/view/html_display.go b/vendor/knative.dev/pkg/test/webhook-apicoverage/view/html_display.go index c8330544..8978af5f 100644 --- a/vendor/knative.dev/pkg/test/webhook-apicoverage/view/html_display.go +++ b/vendor/knative.dev/pkg/test/webhook-apicoverage/view/html_display.go @@ -17,115 +17,53 @@ limitations under the License. package view import ( - "fmt" "strings" + "html/template" "knative.dev/pkg/test/webhook-apicoverage/coveragecalculator" ) -// HTMLHeader sets up an HTML page with the right style format -var HTMLHeader = fmt.Sprintf(` - - - - -`) - -// HTMLFooter closes the tags for the HTML page. -var HTMLFooter = fmt.Sprintf(` - - -`) +type HtmlDisplayData struct { + TypeCoverages []coveragecalculator.TypeCoverage + CoverageNumbers *coveragecalculator.CoverageValues +} // GetHTMLDisplay is a helper method to display API Coverage details in json-like format inside a HTML page. -func GetHTMLDisplay(coverageData []coveragecalculator.TypeCoverage, displayRules DisplayRules) string { - var buffer strings.Builder - buffer.WriteString(HTMLHeader) - for _, typeCoverage := range coverageData { - packageName := typeCoverage.Package - if displayRules.PackageNameRule != nil { - packageName = displayRules.PackageNameRule(packageName) - } - - typeName := typeCoverage.Type - if displayRules.TypeNameRule != nil { - typeName = displayRules.TypeNameRule(typeName) - } - buffer.WriteString(fmt.Sprint(`
`)) - buffer.WriteString(fmt.Sprintf(`
Package: %s`, packageName)) - buffer.WriteString(fmt.Sprintf(`
Type: %s`, typeName)) - buffer.WriteString(fmt.Sprint(`
`)) - buffer.WriteString(fmt.Sprint(`

{
`)) - for _, fieldCoverage := range typeCoverage.Fields { - var fieldDisplay string - if displayRules.FieldRule != nil { - fieldDisplay = displayRules.FieldRule(fieldCoverage) - } else { - fieldDisplay = defaultHTMLTypeDisplay(fieldCoverage) - } - buffer.WriteString(fieldDisplay) - } - - buffer.WriteString(fmt.Sprint(`
}
`)) - buffer.WriteString(fmt.Sprint(`
`)) +func GetHTMLDisplay(coverageData []coveragecalculator.TypeCoverage, coverageValues *coveragecalculator.CoverageValues) (string, error) { + htmlData := HtmlDisplayData{ + TypeCoverages: coverageData, + CoverageNumbers: coverageValues, } - buffer.WriteString(HTMLFooter) - return buffer.String() + tmpl, err := template.ParseFiles("type_coverage.html") + if err != nil { + return "", err + } + + var buffer strings.Builder + err = tmpl.Execute(&buffer, htmlData) + if err != nil { + return "", err + } + + return buffer.String(), nil } -func defaultHTMLTypeDisplay(field *coveragecalculator.FieldCoverage) string { - var buffer strings.Builder - if field.Ignored { - buffer.WriteString(fmt.Sprintf(`
%s
`, field.Field)) - } else if field.Coverage { - buffer.WriteString(fmt.Sprintf(`
%s`, field.Field)) - if len(field.Values) > 0 && !strings.Contains(strings.ToLower(field.Field), "uid") { - buffer.WriteString(fmt.Sprintf(`    Values: [%s]`, strings.Join(field.GetValues(), ", "))) - } - buffer.WriteString(fmt.Sprint(`
`)) - } else { - buffer.WriteString(fmt.Sprintf(`
%s
`, field.Field)) - } - return buffer.String() -} // GetHTMLCoverageValuesDisplay is a helper method to display coverage values inside a HTML table. -func GetHTMLCoverageValuesDisplay(coverageValues *coveragecalculator.CoverageValues) string { - var buffer strings.Builder - buffer.WriteString(fmt.Sprint(`
`)) - buffer.WriteString(fmt.Sprint(`
Coverage Values
`)) - buffer.WriteString(fmt.Sprint(`
`)) - buffer.WriteString(fmt.Sprint(` `)) - buffer.WriteString(fmt.Sprintf(``, coverageValues.TotalFields)) - buffer.WriteString(fmt.Sprintf(``, coverageValues.CoveredFields)) - buffer.WriteString(fmt.Sprintf(``, coverageValues.IgnoredFields)) +func GetHTMLCoverageValuesDisplay(coverageValues *coveragecalculator.CoverageValues) (string, error) { - percentCoverage := 0.0 - if coverageValues.TotalFields > 0 { - percentCoverage = (float64(coverageValues.CoveredFields) / float64(coverageValues.TotalFields-coverageValues.IgnoredFields)) * 100 + tmpl, err := template.ParseFiles("aggregate_coverage.html") + if err != nil { + return "", err } - buffer.WriteString(fmt.Sprintf(``, percentCoverage)) - buffer.WriteString(fmt.Sprint(`
Total Fields%d
Covered Fields%d
Ignored Fields%d
Coverage Percentage%f
`)) - return buffer.String() + + var buffer strings.Builder + err = tmpl.Execute(&buffer, coverageValues) + if err != nil { + return "", err + } + + return buffer.String(), nil } diff --git a/vendor/knative.dev/pkg/test/webhook-apicoverage/view/type_coverage.html b/vendor/knative.dev/pkg/test/webhook-apicoverage/view/type_coverage.html new file mode 100644 index 00000000..a55bbe36 --- /dev/null +++ b/vendor/knative.dev/pkg/test/webhook-apicoverage/view/type_coverage.html @@ -0,0 +1,61 @@ + + + + +{{ range $coverageType := .TypeCoverages }} +
+
Package: {{ $coverageType.Package }} +
Type: {{ $coverageType.Type }} +
+
+
{ +
+ {{ range $key, $value := $coverageType.Fields }} + {{if $value.Ignored }} +
{{ $value.Field }}
+ {{else if $value.Coverage}} +
{{ $value.Field }} + {{ $valueLen := len $value.Values }} + {{if gt $valueLen 0 }} +     Values: [{{$value.GetValuesForDisplay}}] + {{end}} +
+ {{else}} +
{{ $value.Field }}
+ {{end}} + {{end}} +
}
+
+{{end}} + +
+
+
Coverage Values
+
+ + + + + +
Total Fields{{ .CoverageNumbers.TotalFields }}
Covered Fields{{ .CoverageNumbers.CoveredFields }}
Ignored Fields{{ .CoverageNumbers.IgnoredFields }}
Coverage Percentage{{ .CoverageNumbers.PercentCoverage }}
+ + diff --git a/vendor/knative.dev/pkg/test/webhook-apicoverage/webhook/apicoverage_recorder.go b/vendor/knative.dev/pkg/test/webhook-apicoverage/webhook/apicoverage_recorder.go index fed24f71..5e9b6363 100644 --- a/vendor/knative.dev/pkg/test/webhook-apicoverage/webhook/apicoverage_recorder.go +++ b/vendor/knative.dev/pkg/test/webhook-apicoverage/webhook/apicoverage_recorder.go @@ -23,7 +23,6 @@ import ( "net/http" "os" "reflect" - "strings" "go.uber.org/zap" "k8s.io/api/admission/v1beta1" @@ -168,11 +167,13 @@ func (a *APICoverageRecorder) GetResourceCoverage(w http.ResponseWriter, r *http tree := a.ResourceForest.TopLevelTrees[resource] typeCoverage := tree.BuildCoverageData(a.NodeRules, a.FieldRules, ignoredFields) coverageValues := coveragecalculator.CalculateTypeCoverage(typeCoverage) + coverageValues.CalculatePercentageValue() - var buffer strings.Builder - buffer.WriteString(view.GetHTMLDisplay(typeCoverage, a.DisplayRules)) - buffer.WriteString(view.GetHTMLCoverageValuesDisplay(coverageValues)) - fmt.Fprint(w, buffer.String()) + if htmlData, err := view.GetHTMLDisplay(typeCoverage, coverageValues); err != nil { + fmt.Fprintf(w, "Error generating html file %v", err) + } else { + fmt.Fprint(w, htmlData) + } } // GetTotalCoverage goes over all the resources setup for the apicoverage tool and returns total coverage values. @@ -197,6 +198,7 @@ func (a *APICoverageRecorder) GetTotalCoverage(w http.ResponseWriter, r *http.Re totalCoverage.IgnoredFields += coverageValues.IgnoredFields } + totalCoverage.CalculatePercentageValue() var body []byte if body, err = json.Marshal(totalCoverage); err != nil { fmt.Fprintf(w, "error marshalling total coverage response: %v", err) diff --git a/vendor/knative.dev/pkg/webhook/resource_admission_controller.go b/vendor/knative.dev/pkg/webhook/resource_admission_controller.go index cf0ec50c..c6441a24 100644 --- a/vendor/knative.dev/pkg/webhook/resource_admission_controller.go +++ b/vendor/knative.dev/pkg/webhook/resource_admission_controller.go @@ -60,11 +60,24 @@ type GenericCRD interface { runtime.Object } +// ResourceAdmissionController implements the AdmissionController for resources type ResourceAdmissionController struct { - Handlers map[schema.GroupVersionKind]GenericCRD - Options ControllerOptions + handlers map[schema.GroupVersionKind]GenericCRD + options ControllerOptions - DisallowUnknownFields bool + disallowUnknownFields bool +} + +// NewResourceAdmissionController constructs a ResourceAdmissionController +func NewResourceAdmissionController( + handlers map[schema.GroupVersionKind]GenericCRD, + opts ControllerOptions, + disallowUnknownFields bool) AdmissionController { + return &ResourceAdmissionController{ + handlers: handlers, + options: opts, + disallowUnknownFields: disallowUnknownFields, + } } func (ac *ResourceAdmissionController) Admit(ctx context.Context, request *admissionv1beta1.AdmissionRequest) *admissionv1beta1.AdmissionResponse { @@ -98,7 +111,7 @@ func (ac *ResourceAdmissionController) Register(ctx context.Context, kubeClient failurePolicy := admissionregistrationv1beta1.Fail var rules []admissionregistrationv1beta1.RuleWithOperations - for gvk := range ac.Handlers { + for gvk := range ac.handlers { plural := strings.ToLower(inflect.Pluralize(gvk.Kind)) rules = append(rules, admissionregistrationv1beta1.RuleWithOperations{ @@ -128,15 +141,16 @@ func (ac *ResourceAdmissionController) Register(ctx context.Context, kubeClient webhook := &admissionregistrationv1beta1.MutatingWebhookConfiguration{ ObjectMeta: metav1.ObjectMeta{ - Name: ac.Options.WebhookName, + Name: ac.options.WebhookName, }, Webhooks: []admissionregistrationv1beta1.Webhook{{ - Name: ac.Options.WebhookName, + Name: ac.options.WebhookName, Rules: rules, ClientConfig: admissionregistrationv1beta1.WebhookClientConfig{ Service: &admissionregistrationv1beta1.ServiceReference{ - Namespace: ac.Options.Namespace, - Name: ac.Options.ServiceName, + Namespace: ac.options.Namespace, + Name: ac.options.ServiceName, + Path: &ac.options.ResourceAdmissionControllerPath, }, CABundle: caCert, }, @@ -145,7 +159,7 @@ func (ac *ResourceAdmissionController) Register(ctx context.Context, kubeClient } // Set the owner to our deployment. - deployment, err := kubeClient.Apps().Deployments(ac.Options.Namespace).Get(ac.Options.DeploymentName, metav1.GetOptions{}) + deployment, err := kubeClient.Apps().Deployments(ac.options.Namespace).Get(ac.options.DeploymentName, metav1.GetOptions{}) if err != nil { return fmt.Errorf("failed to fetch our deployment: %v", err) } @@ -159,7 +173,7 @@ func (ac *ResourceAdmissionController) Register(ctx context.Context, kubeClient return fmt.Errorf("failed to create a webhook: %v", err) } logger.Info("Webhook already exists") - configuredWebhook, err := client.Get(ac.Options.WebhookName, metav1.GetOptions{}) + configuredWebhook, err := client.Get(ac.options.WebhookName, metav1.GetOptions{}) if err != nil { return fmt.Errorf("error retrieving webhook: %v", err) } @@ -193,7 +207,7 @@ func (ac *ResourceAdmissionController) mutate(ctx context.Context, req *admissio } logger := logging.FromContext(ctx) - handler, ok := ac.Handlers[gvk] + handler, ok := ac.handlers[gvk] if !ok { logger.Errorf("Unhandled kind: %v", gvk) return nil, fmt.Errorf("unhandled kind: %v", gvk) @@ -205,7 +219,7 @@ func (ac *ResourceAdmissionController) mutate(ctx context.Context, req *admissio if len(newBytes) != 0 { newObj = handler.DeepCopyObject().(GenericCRD) newDecoder := json.NewDecoder(bytes.NewBuffer(newBytes)) - if ac.DisallowUnknownFields { + if ac.disallowUnknownFields { newDecoder.DisallowUnknownFields() } if err := newDecoder.Decode(&newObj); err != nil { @@ -215,7 +229,7 @@ func (ac *ResourceAdmissionController) mutate(ctx context.Context, req *admissio if len(oldBytes) != 0 { oldObj = handler.DeepCopyObject().(GenericCRD) oldDecoder := json.NewDecoder(bytes.NewBuffer(oldBytes)) - if ac.DisallowUnknownFields { + if ac.disallowUnknownFields { oldDecoder.DisallowUnknownFields() } if err := oldDecoder.Decode(&oldObj); err != nil { @@ -323,3 +337,41 @@ func roundTripPatch(bytes []byte, unmarshalled interface{}) (duck.JSONPatch, err } return jsonpatch.CreatePatch(bytes, marshaledBytes) } + +// validate performs validation on the provided "new" CRD. +// For legacy purposes, this also does apis.Immutable validation, +// which is deprecated and will be removed in a future release. +func validate(ctx context.Context, new apis.Validatable) error { + if apis.IsInUpdate(ctx) { + old := apis.GetBaseline(ctx) + if immutableNew, ok := new.(apis.Immutable); ok { + immutableOld, ok := old.(apis.Immutable) + if !ok { + return fmt.Errorf("unexpected type mismatch %T vs. %T", old, new) + } + if err := immutableNew.CheckImmutableFields(ctx, immutableOld); err != nil { + return err + } + } + } + + // Can't just `return new.Validate()` because it doesn't properly nil-check. + if err := new.Validate(ctx); err != nil { + return err + } + + return nil +} + +// setDefaults simply leverages apis.Defaultable to set defaults. +func setDefaults(ctx context.Context, patches duck.JSONPatch, crd GenericCRD) (duck.JSONPatch, error) { + before, after := crd.DeepCopyObject(), crd + after.SetDefaults(ctx) + + patch, err := duck.CreatePatch(before, after) + if err != nil { + return nil, err + } + + return append(patches, patch...), nil +} diff --git a/vendor/knative.dev/pkg/webhook/webhook.go b/vendor/knative.dev/pkg/webhook/webhook.go index 2efdaefb..0a6fcef2 100644 --- a/vendor/knative.dev/pkg/webhook/webhook.go +++ b/vendor/knative.dev/pkg/webhook/webhook.go @@ -28,8 +28,6 @@ import ( "go.uber.org/zap" - "knative.dev/pkg/apis" - "knative.dev/pkg/apis/duck" "knative.dev/pkg/logging" "knative.dev/pkg/logging/logkey" @@ -38,7 +36,6 @@ import ( corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/kubernetes" ) @@ -94,27 +91,37 @@ type ControllerOptions struct { // StatsReporter reports metrics about the webhook. // This will be automatically initialized by the constructor if left uninitialized. StatsReporter StatsReporter + + // Service path for ResourceAdmissionController webhook + // Default is "/" for backward compatibility and is set by the constructor + ResourceAdmissionControllerPath string } -// AdmissionController implements the external admission webhook for validation of -// pilot configuration. -type AdmissionController struct { - Client kubernetes.Interface - Options ControllerOptions - Logger *zap.SugaredLogger - resourceAdmissionController ResourceAdmissionController +// AdmissionController provides the interface for different admission controllers +type AdmissionController interface { + Admit(context.Context, *admissionv1beta1.AdmissionRequest) *admissionv1beta1.AdmissionResponse + Register(context.Context, kubernetes.Interface, []byte) error +} + +// Webhook implements the external webhook for validation of +// resources and configuration. +type Webhook struct { + Client kubernetes.Interface + Options ControllerOptions + Logger *zap.SugaredLogger + admissionControllers map[string]AdmissionController WithContext func(context.Context) context.Context } -// NewAdmissionController constructs an AdmissionController -func NewAdmissionController( +// New constructs a Webhook +func New( client kubernetes.Interface, opts ControllerOptions, - handlers map[schema.GroupVersionKind]GenericCRD, + admissionControllers map[string]AdmissionController, logger *zap.SugaredLogger, ctx func(context.Context) context.Context, - disallowUnknownFields bool) (*AdmissionController, error) { + ) (*Webhook, error) { if opts.StatsReporter == nil { reporter, err := NewStatsReporter() @@ -124,19 +131,126 @@ func NewAdmissionController( opts.StatsReporter = reporter } - return &AdmissionController{ - Client: client, - Options: opts, - resourceAdmissionController: ResourceAdmissionController{ - Handlers: handlers, - Options: opts, - DisallowUnknownFields: disallowUnknownFields, - }, - Logger: logger, - WithContext: ctx, + return &Webhook{ + Client: client, + Options: opts, + admissionControllers: admissionControllers, + Logger: logger, + WithContext: ctx, }, nil } +// Run implements the admission controller run loop. +func (ac *Webhook) Run(stop <-chan struct{}) error { + logger := ac.Logger + ctx := logging.WithLogger(context.TODO(), logger) + tlsConfig, caCert, err := configureCerts(ctx, ac.Client, &ac.Options) + if err != nil { + logger.Errorw("could not configure admission webhook certs", zap.Error(err)) + return err + } + + server := &http.Server{ + Handler: ac, + Addr: fmt.Sprintf(":%v", ac.Options.Port), + TLSConfig: tlsConfig, + } + + logger.Info("Found certificates for webhook...") + if ac.Options.RegistrationDelay != 0 { + logger.Infof("Delaying admission webhook registration for %v", ac.Options.RegistrationDelay) + } + + select { + case <-time.After(ac.Options.RegistrationDelay): + for _, c := range ac.admissionControllers { + if err := c.Register(ctx, ac.Client, caCert); err != nil { + logger.Errorw("failed to register webhook", zap.Error(err)) + return err + } + } + logger.Info("Successfully registered webhook") + case <-stop: + return nil + } + + serverBootstrapErrCh := make(chan struct{}) + go func() { + if err := server.ListenAndServeTLS("", ""); err != nil { + logger.Errorw("ListenAndServeTLS for admission webhook returned error", zap.Error(err)) + close(serverBootstrapErrCh) + } + }() + + select { + case <-stop: + return server.Close() + case <-serverBootstrapErrCh: + return errors.New("webhook server bootstrap failed") + } +} + +// ServeHTTP implements the external admission webhook for mutating +// serving resources. +func (ac *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) { + var ttStart = time.Now() + logger := ac.Logger + logger.Infof("Webhook ServeHTTP request=%#v", r) + + // Verify the content type is accurate. + contentType := r.Header.Get("Content-Type") + if contentType != "application/json" { + http.Error(w, "invalid Content-Type, want `application/json`", http.StatusUnsupportedMediaType) + return + } + + var review admissionv1beta1.AdmissionReview + if err := json.NewDecoder(r.Body).Decode(&review); err != nil { + http.Error(w, fmt.Sprintf("could not decode body: %v", err), http.StatusBadRequest) + return + } + + logger = logger.With( + zap.String(logkey.Kind, fmt.Sprint(review.Request.Kind)), + zap.String(logkey.Namespace, review.Request.Namespace), + zap.String(logkey.Name, review.Request.Name), + zap.String(logkey.Operation, fmt.Sprint(review.Request.Operation)), + zap.String(logkey.Resource, fmt.Sprint(review.Request.Resource)), + zap.String(logkey.SubResource, fmt.Sprint(review.Request.SubResource)), + zap.String(logkey.UserInfo, fmt.Sprint(review.Request.UserInfo))) + ctx := logging.WithLogger(r.Context(), logger) + + if ac.WithContext != nil { + ctx = ac.WithContext(ctx) + } + + if _, ok := ac.admissionControllers[r.URL.Path]; !ok { + http.Error(w, fmt.Sprintf("no admission controller registered for: %s", r.URL.Path), http.StatusBadRequest) + return + } + + c := ac.admissionControllers[r.URL.Path] + reviewResponse := c.Admit(ctx, review.Request) + var response admissionv1beta1.AdmissionReview + if reviewResponse != nil { + response.Response = reviewResponse + response.Response.UID = review.Request.UID + } + + logger.Infof("AdmissionReview for %#v: %s/%s response=%#v", + review.Request.Kind, review.Request.Namespace, review.Request.Name, reviewResponse) + + if err := json.NewEncoder(w).Encode(response); err != nil { + http.Error(w, fmt.Sprintf("could encode response: %v", err), http.StatusInternalServerError) + return + } + + if ac.Options.StatsReporter != nil { + // Only report valid requests + ac.Options.StatsReporter.ReportRequest(review.Request, response.Response, time.Since(ttStart)) + } +} + // GetAPIServerExtensionCACert gets the Kubernetes aggregate apiserver // client CA cert used by validator. // @@ -210,44 +324,6 @@ func getOrGenerateKeyCertsFromSecret(ctx context.Context, client kubernetes.Inte return serverKey, serverCert, caCert, nil } -// validate performs validation on the provided "new" CRD. -// For legacy purposes, this also does apis.Immutable validation, -// which is deprecated and will be removed in a future release. -func validate(ctx context.Context, new apis.Validatable) error { - if apis.IsInUpdate(ctx) { - old := apis.GetBaseline(ctx) - if immutableNew, ok := new.(apis.Immutable); ok { - immutableOld, ok := old.(apis.Immutable) - if !ok { - return fmt.Errorf("unexpected type mismatch %T vs. %T", old, new) - } - if err := immutableNew.CheckImmutableFields(ctx, immutableOld); err != nil { - return err - } - } - } - - // Can't just `return new.Validate()` because it doesn't properly nil-check. - if err := new.Validate(ctx); err != nil { - return err - } - - return nil -} - -// setDefaults simply leverages apis.Defaultable to set defaults. -func setDefaults(ctx context.Context, patches duck.JSONPatch, crd GenericCRD) (duck.JSONPatch, error) { - before, after := crd.DeepCopyObject(), crd - after.SetDefaults(ctx) - - patch, err := duck.CreatePatch(before, after) - if err != nil { - return nil, err - } - - return append(patches, patch...), nil -} - func configureCerts(ctx context.Context, client kubernetes.Interface, options *ControllerOptions) (*tls.Config, []byte, error) { var apiServerCACert []byte if options.ClientAuth >= tls.VerifyClientCertIfGiven { @@ -269,109 +345,6 @@ func configureCerts(ctx context.Context, client kubernetes.Interface, options *C return tlsConfig, caCert, nil } -// Run implements the admission controller run loop. -func (ac *AdmissionController) Run(stop <-chan struct{}) error { - logger := ac.Logger - ctx := logging.WithLogger(context.TODO(), logger) - tlsConfig, caCert, err := configureCerts(ctx, ac.Client, &ac.Options) - if err != nil { - logger.Errorw("could not configure admission webhook certs", zap.Error(err)) - return err - } - - server := &http.Server{ - Handler: ac, - Addr: fmt.Sprintf(":%v", ac.Options.Port), - TLSConfig: tlsConfig, - } - - logger.Info("Found certificates for webhook...") - if ac.Options.RegistrationDelay != 0 { - logger.Infof("Delaying admission webhook registration for %v", ac.Options.RegistrationDelay) - } - - select { - case <-time.After(ac.Options.RegistrationDelay): - if err := ac.resourceAdmissionController.Register(ctx, ac.Client, caCert); err != nil { - logger.Errorw("failed to register webhook", zap.Error(err)) - return err - } - logger.Info("Successfully registered webhook") - case <-stop: - return nil - } - - serverBootstrapErrCh := make(chan struct{}) - go func() { - if err := server.ListenAndServeTLS("", ""); err != nil { - logger.Errorw("ListenAndServeTLS for admission webhook returned error", zap.Error(err)) - close(serverBootstrapErrCh) - } - }() - - select { - case <-stop: - return server.Close() - case <-serverBootstrapErrCh: - return errors.New("webhook server bootstrap failed") - } -} - -// ServeHTTP implements the external admission webhook for mutating -// serving resources. -func (ac *AdmissionController) ServeHTTP(w http.ResponseWriter, r *http.Request) { - var ttStart = time.Now() - logger := ac.Logger - logger.Infof("Webhook ServeHTTP request=%#v", r) - - // Verify the content type is accurate. - contentType := r.Header.Get("Content-Type") - if contentType != "application/json" { - http.Error(w, "invalid Content-Type, want `application/json`", http.StatusUnsupportedMediaType) - return - } - - var review admissionv1beta1.AdmissionReview - if err := json.NewDecoder(r.Body).Decode(&review); err != nil { - http.Error(w, fmt.Sprintf("could not decode body: %v", err), http.StatusBadRequest) - return - } - - logger = logger.With( - zap.String(logkey.Kind, fmt.Sprint(review.Request.Kind)), - zap.String(logkey.Namespace, review.Request.Namespace), - zap.String(logkey.Name, review.Request.Name), - zap.String(logkey.Operation, fmt.Sprint(review.Request.Operation)), - zap.String(logkey.Resource, fmt.Sprint(review.Request.Resource)), - zap.String(logkey.SubResource, fmt.Sprint(review.Request.SubResource)), - zap.String(logkey.UserInfo, fmt.Sprint(review.Request.UserInfo))) - ctx := logging.WithLogger(r.Context(), logger) - - if ac.WithContext != nil { - ctx = ac.WithContext(ctx) - } - - reviewResponse := ac.resourceAdmissionController.Admit(ctx, review.Request) - var response admissionv1beta1.AdmissionReview - if reviewResponse != nil { - response.Response = reviewResponse - response.Response.UID = review.Request.UID - } - - logger.Infof("AdmissionReview for %#v: %s/%s response=%#v", - review.Request.Kind, review.Request.Namespace, review.Request.Name, reviewResponse) - - if err := json.NewEncoder(w).Encode(response); err != nil { - http.Error(w, fmt.Sprintf("could encode response: %v", err), http.StatusInternalServerError) - return - } - - if ac.Options.StatsReporter != nil { - // Only report valid requests - ac.Options.StatsReporter.ReportRequest(review.Request, response.Response, time.Since(ttStart)) - } -} - func makeErrorStatus(reason string, args ...interface{}) *admissionv1beta1.AdmissionResponse { result := apierrors.NewBadRequest(fmt.Sprintf(reason, args...)).Status() return &admissionv1beta1.AdmissionResponse{ diff --git a/vendor/knative.dev/pkg/websocket/connection.go b/vendor/knative.dev/pkg/websocket/connection.go index e37202e5..64603925 100644 --- a/vendor/knative.dev/pkg/websocket/connection.go +++ b/vendor/knative.dev/pkg/websocket/connection.go @@ -155,7 +155,7 @@ func NewDurableConnection(target string, messageChan chan []byte, logger *zap.Su select { case <-ticker.C: if err := c.write(websocket.PingMessage, []byte{}); err != nil { - logger.Errorw("Failed to send ping message", zap.Error(err)) + logger.Errorw("Failed to send ping message to "+target, zap.Error(err)) } case <-c.closeChan: return