github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/nomad/structs/config/consul.go (about)

     1  package config
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"strings"
     7  	"time"
     8  
     9  	consul "github.com/hashicorp/consul/api"
    10  	"github.com/hashicorp/go-secure-stdlib/listenerutil"
    11  	"github.com/hashicorp/nomad/helper/pointer"
    12  )
    13  
    14  // ConsulConfig contains the configuration information necessary to
    15  // communicate with a Consul Agent in order to:
    16  //
    17  // - Register services and their checks with Consul
    18  //
    19  //   - Bootstrap this Nomad Client with the list of Nomad Servers registered
    20  //     with Consul
    21  //
    22  //   - Establish how this Nomad Client will resolve Envoy Connect Sidecar
    23  //     images.
    24  //
    25  // Both the Agent and the executor need to be able to import ConsulConfig.
    26  type ConsulConfig struct {
    27  	// ServerServiceName is the name of the service that Nomad uses to register
    28  	// servers with Consul
    29  	ServerServiceName string `hcl:"server_service_name"`
    30  
    31  	// ServerHTTPCheckName is the name of the health check that Nomad uses
    32  	// to register the server HTTP health check with Consul
    33  	ServerHTTPCheckName string `hcl:"server_http_check_name"`
    34  
    35  	// ServerSerfCheckName is the name of the health check that Nomad uses
    36  	// to register the server Serf health check with Consul
    37  	ServerSerfCheckName string `hcl:"server_serf_check_name"`
    38  
    39  	// ServerRPCCheckName is the name of the health check that Nomad uses
    40  	// to register the server RPC health check with Consul
    41  	ServerRPCCheckName string `hcl:"server_rpc_check_name"`
    42  
    43  	// ClientServiceName is the name of the service that Nomad uses to register
    44  	// clients with Consul
    45  	ClientServiceName string `hcl:"client_service_name"`
    46  
    47  	// ClientHTTPCheckName is the name of the health check that Nomad uses
    48  	// to register the client HTTP health check with Consul
    49  	ClientHTTPCheckName string `hcl:"client_http_check_name"`
    50  
    51  	// Tags are optional service tags that get registered with the service
    52  	// in Consul
    53  	Tags []string `hcl:"tags"`
    54  
    55  	// AutoAdvertise determines if this Nomad Agent will advertise its
    56  	// services via Consul.  When true, Nomad Agent will register
    57  	// services with Consul.
    58  	AutoAdvertise *bool `hcl:"auto_advertise"`
    59  
    60  	// ChecksUseAdvertise specifies that Consul checks should use advertise
    61  	// address instead of bind address
    62  	ChecksUseAdvertise *bool `hcl:"checks_use_advertise"`
    63  
    64  	// Addr is the HTTP endpoint address of the local Consul agent
    65  	//
    66  	// Uses Consul's default and env var.
    67  	Addr string `hcl:"address"`
    68  
    69  	// GRPCAddr is the gRPC endpoint address of the local Consul agent
    70  	GRPCAddr string `hcl:"grpc_address"`
    71  
    72  	// Timeout is used by Consul HTTP Client
    73  	Timeout    time.Duration `hcl:"-"`
    74  	TimeoutHCL string        `hcl:"timeout" json:"-"`
    75  
    76  	// Token is used to provide a per-request ACL token. This options overrides
    77  	// the agent's default token
    78  	Token string `hcl:"token"`
    79  
    80  	// AllowUnauthenticated allows users to submit jobs requiring Consul
    81  	// Service Identity tokens without providing a Consul token proving they
    82  	// have access to such policies.
    83  	AllowUnauthenticated *bool `hcl:"allow_unauthenticated"`
    84  
    85  	// Auth is the information to use for http access to Consul agent
    86  	Auth string `hcl:"auth"`
    87  
    88  	// EnableSSL sets the transport scheme to talk to the Consul agent as https
    89  	//
    90  	// Uses Consul's default and env var.
    91  	EnableSSL *bool `hcl:"ssl"`
    92  
    93  	// ShareSSL enables Consul Connect Native applications to use the TLS
    94  	// configuration of the Nomad Client for establishing connections to Consul.
    95  	//
    96  	// Does not include sharing of ACL tokens.
    97  	ShareSSL *bool `hcl:"share_ssl"`
    98  
    99  	// VerifySSL enables or disables SSL verification when the transport scheme
   100  	// for the consul api client is https
   101  	//
   102  	// Uses Consul's default and env var.
   103  	VerifySSL *bool `hcl:"verify_ssl"`
   104  
   105  	// CAFile is the path to the ca certificate used for Consul communication.
   106  	//
   107  	// Uses Consul's default and env var.
   108  	CAFile string `hcl:"ca_file"`
   109  
   110  	// CertFile is the path to the certificate for Consul communication
   111  	CertFile string `hcl:"cert_file"`
   112  
   113  	// KeyFile is the path to the private key for Consul communication
   114  	KeyFile string `hcl:"key_file"`
   115  
   116  	// ServerAutoJoin enables Nomad servers to find peers by querying Consul and
   117  	// joining them
   118  	ServerAutoJoin *bool `hcl:"server_auto_join"`
   119  
   120  	// ClientAutoJoin enables Nomad servers to find addresses of Nomad servers
   121  	// and register with them
   122  	ClientAutoJoin *bool `hcl:"client_auto_join"`
   123  
   124  	// ExtraKeysHCL is used by hcl to surface unexpected keys
   125  	ExtraKeysHCL []string `hcl:",unusedKeys" json:"-"`
   126  
   127  	// Namespace sets the Consul namespace used for all calls against the
   128  	// Consul API. If this is unset, then Nomad does not specify a consul namespace.
   129  	Namespace string `hcl:"namespace"`
   130  }
   131  
   132  // DefaultConsulConfig returns the canonical defaults for the Nomad
   133  // `consul` configuration. Uses Consul's default configuration which reads
   134  // environment variables.
   135  func DefaultConsulConfig() *ConsulConfig {
   136  	def := consul.DefaultConfig()
   137  	return &ConsulConfig{
   138  		ServerServiceName:    "nomad",
   139  		ServerHTTPCheckName:  "Nomad Server HTTP Check",
   140  		ServerSerfCheckName:  "Nomad Server Serf Check",
   141  		ServerRPCCheckName:   "Nomad Server RPC Check",
   142  		ClientServiceName:    "nomad-client",
   143  		ClientHTTPCheckName:  "Nomad Client HTTP Check",
   144  		AutoAdvertise:        pointer.Of(true),
   145  		ChecksUseAdvertise:   pointer.Of(false),
   146  		ServerAutoJoin:       pointer.Of(true),
   147  		ClientAutoJoin:       pointer.Of(true),
   148  		AllowUnauthenticated: pointer.Of(true),
   149  		Timeout:              5 * time.Second,
   150  
   151  		// From Consul api package defaults
   152  		Addr:      def.Address,
   153  		EnableSSL: pointer.Of(def.Scheme == "https"),
   154  		VerifySSL: pointer.Of(!def.TLSConfig.InsecureSkipVerify),
   155  		CAFile:    def.TLSConfig.CAFile,
   156  		Namespace: def.Namespace,
   157  	}
   158  }
   159  
   160  // AllowsUnauthenticated returns whether the config allows unauthenticated
   161  // creation of Consul Service Identity tokens for Consul Connect enabled Tasks.
   162  //
   163  // If allow_unauthenticated is false, the operator must provide a token on
   164  // job submission (i.e. -consul-token or $CONSUL_HTTP_TOKEN).
   165  func (c *ConsulConfig) AllowsUnauthenticated() bool {
   166  	return c.AllowUnauthenticated != nil && *c.AllowUnauthenticated
   167  }
   168  
   169  // Merge merges two Consul Configurations together.
   170  func (c *ConsulConfig) Merge(b *ConsulConfig) *ConsulConfig {
   171  	result := c.Copy()
   172  
   173  	if b.ServerServiceName != "" {
   174  		result.ServerServiceName = b.ServerServiceName
   175  	}
   176  	if b.ServerHTTPCheckName != "" {
   177  		result.ServerHTTPCheckName = b.ServerHTTPCheckName
   178  	}
   179  	if b.ServerSerfCheckName != "" {
   180  		result.ServerSerfCheckName = b.ServerSerfCheckName
   181  	}
   182  	if b.ServerRPCCheckName != "" {
   183  		result.ServerRPCCheckName = b.ServerRPCCheckName
   184  	}
   185  	if b.ClientServiceName != "" {
   186  		result.ClientServiceName = b.ClientServiceName
   187  	}
   188  	if b.ClientHTTPCheckName != "" {
   189  		result.ClientHTTPCheckName = b.ClientHTTPCheckName
   190  	}
   191  	result.Tags = append(result.Tags, b.Tags...)
   192  	if b.AutoAdvertise != nil {
   193  		result.AutoAdvertise = pointer.Of(*b.AutoAdvertise)
   194  	}
   195  	if b.Addr != "" {
   196  		result.Addr = b.Addr
   197  	}
   198  	if b.GRPCAddr != "" {
   199  		result.GRPCAddr = b.GRPCAddr
   200  	}
   201  	if b.Timeout != 0 {
   202  		result.Timeout = b.Timeout
   203  	}
   204  	if b.TimeoutHCL != "" {
   205  		result.TimeoutHCL = b.TimeoutHCL
   206  	}
   207  	if b.Token != "" {
   208  		result.Token = b.Token
   209  	}
   210  	if b.Auth != "" {
   211  		result.Auth = b.Auth
   212  	}
   213  	if b.EnableSSL != nil {
   214  		result.EnableSSL = pointer.Of(*b.EnableSSL)
   215  	}
   216  	if b.VerifySSL != nil {
   217  		result.VerifySSL = pointer.Of(*b.VerifySSL)
   218  	}
   219  	if b.ShareSSL != nil {
   220  		result.ShareSSL = pointer.Of(*b.ShareSSL)
   221  	}
   222  	if b.CAFile != "" {
   223  		result.CAFile = b.CAFile
   224  	}
   225  	if b.CertFile != "" {
   226  		result.CertFile = b.CertFile
   227  	}
   228  	if b.KeyFile != "" {
   229  		result.KeyFile = b.KeyFile
   230  	}
   231  	if b.ServerAutoJoin != nil {
   232  		result.ServerAutoJoin = pointer.Of(*b.ServerAutoJoin)
   233  	}
   234  	if b.ClientAutoJoin != nil {
   235  		result.ClientAutoJoin = pointer.Of(*b.ClientAutoJoin)
   236  	}
   237  	if b.ChecksUseAdvertise != nil {
   238  		result.ChecksUseAdvertise = pointer.Of(*b.ChecksUseAdvertise)
   239  	}
   240  	if b.AllowUnauthenticated != nil {
   241  		result.AllowUnauthenticated = pointer.Of(*b.AllowUnauthenticated)
   242  	}
   243  	if b.Namespace != "" {
   244  		result.Namespace = b.Namespace
   245  	}
   246  	return result
   247  }
   248  
   249  // ApiConfig returns a usable Consul config that can be passed directly to
   250  // hashicorp/consul/api.  NOTE: datacenter is not set
   251  func (c *ConsulConfig) ApiConfig() (*consul.Config, error) {
   252  	// Get the default config from consul to reuse things like the default
   253  	// http.Transport.
   254  	config := consul.DefaultConfig()
   255  	if c.Addr != "" {
   256  		ipStr, err := listenerutil.ParseSingleIPTemplate(c.Addr)
   257  		if err != nil {
   258  			return nil, fmt.Errorf("unable to parse address template %q: %v", c.Addr, err)
   259  		}
   260  		config.Address = ipStr
   261  	}
   262  	if c.Token != "" {
   263  		config.Token = c.Token
   264  	}
   265  	if c.Timeout != 0 {
   266  		// Create a custom Client to set the timeout
   267  		if config.HttpClient == nil {
   268  			config.HttpClient = &http.Client{}
   269  		}
   270  		config.HttpClient.Timeout = c.Timeout
   271  		config.HttpClient.Transport = config.Transport
   272  	}
   273  	if c.Auth != "" {
   274  		var username, password string
   275  		if strings.Contains(c.Auth, ":") {
   276  			split := strings.SplitN(c.Auth, ":", 2)
   277  			username = split[0]
   278  			password = split[1]
   279  		} else {
   280  			username = c.Auth
   281  		}
   282  
   283  		config.HttpAuth = &consul.HttpBasicAuth{
   284  			Username: username,
   285  			Password: password,
   286  		}
   287  	}
   288  	if c.EnableSSL != nil && *c.EnableSSL {
   289  		config.Scheme = "https"
   290  		config.TLSConfig = consul.TLSConfig{
   291  			Address:  config.Address,
   292  			CAFile:   c.CAFile,
   293  			CertFile: c.CertFile,
   294  			KeyFile:  c.KeyFile,
   295  		}
   296  		if c.VerifySSL != nil {
   297  			config.TLSConfig.InsecureSkipVerify = !*c.VerifySSL
   298  		}
   299  		tlsConfig, err := consul.SetupTLSConfig(&config.TLSConfig)
   300  		if err != nil {
   301  			return nil, err
   302  		}
   303  		config.Transport.TLSClientConfig = tlsConfig
   304  	}
   305  	if c.Namespace != "" {
   306  		config.Namespace = c.Namespace
   307  	}
   308  	return config, nil
   309  }
   310  
   311  // Copy returns a copy of this Consul config.
   312  func (c *ConsulConfig) Copy() *ConsulConfig {
   313  	if c == nil {
   314  		return nil
   315  	}
   316  
   317  	nc := new(ConsulConfig)
   318  	*nc = *c
   319  
   320  	// Copy the bools
   321  	if nc.AutoAdvertise != nil {
   322  		nc.AutoAdvertise = pointer.Of(*nc.AutoAdvertise)
   323  	}
   324  	if nc.ChecksUseAdvertise != nil {
   325  		nc.ChecksUseAdvertise = pointer.Of(*nc.ChecksUseAdvertise)
   326  	}
   327  	if nc.EnableSSL != nil {
   328  		nc.EnableSSL = pointer.Of(*nc.EnableSSL)
   329  	}
   330  	if nc.VerifySSL != nil {
   331  		nc.VerifySSL = pointer.Of(*nc.VerifySSL)
   332  	}
   333  	if nc.ShareSSL != nil {
   334  		nc.ShareSSL = pointer.Of(*nc.ShareSSL)
   335  	}
   336  	if nc.ServerAutoJoin != nil {
   337  		nc.ServerAutoJoin = pointer.Of(*nc.ServerAutoJoin)
   338  	}
   339  	if nc.ClientAutoJoin != nil {
   340  		nc.ClientAutoJoin = pointer.Of(*nc.ClientAutoJoin)
   341  	}
   342  	if nc.AllowUnauthenticated != nil {
   343  		nc.AllowUnauthenticated = pointer.Of(*nc.AllowUnauthenticated)
   344  	}
   345  
   346  	return nc
   347  }