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 }