github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/command/agent/consul/namespaces_client.go (about)

     1  package consul
     2  
     3  import (
     4  	"sort"
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/hashicorp/consul/api"
     9  )
    10  
    11  const (
    12  	// namespaceEnabledCacheTTL is how long to cache the response from Consul
    13  	// /v1/agent/self API, which is used to determine whether namespaces are
    14  	// available.
    15  	namespaceEnabledCacheTTL = 1 * time.Minute
    16  )
    17  
    18  // NamespacesClient is a wrapper for the Consul NamespacesAPI, that is used to
    19  // deal with Consul OSS vs Consul Enterprise behavior in listing namespaces.
    20  type NamespacesClient struct {
    21  	namespacesAPI NamespaceAPI
    22  	agentAPI      AgentAPI
    23  
    24  	lock    sync.Mutex
    25  	enabled bool      // namespaces requires Ent + Namespaces feature
    26  	updated time.Time // memoize response for a while
    27  }
    28  
    29  // NewNamespacesClient returns a NamespacesClient backed by a NamespaceAPI.
    30  func NewNamespacesClient(namespacesAPI NamespaceAPI, agentAPI AgentAPI) *NamespacesClient {
    31  	return &NamespacesClient{
    32  		namespacesAPI: namespacesAPI,
    33  		agentAPI:      agentAPI,
    34  	}
    35  }
    36  
    37  func stale(updated, now time.Time) bool {
    38  	return now.After(updated.Add(namespaceEnabledCacheTTL))
    39  }
    40  
    41  func (ns *NamespacesClient) allowable(now time.Time) bool {
    42  	ns.lock.Lock()
    43  	defer ns.lock.Unlock()
    44  
    45  	if !stale(ns.updated, now) {
    46  		return ns.enabled
    47  	}
    48  
    49  	self, err := ns.agentAPI.Self()
    50  	if err != nil {
    51  		return ns.enabled
    52  	}
    53  
    54  	sku, ok := SKU(self)
    55  	if !ok {
    56  		return ns.enabled
    57  	}
    58  
    59  	if sku != "ent" {
    60  		ns.enabled = false
    61  		ns.updated = now
    62  		return ns.enabled
    63  	}
    64  
    65  	ns.enabled = Namespaces(self)
    66  	ns.updated = now
    67  	return ns.enabled
    68  }
    69  
    70  // List returns a list of Consul Namespaces.
    71  func (ns *NamespacesClient) List() ([]string, error) {
    72  	if !ns.allowable(time.Now()) {
    73  		// TODO(shoenig): lets return the empty string instead, that way we do not
    74  		//   need to normalize at call sites later on
    75  		return []string{"default"}, nil
    76  	}
    77  
    78  	qo := &api.QueryOptions{
    79  		AllowStale: true,
    80  	}
    81  	namespaces, _, err := ns.namespacesAPI.List(qo)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	result := make([]string, 0, len(namespaces))
    87  	for _, namespace := range namespaces {
    88  		result = append(result, namespace.Name)
    89  	}
    90  	sort.Strings(result)
    91  	return result, nil
    92  }