k8s.io/apiserver@v0.31.1/pkg/server/options/tracing.go (about)

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package options
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"io/ioutil"
    23  	"net"
    24  
    25  	"github.com/spf13/pflag"
    26  	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
    27  	"go.opentelemetry.io/otel/sdk/resource"
    28  	"go.opentelemetry.io/otel/semconv/v1.12.0"
    29  	"google.golang.org/grpc"
    30  
    31  	"k8s.io/apimachinery/pkg/runtime"
    32  	"k8s.io/apimachinery/pkg/runtime/serializer"
    33  	"k8s.io/apiserver/pkg/apis/apiserver"
    34  	"k8s.io/apiserver/pkg/apis/apiserver/install"
    35  	"k8s.io/apiserver/pkg/features"
    36  	"k8s.io/apiserver/pkg/server"
    37  	"k8s.io/apiserver/pkg/server/egressselector"
    38  	"k8s.io/apiserver/pkg/util/feature"
    39  	tracing "k8s.io/component-base/tracing"
    40  	tracingapi "k8s.io/component-base/tracing/api/v1"
    41  	"k8s.io/utils/path"
    42  )
    43  
    44  const apiserverService = "apiserver"
    45  
    46  var (
    47  	cfgScheme = runtime.NewScheme()
    48  	codecs    = serializer.NewCodecFactory(cfgScheme)
    49  )
    50  
    51  func init() {
    52  	install.Install(cfgScheme)
    53  }
    54  
    55  // TracingOptions contain configuration options for tracing
    56  // exporters
    57  type TracingOptions struct {
    58  	// ConfigFile is the file path with api-server tracing configuration.
    59  	ConfigFile string
    60  }
    61  
    62  // NewTracingOptions creates a new instance of TracingOptions
    63  func NewTracingOptions() *TracingOptions {
    64  	return &TracingOptions{}
    65  }
    66  
    67  // AddFlags adds flags related to tracing to the specified FlagSet
    68  func (o *TracingOptions) AddFlags(fs *pflag.FlagSet) {
    69  	if o == nil {
    70  		return
    71  	}
    72  
    73  	fs.StringVar(&o.ConfigFile, "tracing-config-file", o.ConfigFile,
    74  		"File with apiserver tracing configuration.")
    75  }
    76  
    77  // ApplyTo fills up Tracing config with options.
    78  func (o *TracingOptions) ApplyTo(es *egressselector.EgressSelector, c *server.Config) error {
    79  	if o == nil || o.ConfigFile == "" {
    80  		return nil
    81  	}
    82  	if !feature.DefaultFeatureGate.Enabled(features.APIServerTracing) {
    83  		return fmt.Errorf("APIServerTracing feature is not enabled, but tracing config file was provided")
    84  	}
    85  
    86  	traceConfig, err := ReadTracingConfiguration(o.ConfigFile)
    87  	if err != nil {
    88  		return fmt.Errorf("failed to read tracing config: %v", err)
    89  	}
    90  
    91  	errs := tracingapi.ValidateTracingConfiguration(traceConfig, feature.DefaultFeatureGate, nil)
    92  	if len(errs) > 0 {
    93  		return fmt.Errorf("failed to validate tracing configuration: %v", errs.ToAggregate())
    94  	}
    95  
    96  	opts := []otlptracegrpc.Option{}
    97  	if es != nil {
    98  		// Only use the egressselector dialer if egressselector is enabled.
    99  		// Endpoint is on the "ControlPlane" network
   100  		egressDialer, err := es.Lookup(egressselector.ControlPlane.AsNetworkContext())
   101  		if err != nil {
   102  			return err
   103  		}
   104  		if egressDialer != nil {
   105  			otelDialer := func(ctx context.Context, addr string) (net.Conn, error) {
   106  				return egressDialer(ctx, "tcp", addr)
   107  			}
   108  			opts = append(opts, otlptracegrpc.WithDialOption(grpc.WithContextDialer(otelDialer)))
   109  		}
   110  	}
   111  
   112  	resourceOpts := []resource.Option{
   113  		resource.WithAttributes(
   114  			semconv.ServiceNameKey.String(apiserverService),
   115  			semconv.ServiceInstanceIDKey.String(c.APIServerID),
   116  		),
   117  	}
   118  	tp, err := tracing.NewProvider(context.Background(), traceConfig, opts, resourceOpts)
   119  	if err != nil {
   120  		return err
   121  	}
   122  	c.TracerProvider = tp
   123  	if c.LoopbackClientConfig != nil {
   124  		c.LoopbackClientConfig.Wrap(tracing.WrapperFor(c.TracerProvider))
   125  	}
   126  	return nil
   127  }
   128  
   129  // Validate verifies flags passed to TracingOptions.
   130  func (o *TracingOptions) Validate() (errs []error) {
   131  	if o == nil || o.ConfigFile == "" {
   132  		return
   133  	}
   134  
   135  	if exists, err := path.Exists(path.CheckFollowSymlink, o.ConfigFile); !exists {
   136  		errs = append(errs, fmt.Errorf("tracing-config-file %s does not exist", o.ConfigFile))
   137  	} else if err != nil {
   138  		errs = append(errs, fmt.Errorf("error checking if tracing-config-file %s exists: %v", o.ConfigFile, err))
   139  	}
   140  	return
   141  }
   142  
   143  // ReadTracingConfiguration reads the tracing configuration from a file
   144  func ReadTracingConfiguration(configFilePath string) (*tracingapi.TracingConfiguration, error) {
   145  	if configFilePath == "" {
   146  		return nil, fmt.Errorf("tracing config file was empty")
   147  	}
   148  	data, err := ioutil.ReadFile(configFilePath)
   149  	if err != nil {
   150  		return nil, fmt.Errorf("unable to read tracing configuration from %q: %v", configFilePath, err)
   151  	}
   152  	internalConfig := &apiserver.TracingConfiguration{}
   153  	// this handles json/yaml/whatever, and decodes all registered version to the internal version
   154  	if err := runtime.DecodeInto(codecs.UniversalDecoder(), data, internalConfig); err != nil {
   155  		return nil, fmt.Errorf("unable to decode tracing configuration data: %v", err)
   156  	}
   157  	return &internalConfig.TracingConfiguration, nil
   158  }