From 5843bac5f921102d24c8f5e80f229b02cd3e2d95 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Mon, 23 Mar 2020 13:26:05 +0100 Subject: [PATCH 1/5] ad i18n and renderer and move headers --- go.mod | 7 +- go.sum | 12 ++- internal/api/grpc/auth_interceptor.go | 3 +- internal/api/grpc/header.go | 8 +- internal/api/header.go | 8 ++ internal/api/html/i18n.go | 108 ++++++++++++++++++++++++++ internal/api/html/renderer.go | 82 +++++++++++++++++++ 7 files changed, 213 insertions(+), 15 deletions(-) create mode 100644 internal/api/header.go create mode 100644 internal/api/html/i18n.go create mode 100644 internal/api/html/renderer.go diff --git a/go.mod b/go.mod index e38a3449d6..f3882f1414 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/sprig v2.22.0+incompatible github.com/caos/logging v0.0.0-20191210002624-b3260f690a6a - github.com/ghodss/yaml v1.0.0 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b github.com/golang/mock v1.4.3 github.com/golang/protobuf v1.3.5 @@ -21,16 +20,16 @@ require ( github.com/huandu/xstrings v1.3.0 // indirect github.com/imdario/mergo v0.3.8 // indirect github.com/kr/pretty v0.1.0 // indirect - github.com/magiconair/properties v1.8.1 github.com/mitchellh/copystructure v1.0.0 // indirect + github.com/nicksnyder/go-i18n/v2 v2.0.3 github.com/stretchr/testify v1.5.1 golang.org/x/crypto v0.0.0-20200320181102-891825fb96df golang.org/x/net v0.0.0-20200320220750-118fecf932d8 // indirect golang.org/x/sys v0.0.0-20200321134203-328b4cd54aae // indirect - golang.org/x/text v0.3.2 // indirect + golang.org/x/text v0.3.2 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 google.golang.org/genproto v0.0.0-20200319113533-08878b785e9c // indirect google.golang.org/grpc v1.28.0 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect - gopkg.in/yaml.v2 v2.2.8 // indirect + gopkg.in/yaml.v2 v2.2.7 ) diff --git a/go.sum b/go.sum index 8b4fb19b9b..e5db0f7feb 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,5 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= @@ -64,12 +65,12 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/nicksnyder/go-i18n/v2 v2.0.3 h1:ks/JkQiOEhhuF6jpNvx+Wih1NIiXzUnZeZVnJuI8R8M= +github.com/nicksnyder/go-i18n/v2 v2.0.3/go.mod h1:oDab7q8XCYMRlcrBnaY/7B1eOectbvj6B1UPBT+p5jo= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -89,6 +90,7 @@ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200320181102-891825fb96df h1:lDWgvUvNnaTnNBc/dwOty86cFeKoKWbwy2wQj0gIxbU= golang.org/x/crypto v0.0.0-20200320181102-891825fb96df/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -100,6 +102,7 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200320220750-118fecf932d8 h1:1+zQlQqEEhUeStBTi653GZAnAuivZq/2hz+Iz+OP7rg= golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -111,6 +114,7 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191206220618-eeba5f6aabab h1:FvshnhkKW+LO3HWHodML8kuVX8rnJTxKm9dFPuI68UM= golang.org/x/sys v0.0.0-20191206220618-eeba5f6aabab/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200321134203-328b4cd54aae h1:3tcmuaB7wwSZtelmiv479UjUB+vviwABz7a133ZwOKQ= @@ -125,6 +129,7 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= @@ -146,12 +151,11 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= diff --git a/internal/api/grpc/auth_interceptor.go b/internal/api/grpc/auth_interceptor.go index 4e91d11714..6ade793b1e 100644 --- a/internal/api/grpc/auth_interceptor.go +++ b/internal/api/grpc/auth_interceptor.go @@ -7,6 +7,7 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "github.com/caos/zitadel/internal/api" "github.com/caos/zitadel/internal/api/auth" ) @@ -22,7 +23,7 @@ func AuthorizationInterceptor(verifier auth.TokenVerifier, authConfig *auth.Conf return nil, status.Error(codes.Unauthenticated, "auth header missing") } - orgID := GetHeader(ctx, ZitadelOrgID) + orgID := GetHeader(ctx, api.ZitadelOrgID) ctx, err := auth.CheckUserAuthorization(ctx, req, authToken, orgID, verifier, authConfig, authOpt) if err != nil { diff --git a/internal/api/grpc/header.go b/internal/api/grpc/header.go index 5d217b7518..f1498c1562 100644 --- a/internal/api/grpc/header.go +++ b/internal/api/grpc/header.go @@ -4,12 +4,8 @@ import ( "context" "github.com/grpc-ecosystem/go-grpc-middleware/util/metautils" -) -const ( - Authorization = "authorization" - - ZitadelOrgID = "x-zitadel-orgid" + "github.com/caos/zitadel/internal/api" ) func GetHeader(ctx context.Context, headername string) string { @@ -17,5 +13,5 @@ func GetHeader(ctx context.Context, headername string) string { } func GetAuthorizationHeader(ctx context.Context) string { - return GetHeader(ctx, Authorization) + return GetHeader(ctx, api.Authorization) } diff --git a/internal/api/header.go b/internal/api/header.go new file mode 100644 index 0000000000..fa8efc0282 --- /dev/null +++ b/internal/api/header.go @@ -0,0 +1,8 @@ +package api + +const ( + Authorization = "authorization" + AcceptLanguage = "Accept-Language" + + ZitadelOrgID = "x-zitadel-orgid" +) diff --git a/internal/api/html/i18n.go b/internal/api/html/i18n.go new file mode 100644 index 0000000000..dd5d272668 --- /dev/null +++ b/internal/api/html/i18n.go @@ -0,0 +1,108 @@ +package html + +import ( + "encoding/json" + "io/ioutil" + "net/http" + "path" + + "github.com/BurntSushi/toml" + "github.com/caos/logging" + "github.com/nicksnyder/go-i18n/v2/i18n" + "golang.org/x/text/language" + "gopkg.in/yaml.v2" + + "github.com/caos/zitadel/internal/api" + http_util "github.com/caos/zitadel/internal/api/http" + "github.com/caos/zitadel/internal/errors" +) + +type Translator struct { + bundle *i18n.Bundle + cookieName string + cookieHandler *http_util.CookieHandler +} + +type TranslatorConfig struct { + Path string + DefaultLanguage language.Tag + CookieName string +} + +func NewTranslator(config TranslatorConfig) (*Translator, error) { + t := new(Translator) + var err error + t.bundle, err = newBundle(config.Path, config.DefaultLanguage) + if err != nil { + return nil, err + } + t.cookieHandler = http_util.NewCookieHandler() + t.cookieName = config.CookieName + return t, nil +} + +func newBundle(i18nDir string, defaultLanguage language.Tag) (*i18n.Bundle, error) { + bundle := i18n.NewBundle(defaultLanguage) + bundle.RegisterUnmarshalFunc("yaml", yaml.Unmarshal) + bundle.RegisterUnmarshalFunc("yml", yaml.Unmarshal) + bundle.RegisterUnmarshalFunc("json", json.Unmarshal) + bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal) + files, err := ioutil.ReadDir(i18nDir) + if err != nil { + return nil, errors.ThrowNotFound(err, "HTML-MnXRie", "path not found") + } + for _, file := range files { + bundle.MustLoadMessageFile(path.Join(i18nDir, file.Name())) + } + return bundle, nil +} + +func (t *Translator) LocalizeFromRequest(r *http.Request, id string, args map[string]interface{}) string { + s, err := t.localizerFromRequest(r).Localize(&i18n.LocalizeConfig{ + MessageID: id, + TemplateData: args, + }) + if err != nil { + logging.Log("HTML-MsF5sx").WithError(err).Warnf("missing translation") + return id + } + return s +} + +func (t *Translator) Localize(id string, args map[string]interface{}) string { + s, _ := t.localizer().Localize(&i18n.LocalizeConfig{ + MessageID: id, + TemplateData: args, + }) + return s +} + +func (t *Translator) Lang(r *http.Request) language.Tag { + matcher := language.NewMatcher(t.bundle.LanguageTags()) + tag, _ := language.MatchStrings(matcher, t.langsFromRequest(r)...) + return tag +} + +func (t *Translator) SetLangCookie(w http.ResponseWriter, lang language.Tag) { + t.cookieHandler.SetCookie(w, t.cookieName, lang.String()) +} + +func (t *Translator) localizerFromRequest(r *http.Request) *i18n.Localizer { + return t.localizer(t.langsFromRequest(r)...) +} + +func (t *Translator) localizer(langs ...string) *i18n.Localizer { + return i18n.NewLocalizer(t.bundle, langs...) +} + +func (t *Translator) langsFromRequest(r *http.Request) []string { + langs := make([]string, 0) + if r != nil { + lang, err := t.cookieHandler.GetCookieValue(r, t.cookieName) + if err == nil { + langs = append(langs, lang) + } + langs = append(langs, r.Header.Get(api.AcceptLanguage)) + } + return langs +} diff --git a/internal/api/html/renderer.go b/internal/api/html/renderer.go new file mode 100644 index 0000000000..182087e31d --- /dev/null +++ b/internal/api/html/renderer.go @@ -0,0 +1,82 @@ +package html + +import ( + "net/http" + "path" + "text/template" + + "github.com/caos/logging" + "golang.org/x/text/language" +) + +const ( + TranslateFn = "t" +) + +type Renderer struct { + Templates map[string]*template.Template + i18n *Translator +} + +func NewRenderer(templatesDir string, tmplMapping map[string]string, funcs map[string]interface{}, translatorConfig TranslatorConfig) (*Renderer, error) { + var err error + r := new(Renderer) + r.i18n, err = NewTranslator(translatorConfig) + if err != nil { + return nil, err + } + r.loadTemplates(templatesDir, tmplMapping, funcs) + return r, nil +} + +func (r *Renderer) RenderTemplate(w http.ResponseWriter, req *http.Request, tmpl *template.Template, data interface{}, reqFuncs map[string]interface{}) { + reqFuncs = r.registerTranslateFn(req, reqFuncs) + if err := tmpl.Funcs(reqFuncs).Execute(w, data); err != nil { + logging.Log("HTML-lF8F6w").WithError(err).WithField("template", tmpl.Name).Error("error rendering template") + } +} + +func (r *Renderer) Localize(id string, args map[string]interface{}) string { + return r.i18n.Localize(id, args) +} + +func (r *Renderer) LocalizeFromRequest(req *http.Request, id string, args map[string]interface{}) string { + return r.i18n.LocalizeFromRequest(req, id, args) +} +func (r *Renderer) Lang(req *http.Request) language.Tag { + return r.i18n.Lang(req) +} + +func (r *Renderer) loadTemplates(templatesDir string, tmplMapping map[string]string, funcs map[string]interface{}) { + funcs = r.registerTranslateFn(nil, funcs) + funcs[TranslateFn] = func(id string, args ...interface{}) string { + return id + } + tmpls := template.Must(template.New("").Funcs(funcs).ParseGlob(path.Join(templatesDir, "*.html"))) + r.Templates = make(map[string]*template.Template, len(tmplMapping)) + for name, file := range tmplMapping { + r.Templates[name] = tmpls.Lookup(file) + } +} + +func (r *Renderer) registerTranslateFn(req *http.Request, funcs map[string]interface{}) map[string]interface{} { + if funcs == nil { + funcs = make(map[string]interface{}) + } + funcs[TranslateFn] = func(id string, args ...interface{}) string { + m := map[string]interface{}{} + var key string + for i, arg := range args { + if i%2 == 0 { + key = arg.(string) + continue + } + m[key] = arg + } + if r == nil { + return r.Localize(id, m) + } + return r.LocalizeFromRequest(req, id, m) + } + return funcs +} From ecb894258f8edf7bd914deb7071040651b4c2246 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Mon, 23 Mar 2020 14:25:01 +0100 Subject: [PATCH 2/5] remove eventstore flag from cmd --- cmd/zitadel/main.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cmd/zitadel/main.go b/cmd/zitadel/main.go index 24f3f57abd..f44be202ab 100644 --- a/cmd/zitadel/main.go +++ b/cmd/zitadel/main.go @@ -22,7 +22,6 @@ type Config struct { func main() { configPath := flag.String("config-file", "/zitadel/config/startup.yaml", "path to the config file") - eventstoreEnabled := flag.Bool("eventstore", true, "enable eventstore") managementEnabled := flag.Bool("management", true, "enable management api") authEnabled := flag.Bool("auth", true, "enable auth api") adminEnabled := flag.Bool("admin", true, "enable admin api") @@ -34,10 +33,6 @@ func main() { logging.Log("MAIN-FaF2r").OnError(err).Fatal("cannot read config") ctx := context.Background() - if *eventstoreEnabled { - err = eventstore.Start(ctx, conf.Eventstore) - logging.Log("MAIN-sj2Sd").OnError(err).Fatal("error starting eventstore") - } if *managementEnabled { err = management.Start(ctx, conf.Management) logging.Log("MAIN-39Nv5").OnError(err).Fatal("error starting management api") From 6deb0b029a6a07cdb6ddea0c8cb20c9a3fb82457 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Mon, 23 Mar 2020 16:31:26 +0100 Subject: [PATCH 3/5] add tests for config --- internal/config/config.go | 32 +--- internal/config/config_test.go | 198 +++++++++++++++++++++ internal/config/testdata/invalid.json | 3 + internal/config/testdata/non_parsable.json | 1 + internal/config/testdata/valid.json | 3 + 5 files changed, 214 insertions(+), 23 deletions(-) create mode 100644 internal/config/config_test.go create mode 100644 internal/config/testdata/invalid.json create mode 100644 internal/config/testdata/non_parsable.json create mode 100644 internal/config/testdata/valid.json diff --git a/internal/config/config.go b/internal/config/config.go index 5c524e1525..5740cd5f1d 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -7,31 +7,21 @@ import ( "path/filepath" "github.com/BurntSushi/toml" - "github.com/ghodss/yaml" + "gopkg.in/yaml.v2" "github.com/caos/zitadel/internal/errors" ) -type Reader interface { - Unmarshal(data []byte, o interface{}) error -} - type ValidatableConfiguration interface { Validate() error } type ReaderFunc func(data []byte, o interface{}) error -func (c ReaderFunc) Unmarshal(data []byte, o interface{}) error { - return c(data, o) -} - var ( - JSONReader = ReaderFunc(json.Unmarshal) - TOMLReader = ReaderFunc(toml.Unmarshal) - YAMLReader = ReaderFunc(func(y []byte, o interface{}) error { - return yaml.Unmarshal(y, o) - }) + JSONReader = json.Unmarshal + TOMLReader = toml.Unmarshal + YAMLReader = yaml.Unmarshal ) // Read deserializes each config file to the target obj @@ -39,11 +29,11 @@ var ( // env vars are replaced in the config file as well as the file path func Read(obj interface{}, configFiles ...string) error { for _, cf := range configFiles { - configReader, err := configReaderForFile(cf) + readerFunc, err := readerFuncForFile(cf) if err != nil { return err } - if err := readConfigFile(configReader, cf, obj); err != nil { + if err := readConfigFile(readerFunc, cf, obj); err != nil { return err } } @@ -57,13 +47,9 @@ func Read(obj interface{}, configFiles ...string) error { return nil } -func readConfigFile(configReader Reader, configFile string, obj interface{}) error { +func readConfigFile(readerFunc ReaderFunc, configFile string, obj interface{}) error { configFile = os.ExpandEnv(configFile) - if _, err := os.Stat(configFile); err != nil { - return errors.ThrowNotFoundf(err, "CONFI-Hs93M", "config file %s does not exist", configFile) - } - configStr, err := ioutil.ReadFile(configFile) if err != nil { return errors.ThrowInternalf(err, "CONFI-nJk2a", "failed to read config file %s", configFile) @@ -71,14 +57,14 @@ func readConfigFile(configReader Reader, configFile string, obj interface{}) err configStr = []byte(os.ExpandEnv(string(configStr))) - if err := configReader.Unmarshal(configStr, obj); err != nil { + if err := readerFunc(configStr, obj); err != nil { return errors.ThrowInternalf(err, "CONFI-2Mc3c", "error parse config file %s", configFile) } return nil } -func configReaderForFile(configFile string) (Reader, error) { +func readerFuncForFile(configFile string) (ReaderFunc, error) { ext := filepath.Ext(configFile) switch ext { case ".yaml", ".yml": diff --git a/internal/config/config_test.go b/internal/config/config_test.go new file mode 100644 index 0000000000..ba517b6061 --- /dev/null +++ b/internal/config/config_test.go @@ -0,0 +1,198 @@ +package config + +import ( + "errors" + "reflect" + "runtime" + "testing" + + "github.com/stretchr/testify/assert" +) + +type test struct { + Test bool +} + +type validatable struct { + Test bool +} + +func (v *validatable) Validate() error { + if v.Test { + return nil + } + return errors.New("invalid") +} + +func TestRead(t *testing.T) { + type args struct { + obj interface{} + configFiles []string + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + "not supoorted config file error", + args{ + configFiles: []string{"notsupported.unknown"}, + obj: nil, + }, + true, + }, + { + "non existing config file error", + args{ + configFiles: []string{"nonexisting.yaml"}, + obj: nil, + }, + true, + }, + { + "non parsable config file error", + args{ + configFiles: []string{"./testdata/non_parsable.json"}, + obj: &test{}, + }, + true, + }, + { + "invalid parsable config file error", + args{ + configFiles: []string{"./testdata/invalid.json"}, + obj: &validatable{}, + }, + true, + }, + { + "parsable config file ok", + args{ + configFiles: []string{"./testdata/valid.json"}, + obj: &test{}, + }, + false, + }, + { + "valid parsable config file ok", + args{ + configFiles: []string{"./testdata/valid.json"}, + obj: &validatable{}, + }, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := Read(tt.args.obj, tt.args.configFiles...); (err != nil) != tt.wantErr { + t.Errorf("Read() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_readerFuncForFile(t *testing.T) { + type args struct { + configFile string + } + tests := []struct { + name string + args args + want ReaderFunc + wantErr bool + }{ + { + "unknown extension error", + args{configFile: "test.unknown"}, + nil, + true, + }, + { + "toml", + args{configFile: "test.toml"}, + TOMLReader, + false, + }, + { + "json", + args{configFile: "test.json"}, + JSONReader, + false, + }, + { + "yaml", + args{configFile: "test.yaml"}, + YAMLReader, + false, + }, + { + "yml", + args{configFile: "test.yml"}, + YAMLReader, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := readerFuncForFile(tt.args.configFile) + if (err != nil) != tt.wantErr { + t.Errorf("configReaderForFile() error = %v, wantErr %v", err, tt.wantErr) + return + } + funcName1 := runtime.FuncForPC(reflect.ValueOf(got).Pointer()).Name() + funcName2 := runtime.FuncForPC(reflect.ValueOf(tt.want).Pointer()).Name() + if !assert.Equal(t, funcName1, funcName2) { + t.Errorf("configReaderForFile() got = %v, want %v", funcName1, funcName2) + } + }) + } +} + +func Test_readConfigFile(t *testing.T) { + type args struct { + configReader ReaderFunc + configFile string + obj interface{} + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + "non existing config file error", + args{ + configReader: YAMLReader, + configFile: "nonexisting.json", + obj: nil, + }, + true, + }, + { + "non parsable config file error", + args{ + configReader: YAMLReader, + configFile: "./testdata/non_parsable.json", + obj: &test{}, + }, + true, + }, + { + "parsable config file no error", + args{ + configReader: YAMLReader, + configFile: "./testdata/valid.json", + obj: &test{}, + }, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := readConfigFile(tt.args.configReader, tt.args.configFile, tt.args.obj); (err != nil) != tt.wantErr { + t.Errorf("readConfigFile() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/internal/config/testdata/invalid.json b/internal/config/testdata/invalid.json new file mode 100644 index 0000000000..454b39367e --- /dev/null +++ b/internal/config/testdata/invalid.json @@ -0,0 +1,3 @@ +{ + "Test" : false +} \ No newline at end of file diff --git a/internal/config/testdata/non_parsable.json b/internal/config/testdata/non_parsable.json new file mode 100644 index 0000000000..8318c86b35 --- /dev/null +++ b/internal/config/testdata/non_parsable.json @@ -0,0 +1 @@ +Test \ No newline at end of file diff --git a/internal/config/testdata/valid.json b/internal/config/testdata/valid.json new file mode 100644 index 0000000000..65f3700045 --- /dev/null +++ b/internal/config/testdata/valid.json @@ -0,0 +1,3 @@ +{ + "Test" : true +} \ No newline at end of file From a5ca611dccc51086b6c0b78640a3e5b1ca13f4aa Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Tue, 24 Mar 2020 07:22:31 +0100 Subject: [PATCH 4/5] fix path in error_creator.go message --- internal/errors/generate/error_creator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/errors/generate/error_creator.go b/internal/errors/generate/error_creator.go index 02a5538136..52139196a1 100644 --- a/internal/errors/generate/error_creator.go +++ b/internal/errors/generate/error_creator.go @@ -32,7 +32,7 @@ func main() { fmt.Print(` !!!!! - Add status mapping in grpc/errors/caos_errors.go + Add status mapping in internal/api/grpc/caos_errors.go !!!!!`) } From 96b88f5d8cb87cb751d224eec875ba9261bd054c Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Tue, 24 Mar 2020 14:15:01 +0100 Subject: [PATCH 5/5] add tracing and refactor some api pkgs --- go.mod | 13 +- go.sum | 180 ++++++++++++++++++ .../api/grpc/client/middleware/tracing.go | 38 ++++ internal/api/grpc/server/gateway.go | 110 +++++++++++ .../middleware}/auth_interceptor.go | 7 +- .../middleware}/error_interceptor.go | 6 +- .../api/grpc/server/middleware/tracing.go | 33 ++++ internal/api/grpc/{ => server}/probes.go | 2 +- internal/api/grpc/server/server.go | 53 ++++++ internal/api/header.go | 6 +- internal/api/http/listener.go | 21 ++ .../api/http/middleware/cors_interceptor.go | 47 +++++ .../api/http/middleware/trace_interceptor.go | 12 ++ internal/api/probes.go | 11 ++ internal/tracing/caller.go | 24 +++ internal/tracing/config/config.go | 61 ++++++ internal/tracing/generate.go | 3 + internal/tracing/google/config.go | 25 +++ internal/tracing/google/googletracing.go | 95 +++++++++ internal/tracing/http_handler.go | 30 +++ internal/tracing/log/config.go | 21 ++ internal/tracing/log/logTracing.go | 74 +++++++ internal/tracing/mock/tracing_mock.go | 155 +++++++++++++++ internal/tracing/mock/tracing_mock_impl.go | 20 ++ internal/tracing/span.go | 89 +++++++++ internal/tracing/tracing.go | 74 +++++++ 26 files changed, 1198 insertions(+), 12 deletions(-) create mode 100644 internal/api/grpc/client/middleware/tracing.go create mode 100644 internal/api/grpc/server/gateway.go rename internal/api/grpc/{ => server/middleware}/auth_interceptor.go (84%) rename internal/api/grpc/{ => server/middleware}/error_interceptor.go (74%) create mode 100644 internal/api/grpc/server/middleware/tracing.go rename internal/api/grpc/{ => server}/probes.go (98%) create mode 100644 internal/api/grpc/server/server.go create mode 100644 internal/api/http/listener.go create mode 100644 internal/api/http/middleware/cors_interceptor.go create mode 100644 internal/api/http/middleware/trace_interceptor.go create mode 100644 internal/api/probes.go create mode 100644 internal/tracing/caller.go create mode 100644 internal/tracing/config/config.go create mode 100644 internal/tracing/generate.go create mode 100644 internal/tracing/google/config.go create mode 100644 internal/tracing/google/googletracing.go create mode 100644 internal/tracing/http_handler.go create mode 100644 internal/tracing/log/config.go create mode 100644 internal/tracing/log/logTracing.go create mode 100644 internal/tracing/mock/tracing_mock.go create mode 100644 internal/tracing/mock/tracing_mock_impl.go create mode 100644 internal/tracing/span.go create mode 100644 internal/tracing/tracing.go diff --git a/go.mod b/go.mod index f3882f1414..34c29cbd49 100644 --- a/go.mod +++ b/go.mod @@ -3,15 +3,17 @@ module github.com/caos/zitadel go 1.14 require ( + cloud.google.com/go v0.53.0 // indirect + contrib.go.opencensus.io/exporter/stackdriver v0.13.0 github.com/BurntSushi/toml v0.3.1 github.com/Masterminds/goutils v1.1.0 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/sprig v2.22.0+incompatible + github.com/aws/aws-sdk-go v1.29.16 // indirect github.com/caos/logging v0.0.0-20191210002624-b3260f690a6a github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b github.com/golang/mock v1.4.3 github.com/golang/protobuf v1.3.5 - github.com/google/go-cmp v0.4.0 // indirect github.com/google/uuid v1.1.1 // indirect github.com/gorilla/schema v1.1.0 github.com/gorilla/securecookie v1.1.1 @@ -19,17 +21,18 @@ require ( github.com/grpc-ecosystem/grpc-gateway v1.14.3 github.com/huandu/xstrings v1.3.0 // indirect github.com/imdario/mergo v0.3.8 // indirect - github.com/kr/pretty v0.1.0 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect github.com/nicksnyder/go-i18n/v2 v2.0.3 + github.com/rs/cors v1.7.0 github.com/stretchr/testify v1.5.1 + go.opencensus.io v0.22.3 golang.org/x/crypto v0.0.0-20200320181102-891825fb96df golang.org/x/net v0.0.0-20200320220750-118fecf932d8 // indirect golang.org/x/sys v0.0.0-20200321134203-328b4cd54aae // indirect golang.org/x/text v0.3.2 - golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 + golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56 + google.golang.org/api v0.20.0 // indirect google.golang.org/genproto v0.0.0-20200319113533-08878b785e9c // indirect google.golang.org/grpc v1.28.0 - gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect - gopkg.in/yaml.v2 v2.2.7 + gopkg.in/yaml.v2 v2.2.8 ) diff --git a/go.sum b/go.sum index e5db0f7feb..c277b44880 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,27 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.53.0 h1:MZQCQQaRwOrAcuKjiHWHrgKykt4fZyuwF2dtiG3fGW8= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +contrib.go.opencensus.io/exporter/stackdriver v0.13.0 h1:Jaz7WbqjtfoCPa1KbfisCX+P5aM3DizEY9pQMU0oAQo= +contrib.go.opencensus.io/exporter/stackdriver v0.13.0/go.mod h1:z2tyTZtPmQ2HvWH4cOmVDgtY+1lomfKdbLnkJvZdc8c= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= @@ -9,9 +29,16 @@ github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF0 github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= +github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.29.16 h1:Gbtod7Y4W/Ai7wPtesdvgGVTkFN8JxAaGouRLlcQfQs= +github.com/aws/aws-sdk-go v1.29.16/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg= github.com/caos/logging v0.0.0-20191210002624-b3260f690a6a h1:HOU/3xL/afsZ+2aCstfJlrzRkwYMTFR1TIEgps5ny8s= github.com/caos/logging v0.0.0-20191210002624-b3260f690a6a/go.mod h1:9LKiDE2ChuGv6CHYif/kiugrfEXu9AwDiFWSreX7Wp0= +github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -23,25 +50,48 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY= github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= @@ -50,10 +100,17 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQ github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s= github.com/grpc-ecosystem/grpc-gateway v1.14.3 h1:OCJlWkOUoTnl0neNGlf4fUm3TmbEtguw7vR+nGtnDjY= github.com/grpc-ecosystem/grpc-gateway v1.14.3/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/huandu/xstrings v1.3.0 h1:gvV6jG9dTgFEncxo+AF7PH6MZXi/vZl25owA/8Dg8Wo= github.com/huandu/xstrings v1.3.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -73,10 +130,14 @@ github.com/nicksnyder/go-i18n/v2 v2.0.3 h1:ks/JkQiOEhhuF6jpNvx+Wih1NIiXzUnZeZVnJ github.com/nicksnyder/go-i18n/v2 v2.0.3/go.mod h1:oDab7q8XCYMRlcrBnaY/7B1eOectbvj6B1UPBT+p5jo= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -86,78 +147,197 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200320181102-891825fb96df h1:lDWgvUvNnaTnNBc/dwOty86cFeKoKWbwy2wQj0gIxbU= golang.org/x/crypto v0.0.0-20200320181102-891825fb96df/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200320220750-118fecf932d8 h1:1+zQlQqEEhUeStBTi653GZAnAuivZq/2hz+Iz+OP7rg= golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191206220618-eeba5f6aabab h1:FvshnhkKW+LO3HWHodML8kuVX8rnJTxKm9dFPuI68UM= golang.org/x/sys v0.0.0-20191206220618-eeba5f6aabab/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200321134203-328b4cd54aae h1:3tcmuaB7wwSZtelmiv479UjUB+vviwABz7a133ZwOKQ= golang.org/x/sys v0.0.0-20200321134203-328b4cd54aae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56 h1:DFtSed2q3HtNuVazwVDZ4nSRS/JrZEig0gz2BY4VNrg= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0 h1:jz2KixHX7EcCPiQrySzPdnYT7DbINAypCqKZ1Z7GM40= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200319113533-08878b785e9c h1:5aI3/f/3eCZps9xwoEnmgfDJDhMbnJpfqeGpjVNgVEI= google.golang.org/genproto v0.0.0-20200319113533-08878b785e9c/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= diff --git a/internal/api/grpc/client/middleware/tracing.go b/internal/api/grpc/client/middleware/tracing.go new file mode 100644 index 0000000000..dbcfedcfac --- /dev/null +++ b/internal/api/grpc/client/middleware/tracing.go @@ -0,0 +1,38 @@ +package middleware + +import ( + "context" + "strings" + + "go.opencensus.io/plugin/ocgrpc" + "go.opencensus.io/trace" + "google.golang.org/grpc" + "google.golang.org/grpc/stats" + + "github.com/caos/zitadel/internal/api" + "github.com/caos/zitadel/internal/tracing" +) + +type GRPCMethod string + +func TracingStatsClient(ignoredMethods ...GRPCMethod) grpc.DialOption { + return grpc.WithStatsHandler(&tracingClientHandler{ignoredMethods, ocgrpc.ClientHandler{StartOptions: trace.StartOptions{Sampler: tracing.Sampler(), SpanKind: trace.SpanKindClient}}}) +} + +func DefaultTracingStatsClient() grpc.DialOption { + return TracingStatsClient(api.Healthz, api.Readiness, api.Validation) +} + +type tracingClientHandler struct { + IgnoredMethods []GRPCMethod + ocgrpc.ClientHandler +} + +func (s *tracingClientHandler) TagRPC(ctx context.Context, tagInfo *stats.RPCTagInfo) context.Context { + for _, method := range s.IgnoredMethods { + if strings.HasSuffix(tagInfo.FullMethodName, string(method)) { + return ctx + } + } + return s.ClientHandler.TagRPC(ctx, tagInfo) +} diff --git a/internal/api/grpc/server/gateway.go b/internal/api/grpc/server/gateway.go new file mode 100644 index 0000000000..955706b6ad --- /dev/null +++ b/internal/api/grpc/server/gateway.go @@ -0,0 +1,110 @@ +package server + +import ( + "context" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "google.golang.org/grpc" + + "github.com/caos/logging" + + client_middleware "github.com/caos/zitadel/internal/api/grpc/client/middleware" + http_util "github.com/caos/zitadel/internal/api/http" + http_mw "github.com/caos/zitadel/internal/api/http/middleware" +) + +const ( + defaultGatewayPort = "8080" + mimeWildcard = "*/*" +) + +var ( + DefaultJSONMarshaler = &runtime.JSONPb{OrigName: false, EmitDefaults: false} + + DefaultServeMuxOptions = []runtime.ServeMuxOption{ + runtime.WithMarshalerOption(DefaultJSONMarshaler.ContentType(), DefaultJSONMarshaler), + runtime.WithMarshalerOption(mimeWildcard, DefaultJSONMarshaler), + runtime.WithMarshalerOption(runtime.MIMEWildcard, DefaultJSONMarshaler), + runtime.WithIncomingHeaderMatcher(runtime.DefaultHeaderMatcher), + runtime.WithOutgoingHeaderMatcher(runtime.DefaultHeaderMatcher), + } +) + +type Gateway interface { + GRPCEndpoint() string + GatewayPort() string + Gateway() GatewayFunc +} + +type GatewayFunc func(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) error + +type gatewayCustomServeMuxOptions interface { + GatewayServeMuxOptions() []runtime.ServeMuxOption +} +type grpcGatewayCustomInterceptor interface { + GatewayHTTPInterceptor(http.Handler) http.Handler +} + +type gatewayCustomCallOptions interface { + GatewayCallOptions() []grpc.DialOption +} + +func StartGateway(ctx context.Context, g Gateway) { + mux := createMux(ctx, g) + serveGateway(ctx, mux, gatewayPort(g.GatewayPort()), g) +} + +func createMux(ctx context.Context, g Gateway) *runtime.ServeMux { + muxOptions := DefaultServeMuxOptions + if customOpts, ok := g.(gatewayCustomServeMuxOptions); ok { + muxOptions = customOpts.GatewayServeMuxOptions() + } + mux := runtime.NewServeMux(muxOptions...) + + opts := []grpc.DialOption{grpc.WithInsecure()} + opts = append(opts, client_middleware.DefaultTracingStatsClient()) + + if customOpts, ok := g.(gatewayCustomCallOptions); ok { + opts = append(opts, customOpts.GatewayCallOptions()...) + } + err := g.Gateway()(ctx, mux, g.GRPCEndpoint(), opts) + logging.Log("SERVE-7B7G0E").OnError(err).Panic("failed to create mux for grpc gateway") + + return mux +} + +func addInterceptors(handler http.Handler, g Gateway) http.Handler { + handler = http_mw.DefaultTraceHandler(handler) + if interceptor, ok := g.(grpcGatewayCustomInterceptor); ok { + handler = interceptor.GatewayHTTPInterceptor(handler) + } + return http_mw.CORSInterceptorOpts(http_mw.DefaultCORSOptions, handler) +} + +func serveGateway(ctx context.Context, handler http.Handler, port string, g Gateway) { + server := &http.Server{ + Handler: addInterceptors(handler, g), + } + + listener := http_util.CreateListener(port) + + go func() { + <-ctx.Done() + err := server.Shutdown(ctx) + logging.Log("SERVE-m7kBlq").OnError(err).Warn("error during graceful shutdown of grpc gateway") + }() + + go func() { + err := server.Serve(listener) + logging.Log("SERVE-tBHR60").OnError(err).Panic("grpc gateway serve failed") + }() + logging.LogWithFields("SERVE-KHh0Cb", "port", port).Info("grpc gateway is listening") +} + +func gatewayPort(port string) string { + if port == "" { + return defaultGatewayPort + } + return port +} diff --git a/internal/api/grpc/auth_interceptor.go b/internal/api/grpc/server/middleware/auth_interceptor.go similarity index 84% rename from internal/api/grpc/auth_interceptor.go rename to internal/api/grpc/server/middleware/auth_interceptor.go index 6ade793b1e..fbdd42bc9b 100644 --- a/internal/api/grpc/auth_interceptor.go +++ b/internal/api/grpc/server/middleware/auth_interceptor.go @@ -1,4 +1,4 @@ -package grpc +package middleware import ( "context" @@ -9,6 +9,7 @@ import ( "github.com/caos/zitadel/internal/api" "github.com/caos/zitadel/internal/api/auth" + grpc_util "github.com/caos/zitadel/internal/api/grpc" ) func AuthorizationInterceptor(verifier auth.TokenVerifier, authConfig *auth.Config, authMethods auth.MethodMapping) func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { @@ -18,12 +19,12 @@ func AuthorizationInterceptor(verifier auth.TokenVerifier, authConfig *auth.Conf return handler(ctx, req) } - authToken := GetAuthorizationHeader(ctx) + authToken := grpc_util.GetAuthorizationHeader(ctx) if authToken == "" { return nil, status.Error(codes.Unauthenticated, "auth header missing") } - orgID := GetHeader(ctx, api.ZitadelOrgID) + orgID := grpc_util.GetHeader(ctx, api.ZitadelOrgID) ctx, err := auth.CheckUserAuthorization(ctx, req, authToken, orgID, verifier, authConfig, authOpt) if err != nil { diff --git a/internal/api/grpc/error_interceptor.go b/internal/api/grpc/server/middleware/error_interceptor.go similarity index 74% rename from internal/api/grpc/error_interceptor.go rename to internal/api/grpc/server/middleware/error_interceptor.go index 690ab9a2e9..2d8f7f3e47 100644 --- a/internal/api/grpc/error_interceptor.go +++ b/internal/api/grpc/server/middleware/error_interceptor.go @@ -1,14 +1,16 @@ -package grpc +package middleware import ( "context" "google.golang.org/grpc" + + grpc_util "github.com/caos/zitadel/internal/api/grpc" ) func ErrorHandler() func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { resp, err := handler(ctx, req) - return resp, CaosToGRPCError(err) + return resp, grpc_util.CaosToGRPCError(err) } } diff --git a/internal/api/grpc/server/middleware/tracing.go b/internal/api/grpc/server/middleware/tracing.go new file mode 100644 index 0000000000..c8e2cc9d5d --- /dev/null +++ b/internal/api/grpc/server/middleware/tracing.go @@ -0,0 +1,33 @@ +package middleware + +import ( + "context" + "strings" + + "go.opencensus.io/plugin/ocgrpc" + "go.opencensus.io/trace" + "google.golang.org/grpc" + "google.golang.org/grpc/stats" + + "github.com/caos/zitadel/internal/tracing" +) + +type GRPCMethod string + +func TracingStatsServer(ignoredMethods ...GRPCMethod) grpc.ServerOption { + return grpc.StatsHandler(&tracingServerHandler{ignoredMethods, ocgrpc.ServerHandler{StartOptions: trace.StartOptions{Sampler: tracing.Sampler(), SpanKind: trace.SpanKindServer}}}) +} + +type tracingServerHandler struct { + IgnoredMethods []GRPCMethod + ocgrpc.ServerHandler +} + +func (s *tracingServerHandler) TagRPC(ctx context.Context, tagInfo *stats.RPCTagInfo) context.Context { + for _, method := range s.IgnoredMethods { + if strings.HasSuffix(tagInfo.FullMethodName, string(method)) { + return ctx + } + } + return s.ServerHandler.TagRPC(ctx, tagInfo) +} diff --git a/internal/api/grpc/probes.go b/internal/api/grpc/server/probes.go similarity index 98% rename from internal/api/grpc/probes.go rename to internal/api/grpc/server/probes.go index 8ec10c585c..91379da51e 100644 --- a/internal/api/grpc/probes.go +++ b/internal/api/grpc/server/probes.go @@ -1,4 +1,4 @@ -package grpc +package server import ( "context" diff --git a/internal/api/grpc/server/server.go b/internal/api/grpc/server/server.go new file mode 100644 index 0000000000..eb6f9f51f7 --- /dev/null +++ b/internal/api/grpc/server/server.go @@ -0,0 +1,53 @@ +package server + +import ( + "context" + "net" + + "github.com/caos/logging" + "google.golang.org/grpc" + + "github.com/caos/zitadel/internal/api/http" +) + +const ( + defaultGrpcPort = "80" +) + +type Server interface { + GRPCPort() string + GRPCServer() (*grpc.Server, error) +} + +func StartServer(ctx context.Context, s Server) { + port := grpcPort(s.GRPCPort()) + listener := http.CreateListener(port) + server := createGrpcServer(s) + serveServer(ctx, server, listener, port) +} + +func createGrpcServer(s Server) *grpc.Server { + grpcServer, err := s.GRPCServer() + logging.Log("SERVE-k280HZ").OnError(err).Panic("failed to create grpc server") + return grpcServer +} + +func serveServer(ctx context.Context, server *grpc.Server, listener net.Listener, port string) { + go func() { + <-ctx.Done() + server.GracefulStop() + }() + + go func() { + err := server.Serve(listener) + logging.Log("SERVE-Ga3e94").OnError(err).Panic("grpc server serve failed") + }() + logging.LogWithFields("SERVE-bZ44QM", "port", port).Info("grpc server is listening") +} + +func grpcPort(port string) string { + if port == "" { + return defaultGrpcPort + } + return port +} diff --git a/internal/api/header.go b/internal/api/header.go index fa8efc0282..857a91ff3b 100644 --- a/internal/api/header.go +++ b/internal/api/header.go @@ -2,7 +2,11 @@ package api const ( Authorization = "authorization" - AcceptLanguage = "Accept-Language" + Accept = "accept" + AcceptLanguage = "accept-language" + ContentType = "content-type" + Location = "location" + Origin = "origin" ZitadelOrgID = "x-zitadel-orgid" ) diff --git a/internal/api/http/listener.go b/internal/api/http/listener.go new file mode 100644 index 0000000000..3e9056c384 --- /dev/null +++ b/internal/api/http/listener.go @@ -0,0 +1,21 @@ +package http + +import ( + "net" + "strings" + + "github.com/caos/logging" +) + +func CreateListener(endpoint string) net.Listener { + l, err := net.Listen("tcp", listenerEndpoint(endpoint)) + logging.Log("SERVE-6vasef").OnError(err).Fatal("creating listener failed") + return l +} + +func listenerEndpoint(endpoint string) string { + if strings.Contains(endpoint, ":") { + return endpoint + } + return ":" + endpoint +} diff --git a/internal/api/http/middleware/cors_interceptor.go b/internal/api/http/middleware/cors_interceptor.go new file mode 100644 index 0000000000..6fd154b469 --- /dev/null +++ b/internal/api/http/middleware/cors_interceptor.go @@ -0,0 +1,47 @@ +package middleware + +import ( + "net/http" + + "github.com/rs/cors" + + "github.com/caos/zitadel/internal/api" +) + +var ( + DefaultCORSOptions = cors.Options{ + AllowCredentials: true, + AllowedHeaders: []string{ + api.Origin, + api.ContentType, + api.Accept, + api.AcceptLanguage, + api.Authorization, + api.ZitadelOrgID, + "x-grpc-web", //TODO: needed + }, + AllowedMethods: []string{ + http.MethodOptions, + http.MethodGet, + http.MethodHead, + http.MethodPost, + http.MethodPut, + http.MethodPatch, + http.MethodDelete, + }, + ExposedHeaders: []string{ + api.Location, + }, + AllowedOrigins: []string{ + "http://localhost:*", + }, + } +) + +func CORSInterceptorOpts(opts cors.Options, h http.Handler) http.Handler { + return cors.New(opts).Handler(h) +} + +func CORSInterceptor(h http.Handler) http.Handler { + return CORSInterceptorOpts(DefaultCORSOptions, h) +} diff --git a/internal/api/http/middleware/trace_interceptor.go b/internal/api/http/middleware/trace_interceptor.go new file mode 100644 index 0000000000..4f95cc67c1 --- /dev/null +++ b/internal/api/http/middleware/trace_interceptor.go @@ -0,0 +1,12 @@ +package middleware + +import ( + "net/http" + + "github.com/caos/zitadel/internal/api" + "github.com/caos/zitadel/internal/tracing" +) + +func DefaultTraceHandler(handler http.Handler) http.Handler { + return tracing.TraceHandler(handler, api.Probes...) +} diff --git a/internal/api/probes.go b/internal/api/probes.go new file mode 100644 index 0000000000..9ae0f6a1d9 --- /dev/null +++ b/internal/api/probes.go @@ -0,0 +1,11 @@ +package api + +const ( + Healthz = "/Healthz" + Readiness = "/Ready" + Validation = "/Validate" +) + +var ( + Probes = []string{Healthz, Readiness, Validation} +) diff --git a/internal/tracing/caller.go b/internal/tracing/caller.go new file mode 100644 index 0000000000..c5dfb9a319 --- /dev/null +++ b/internal/tracing/caller.go @@ -0,0 +1,24 @@ +package tracing + +import ( + "runtime" + + "github.com/caos/logging" +) + +func GetCaller() string { + fpcs := make([]uintptr, 1) + + n := runtime.Callers(3, fpcs) + if n == 0 { + logging.Log("HELPE-rWjfC").Debug("no caller") + } + + caller := runtime.FuncForPC(fpcs[0] - 1) + if caller == nil { + logging.Log("HELPE-25POw").Debug("caller was nil") + } + + // Print the name of the function + return caller.Name() +} diff --git a/internal/tracing/config/config.go b/internal/tracing/config/config.go new file mode 100644 index 0000000000..e2310fbce3 --- /dev/null +++ b/internal/tracing/config/config.go @@ -0,0 +1,61 @@ +package config + +import ( + "encoding/json" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/caos/zitadel/internal/tracing" + tracing_g "github.com/caos/zitadel/internal/tracing/google" + tracing_log "github.com/caos/zitadel/internal/tracing/log" +) + +type TracingConfig struct { + Type string + Config tracing.Config +} + +var tracer = map[string]func() tracing.Config{ + "google": func() tracing.Config { return &tracing_g.Config{} }, + "log": func() tracing.Config { return &tracing_log.Config{} }, +} + +func (c *TracingConfig) UnmarshalJSON(data []byte) error { + var rc struct { + Type string + Config json.RawMessage + } + + if err := json.Unmarshal(data, &rc); err != nil { + return status.Errorf(codes.Internal, "%v parse config: %v", "TRACE-vmjS", err) + } + + c.Type = rc.Type + + var err error + c.Config, err = newTracingConfig(c.Type, rc.Config) + if err != nil { + return status.Errorf(codes.Internal, "%v parse config: %v", "TRACE-Ws9E", err) + } + + return c.Config.NewTracer() +} + +func newTracingConfig(tracerType string, configData []byte) (tracing.Config, error) { + t, ok := tracer[tracerType] + if !ok { + return nil, status.Errorf(codes.Internal, "%v No config: %v", "TRACE-HMEJ", tracerType) + } + + tracingConfig := t() + if len(configData) == 0 { + return tracingConfig, nil + } + + if err := json.Unmarshal(configData, tracingConfig); err != nil { + return nil, status.Errorf(codes.Internal, "%v Could not read conifg: %v", "TRACE-1tSS", err) + } + + return tracingConfig, nil +} diff --git a/internal/tracing/generate.go b/internal/tracing/generate.go new file mode 100644 index 0000000000..21b97622cb --- /dev/null +++ b/internal/tracing/generate.go @@ -0,0 +1,3 @@ +package tracing + +//go:generate mockgen -package mock -destination mock/tracing_mock.go github.com/caos/zitadel/internal/tracing Tracer diff --git a/internal/tracing/google/config.go b/internal/tracing/google/config.go new file mode 100644 index 0000000000..92d93f61bd --- /dev/null +++ b/internal/tracing/google/config.go @@ -0,0 +1,25 @@ +package google + +import ( + "go.opencensus.io/trace" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/caos/zitadel/internal/tracing" +) + +type Config struct { + ProjectID string + MetricPrefix string + Fraction float64 +} + +func (c *Config) NewTracer() error { + if !envIsSet() { + return status.Error(codes.InvalidArgument, "env not properly set, GOOGLE_APPLICATION_CREDENTIALS is misconfigured or missing") + } + + tracing.T = &Tracer{projectID: c.ProjectID, metricPrefix: c.MetricPrefix, sampler: trace.ProbabilitySampler(c.Fraction)} + + return tracing.T.Start() +} diff --git a/internal/tracing/google/googletracing.go b/internal/tracing/google/googletracing.go new file mode 100644 index 0000000000..3958592d00 --- /dev/null +++ b/internal/tracing/google/googletracing.go @@ -0,0 +1,95 @@ +package google + +import ( + "context" + "net/http" + "os" + "strings" + + "contrib.go.opencensus.io/exporter/stackdriver" + "go.opencensus.io/plugin/ocgrpc" + "go.opencensus.io/plugin/ochttp" + "go.opencensus.io/stats/view" + "go.opencensus.io/trace" + + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/tracing" +) + +type Tracer struct { + Exporter *stackdriver.Exporter + projectID string + metricPrefix string + sampler trace.Sampler +} + +func (t *Tracer) Start() (err error) { + t.Exporter, err = stackdriver.NewExporter(stackdriver.Options{ + ProjectID: t.projectID, + MetricPrefix: t.metricPrefix, + }) + if err != nil { + return errors.ThrowInternal(err, "GOOGL-4dCnX", "unable to start exporter") + } + + views := append(ocgrpc.DefaultServerViews, ocgrpc.DefaultClientViews...) + views = append(views, ochttp.DefaultClientViews...) + views = append(views, ochttp.DefaultServerViews...) + + if err = view.Register(views...); err != nil { + return errors.ThrowInternal(err, "GOOGL-Q6L6w", "unable to register view") + } + + trace.RegisterExporter(t.Exporter) + trace.ApplyConfig(trace.Config{DefaultSampler: t.sampler}) + + return nil +} + +func (t *Tracer) Sampler() trace.Sampler { + return t.sampler +} + +func (t *Tracer) NewServerInterceptorSpan(ctx context.Context, name string) (context.Context, *tracing.Span) { + return t.newSpanFromName(ctx, name, trace.WithSpanKind(trace.SpanKindServer)) +} + +func (t *Tracer) NewServerSpan(ctx context.Context, caller string) (context.Context, *tracing.Span) { + return t.newSpan(ctx, caller, trace.WithSpanKind(trace.SpanKindServer)) +} + +func (t *Tracer) NewClientInterceptorSpan(ctx context.Context, name string) (context.Context, *tracing.Span) { + return t.newSpanFromName(ctx, name, trace.WithSpanKind(trace.SpanKindClient)) +} + +func (t *Tracer) NewClientSpan(ctx context.Context, caller string) (context.Context, *tracing.Span) { + return t.newSpan(ctx, caller, trace.WithSpanKind(trace.SpanKindClient)) +} + +func (t *Tracer) NewSpan(ctx context.Context, caller string) (context.Context, *tracing.Span) { + return t.newSpan(ctx, caller) +} + +func (t *Tracer) newSpan(ctx context.Context, caller string, options ...trace.StartOption) (context.Context, *tracing.Span) { + return t.newSpanFromName(ctx, caller, options...) +} + +func (t *Tracer) newSpanFromName(ctx context.Context, name string, options ...trace.StartOption) (context.Context, *tracing.Span) { + ctx, span := trace.StartSpan(ctx, name, options...) + return ctx, tracing.CreateSpan(span) +} + +func (t *Tracer) NewSpanHTTP(r *http.Request, caller string) (*http.Request, *tracing.Span) { + ctx, span := t.NewSpan(r.Context(), caller) + r = r.WithContext(ctx) + return r, span +} + +func envIsSet() bool { + gAuthCred := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS") + return strings.Contains(gAuthCred, ".json") +} + +func (t *Tracer) SetErrStatus(span *trace.Span, code int32, err error, obj ...string) { + span.SetStatus(trace.Status{Code: code, Message: err.Error() + strings.Join(obj, ", ")}) +} diff --git a/internal/tracing/http_handler.go b/internal/tracing/http_handler.go new file mode 100644 index 0000000000..a3b9631863 --- /dev/null +++ b/internal/tracing/http_handler.go @@ -0,0 +1,30 @@ +package tracing + +import ( + "net/http" + "strings" + + "go.opencensus.io/plugin/ochttp" + "go.opencensus.io/trace" +) + +func TraceHandler(handler http.Handler, ignoredMethods ...string) http.Handler { + healthEndpoints := strings.Join(ignoredMethods, ";;") + + return &ochttp.Handler{ + Handler: handler, + FormatSpanName: func(r *http.Request) string { + host := r.URL.Host + if host == "" { + host = r.Host + } + return host + r.URL.Path + }, + + StartOptions: trace.StartOptions{Sampler: Sampler()}, + IsHealthEndpoint: func(r *http.Request) bool { + n := strings.Contains(healthEndpoints, r.URL.RequestURI()) + return n + }, + } +} diff --git a/internal/tracing/log/config.go b/internal/tracing/log/config.go new file mode 100644 index 0000000000..be2d9bec7a --- /dev/null +++ b/internal/tracing/log/config.go @@ -0,0 +1,21 @@ +package log + +import ( + "go.opencensus.io/trace" + + "github.com/caos/zitadel/internal/tracing" +) + +type Config struct { + Fraction float64 +} + +func (c *Config) NewTracer() error { + if c.Fraction < 1 { + c.Fraction = 1 + } + + tracing.T = &Tracer{trace.ProbabilitySampler(c.Fraction)} + + return tracing.T.Start() +} diff --git a/internal/tracing/log/logTracing.go b/internal/tracing/log/logTracing.go new file mode 100644 index 0000000000..ce34c68fce --- /dev/null +++ b/internal/tracing/log/logTracing.go @@ -0,0 +1,74 @@ +package log + +import ( + "context" + "net/http" + + "go.opencensus.io/examples/exporter" + "go.opencensus.io/plugin/ocgrpc" + "go.opencensus.io/plugin/ochttp" + "go.opencensus.io/stats/view" + "go.opencensus.io/trace" + + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/tracing" +) + +type Tracer struct { + sampler trace.Sampler +} + +func (t *Tracer) Start() error { + trace.RegisterExporter(&exporter.PrintExporter{}) + + views := append(ocgrpc.DefaultServerViews, ocgrpc.DefaultClientViews...) + views = append(views, ochttp.DefaultClientViews...) + views = append(views, ochttp.DefaultServerViews...) + + if err := view.Register(views...); err != nil { + return errors.ThrowInternal(err, "LOG-PoFiB", "unable to register view") + } + + trace.ApplyConfig(trace.Config{DefaultSampler: t.sampler}) + + return nil +} + +func (t *Tracer) Sampler() trace.Sampler { + return t.sampler +} + +func (t *Tracer) NewServerInterceptorSpan(ctx context.Context, name string) (context.Context, *tracing.Span) { + return t.newSpanFromName(ctx, name, trace.WithSpanKind(trace.SpanKindServer)) +} + +func (t *Tracer) NewServerSpan(ctx context.Context, caller string) (context.Context, *tracing.Span) { + return t.newSpan(ctx, caller, trace.WithSpanKind(trace.SpanKindServer)) +} + +func (t *Tracer) NewClientInterceptorSpan(ctx context.Context, name string) (context.Context, *tracing.Span) { + return t.newSpanFromName(ctx, name, trace.WithSpanKind(trace.SpanKindClient)) +} + +func (t *Tracer) NewClientSpan(ctx context.Context, caller string) (context.Context, *tracing.Span) { + return t.newSpan(ctx, caller, trace.WithSpanKind(trace.SpanKindClient)) +} + +func (t *Tracer) NewSpan(ctx context.Context, caller string) (context.Context, *tracing.Span) { + return t.newSpan(ctx, caller) +} + +func (t *Tracer) newSpan(ctx context.Context, caller string, options ...trace.StartOption) (context.Context, *tracing.Span) { + return t.newSpanFromName(ctx, caller, options...) +} + +func (t *Tracer) newSpanFromName(ctx context.Context, name string, options ...trace.StartOption) (context.Context, *tracing.Span) { + ctx, span := trace.StartSpan(ctx, name, options...) + return ctx, tracing.CreateSpan(span) +} + +func (t *Tracer) NewSpanHTTP(r *http.Request, caller string) (*http.Request, *tracing.Span) { + ctx, span := t.NewSpan(r.Context(), caller) + r = r.WithContext(ctx) + return r, span +} diff --git a/internal/tracing/mock/tracing_mock.go b/internal/tracing/mock/tracing_mock.go new file mode 100644 index 0000000000..d765782360 --- /dev/null +++ b/internal/tracing/mock/tracing_mock.go @@ -0,0 +1,155 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/caos/zitadel/internal/tracing (interfaces: Tracer) + +// Package mock is a generated GoMock package. +package mock + +import ( + context "context" + tracing "github.com/caos/zitadel/internal/tracing" + gomock "github.com/golang/mock/gomock" + trace "go.opencensus.io/trace" + http "net/http" + reflect "reflect" +) + +// MockTracer is a mock of Tracer interface +type MockTracer struct { + ctrl *gomock.Controller + recorder *MockTracerMockRecorder +} + +// MockTracerMockRecorder is the mock recorder for MockTracer +type MockTracerMockRecorder struct { + mock *MockTracer +} + +// NewMockTracer creates a new mock instance +func NewMockTracer(ctrl *gomock.Controller) *MockTracer { + mock := &MockTracer{ctrl: ctrl} + mock.recorder = &MockTracerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockTracer) EXPECT() *MockTracerMockRecorder { + return m.recorder +} + +// NewClientInterceptorSpan mocks base method +func (m *MockTracer) NewClientInterceptorSpan(arg0 context.Context, arg1 string) (context.Context, *tracing.Span) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewClientInterceptorSpan", arg0, arg1) + ret0, _ := ret[0].(context.Context) + ret1, _ := ret[1].(*tracing.Span) + return ret0, ret1 +} + +// NewClientInterceptorSpan indicates an expected call of NewClientInterceptorSpan +func (mr *MockTracerMockRecorder) NewClientInterceptorSpan(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewClientInterceptorSpan", reflect.TypeOf((*MockTracer)(nil).NewClientInterceptorSpan), arg0, arg1) +} + +// NewClientSpan mocks base method +func (m *MockTracer) NewClientSpan(arg0 context.Context, arg1 string) (context.Context, *tracing.Span) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewClientSpan", arg0, arg1) + ret0, _ := ret[0].(context.Context) + ret1, _ := ret[1].(*tracing.Span) + return ret0, ret1 +} + +// NewClientSpan indicates an expected call of NewClientSpan +func (mr *MockTracerMockRecorder) NewClientSpan(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewClientSpan", reflect.TypeOf((*MockTracer)(nil).NewClientSpan), arg0, arg1) +} + +// NewServerInterceptorSpan mocks base method +func (m *MockTracer) NewServerInterceptorSpan(arg0 context.Context, arg1 string) (context.Context, *tracing.Span) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewServerInterceptorSpan", arg0, arg1) + ret0, _ := ret[0].(context.Context) + ret1, _ := ret[1].(*tracing.Span) + return ret0, ret1 +} + +// NewServerInterceptorSpan indicates an expected call of NewServerInterceptorSpan +func (mr *MockTracerMockRecorder) NewServerInterceptorSpan(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewServerInterceptorSpan", reflect.TypeOf((*MockTracer)(nil).NewServerInterceptorSpan), arg0, arg1) +} + +// NewServerSpan mocks base method +func (m *MockTracer) NewServerSpan(arg0 context.Context, arg1 string) (context.Context, *tracing.Span) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewServerSpan", arg0, arg1) + ret0, _ := ret[0].(context.Context) + ret1, _ := ret[1].(*tracing.Span) + return ret0, ret1 +} + +// NewServerSpan indicates an expected call of NewServerSpan +func (mr *MockTracerMockRecorder) NewServerSpan(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewServerSpan", reflect.TypeOf((*MockTracer)(nil).NewServerSpan), arg0, arg1) +} + +// NewSpan mocks base method +func (m *MockTracer) NewSpan(arg0 context.Context, arg1 string) (context.Context, *tracing.Span) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewSpan", arg0, arg1) + ret0, _ := ret[0].(context.Context) + ret1, _ := ret[1].(*tracing.Span) + return ret0, ret1 +} + +// NewSpan indicates an expected call of NewSpan +func (mr *MockTracerMockRecorder) NewSpan(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewSpan", reflect.TypeOf((*MockTracer)(nil).NewSpan), arg0, arg1) +} + +// NewSpanHTTP mocks base method +func (m *MockTracer) NewSpanHTTP(arg0 *http.Request, arg1 string) (*http.Request, *tracing.Span) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewSpanHTTP", arg0, arg1) + ret0, _ := ret[0].(*http.Request) + ret1, _ := ret[1].(*tracing.Span) + return ret0, ret1 +} + +// NewSpanHTTP indicates an expected call of NewSpanHTTP +func (mr *MockTracerMockRecorder) NewSpanHTTP(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewSpanHTTP", reflect.TypeOf((*MockTracer)(nil).NewSpanHTTP), arg0, arg1) +} + +// Sampler mocks base method +func (m *MockTracer) Sampler() trace.Sampler { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Sampler") + ret0, _ := ret[0].(trace.Sampler) + return ret0 +} + +// Sampler indicates an expected call of Sampler +func (mr *MockTracerMockRecorder) Sampler() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sampler", reflect.TypeOf((*MockTracer)(nil).Sampler)) +} + +// Start mocks base method +func (m *MockTracer) Start() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Start") + ret0, _ := ret[0].(error) + return ret0 +} + +// Start indicates an expected call of Start +func (mr *MockTracerMockRecorder) Start() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockTracer)(nil).Start)) +} diff --git a/internal/tracing/mock/tracing_mock_impl.go b/internal/tracing/mock/tracing_mock_impl.go new file mode 100644 index 0000000000..7e7335d851 --- /dev/null +++ b/internal/tracing/mock/tracing_mock_impl.go @@ -0,0 +1,20 @@ +package mock + +import ( + "context" + "testing" + + "github.com/golang/mock/gomock" + + "github.com/caos/zitadel/internal/tracing" +) + +func NewSimpleMockTracer(t *testing.T) *MockTracer { + return NewMockTracer(gomock.NewController(t)) +} + +func ExpectServerSpan(ctx context.Context, mock interface{}) { + m := mock.(*MockTracer) + any := gomock.Any() + m.EXPECT().NewServerSpan(any, any).AnyTimes().Return(ctx, &tracing.Span{}) +} diff --git a/internal/tracing/span.go b/internal/tracing/span.go new file mode 100644 index 0000000000..d958cb4b52 --- /dev/null +++ b/internal/tracing/span.go @@ -0,0 +1,89 @@ +package tracing + +import ( + "fmt" + "strconv" + + "go.opencensus.io/trace" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type Span struct { + span *trace.Span + attributes []trace.Attribute +} + +func CreateSpan(span *trace.Span) *Span { + return &Span{span: span, attributes: []trace.Attribute{}} +} + +func (s *Span) End() { + if s.span == nil { + return + } + s.span.AddAttributes(s.attributes...) + s.span.End() +} + +func (s *Span) EndWithError(err error) { + s.SetStatusByError(err) + s.End() +} + +func (s *Span) SetStatusByError(err error) { + if s.span == nil { + return + } + s.span.SetStatus(statusFromError(err)) +} + +func statusFromError(err error) trace.Status { + if statusErr, ok := status.FromError(err); ok { + return trace.Status{Code: int32(statusErr.Code()), Message: statusErr.Message()} + } + return trace.Status{Code: int32(codes.Unknown), Message: "Unknown"} +} + +// AddAnnotation creates an annotation. The annotation will not be added to the tracing use Annotate(msg) afterwards +func (s *Span) AddAnnotation(key string, value interface{}) *Span { + attribute, err := toTraceAttribute(key, value) + if err != nil { + return s + } + s.attributes = append(s.attributes, attribute) + return s +} + +// Annotate creates an annotation in tracing. Before added annotations will be set +func (s *Span) Annotate(message string) *Span { + if s.span == nil { + return s + } + s.span.Annotate(s.attributes, message) + s.attributes = []trace.Attribute{} + return s +} + +func (s *Span) Annotatef(format string, addiations ...interface{}) *Span { + s.Annotate(fmt.Sprintf(format, addiations...)) + return s +} + +func toTraceAttribute(key string, value interface{}) (attr trace.Attribute, err error) { + switch value := value.(type) { + case bool: + return trace.BoolAttribute(key, value), nil + case string: + return trace.StringAttribute(key, value), nil + } + if valueInt, err := convertToInt64(value); err == nil { + return trace.Int64Attribute(key, valueInt), nil + } + return attr, status.Error(codes.InvalidArgument, "Attribute is not of type bool, string or int64") +} + +func convertToInt64(value interface{}) (int64, error) { + valueString := fmt.Sprintf("%v", value) + return strconv.ParseInt(valueString, 10, 64) +} diff --git a/internal/tracing/tracing.go b/internal/tracing/tracing.go new file mode 100644 index 0000000000..61fdc4b318 --- /dev/null +++ b/internal/tracing/tracing.go @@ -0,0 +1,74 @@ +package tracing + +import ( + "context" + "net/http" + + "go.opencensus.io/trace" +) + +type Tracer interface { + Start() error + NewSpan(ctx context.Context, caller string) (context.Context, *Span) + NewClientSpan(ctx context.Context, caller string) (context.Context, *Span) + NewServerSpan(ctx context.Context, caller string) (context.Context, *Span) + NewClientInterceptorSpan(ctx context.Context, name string) (context.Context, *Span) + NewServerInterceptorSpan(ctx context.Context, name string) (context.Context, *Span) + NewSpanHTTP(r *http.Request, caller string) (*http.Request, *Span) + Sampler() trace.Sampler +} + +type Config interface { + NewTracer() error +} + +var T Tracer + +func Sampler() trace.Sampler { + if T == nil { + return trace.NeverSample() + } + return T.Sampler() +} + +func NewSpan(ctx context.Context) (context.Context, *Span) { + if T == nil { + return ctx, CreateSpan(nil) + } + return T.NewSpan(ctx, GetCaller()) +} + +func NewClientSpan(ctx context.Context) (context.Context, *Span) { + if T == nil { + return ctx, CreateSpan(nil) + } + return T.NewClientSpan(ctx, GetCaller()) +} + +func NewServerSpan(ctx context.Context) (context.Context, *Span) { + if T == nil { + return ctx, CreateSpan(nil) + } + return T.NewServerSpan(ctx, GetCaller()) +} + +func NewClientInterceptorSpan(ctx context.Context, name string) (context.Context, *Span) { + if T == nil { + return ctx, CreateSpan(nil) + } + return T.NewClientInterceptorSpan(ctx, name) +} + +func NewServerInterceptorSpan(ctx context.Context, name string) (context.Context, *Span) { + if T == nil { + return ctx, CreateSpan(nil) + } + return T.NewServerInterceptorSpan(ctx, name) +} + +func NewSpanHTTP(r *http.Request) (*http.Request, *Span) { + if T == nil { + return r, CreateSpan(nil) + } + return T.NewSpanHTTP(r, GetCaller()) +}