github.com/hernad/nomad@v1.6.112/command/agent/consul/namespaces_client.go (about)

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