github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/client/config/config.go (about) 1 package config 2 3 import ( 4 "errors" 5 "fmt" 6 "reflect" 7 "strconv" 8 "strings" 9 "time" 10 11 "github.com/hashicorp/consul-template/config" 12 "github.com/hashicorp/nomad/client/lib/cgutil" 13 "github.com/hashicorp/nomad/command/agent/host" 14 "golang.org/x/exp/maps" 15 "golang.org/x/exp/slices" 16 17 log "github.com/hashicorp/go-hclog" 18 "github.com/hashicorp/nomad/client/state" 19 "github.com/hashicorp/nomad/helper/bufconndialer" 20 "github.com/hashicorp/nomad/helper/pluginutils/loader" 21 "github.com/hashicorp/nomad/helper/pointer" 22 "github.com/hashicorp/nomad/nomad/structs" 23 structsc "github.com/hashicorp/nomad/nomad/structs/config" 24 "github.com/hashicorp/nomad/plugins/base" 25 "github.com/hashicorp/nomad/version" 26 ) 27 28 var ( 29 // DefaultEnvDenylist is the default set of environment variables that are 30 // filtered when passing the environment variables of the host to a task. 31 DefaultEnvDenylist = strings.Join(host.DefaultEnvDenyList, ",") 32 33 // DefaultUserDenylist is the default set of users that tasks are not 34 // allowed to run as when using a driver in "user.checked_drivers" 35 DefaultUserDenylist = strings.Join([]string{ 36 "root", 37 "Administrator", 38 }, ",") 39 40 // DefaultUserCheckedDrivers is the set of drivers we apply the user 41 // denylist onto. For virtualized drivers it often doesn't make sense to 42 // make this stipulation so by default they are ignored. 43 DefaultUserCheckedDrivers = strings.Join([]string{ 44 "exec", 45 "qemu", 46 "java", 47 }, ",") 48 49 // DefaultChrootEnv is a mapping of directories on the host OS to attempt to embed inside each 50 // task's chroot. 51 DefaultChrootEnv = map[string]string{ 52 "/bin": "/bin", 53 "/etc": "/etc", 54 "/lib": "/lib", 55 "/lib32": "/lib32", 56 "/lib64": "/lib64", 57 "/run/resolvconf": "/run/resolvconf", 58 "/sbin": "/sbin", 59 "/usr": "/usr", 60 61 // embed systemd-resolved paths for systemd-resolved paths: 62 // /etc/resolv.conf is a symlink to /run/systemd/resolve/stub-resolv.conf in such systems. 63 // In non-systemd systems, this mount is a no-op and the path is ignored if not present. 64 "/run/systemd/resolve": "/run/systemd/resolve", 65 } 66 67 DefaultTemplateMaxStale = 87600 * time.Hour 68 69 DefaultTemplateFunctionDenylist = []string{"plugin", "writeToFile"} 70 ) 71 72 // RPCHandler can be provided to the Client if there is a local server 73 // to avoid going over the network. If not provided, the Client will 74 // maintain a connection pool to the servers 75 type RPCHandler interface { 76 RPC(method string, args interface{}, reply interface{}) error 77 } 78 79 // Config is used to parameterize and configure the behavior of the client 80 type Config struct { 81 // DevMode controls if we are in a development mode which 82 // avoids persistent storage. 83 DevMode bool 84 85 // EnableDebug is used to enable debugging RPC endpoints 86 // in the absence of ACLs 87 EnableDebug bool 88 89 // StateDir is where we store our state 90 StateDir string 91 92 // AllocDir is where we store data for allocations 93 AllocDir string 94 95 // Logger provides a logger to the client 96 Logger log.InterceptLogger 97 98 // Region is the clients region 99 Region string 100 101 // Network interface to be used in network fingerprinting 102 NetworkInterface string 103 104 // Network speed is the default speed of network interfaces if they can not 105 // be determined dynamically. 106 NetworkSpeed int 107 108 // CpuCompute is the default total CPU compute if they can not be determined 109 // dynamically. It should be given as Cores * MHz (2 Cores * 2 Ghz = 4000) 110 CpuCompute int 111 112 // MemoryMB is the default node total memory in megabytes if it cannot be 113 // determined dynamically. 114 MemoryMB int 115 116 // MaxKillTimeout allows capping the user-specifiable KillTimeout. If the 117 // task's KillTimeout is greater than the MaxKillTimeout, MaxKillTimeout is 118 // used. 119 MaxKillTimeout time.Duration 120 121 // Servers is a list of known server addresses. These are as "host:port" 122 Servers []string 123 124 // RPCHandler can be provided to avoid network traffic if the 125 // server is running locally. 126 RPCHandler RPCHandler 127 128 // Node provides the base node 129 Node *structs.Node 130 131 // ClientMaxPort is the upper range of the ports that the client uses for 132 // communicating with plugin subsystems over loopback 133 ClientMaxPort uint 134 135 // ClientMinPort is the lower range of the ports that the client uses for 136 // communicating with plugin subsystems over loopback 137 ClientMinPort uint 138 139 // MaxDynamicPort is the largest dynamic port generated 140 MaxDynamicPort int 141 142 // MinDynamicPort is the smallest dynamic port generated 143 MinDynamicPort int 144 145 // A mapping of directories on the host OS to attempt to embed inside each 146 // task's chroot. 147 ChrootEnv map[string]string 148 149 // Options provides arbitrary key-value configuration for nomad internals, 150 // like fingerprinters and drivers. The format is: 151 // 152 // namespace.option = value 153 Options map[string]string 154 155 // Version is the version of the Nomad client 156 Version *version.VersionInfo 157 158 // ConsulConfig is this Agent's Consul configuration 159 ConsulConfig *structsc.ConsulConfig 160 161 // VaultConfig is this Agent's Vault configuration 162 VaultConfig *structsc.VaultConfig 163 164 // StatsCollectionInterval is the interval at which the Nomad client 165 // collects resource usage stats 166 StatsCollectionInterval time.Duration 167 168 // PublishNodeMetrics determines whether nomad is going to publish node 169 // level metrics to remote Telemetry sinks 170 PublishNodeMetrics bool 171 172 // PublishAllocationMetrics determines whether nomad is going to publish 173 // allocation metrics to remote Telemetry sinks 174 PublishAllocationMetrics bool 175 176 // TLSConfig holds various TLS related configurations 177 TLSConfig *structsc.TLSConfig 178 179 // GCInterval is the time interval at which the client triggers garbage 180 // collection 181 GCInterval time.Duration 182 183 // GCParallelDestroys is the number of parallel destroys the garbage 184 // collector will allow. 185 GCParallelDestroys int 186 187 // GCDiskUsageThreshold is the disk usage threshold given as a percent 188 // beyond which the Nomad client triggers GC of terminal allocations 189 GCDiskUsageThreshold float64 190 191 // GCInodeUsageThreshold is the inode usage threshold given as a percent 192 // beyond which the Nomad client triggers GC of the terminal allocations 193 GCInodeUsageThreshold float64 194 195 // GCMaxAllocs is the maximum number of allocations a node can have 196 // before garbage collection is triggered. 197 GCMaxAllocs int 198 199 // NoHostUUID disables using the host's UUID and will force generation of a 200 // random UUID. 201 NoHostUUID bool 202 203 // ACLEnabled controls if ACL enforcement and management is enabled. 204 ACLEnabled bool 205 206 // ACLTokenTTL is how long we cache token values for 207 ACLTokenTTL time.Duration 208 209 // ACLPolicyTTL is how long we cache policy values for 210 ACLPolicyTTL time.Duration 211 212 // ACLRoleTTL is how long we cache ACL role value for within each Nomad 213 // client. 214 ACLRoleTTL time.Duration 215 216 // DisableRemoteExec disables remote exec targeting tasks on this client 217 DisableRemoteExec bool 218 219 // TemplateConfig includes configuration for template rendering 220 TemplateConfig *ClientTemplateConfig 221 222 // RPCHoldTimeout is how long an RPC can be "held" before it is errored. 223 // This is used to paper over a loss of leadership by instead holding RPCs, 224 // so that the caller experiences a slow response rather than an error. 225 // This period is meant to be long enough for a leader election to take 226 // place, and a small jitter is applied to avoid a thundering herd. 227 RPCHoldTimeout time.Duration 228 229 // PluginLoader is used to load plugins. 230 PluginLoader loader.PluginCatalog 231 232 // PluginSingletonLoader is a plugin loader that will returns singleton 233 // instances of the plugins. 234 PluginSingletonLoader loader.PluginCatalog 235 236 // StateDBFactory is used to override stateDB implementations, 237 StateDBFactory state.NewStateDBFunc 238 239 // CNIPath is the path used to search for CNI plugins. Multiple paths can 240 // be specified with colon delimited 241 CNIPath string 242 243 // CNIConfigDir is the directory where CNI network configuration is located. The 244 // client will use this path when fingerprinting CNI networks. 245 CNIConfigDir string 246 247 // CNIInterfacePrefix is the prefix to use when creating CNI network interfaces. This 248 // defaults to 'eth', therefore the first interface created by CNI inside the alloc 249 // network will be 'eth0'. 250 CNIInterfacePrefix string 251 252 // BridgeNetworkName is the name to use for the bridge created in bridge 253 // networking mode. This defaults to 'nomad' if not set 254 BridgeNetworkName string 255 256 // BridgeNetworkAllocSubnet is the IP subnet to use for address allocation 257 // for allocations in bridge networking mode. Subnet must be in CIDR 258 // notation 259 BridgeNetworkAllocSubnet string 260 261 // HostVolumes is a map of the configured host volumes by name. 262 HostVolumes map[string]*structs.ClientHostVolumeConfig 263 264 // HostNetworks is a map of the conigured host networks by name. 265 HostNetworks map[string]*structs.ClientHostNetworkConfig 266 267 // BindWildcardDefaultHostNetwork toggles if the default host network should accept all 268 // destinations (true) or only filter on the IP of the default host network (false) when 269 // port mapping. This allows Nomad clients with no defined host networks to accept and 270 // port forward traffic only matching on the destination port. An example use of this 271 // is when a network loadbalancer is utilizing direct server return and the destination 272 // address of incomming packets does not match the IP address of the host interface. 273 // 274 // This configuration is only considered if no host networks are defined. 275 BindWildcardDefaultHostNetwork bool 276 277 // CgroupParent is the parent cgroup Nomad should use when managing any cgroup subsystems. 278 // Currently this only includes the 'cpuset' cgroup subsystem. 279 CgroupParent string 280 281 // ReservableCores if set overrides the set of reservable cores reported in fingerprinting. 282 ReservableCores []uint16 283 284 // NomadServiceDiscovery determines whether the Nomad native service 285 // discovery client functionality is enabled. 286 NomadServiceDiscovery bool 287 288 // TemplateDialer is our custom HTTP dialer for consul-template. This is 289 // used for template functions which require access to the Nomad API. 290 TemplateDialer *bufconndialer.BufConnWrapper 291 292 // Artifact configuration from the agent's config file. 293 Artifact *ArtifactConfig 294 } 295 296 // ClientTemplateConfig is configuration on the client specific to template 297 // rendering 298 type ClientTemplateConfig struct { 299 // FunctionDenylist disables functions in consul-template that 300 // are unsafe because they expose information from the client host. 301 FunctionDenylist []string `hcl:"function_denylist"` 302 303 // Deprecated: COMPAT(1.0) consul-template uses inclusive language from 304 // v0.25.0 - function_blacklist is kept for compatibility 305 FunctionBlacklist []string `hcl:"function_blacklist"` 306 307 // DisableSandbox allows templates to access arbitrary files on the 308 // client host. By default templates can access files only within 309 // the task directory. 310 DisableSandbox bool `hcl:"disable_file_sandbox"` 311 312 // This is the maximum interval to allow "stale" data. By default, only the 313 // Consul leader will respond to queries; any requests to a follower will 314 // forward to the leader. In large clusters with many requests, this is not as 315 // scalable, so this option allows any follower to respond to a query, so long 316 // as the last-replicated data is within these bounds. Higher values result in 317 // less cluster load, but are more likely to have outdated data. 318 // NOTE: Since Consul Template uses a pointer, this field uses a pointer which 319 // is inconsistent with how Nomad typically works. This decision was made to 320 // maintain parity with the external subsystem, not to establish a new standard. 321 MaxStale *time.Duration `hcl:"-"` 322 MaxStaleHCL string `hcl:"max_stale,optional"` 323 324 // BlockQueryWaitTime is amount of time in seconds to do a blocking query for. 325 // Many endpoints in Consul support a feature known as "blocking queries". 326 // A blocking query is used to wait for a potential change using long polling. 327 // NOTE: Since Consul Template uses a pointer, this field uses a pointer which 328 // is inconsistent with how Nomad typically works. This decision was made to 329 // maintain parity with the external subsystem, not to establish a new standard. 330 BlockQueryWaitTime *time.Duration `hcl:"-"` 331 BlockQueryWaitTimeHCL string `hcl:"block_query_wait,optional"` 332 333 // Wait is the quiescence timers; it defines the minimum and maximum amount of 334 // time to wait for the Consul cluster to reach a consistent state before rendering a 335 // template. This is useful to enable in systems where Consul is experiencing 336 // a lot of flapping because it will reduce the number of times a template is rendered. 337 Wait *WaitConfig `hcl:"wait,optional" json:"-"` 338 339 // WaitBounds allows operators to define boundaries on individual template wait 340 // configuration overrides. If set, this ensures that if a job author specifies 341 // a wait configuration with values the cluster operator does not allow, the 342 // cluster operator's boundary will be applied rather than the job author's 343 // out of bounds configuration. 344 WaitBounds *WaitConfig `hcl:"wait_bounds,optional" json:"-"` 345 346 // This controls the retry behavior when an error is returned from Consul. 347 // Consul Template is highly fault tolerant, meaning it does not exit in the 348 // face of failure. Instead, it uses exponential back-off and retry functions 349 // to wait for the cluster to become available, as is customary in distributed 350 // systems. 351 ConsulRetry *RetryConfig `hcl:"consul_retry,optional"` 352 353 // This controls the retry behavior when an error is returned from Vault. 354 // Consul Template is highly fault tolerant, meaning it does not exit in the 355 // face of failure. Instead, it uses exponential back-off and retry functions 356 // to wait for the cluster to become available, as is customary in distributed 357 // systems. 358 VaultRetry *RetryConfig `hcl:"vault_retry,optional"` 359 360 // This controls the retry behavior when an error is returned from Nomad. 361 // Consul Template is highly fault tolerant, meaning it does not exit in the 362 // face of failure. Instead, it uses exponential back-off and retry functions 363 // to wait for the cluster to become available, as is customary in distributed 364 // systems. 365 NomadRetry *RetryConfig `hcl:"nomad_retry,optional"` 366 } 367 368 // Copy returns a deep copy of a ClientTemplateConfig 369 func (c *ClientTemplateConfig) Copy() *ClientTemplateConfig { 370 if c == nil { 371 return nil 372 } 373 374 nc := new(ClientTemplateConfig) 375 *nc = *c 376 377 if len(c.FunctionDenylist) > 0 { 378 nc.FunctionDenylist = slices.Clone(nc.FunctionDenylist) 379 } else if c.FunctionDenylist != nil { 380 // Explicitly no functions denied (which is different than nil) 381 nc.FunctionDenylist = []string{} 382 } 383 384 if c.BlockQueryWaitTime != nil { 385 nc.BlockQueryWaitTime = &*c.BlockQueryWaitTime 386 } 387 388 if c.MaxStale != nil { 389 nc.MaxStale = &*c.MaxStale 390 } 391 392 if c.Wait != nil { 393 nc.Wait = c.Wait.Copy() 394 } 395 396 if c.ConsulRetry != nil { 397 nc.ConsulRetry = c.ConsulRetry.Copy() 398 } 399 400 if c.VaultRetry != nil { 401 nc.VaultRetry = c.VaultRetry.Copy() 402 } 403 404 if c.NomadRetry != nil { 405 nc.NomadRetry = c.NomadRetry.Copy() 406 } 407 408 return nc 409 } 410 411 func (c *ClientTemplateConfig) IsEmpty() bool { 412 if c == nil { 413 return true 414 } 415 416 return !c.DisableSandbox && 417 c.FunctionDenylist == nil && 418 c.FunctionBlacklist == nil && 419 c.BlockQueryWaitTime == nil && 420 c.BlockQueryWaitTimeHCL == "" && 421 c.MaxStale == nil && 422 c.MaxStaleHCL == "" && 423 c.Wait.IsEmpty() && 424 c.ConsulRetry.IsEmpty() && 425 c.VaultRetry.IsEmpty() && 426 c.NomadRetry.IsEmpty() 427 } 428 429 // WaitConfig is mirrored from templateconfig.WaitConfig because we need to handle 430 // the HCL conversion which happens in agent.ParseConfigFile 431 // NOTE: Since Consul Template requires pointers, this type uses pointers to fields 432 // which is inconsistent with how Nomad typically works. This decision was made 433 // to maintain parity with the external subsystem, not to establish a new standard. 434 type WaitConfig struct { 435 Min *time.Duration `hcl:"-"` 436 MinHCL string `hcl:"min,optional" json:"-"` 437 Max *time.Duration `hcl:"-"` 438 MaxHCL string `hcl:"max,optional" json:"-"` 439 } 440 441 // Copy returns a deep copy of the receiver. 442 func (wc *WaitConfig) Copy() *WaitConfig { 443 if wc == nil { 444 return nil 445 } 446 447 nwc := new(WaitConfig) 448 449 if wc.Min != nil { 450 nwc.Min = &*wc.Min 451 } 452 453 if wc.Max != nil { 454 nwc.Max = &*wc.Max 455 } 456 457 return wc 458 } 459 460 // Equal returns the result of reflect.DeepEqual 461 func (wc *WaitConfig) Equal(other *WaitConfig) bool { 462 return reflect.DeepEqual(wc, other) 463 } 464 465 // IsEmpty returns true if the receiver only contains an instance with no fields set. 466 func (wc *WaitConfig) IsEmpty() bool { 467 if wc == nil { 468 return true 469 } 470 return wc.Equal(&WaitConfig{}) 471 } 472 473 // Validate returns an error if the receiver is nil or empty or if Min is greater 474 // than Max the user specified Max. 475 func (wc *WaitConfig) Validate() error { 476 // If the config is nil or empty return false so that it is never assigned. 477 if wc == nil || wc.IsEmpty() { 478 return errors.New("wait config is nil or empty") 479 } 480 481 // If min is nil, return 482 if wc.Min == nil { 483 return nil 484 } 485 486 // If min isn't nil, make sure Max is less than Min. 487 if wc.Max != nil { 488 if *wc.Min > *wc.Max { 489 return fmt.Errorf("wait config min %d is greater than max %d", *wc.Min, *wc.Max) 490 } 491 } 492 493 // Otherwise, return nil. Consul Template will set a Max based off of Min. 494 return nil 495 } 496 497 // Merge merges two WaitConfigs. The passed instance always takes precedence. 498 func (wc *WaitConfig) Merge(b *WaitConfig) *WaitConfig { 499 if wc == nil { 500 return b 501 } 502 503 result := *wc 504 if b == nil { 505 return &result 506 } 507 508 if b.Min != nil { 509 result.Min = &*b.Min 510 } 511 512 if b.MinHCL != "" { 513 result.MinHCL = b.MinHCL 514 } 515 516 if b.Max != nil { 517 result.Max = &*b.Max 518 } 519 520 if b.MaxHCL != "" { 521 result.MaxHCL = b.MaxHCL 522 } 523 524 return &result 525 } 526 527 // ToConsulTemplate converts a client WaitConfig instance to a consul-template WaitConfig 528 func (wc *WaitConfig) ToConsulTemplate() (*config.WaitConfig, error) { 529 if wc.IsEmpty() { 530 return nil, errors.New("wait config is empty") 531 } 532 533 if err := wc.Validate(); err != nil { 534 return nil, err 535 } 536 537 result := &config.WaitConfig{Enabled: pointer.Of(true)} 538 539 if wc.Min != nil { 540 result.Min = wc.Min 541 } 542 543 if wc.Max != nil { 544 result.Max = wc.Max 545 } 546 547 return result, nil 548 } 549 550 // RetryConfig is mirrored from templateconfig.WaitConfig because we need to handle 551 // the HCL indirection to support mapping in agent.ParseConfigFile. 552 // NOTE: Since Consul Template requires pointers, this type uses pointers to fields 553 // which is inconsistent with how Nomad typically works. However, since zero in 554 // Attempts and MaxBackoff have special meaning, it is necessary to know if the 555 // value was actually set rather than if it defaulted to 0. The rest of the fields 556 // use pointers to maintain parity with the external subystem, not to establish 557 // a new standard. 558 type RetryConfig struct { 559 // Attempts is the total number of maximum attempts to retry before letting 560 // the error fall through. 561 // 0 means unlimited. 562 Attempts *int `hcl:"attempts,optional"` 563 // Backoff is the base of the exponential backoff. This number will be 564 // multiplied by the next power of 2 on each iteration. 565 Backoff *time.Duration `hcl:"-"` 566 BackoffHCL string `hcl:"backoff,optional" json:"-"` 567 // MaxBackoff is an upper limit to the sleep time between retries 568 // A MaxBackoff of 0 means there is no limit to the exponential growth of the backoff. 569 MaxBackoff *time.Duration `hcl:"-"` 570 MaxBackoffHCL string `hcl:"max_backoff,optional" json:"-"` 571 } 572 573 func (rc *RetryConfig) Copy() *RetryConfig { 574 if rc == nil { 575 return nil 576 } 577 578 nrc := new(RetryConfig) 579 *nrc = *rc 580 581 // Now copy pointer values 582 if rc.Attempts != nil { 583 nrc.Attempts = &*rc.Attempts 584 } 585 if rc.Backoff != nil { 586 nrc.Backoff = &*rc.Backoff 587 } 588 if rc.MaxBackoff != nil { 589 nrc.MaxBackoff = &*rc.MaxBackoff 590 } 591 592 return nrc 593 } 594 595 // Equal returns the result of reflect.DeepEqual 596 func (rc *RetryConfig) Equal(other *RetryConfig) bool { 597 return reflect.DeepEqual(rc, other) 598 } 599 600 // IsEmpty returns true if the receiver only contains an instance with no fields set. 601 func (rc *RetryConfig) IsEmpty() bool { 602 if rc == nil { 603 return true 604 } 605 606 return rc.Equal(&RetryConfig{}) 607 } 608 609 // Validate returns an error if the receiver is nil or empty, or if Backoff 610 // is greater than MaxBackoff. 611 func (rc *RetryConfig) Validate() error { 612 // If the config is nil or empty return false so that it is never assigned. 613 if rc == nil || rc.IsEmpty() { 614 return errors.New("retry config is nil or empty") 615 } 616 617 // If Backoff not set, no need to validate 618 if rc.Backoff == nil { 619 return nil 620 } 621 622 // MaxBackoff nil will end up defaulted to 1 minutes. We should validate that 623 // the user supplied backoff does not exceed that. 624 if rc.MaxBackoff == nil && *rc.Backoff > config.DefaultRetryMaxBackoff { 625 return fmt.Errorf("retry config backoff %d is greater than default max_backoff %d", *rc.Backoff, config.DefaultRetryMaxBackoff) 626 } 627 628 // MaxBackoff == 0 means backoff is unbounded. No need to validate. 629 if rc.MaxBackoff != nil && *rc.MaxBackoff == 0 { 630 return nil 631 } 632 633 if rc.MaxBackoff != nil && *rc.Backoff > *rc.MaxBackoff { 634 return fmt.Errorf("retry config backoff %d is greater than max_backoff %d", *rc.Backoff, *rc.MaxBackoff) 635 } 636 637 return nil 638 } 639 640 // Merge merges two RetryConfigs. The passed instance always takes precedence. 641 func (rc *RetryConfig) Merge(b *RetryConfig) *RetryConfig { 642 if rc == nil { 643 return b 644 } 645 646 result := *rc 647 if b == nil { 648 return &result 649 } 650 651 if b.Attempts != nil { 652 result.Attempts = &*b.Attempts 653 } 654 655 if b.Backoff != nil { 656 result.Backoff = &*b.Backoff 657 } 658 659 if b.BackoffHCL != "" { 660 result.BackoffHCL = b.BackoffHCL 661 } 662 663 if b.MaxBackoff != nil { 664 result.MaxBackoff = &*b.MaxBackoff 665 } 666 667 if b.MaxBackoffHCL != "" { 668 result.MaxBackoffHCL = b.MaxBackoffHCL 669 } 670 671 return &result 672 } 673 674 // ToConsulTemplate converts a client RetryConfig instance to a consul-template RetryConfig 675 func (rc *RetryConfig) ToConsulTemplate() (*config.RetryConfig, error) { 676 if err := rc.Validate(); err != nil { 677 return nil, err 678 } 679 680 result := &config.RetryConfig{Enabled: pointer.Of(true)} 681 682 if rc.Attempts != nil { 683 result.Attempts = rc.Attempts 684 } 685 686 if rc.Backoff != nil { 687 result.Backoff = rc.Backoff 688 } 689 690 if rc.MaxBackoff != nil { 691 result.MaxBackoff = &*rc.MaxBackoff 692 } 693 694 return result, nil 695 } 696 697 func (c *Config) Copy() *Config { 698 if c == nil { 699 return nil 700 } 701 702 nc := *c 703 nc.Node = nc.Node.Copy() 704 nc.Servers = slices.Clone(nc.Servers) 705 nc.Options = maps.Clone(nc.Options) 706 nc.HostVolumes = structs.CopyMapStringClientHostVolumeConfig(nc.HostVolumes) 707 nc.ConsulConfig = c.ConsulConfig.Copy() 708 nc.VaultConfig = c.VaultConfig.Copy() 709 nc.TemplateConfig = c.TemplateConfig.Copy() 710 nc.ReservableCores = slices.Clone(c.ReservableCores) 711 nc.Artifact = c.Artifact.Copy() 712 return &nc 713 } 714 715 // DefaultConfig returns the default configuration 716 func DefaultConfig() *Config { 717 return &Config{ 718 Version: version.GetVersion(), 719 VaultConfig: structsc.DefaultVaultConfig(), 720 ConsulConfig: structsc.DefaultConsulConfig(), 721 Region: "global", 722 StatsCollectionInterval: 1 * time.Second, 723 TLSConfig: &structsc.TLSConfig{}, 724 GCInterval: 1 * time.Minute, 725 GCParallelDestroys: 2, 726 GCDiskUsageThreshold: 80, 727 GCInodeUsageThreshold: 70, 728 GCMaxAllocs: 50, 729 NoHostUUID: true, 730 DisableRemoteExec: false, 731 TemplateConfig: &ClientTemplateConfig{ 732 FunctionDenylist: DefaultTemplateFunctionDenylist, 733 DisableSandbox: false, 734 BlockQueryWaitTime: pointer.Of(5 * time.Minute), // match Consul default 735 MaxStale: pointer.Of(DefaultTemplateMaxStale), // match Consul default 736 Wait: &WaitConfig{ 737 Min: pointer.Of(5 * time.Second), 738 Max: pointer.Of(4 * time.Minute), 739 }, 740 ConsulRetry: &RetryConfig{ 741 Attempts: pointer.Of(0), // unlimited 742 }, 743 VaultRetry: &RetryConfig{ 744 Attempts: pointer.Of(0), // unlimited 745 }, 746 NomadRetry: &RetryConfig{ 747 Attempts: pointer.Of(0), // unlimited 748 }, 749 }, 750 RPCHoldTimeout: 5 * time.Second, 751 CNIPath: "/opt/cni/bin", 752 CNIConfigDir: "/opt/cni/config", 753 CNIInterfacePrefix: "eth", 754 HostNetworks: map[string]*structs.ClientHostNetworkConfig{}, 755 CgroupParent: cgutil.GetCgroupParent(""), 756 MaxDynamicPort: structs.DefaultMinDynamicPort, 757 MinDynamicPort: structs.DefaultMaxDynamicPort, 758 } 759 } 760 761 // Read returns the specified configuration value or "". 762 func (c *Config) Read(id string) string { 763 return c.Options[id] 764 } 765 766 // ReadDefault returns the specified configuration value, or the specified 767 // default value if none is set. 768 func (c *Config) ReadDefault(id string, defaultValue string) string { 769 return c.ReadAlternativeDefault([]string{id}, defaultValue) 770 } 771 772 // ReadAlternativeDefault returns the specified configuration value, or the 773 // specified value if none is set. 774 func (c *Config) ReadAlternativeDefault(ids []string, defaultValue string) string { 775 for _, id := range ids { 776 val, ok := c.Options[id] 777 if ok { 778 return val 779 } 780 } 781 782 return defaultValue 783 } 784 785 // ReadBool parses the specified option as a boolean. 786 func (c *Config) ReadBool(id string) (bool, error) { 787 val, ok := c.Options[id] 788 if !ok { 789 return false, fmt.Errorf("Specified config is missing from options") 790 } 791 bval, err := strconv.ParseBool(val) 792 if err != nil { 793 return false, fmt.Errorf("Failed to parse %s as bool: %s", val, err) 794 } 795 return bval, nil 796 } 797 798 // ReadBoolDefault tries to parse the specified option as a boolean. If there is 799 // an error in parsing, the default option is returned. 800 func (c *Config) ReadBoolDefault(id string, defaultValue bool) bool { 801 val, err := c.ReadBool(id) 802 if err != nil { 803 return defaultValue 804 } 805 return val 806 } 807 808 // ReadInt parses the specified option as a int. 809 func (c *Config) ReadInt(id string) (int, error) { 810 val, ok := c.Options[id] 811 if !ok { 812 return 0, fmt.Errorf("Specified config is missing from options") 813 } 814 ival, err := strconv.Atoi(val) 815 if err != nil { 816 return 0, fmt.Errorf("Failed to parse %s as int: %s", val, err) 817 } 818 return ival, nil 819 } 820 821 // ReadIntDefault tries to parse the specified option as a int. If there is 822 // an error in parsing, the default option is returned. 823 func (c *Config) ReadIntDefault(id string, defaultValue int) int { 824 val, err := c.ReadInt(id) 825 if err != nil { 826 return defaultValue 827 } 828 return val 829 } 830 831 // ReadDuration parses the specified option as a duration. 832 func (c *Config) ReadDuration(id string) (time.Duration, error) { 833 val, ok := c.Options[id] 834 if !ok { 835 return time.Duration(0), fmt.Errorf("Specified config is missing from options") 836 } 837 dval, err := time.ParseDuration(val) 838 if err != nil { 839 return time.Duration(0), fmt.Errorf("Failed to parse %s as time duration: %s", val, err) 840 } 841 return dval, nil 842 } 843 844 // ReadDurationDefault tries to parse the specified option as a duration. If there is 845 // an error in parsing, the default option is returned. 846 func (c *Config) ReadDurationDefault(id string, defaultValue time.Duration) time.Duration { 847 val, err := c.ReadDuration(id) 848 if err != nil { 849 return defaultValue 850 } 851 return val 852 } 853 854 // ReadStringListToMap tries to parse the specified option(s) as a comma separated list. 855 // If there is an error in parsing, an empty list is returned. 856 func (c *Config) ReadStringListToMap(keys ...string) map[string]struct{} { 857 val := c.ReadAlternativeDefault(keys, "") 858 859 return splitValue(val) 860 } 861 862 // ReadStringListToMapDefault tries to parse the specified option as a comma 863 // separated list. If there is an error in parsing, an empty list is returned. 864 func (c *Config) ReadStringListToMapDefault(key, defaultValue string) map[string]struct{} { 865 return c.ReadStringListAlternativeToMapDefault([]string{key}, defaultValue) 866 } 867 868 // ReadStringListAlternativeToMapDefault tries to parse the specified options as a comma sparated list. 869 // If there is an error in parsing, an empty list is returned. 870 func (c *Config) ReadStringListAlternativeToMapDefault(keys []string, defaultValue string) map[string]struct{} { 871 val := c.ReadAlternativeDefault(keys, defaultValue) 872 873 return splitValue(val) 874 } 875 876 // splitValue parses the value as a comma separated list. 877 func splitValue(val string) map[string]struct{} { 878 list := make(map[string]struct{}) 879 if val != "" { 880 for _, e := range strings.Split(val, ",") { 881 trimmed := strings.TrimSpace(e) 882 list[trimmed] = struct{}{} 883 } 884 } 885 return list 886 } 887 888 // NomadPluginConfig produces the NomadConfig struct which is sent to Nomad plugins 889 func (c *Config) NomadPluginConfig() *base.AgentConfig { 890 return &base.AgentConfig{ 891 Driver: &base.ClientDriverConfig{ 892 ClientMinPort: c.ClientMinPort, 893 ClientMaxPort: c.ClientMaxPort, 894 }, 895 } 896 }