github.com/uber/kraken@v0.1.4/lib/backend/manager.go (about) 1 // Copyright (c) 2016-2019 Uber Technologies, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package backend 15 16 import ( 17 "errors" 18 "fmt" 19 "regexp" 20 21 "github.com/uber/kraken/utils/bandwidth" 22 "github.com/uber/kraken/utils/log" 23 ) 24 25 // Manager errors. 26 var ( 27 ErrNamespaceNotFound = errors.New("no matches for namespace") 28 ) 29 30 type backend struct { 31 regexp *regexp.Regexp 32 client Client 33 } 34 35 func newBackend(namespace string, c Client) (*backend, error) { 36 re, err := regexp.Compile(namespace) 37 if err != nil { 38 return nil, fmt.Errorf("regexp: %s", err) 39 } 40 return &backend{ 41 regexp: re, 42 client: c, 43 }, nil 44 } 45 46 // Manager manages backend clients for namespace regular expressions. 47 type Manager struct { 48 backends []*backend 49 } 50 51 // NewManager creates a new backend Manager. 52 func NewManager(configs []Config, auth AuthConfig) (*Manager, error) { 53 var backends []*backend 54 for _, config := range configs { 55 config = config.applyDefaults() 56 var c Client 57 58 if len(config.Backend) != 1 { 59 return nil, fmt.Errorf("no backend or more than one backend configured") 60 } 61 var name string 62 var backendConfig interface{} 63 for name, backendConfig = range config.Backend { // Pull the only key/value out of map 64 } 65 factory, err := getFactory(name) 66 if err != nil { 67 return nil, fmt.Errorf("get backend client factory: %s", err) 68 } 69 c, err = factory.Create(backendConfig, auth[name]) 70 if err != nil { 71 return nil, fmt.Errorf("create backend client: %s", err) 72 } 73 74 if config.Bandwidth.Enable { 75 l, err := bandwidth.NewLimiter(config.Bandwidth) 76 if err != nil { 77 return nil, fmt.Errorf("bandwidth: %s", err) 78 } 79 c = throttle(c, l) 80 } 81 b, err := newBackend(config.Namespace, c) 82 if err != nil { 83 return nil, fmt.Errorf("new backend for namespace %s: %s", config.Namespace, err) 84 } 85 backends = append(backends, b) 86 } 87 return &Manager{backends}, nil 88 } 89 90 // AdjustBandwidth adjusts bandwidth limits across all throttled clients to the 91 // originally configured bandwidth divided by denominator. 92 func (m *Manager) AdjustBandwidth(denominator int) error { 93 for _, b := range m.backends { 94 tc, ok := b.client.(*ThrottledClient) 95 if !ok { 96 continue 97 } 98 if err := tc.adjustBandwidth(denominator); err != nil { 99 return err 100 } 101 log.With( 102 "namespace", b.regexp.String(), 103 "ingress", tc.IngressLimit(), 104 "egress", tc.EgressLimit(), 105 "denominator", denominator).Info("Adjusted backend bandwidth") 106 } 107 return nil 108 } 109 110 // Register dynamically registers a namespace with a provided client. Register 111 // should be primarily used for testing purposes -- normally, namespaces should 112 // be statically configured and provided upon construction of the Manager. 113 func (m *Manager) Register(namespace string, c Client) error { 114 for _, b := range m.backends { 115 if b.regexp.String() == namespace { 116 return fmt.Errorf("namespace %s already exists", namespace) 117 } 118 } 119 b, err := newBackend(namespace, c) 120 if err != nil { 121 return fmt.Errorf("new backend: %s", err) 122 } 123 m.backends = append(m.backends, b) 124 return nil 125 } 126 127 // GetClient matches namespace to the configured Client. Returns ErrNamespaceNotFound 128 // if no clients match namespace. 129 func (m *Manager) GetClient(namespace string) (Client, error) { 130 if namespace == NoopNamespace { 131 return NoopClient{}, nil 132 } 133 for _, b := range m.backends { 134 if b.regexp.MatchString(namespace) { 135 return b.client, nil 136 } 137 } 138 return nil, ErrNamespaceNotFound 139 }