agones.dev/agones@v1.53.0/pkg/util/runtime/runtime.go (about)

     1  // Copyright 2017 Google LLC All Rights Reserved.
     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  
    15  // Package runtime handles runtime errors
    16  // Wraps and reconfigures functionality in apimachinery/pkg/runtime
    17  package runtime
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"time"
    23  
    24  	gwruntime "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
    25  	"github.com/pkg/errors"
    26  	"github.com/sirupsen/logrus"
    27  	"google.golang.org/protobuf/encoding/protojson"
    28  	"k8s.io/apimachinery/pkg/util/runtime"
    29  	restclient "k8s.io/client-go/rest"
    30  	clientcmd "k8s.io/client-go/tools/clientcmd"
    31  )
    32  
    33  const sourceKey = "source"
    34  
    35  // stackTracer is the pkg/errors stacktrace interface
    36  type stackTracer interface {
    37  	StackTrace() errors.StackTrace
    38  }
    39  
    40  // replace the standard glog error logger, with a logrus one
    41  func init() {
    42  	logrus.SetFormatter(&logrus.JSONFormatter{
    43  		TimestampFormat: time.RFC3339Nano,
    44  		FieldMap: logrus.FieldMap{
    45  			logrus.FieldKeyTime:  "time",
    46  			logrus.FieldKeyLevel: "severity",
    47  			logrus.FieldKeyMsg:   "message",
    48  		},
    49  	})
    50  
    51  	runtime.ErrorHandlers[0] = func(_ context.Context, err error, _ string, _ ...interface{}) {
    52  		if stackTrace, ok := err.(stackTracer); ok {
    53  			var stack []string
    54  			for _, f := range stackTrace.StackTrace() {
    55  				stack = append(stack, fmt.Sprintf("%+v", f))
    56  			}
    57  			logrus.WithField("stack", stack).Error(err)
    58  		} else {
    59  			logrus.Error(err)
    60  		}
    61  	}
    62  }
    63  
    64  // SetLevel select level to filter logger output
    65  func SetLevel(level logrus.Level) {
    66  	logrus.SetLevel(level)
    67  }
    68  
    69  // HandleError wraps runtime.HandleError so that it is possible to
    70  // use WithField with logrus.
    71  func HandleError(logger *logrus.Entry, err error) {
    72  	if logger != nil {
    73  		// it's a bit of a double handle, but I can't see a better way to do it
    74  		logger.WithError(err).Error()
    75  	}
    76  	runtime.HandleError(err)
    77  }
    78  
    79  // Must panics if there is an error
    80  func Must(err error) {
    81  	if err != nil {
    82  		panic(err)
    83  	}
    84  }
    85  
    86  // NewLoggerWithSource returns a logrus.Entry to use when you want to specify an source
    87  func NewLoggerWithSource(source string) *logrus.Entry {
    88  	return logrus.WithField(sourceKey, source)
    89  }
    90  
    91  // NewLoggerWithType returns a logrus.Entry to use when you want to use a data type as the source
    92  // such as when you have a struct with methods
    93  func NewLoggerWithType(obj interface{}) *logrus.Entry {
    94  	return NewLoggerWithSource(fmt.Sprintf("%T", obj))
    95  }
    96  
    97  // NewServerMux returns a ServeMux which is a request multiplexer for grpc-gateway.
    98  // It matches http requests to pattern and invokes the corresponding handler.
    99  // ref: https://grpc-ecosystem.github.io/grpc-gateway/docs/development/grpc-gateway_v2_migration_guide/#we-now-emit-default-values-for-all-fields
   100  func NewServerMux() *gwruntime.ServeMux {
   101  	mux := gwruntime.NewServeMux(
   102  		gwruntime.WithMarshalerOption(gwruntime.MIMEWildcard, &gwruntime.HTTPBodyMarshaler{
   103  			Marshaler: &gwruntime.JSONPb{
   104  				MarshalOptions: protojson.MarshalOptions{
   105  					UseProtoNames:   true,
   106  					EmitUnpopulated: true,
   107  				},
   108  				UnmarshalOptions: protojson.UnmarshalOptions{
   109  					DiscardUnknown: true,
   110  				},
   111  			},
   112  		}),
   113  	)
   114  	return mux
   115  }
   116  
   117  // InClusterBuildConfig is a helper function that first attempts to build configurations
   118  // using InClusterConfig(). If InClusterConfig is unsuccessful, it then tries to build
   119  // configurations from a kubeconfigPath. This path is typically passed in as a command line
   120  // flag for cluster components. If neither the InClusterConfig nor the kubeconfigPath
   121  // are successful, the function logs a warning and falls back to a default configuration.
   122  func InClusterBuildConfig(logger *logrus.Entry, kubeconfigPath string) (*restclient.Config, error) {
   123  	kubeconfig, err := restclient.InClusterConfig()
   124  	if err == nil {
   125  		return kubeconfig, nil
   126  	}
   127  	logger.WithError(err).Warning("Error creating inClusterConfig, trying to build config from flags", err)
   128  
   129  	if kubeconfigPath == "" {
   130  		logrus.Warning("No kubeconfigPath provided. Attempting to use a default configuration.")
   131  	}
   132  
   133  	return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
   134  		&clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfigPath},
   135  		&clientcmd.ConfigOverrides{}).ClientConfig()
   136  }