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  }