148 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			148 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| Copyright 2020 The Kubernetes 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 rest
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"net/http"
 | |
| 	"sync"
 | |
| 
 | |
| 	"k8s.io/klog/v2"
 | |
| 
 | |
| 	"k8s.io/apimachinery/pkg/util/net"
 | |
| )
 | |
| 
 | |
| // WarningHandler is an interface for handling warning headers
 | |
| type WarningHandler interface {
 | |
| 	// HandleWarningHeader is called with the warn code, agent, and text when a warning header is countered.
 | |
| 	HandleWarningHeader(code int, agent string, text string)
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	defaultWarningHandler     WarningHandler = WarningLogger{}
 | |
| 	defaultWarningHandlerLock sync.RWMutex
 | |
| )
 | |
| 
 | |
| // SetDefaultWarningHandler sets the default handler clients use when warning headers are encountered.
 | |
| // By default, warnings are logged. Several built-in implementations are provided:
 | |
| //  - NoWarnings suppresses warnings.
 | |
| //  - WarningLogger logs warnings.
 | |
| //  - NewWarningWriter() outputs warnings to the provided writer.
 | |
| func SetDefaultWarningHandler(l WarningHandler) {
 | |
| 	defaultWarningHandlerLock.Lock()
 | |
| 	defer defaultWarningHandlerLock.Unlock()
 | |
| 	defaultWarningHandler = l
 | |
| }
 | |
| func getDefaultWarningHandler() WarningHandler {
 | |
| 	defaultWarningHandlerLock.RLock()
 | |
| 	defer defaultWarningHandlerLock.RUnlock()
 | |
| 	l := defaultWarningHandler
 | |
| 	return l
 | |
| }
 | |
| 
 | |
| // NoWarnings is an implementation of WarningHandler that suppresses warnings.
 | |
| type NoWarnings struct{}
 | |
| 
 | |
| func (NoWarnings) HandleWarningHeader(code int, agent string, message string) {}
 | |
| 
 | |
| // WarningLogger is an implementation of WarningHandler that logs code 299 warnings
 | |
| type WarningLogger struct{}
 | |
| 
 | |
| func (WarningLogger) HandleWarningHeader(code int, agent string, message string) {
 | |
| 	if code != 299 || len(message) == 0 {
 | |
| 		return
 | |
| 	}
 | |
| 	klog.Warning(message)
 | |
| }
 | |
| 
 | |
| type warningWriter struct {
 | |
| 	// out is the writer to output warnings to
 | |
| 	out io.Writer
 | |
| 	// opts contains options controlling warning output
 | |
| 	opts WarningWriterOptions
 | |
| 	// writtenLock guards written and writtenCount
 | |
| 	writtenLock  sync.Mutex
 | |
| 	writtenCount int
 | |
| 	written      map[string]struct{}
 | |
| }
 | |
| 
 | |
| // WarningWriterOptions controls the behavior of a WarningHandler constructed using NewWarningWriter()
 | |
| type WarningWriterOptions struct {
 | |
| 	// Deduplicate indicates a given warning message should only be written once.
 | |
| 	// Setting this to true in a long-running process handling many warnings can result in increased memory use.
 | |
| 	Deduplicate bool
 | |
| 	// Color indicates that warning output can include ANSI color codes
 | |
| 	Color bool
 | |
| }
 | |
| 
 | |
| // NewWarningWriter returns an implementation of WarningHandler that outputs code 299 warnings to the specified writer.
 | |
| func NewWarningWriter(out io.Writer, opts WarningWriterOptions) *warningWriter {
 | |
| 	h := &warningWriter{out: out, opts: opts}
 | |
| 	if opts.Deduplicate {
 | |
| 		h.written = map[string]struct{}{}
 | |
| 	}
 | |
| 	return h
 | |
| }
 | |
| 
 | |
| const (
 | |
| 	yellowColor = "\u001b[33;1m"
 | |
| 	resetColor  = "\u001b[0m"
 | |
| )
 | |
| 
 | |
| // HandleWarningHeader prints warnings with code=299 to the configured writer.
 | |
| func (w *warningWriter) HandleWarningHeader(code int, agent string, message string) {
 | |
| 	if code != 299 || len(message) == 0 {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	w.writtenLock.Lock()
 | |
| 	defer w.writtenLock.Unlock()
 | |
| 
 | |
| 	if w.opts.Deduplicate {
 | |
| 		if _, alreadyWritten := w.written[message]; alreadyWritten {
 | |
| 			return
 | |
| 		}
 | |
| 		w.written[message] = struct{}{}
 | |
| 	}
 | |
| 	w.writtenCount++
 | |
| 
 | |
| 	if w.opts.Color {
 | |
| 		fmt.Fprintf(w.out, "%sWarning:%s %s\n", yellowColor, resetColor, message)
 | |
| 	} else {
 | |
| 		fmt.Fprintf(w.out, "Warning: %s\n", message)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (w *warningWriter) WarningCount() int {
 | |
| 	w.writtenLock.Lock()
 | |
| 	defer w.writtenLock.Unlock()
 | |
| 	return w.writtenCount
 | |
| }
 | |
| 
 | |
| func handleWarnings(headers http.Header, handler WarningHandler) []net.WarningHeader {
 | |
| 	if handler == nil {
 | |
| 		handler = getDefaultWarningHandler()
 | |
| 	}
 | |
| 
 | |
| 	warnings, _ := net.ParseWarningHeaders(headers["Warning"])
 | |
| 	for _, warning := range warnings {
 | |
| 		handler.HandleWarningHeader(warning.Code, warning.Agent, warning.Text)
 | |
| 	}
 | |
| 	return warnings
 | |
| }
 | 
