forked from TykTechnologies/tyk
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Load open tracing configuration options with environment variables (T…
…ykTechnologies#3112) <!-- Provide a general summary of your changes in the Title above --> ## Description <!-- Describe your changes in detail --> This refactors loading of configuration with environment variables to load open tracing configurations for both zipkin and jaeger. ## Related Issue <!-- This project only accepts pull requests related to open issues --> <!-- If suggesting a new feature or change, please discuss it in an issue first --> <!-- If fixing a bug, there should be an issue describing it with steps to reproduce --> <!-- Please link to the issue here --> fixes TykTechnologies#3104 ## Motivation and Context <!-- Why is this change required? What problem does it solve? --> Opent tracing options are passed as `map[string]interface{}` this was hard to map to environment variables. This PR add support for custom configuration option loaders that are invoked after the default envconfig loader is complete allowing safe and sane passing of opentracing options as env vars. Supported zipkin env vars ``` TYK_GW_TRACER_OPTIONS_REPORTER_URL TYK_GW_TRACER_OPTIONS_REPORTER_BATCHSIZE TYK_GW_TRACER_OPTIONS_REPORTER_MAXBACKLOG TYK_GW_TRACER_OPTIONS_SAMPLER_NAME TYK_GW_TRACER_OPTIONS_SAMPLER_RATE TYK_GW_TRACER_OPTIONS_SAMPLER_SALT TYK_GW_TRACER_OPTIONS_SAMPLER_MOD ``` supported jaeger env vars ``` TYK_GW_TRACER_OPTIONS_SERVICENAME TYK_GW_TRACER_OPTIONS_DISABLED TYK_GW_TRACER_OPTIONS_RPCMETRICS TYK_GW_TRACER_OPTIONS_TAGS TYK_GW_TRACER_OPTIONS_SAMPLER_TYPE TYK_GW_TRACER_OPTIONS_SAMPLER_PARAM TYK_GW_TRACER_OPTIONS_SAMPLER_SAMPLINGSERVERURL TYK_GW_TRACER_OPTIONS_SAMPLER_MAXOPERATIONS TYK_GW_TRACER_OPTIONS_SAMPLER_SAMPLINGREFRESHINTERVAL TYK_GW_TRACER_OPTIONS_REPORTER_QUEUESIZE TYK_GW_TRACER_OPTIONS_REPORTER_BUFFERFLUSHINTERVAL TYK_GW_TRACER_OPTIONS_REPORTER_LOGSPANS TYK_GW_TRACER_OPTIONS_REPORTER_LOCALAGENTHOSTPORT TYK_GW_TRACER_OPTIONS_REPORTER_COLLECTORENDPOINT TYK_GW_TRACER_OPTIONS_REPORTER_USER TYK_GW_TRACER_OPTIONS_REPORTER_PASSWORD TYK_GW_TRACER_OPTIONS_HEADERS_JAEGERDEBUGHEADER TYK_GW_TRACER_OPTIONS_HEADERS_JAEGERBAGGAGEHEADER TYK_GW_TRACER_OPTIONS_HEADERS_TRACECONTEXTHEADERNAME TYK_GW_TRACER_OPTIONS_HEADERS_TRACEBAGGAGEHEADERPREFIX TYK_GW_TRACER_OPTIONS_BAGGAGERESTRICTIONS_DENYBAGGAGEONINITIALIZATIONFAILURE TYK_GW_TRACER_OPTIONS_BAGGAGERESTRICTIONS_HOSTPORT TYK_GW_TRACER_OPTIONS_BAGGAGERESTRICTIONS_REFRESHINTERVAL TYK_GW_TRACER_OPTIONS_THROTTLER_HOSTPORT TYK_GW_TRACER_OPTIONS_THROTTLER_REFRESHINTERVAL TYK_GW_TRACER_OPTIONS_THROTTLER_SYNCHRONOUSINITIALIZATION ``` ## How This Has Been Tested <!-- Please describe in detail how you tested your changes --> <!-- Include details of your testing environment, and the tests you ran to see how your change affects other areas of the code, etc. --> Provided with unit tests ``` go test ./config ``` ## Screenshots (if appropriate) ## Types of changes <!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply: --> - [x] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) ## Checklist <!-- Go over all the following points, and put an `x` in all the boxes that apply --> <!-- If you're unsure about any of these, don't hesitate to ask; we're here to help! --> - [x] Make sure you are requesting to **pull a topic/feature/bugfix branch** (right side). If pulling from your own fork, don't request your `master`! - [x] Make sure you are making a pull request against the **`master` branch** (left side). Also, you should start *your branch* off *our latest `master`*. - [ ] My change requires a change to the documentation. - [ ] If you've changed APIs, describe what needs to be updated in the documentation. - [ ] I have updated the documentation accordingly. - [ ] Modules and vendor dependencies have been updated; run `go mod tidy && go mod vendor` - [ ] When updating library version must provide reason/explanation for this update. - [ ] I have added tests to cover my changes. - [ ] All new and existing tests passed. - [ ] Check your code additions will not fail linting checks: - [ ] `go fmt -s` - [ ] `go vet`
- Loading branch information
Showing
5 changed files
with
316 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
package config | ||
|
||
import ( | ||
"encoding/json" | ||
|
||
"github.com/kelseyhightower/envconfig" | ||
jaeger "github.com/uber/jaeger-client-go/config" | ||
) | ||
|
||
// ZipkinConfig configuration options used to initialize openzipkin opentracing | ||
// client. | ||
type ZipkinConfig struct { | ||
Reporter Reporter `json:"reporter"` | ||
Sampler Sampler `json:"sampler"` | ||
} | ||
|
||
type Reporter struct { | ||
// URL connection url to the zipkin server | ||
URL string `json:"url"` | ||
BatchSize int `json:"batch_size"` | ||
MaxBacklog int `json:"max_backlog"` | ||
} | ||
|
||
type Sampler struct { | ||
//Name is the name of the sampler to use. Options are | ||
// | ||
// "boundary" | ||
// is appropriate for high-traffic instrumentation who | ||
// provision random trace ids, and make the sampling decision only once. | ||
// It defends against nodes in the cluster selecting exactly the same ids. | ||
// | ||
// "count" | ||
// is appropriate for low-traffic instrumentation or | ||
// those who do not provision random trace ids. It is not appropriate for | ||
// collectors as the sampling decision isn't idempotent (consistent based | ||
// on trace id). | ||
// | ||
// "mod" | ||
// provides a generic type Sampler | ||
Name string `json:"name"` | ||
//Rate is used by both "boundary" and "count" samplers | ||
Rate float64 `json:"rate"` | ||
//Salt is used by "boundary" sampler | ||
Salt int64 `json:"salt"` | ||
// Mod is only used when sampler is mod | ||
Mod uint64 `json:"mod"` | ||
} | ||
|
||
// DecodeJSON marshals src to json and tries to unmarshal the result into | ||
// dest. | ||
func DecodeJSON(dest, src interface{}) error { | ||
b, err := json.Marshal(src) | ||
if err != nil { | ||
return err | ||
} | ||
return json.Unmarshal(b, dest) | ||
} | ||
|
||
// loadZipkin tries to lad zipkin configuration from environment variables. | ||
// | ||
// list of zipkin configuration env variables | ||
// | ||
// TYK_GW_TRACER_OPTIONS_REPORTER_URL | ||
// TYK_GW_TRACER_OPTIONS_REPORTER_BATCHSIZE | ||
// TYK_GW_TRACER_OPTIONS_REPORTER_MAXBACKLOG | ||
// TYK_GW_TRACER_OPTIONS_SAMPLER_NAME | ||
// TYK_GW_TRACER_OPTIONS_SAMPLER_RATE | ||
// TYK_GW_TRACER_OPTIONS_SAMPLER_SALT | ||
// TYK_GW_TRACER_OPTIONS_SAMPLER_MOD | ||
func loadZipkin(prefix string, c *Config) error { | ||
if c.Tracer.Name != "zipkin" || c.Tracer.Options == nil { | ||
return nil | ||
} | ||
var zip ZipkinConfig | ||
if err := DecodeJSON(&zip, c.Tracer.Options); err != nil { | ||
return err | ||
} | ||
qualifyPrefix := prefix + "_TRACER_OPTIONS" | ||
err := envconfig.Process(qualifyPrefix, &zip) | ||
if err != nil { | ||
return err | ||
} | ||
o := make(map[string]interface{}) | ||
if err := DecodeJSON(&o, zip); err != nil { | ||
return err | ||
} | ||
c.Tracer.Options = o | ||
return nil | ||
} | ||
|
||
// loads jaeger configuration from environment variables. | ||
// | ||
// List of jaeger configuration env vars | ||
// | ||
// TYK_GW_TRACER_OPTIONS_SERVICENAME | ||
// TYK_GW_TRACER_OPTIONS_DISABLED | ||
// TYK_GW_TRACER_OPTIONS_RPCMETRICS | ||
// TYK_GW_TRACER_OPTIONS_TAGS | ||
// TYK_GW_TRACER_OPTIONS_SAMPLER_TYPE | ||
// TYK_GW_TRACER_OPTIONS_SAMPLER_PARAM | ||
// TYK_GW_TRACER_OPTIONS_SAMPLER_SAMPLINGSERVERURL | ||
// TYK_GW_TRACER_OPTIONS_SAMPLER_MAXOPERATIONS | ||
// TYK_GW_TRACER_OPTIONS_SAMPLER_SAMPLINGREFRESHINTERVAL | ||
// TYK_GW_TRACER_OPTIONS_REPORTER_QUEUESIZE | ||
// TYK_GW_TRACER_OPTIONS_REPORTER_BUFFERFLUSHINTERVAL | ||
// TYK_GW_TRACER_OPTIONS_REPORTER_LOGSPANS | ||
// TYK_GW_TRACER_OPTIONS_REPORTER_LOCALAGENTHOSTPORT | ||
// TYK_GW_TRACER_OPTIONS_REPORTER_COLLECTORENDPOINT | ||
// TYK_GW_TRACER_OPTIONS_REPORTER_USER | ||
// TYK_GW_TRACER_OPTIONS_REPORTER_PASSWORD | ||
// TYK_GW_TRACER_OPTIONS_HEADERS_JAEGERDEBUGHEADER | ||
// TYK_GW_TRACER_OPTIONS_HEADERS_JAEGERBAGGAGEHEADER | ||
// TYK_GW_TRACER_OPTIONS_HEADERS_TRACECONTEXTHEADERNAME | ||
// TYK_GW_TRACER_OPTIONS_HEADERS_TRACEBAGGAGEHEADERPREFIX | ||
// TYK_GW_TRACER_OPTIONS_BAGGAGERESTRICTIONS_DENYBAGGAGEONINITIALIZATIONFAILURE | ||
// TYK_GW_TRACER_OPTIONS_BAGGAGERESTRICTIONS_HOSTPORT | ||
// TYK_GW_TRACER_OPTIONS_BAGGAGERESTRICTIONS_REFRESHINTERVAL | ||
// TYK_GW_TRACER_OPTIONS_THROTTLER_HOSTPORT | ||
// TYK_GW_TRACER_OPTIONS_THROTTLER_REFRESHINTERVAL | ||
// TYK_GW_TRACER_OPTIONS_THROTTLER_SYNCHRONOUSINITIALIZATION | ||
func loadJaeger(prefix string, c *Config) error { | ||
if c.Tracer.Name != "jaeger" || c.Tracer.Options == nil { | ||
return nil | ||
} | ||
var j jaeger.Configuration | ||
if err := DecodeJSON(&j, c.Tracer.Options); err != nil { | ||
return err | ||
} | ||
qualifyPrefix := prefix + "_TRACER_OPTIONS" | ||
err := envconfig.Process(qualifyPrefix, &j) | ||
if err != nil { | ||
return err | ||
} | ||
o := make(map[string]interface{}) | ||
if err := DecodeJSON(&o, j); err != nil { | ||
return err | ||
} | ||
c.Tracer.Options = o | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package config | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"reflect" | ||
"testing" | ||
|
||
jaeger "github.com/uber/jaeger-client-go/config" | ||
) | ||
|
||
func TestLoadZipkin(t *testing.T) { | ||
base := ZipkinConfig{ | ||
Reporter: Reporter{ | ||
URL: "repoturl", | ||
BatchSize: 10, | ||
MaxBacklog: 20, | ||
}, | ||
Sampler: Sampler{ | ||
Name: "boundary", | ||
Rate: 10.1, | ||
Salt: 10, | ||
Mod: 12, | ||
}, | ||
} | ||
sample := []struct { | ||
env string | ||
value string | ||
}{ | ||
{"TYK_GW_TRACER_OPTIONS_REPORTER_URL", base.Reporter.URL}, | ||
{"TYK_GW_TRACER_OPTIONS_REPORTER_BATCHSIZE", fmt.Sprint(base.Reporter.BatchSize)}, | ||
{"TYK_GW_TRACER_OPTIONS_REPORTER_MAXBACKLOG", fmt.Sprint(base.Reporter.MaxBacklog)}, | ||
{"TYK_GW_TRACER_OPTIONS_SAMPLER_NAME", base.Sampler.Name}, | ||
{"TYK_GW_TRACER_OPTIONS_SAMPLER_SALT", fmt.Sprint(base.Sampler.Salt)}, | ||
{"TYK_GW_TRACER_OPTIONS_SAMPLER_MOD", fmt.Sprint(base.Sampler.Mod)}, | ||
} | ||
for _, v := range sample { | ||
err := os.Setenv(v.env, v.value) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
} | ||
defer func() { | ||
for _, v := range sample { | ||
os.Unsetenv(v.env) | ||
} | ||
}() | ||
t.Run("Returns nil when it is not zipkin config", func(t *testing.T) { | ||
conf := &Config{} | ||
err := loadZipkin(envPrefix, conf) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if conf.Tracer.Options != nil { | ||
t.Error("expected options to be nil") | ||
} | ||
}) | ||
t.Run("handles nil options", func(t *testing.T) { | ||
conf := &Config{Tracer: Tracer{Name: "zipkin"}} | ||
err := loadZipkin(envPrefix, conf) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if conf.Tracer.Options != nil { | ||
t.Error("expected options to be nil") | ||
} | ||
}) | ||
|
||
t.Run("loads env vars", func(t *testing.T) { | ||
o := make(map[string]interface{}) | ||
err := DecodeJSON(&o, base) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
conf := &Config{Tracer: Tracer{Name: "zipkin", Options: o}} | ||
err = loadZipkin(envPrefix, conf) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
var got ZipkinConfig | ||
err = DecodeJSON(&got, conf.Tracer.Options) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if !reflect.DeepEqual(base, got) { | ||
t.Errorf("expected %#v got %#v", base, got) | ||
} | ||
}) | ||
} | ||
func TestLoadJaeger(t *testing.T) { | ||
base := &jaeger.Configuration{ServiceName: "jaeger-test-service"} | ||
sample := []struct { | ||
env string | ||
value string | ||
}{ | ||
{"TYK_GW_TRACER_OPTIONS_SERVICENAME", base.ServiceName}, | ||
} | ||
for _, v := range sample { | ||
err := os.Setenv(v.env, v.value) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
} | ||
defer func() { | ||
for _, v := range sample { | ||
os.Unsetenv(v.env) | ||
} | ||
}() | ||
t.Run("Returns nil when it is not jaeger config", func(t *testing.T) { | ||
conf := &Config{} | ||
err := loadJaeger(envPrefix, conf) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if conf.Tracer.Options != nil { | ||
t.Error("expected options to be nil") | ||
} | ||
}) | ||
t.Run("Handles nil options", func(t *testing.T) { | ||
conf := &Config{Tracer: Tracer{Name: "jaeger"}} | ||
err := loadJaeger(envPrefix, conf) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if conf.Tracer.Options != nil { | ||
t.Error("expected options to be nil") | ||
} | ||
}) | ||
|
||
t.Run("Loads env vars", func(t *testing.T) { | ||
o := make(map[string]interface{}) | ||
err := DecodeJSON(&o, base) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
conf := &Config{Tracer: Tracer{Name: "jaeger", Options: o}} | ||
err = loadJaeger(envPrefix, conf) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
var got jaeger.Configuration | ||
err = DecodeJSON(&got, conf.Tracer.Options) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if base.ServiceName != got.ServiceName { | ||
t.Errorf("expected %#v got %#v", base.ServiceName, got.ServiceName) | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,14 @@ | ||
package openzipkin | ||
|
||
import "encoding/json" | ||
|
||
type Config struct { | ||
Reporter Reporter `json:"reporter"` | ||
Sampler Sampler `json:"sampler"` | ||
} | ||
|
||
type Reporter struct { | ||
URL string `json:"url"` | ||
BatchSize int `json:"batch_size"` | ||
MaxBacklog int `json:"max_backlog"` | ||
} | ||
|
||
type Sampler struct { | ||
Name string `json:"name"` | ||
Rate float64 `json:"rate"` | ||
Salt int64 `json:"salt"` | ||
Mod uint64 `json:"mod"` | ||
} | ||
|
||
func Load(opts map[string]interface{}) (*Config, error) { | ||
b, err := json.Marshal(opts) | ||
if err != nil { | ||
import ( | ||
"github.com/TykTechnologies/tyk/config" | ||
) | ||
|
||
// Load retusn a zipkin configuration from the opts. | ||
func Load(opts map[string]interface{}) (*config.ZipkinConfig, error) { | ||
var c config.ZipkinConfig | ||
if err := config.DecodeJSON(&c, opts); err != nil { | ||
return nil, err | ||
} | ||
var c Config | ||
err = json.Unmarshal(b, &c) | ||
return &c, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters