// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package config import ( "fmt" "math" "time" "github.com/dustin/go-humanize" "github.com/hashicorp/nomad/helper/pointer" ) // ArtifactConfig is the configuration specific to the Artifact block type ArtifactConfig struct { // HTTPReadTimeout is the duration in which a download must complete or // it will be canceled. Defaults to 30m. HTTPReadTimeout *string `hcl:"http_read_timeout"` // HTTPMaxSize is the maximum size of an artifact that will be downloaded. // Defaults to 100GB. HTTPMaxSize *string `hcl:"http_max_size"` // GCSTimeout is the duration in which a GCS operation must complete or // it will be canceled. Defaults to 30m. GCSTimeout *string `hcl:"gcs_timeout"` // GitTimeout is the duration in which a git operation must complete or // it will be canceled. Defaults to 30m. GitTimeout *string `hcl:"git_timeout"` // HgTimeout is the duration in which an hg operation must complete or // it will be canceled. Defaults to 30m. HgTimeout *string `hcl:"hg_timeout"` // S3Timeout is the duration in which an S3 operation must complete or // it will be canceled. Defaults to 30m. S3Timeout *string `hcl:"s3_timeout"` // DecompressionFileCountLimit is the maximum number of files that will // be decompressed before triggering an error and cancelling the operation. // // Default is 4006 files. DecompressionFileCountLimit *int `hcl:"decompression_file_count_limit"` // DecompressionSizeLimit is the maximum amount of data that will be // decompressed before triggering an error and cancelling the operation. // // Default is 100GB. DecompressionSizeLimit *string `hcl:"decompression_size_limit"` // DisableFilesystemIsolation will turn off the security feature where the // artifact downloader can write only to the task sandbox directory, and can // read only from specific locations on the host filesystem. DisableFilesystemIsolation *bool `hcl:"disable_filesystem_isolation"` // SetEnvironmentVariables is a comma-separated list of environment // variable names to inherit from the Nomad Client and set in the artifact // download sandbox process. SetEnvironmentVariables *string `hcl:"set_environment_variables"` } func (a *ArtifactConfig) Copy() *ArtifactConfig { if a == nil { return nil } return &ArtifactConfig{ HTTPReadTimeout: pointer.Copy(a.HTTPReadTimeout), HTTPMaxSize: pointer.Copy(a.HTTPMaxSize), GCSTimeout: pointer.Copy(a.GCSTimeout), GitTimeout: pointer.Copy(a.GitTimeout), HgTimeout: pointer.Copy(a.HgTimeout), S3Timeout: pointer.Copy(a.S3Timeout), DecompressionFileCountLimit: pointer.Copy(a.DecompressionFileCountLimit), DecompressionSizeLimit: pointer.Copy(a.DecompressionSizeLimit), DisableFilesystemIsolation: pointer.Copy(a.DisableFilesystemIsolation), SetEnvironmentVariables: pointer.Copy(a.SetEnvironmentVariables), } } func (a *ArtifactConfig) Merge(o *ArtifactConfig) *ArtifactConfig { switch { case a == nil: return o.Copy() case o == nil: return a.Copy() default: return &ArtifactConfig{ HTTPReadTimeout: pointer.Merge(a.HTTPReadTimeout, o.HTTPReadTimeout), HTTPMaxSize: pointer.Merge(a.HTTPMaxSize, o.HTTPMaxSize), GCSTimeout: pointer.Merge(a.GCSTimeout, o.GCSTimeout), GitTimeout: pointer.Merge(a.GitTimeout, o.GitTimeout), HgTimeout: pointer.Merge(a.HgTimeout, o.HgTimeout), S3Timeout: pointer.Merge(a.S3Timeout, o.S3Timeout), DecompressionFileCountLimit: pointer.Merge(a.DecompressionFileCountLimit, o.DecompressionFileCountLimit), DecompressionSizeLimit: pointer.Merge(a.DecompressionSizeLimit, o.DecompressionSizeLimit), DisableFilesystemIsolation: pointer.Merge(a.DisableFilesystemIsolation, o.DisableFilesystemIsolation), SetEnvironmentVariables: pointer.Merge(a.SetEnvironmentVariables, o.SetEnvironmentVariables), } } } func (a *ArtifactConfig) Equal(o *ArtifactConfig) bool { if a == nil || o == nil { return a == o } switch { case !pointer.Eq(a.HTTPReadTimeout, o.HTTPReadTimeout): return false case !pointer.Eq(a.HTTPMaxSize, o.HTTPMaxSize): return false case !pointer.Eq(a.GCSTimeout, o.GCSTimeout): return false case !pointer.Eq(a.GitTimeout, o.GitTimeout): return false case !pointer.Eq(a.HgTimeout, o.HgTimeout): return false case !pointer.Eq(a.S3Timeout, o.S3Timeout): return false case !pointer.Eq(a.DecompressionFileCountLimit, o.DecompressionFileCountLimit): return false case !pointer.Eq(a.DecompressionSizeLimit, o.DecompressionSizeLimit): return false case !pointer.Eq(a.DisableFilesystemIsolation, o.DisableFilesystemIsolation): return false case !pointer.Eq(a.SetEnvironmentVariables, o.SetEnvironmentVariables): return false } return true } func (a *ArtifactConfig) Validate() error { if a == nil { return fmt.Errorf("artifact must not be nil") } if a.HTTPReadTimeout == nil { return fmt.Errorf("http_read_timeout must be set") } if v, err := time.ParseDuration(*a.HTTPReadTimeout); err != nil { return fmt.Errorf("http_read_timeout not a valid duration: %w", err) } else if v < 0 { return fmt.Errorf("http_read_timeout must be > 0") } if a.HTTPMaxSize == nil { return fmt.Errorf("http_max_size must be set") } if v, err := humanize.ParseBytes(*a.HTTPMaxSize); err != nil { return fmt.Errorf("http_max_size not a valid size: %w", err) } else if v > math.MaxInt64 { return fmt.Errorf("http_max_size must be < %d but found %d", int64(math.MaxInt64), v) } if a.GCSTimeout == nil { return fmt.Errorf("gcs_timeout must be set") } if v, err := time.ParseDuration(*a.GCSTimeout); err != nil { return fmt.Errorf("gcs_timeout not a valid duration: %w", err) } else if v < 0 { return fmt.Errorf("gcs_timeout must be > 0") } if a.GitTimeout == nil { return fmt.Errorf("git_timeout must be set") } if v, err := time.ParseDuration(*a.GitTimeout); err != nil { return fmt.Errorf("git_timeout not a valid duration: %w", err) } else if v < 0 { return fmt.Errorf("git_timeout must be > 0") } if a.HgTimeout == nil { return fmt.Errorf("hg_timeout must be set") } if v, err := time.ParseDuration(*a.HgTimeout); err != nil { return fmt.Errorf("hg_timeout not a valid duration: %w", err) } else if v < 0 { return fmt.Errorf("hg_timeout must be > 0") } if a.S3Timeout == nil { return fmt.Errorf("s3_timeout must be set") } if v, err := time.ParseDuration(*a.S3Timeout); err != nil { return fmt.Errorf("s3_timeout not a valid duration: %w", err) } else if v < 0 { return fmt.Errorf("s3_timeout must be > 0") } if a.DecompressionFileCountLimit == nil { return fmt.Errorf("decompression_file_count_limit must not be nil") } if v := *a.DecompressionFileCountLimit; v < 0 { return fmt.Errorf("decompression_file_count_limit must be >= 0 but found %d", v) } if a.DecompressionSizeLimit == nil { return fmt.Errorf("decompression_size_limit must not be nil") } if v, err := humanize.ParseBytes(*a.DecompressionSizeLimit); err != nil { return fmt.Errorf("decompression_size_limit is not a valid size: %w", err) } else if v > math.MaxInt64 { return fmt.Errorf("decompression_size_limit must be < %d but found %d", int64(math.MaxInt64), v) } if a.DisableFilesystemIsolation == nil { return fmt.Errorf("disable_filesystem_isolation must be set") } if a.SetEnvironmentVariables == nil { return fmt.Errorf("set_environment_variables must be set") } return nil } func DefaultArtifactConfig() *ArtifactConfig { return &ArtifactConfig{ // Read timeout for HTTP operations. Must be long enough to // accommodate large/slow downloads. HTTPReadTimeout: pointer.Of("30m"), // Maximum download size. Must be large enough to accommodate // large downloads. HTTPMaxSize: pointer.Of("100GB"), // Timeout for GCS operations. Must be long enough to // accommodate large/slow downloads. GCSTimeout: pointer.Of("30m"), // Timeout for Git operations. Must be long enough to // accommodate large/slow clones. GitTimeout: pointer.Of("30m"), // Timeout for Hg operations. Must be long enough to // accommodate large/slow clones. HgTimeout: pointer.Of("30m"), // Timeout for S3 operations. Must be long enough to // accommodate large/slow downloads. S3Timeout: pointer.Of("30m"), // DecompressionFileCountLimit limits the number of files decompressed // for a single artifact. Must be large enough for payloads with lots // of files. DecompressionFileCountLimit: pointer.Of(4096), // DecompressionSizeLimit limits the amount of data decompressed for // a single artifact. Must be large enough to accommodate large payloads. DecompressionSizeLimit: pointer.Of("100GB"), // Toggle for disabling filesystem isolation, where available. DisableFilesystemIsolation: pointer.Of(false), // No environment variables are inherited from Client by default. SetEnvironmentVariables: pointer.Of(""), } }