Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
134
plugins/parsers/avro/schema_registry.go
Normal file
134
plugins/parsers/avro/schema_registry.go
Normal file
|
@ -0,0 +1,134 @@
|
|||
package avro
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/linkedin/goavro/v2"
|
||||
)
|
||||
|
||||
type schemaAndCodec struct {
|
||||
Schema string
|
||||
Codec *goavro.Codec
|
||||
}
|
||||
|
||||
type schemaRegistry struct {
|
||||
url string
|
||||
username string
|
||||
password string
|
||||
cache map[int]*schemaAndCodec
|
||||
client *http.Client
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
const schemaByID = "%s/schemas/ids/%d"
|
||||
|
||||
func newSchemaRegistry(addr, caCertPath string) (*schemaRegistry, error) {
|
||||
var client *http.Client
|
||||
var tlsCfg *tls.Config
|
||||
if caCertPath != "" {
|
||||
caCert, err := os.ReadFile(caCertPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
caCertPool := x509.NewCertPool()
|
||||
caCertPool.AppendCertsFromPEM(caCert)
|
||||
tlsCfg = &tls.Config{
|
||||
RootCAs: caCertPool,
|
||||
}
|
||||
}
|
||||
client = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: tlsCfg,
|
||||
MaxIdleConns: 10,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
u, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing registry URL failed: %w", err)
|
||||
}
|
||||
|
||||
var username, password string
|
||||
if u.User != nil {
|
||||
username = u.User.Username()
|
||||
password, _ = u.User.Password()
|
||||
}
|
||||
|
||||
registry := &schemaRegistry{
|
||||
url: u.String(),
|
||||
username: username,
|
||||
password: password,
|
||||
cache: make(map[int]*schemaAndCodec),
|
||||
client: client,
|
||||
}
|
||||
|
||||
return registry, nil
|
||||
}
|
||||
|
||||
// Helper function to make managing lock easier
|
||||
func (sr *schemaRegistry) getSchemaAndCodecFromCache(id int) (*schemaAndCodec, error) {
|
||||
// Read-lock the cache map before access.
|
||||
sr.mu.RLock()
|
||||
defer sr.mu.RUnlock()
|
||||
if v, ok := sr.cache[id]; ok {
|
||||
return v, nil
|
||||
}
|
||||
return nil, fmt.Errorf("schema %d not in cache", id)
|
||||
}
|
||||
|
||||
func (sr *schemaRegistry) getSchemaAndCodec(id int) (*schemaAndCodec, error) {
|
||||
v, err := sr.getSchemaAndCodecFromCache(id)
|
||||
if err == nil {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf(schemaByID, sr.url, id), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if sr.username != "" {
|
||||
req.SetBasicAuth(sr.username, sr.password)
|
||||
}
|
||||
|
||||
resp, err := sr.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var jsonResponse map[string]interface{}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&jsonResponse); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
schema, ok := jsonResponse["schema"]
|
||||
if !ok {
|
||||
return nil, errors.New("malformed response from schema registry: no 'schema' key")
|
||||
}
|
||||
|
||||
schemaValue, ok := schema.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("malformed response from schema registry: %v cannot be cast to string", schema)
|
||||
}
|
||||
codec, err := goavro.NewCodec(schemaValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
retval := &schemaAndCodec{Schema: schemaValue, Codec: codec}
|
||||
// Lock the cache map before update.
|
||||
sr.mu.Lock()
|
||||
defer sr.mu.Unlock()
|
||||
sr.cache[id] = retval
|
||||
return retval, nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue