From 72f92ae7ca4cabc1dc3069362a9b64ef46941432 Mon Sep 17 00:00:00 2001 From: Hans Hasselberg Date: Mon, 8 Jun 2020 10:08:12 +0200 Subject: [PATCH] agent: add option to disable agent cache for HTTP endpoints (#8023) This allows the operator to disable agent caching for the http endpoint. It is on by default for backwards compatibility and if disabled will ignore the url parameter `cached`. --- agent/catalog_endpoint.go | 6 ++-- agent/config/builder.go | 1 + agent/config/config.go | 1 + agent/config/runtime.go | 6 ++++ agent/config/runtime_test.go | 54 +++++++++++++++++++++++++++- agent/discovery_chain_endpoint.go | 2 +- agent/health_endpoint.go | 2 +- agent/prepared_query_endpoint.go | 2 +- website/pages/docs/agent/options.mdx | 2 ++ 9 files changed, 69 insertions(+), 7 deletions(-) diff --git a/agent/catalog_endpoint.go b/agent/catalog_endpoint.go index ff0cda3de7..0b90ee2fc8 100644 --- a/agent/catalog_endpoint.go +++ b/agent/catalog_endpoint.go @@ -84,7 +84,7 @@ func (s *HTTPServer) CatalogDatacenters(resp http.ResponseWriter, req *http.Requ parseCacheControl(resp, req, &args.QueryOptions) var out []string - if args.QueryOptions.UseCache { + if s.agent.config.HTTPUseCache && args.QueryOptions.UseCache { raw, m, err := s.agent.cache.Get(cachetype.CatalogDatacentersName, &args) if err != nil { metrics.IncrCounterWithLabels([]string{"client", "rpc", "error", "catalog_datacenters"}, 1, @@ -166,7 +166,7 @@ func (s *HTTPServer) CatalogServices(resp http.ResponseWriter, req *http.Request var out structs.IndexedServices defer setMeta(resp, &out.QueryMeta) - if args.QueryOptions.UseCache { + if s.agent.config.HTTPUseCache && args.QueryOptions.UseCache { raw, m, err := s.agent.cache.Get(cachetype.CatalogListServicesName, &args) if err != nil { metrics.IncrCounterWithLabels([]string{"client", "rpc", "error", "catalog_services"}, 1, @@ -255,7 +255,7 @@ func (s *HTTPServer) catalogServiceNodes(resp http.ResponseWriter, req *http.Req var out structs.IndexedServiceNodes defer setMeta(resp, &out.QueryMeta) - if args.QueryOptions.UseCache { + if s.agent.config.HTTPUseCache && args.QueryOptions.UseCache { raw, m, err := s.agent.cache.Get(cachetype.CatalogServicesName, &args) if err != nil { metrics.IncrCounterWithLabels([]string{"client", "rpc", "error", "catalog_service_nodes"}, 1, diff --git a/agent/config/builder.go b/agent/config/builder.go index 6b69737b63..026fa35673 100644 --- a/agent/config/builder.go +++ b/agent/config/builder.go @@ -858,6 +858,7 @@ func (b *Builder) Build() (rt RuntimeConfig, err error) { HTTPBlockEndpoints: c.HTTPConfig.BlockEndpoints, HTTPResponseHeaders: c.HTTPConfig.ResponseHeaders, AllowWriteHTTPFrom: b.cidrsVal("allow_write_http_from", c.HTTPConfig.AllowWriteHTTPFrom), + HTTPUseCache: b.boolValWithDefault(c.HTTPConfig.UseCache, true), // Telemetry Telemetry: lib.TelemetryConfig{ diff --git a/agent/config/config.go b/agent/config/config.go index 6d1da49f24..d7fc5ba675 100644 --- a/agent/config/config.go +++ b/agent/config/config.go @@ -611,6 +611,7 @@ type HTTPConfig struct { BlockEndpoints []string `json:"block_endpoints,omitempty" hcl:"block_endpoints" mapstructure:"block_endpoints"` AllowWriteHTTPFrom []string `json:"allow_write_http_from,omitempty" hcl:"allow_write_http_from" mapstructure:"allow_write_http_from"` ResponseHeaders map[string]string `json:"response_headers,omitempty" hcl:"response_headers" mapstructure:"response_headers"` + UseCache *bool `json:"use_cache,omitempty" hcl:"use_cache" mapstructure:"use_cache"` } type Performance struct { diff --git a/agent/config/runtime.go b/agent/config/runtime.go index b97ca8619c..2d45f57882 100644 --- a/agent/config/runtime.go +++ b/agent/config/runtime.go @@ -342,6 +342,12 @@ type RuntimeConfig struct { // hcl: dns_config { cache_max_age = "duration" } DNSCacheMaxAge time.Duration + // HTTPUseCache whether or not to use cache for http queries. Defaults + // to true. + // + // hcl: http_config { use_cache = (true|false) } + HTTPUseCache bool + // HTTPBlockEndpoints is a list of endpoint prefixes to block in the // HTTP API. Any requests to these will get a 403 response. // diff --git a/agent/config/runtime_test.go b/agent/config/runtime_test.go index 2b234a44c5..73bb16b161 100644 --- a/agent/config/runtime_test.go +++ b/agent/config/runtime_test.go @@ -2118,6 +2118,54 @@ func TestConfigFlagsAndEdgecases(t *testing.T) { `}, err: "Serf Advertise WAN address 10.0.0.1:1000 already configured for RPC Advertise", }, + { + desc: "http use_cache defaults to true", + args: []string{ + `-data-dir=` + dataDir, + }, + json: []string{`{ + "http_config": {} + }`}, + hcl: []string{` + http_config = {} + `}, + patch: func(rt *RuntimeConfig) { + rt.DataDir = dataDir + rt.HTTPUseCache = true + }, + }, + { + desc: "http use_cache is enabled when true", + args: []string{ + `-data-dir=` + dataDir, + }, + json: []string{`{ + "http_config": { "use_cache": true } + }`}, + hcl: []string{` + http_config = { use_cache = true } + `}, + patch: func(rt *RuntimeConfig) { + rt.DataDir = dataDir + rt.HTTPUseCache = true + }, + }, + { + desc: "http use_cache is disabled when false", + args: []string{ + `-data-dir=` + dataDir, + }, + json: []string{`{ + "http_config": { "use_cache": false } + }`}, + hcl: []string{` + http_config = { use_cache = false } + `}, + patch: func(rt *RuntimeConfig) { + rt.DataDir = dataDir + rt.HTTPUseCache = false + }, + }, { desc: "sidecar_service can't have ID", args: []string{ @@ -4167,7 +4215,8 @@ func TestFullConfig(t *testing.T) { "response_headers": { "M6TKa9NP": "xjuxjOzQ", "JRCrHZed": "rl0mTx81" - } + }, + "use_cache": false }, "key_file": "IEkkwgIA", "leave_on_terminate": true, @@ -4804,6 +4853,7 @@ func TestFullConfig(t *testing.T) { "M6TKa9NP" = "xjuxjOzQ" "JRCrHZed" = "rl0mTx81" } + use_cache = false } key_file = "IEkkwgIA" leave_on_terminate = true @@ -5517,6 +5567,7 @@ func TestFullConfig(t *testing.T) { HTTPMaxConnsPerClient: 100, HTTPSHandshakeTimeout: 2391 * time.Millisecond, HTTPSPort: 15127, + HTTPUseCache: false, KeyFile: "IEkkwgIA", KVMaxValueSize: 1234567800000000, LeaveDrainTime: 8265 * time.Second, @@ -6402,6 +6453,7 @@ func TestSanitize(t *testing.T) { "HTTPMaxConnsPerClient": 0, "HTTPPort": 0, "HTTPResponseHeaders": {}, + "HTTPUseCache": false, "HTTPSAddrs": [], "HTTPSHandshakeTimeout": "0s", "HTTPSPort": 0, diff --git a/agent/discovery_chain_endpoint.go b/agent/discovery_chain_endpoint.go index 01bdf81eb9..96385a7ca2 100644 --- a/agent/discovery_chain_endpoint.go +++ b/agent/discovery_chain_endpoint.go @@ -60,7 +60,7 @@ func (s *HTTPServer) DiscoveryChainRead(resp http.ResponseWriter, req *http.Requ var out structs.DiscoveryChainResponse defer setMeta(resp, &out.QueryMeta) - if args.QueryOptions.UseCache { + if s.agent.config.HTTPUseCache && args.QueryOptions.UseCache { raw, m, err := s.agent.cache.Get(cachetype.CompiledDiscoveryChainName, &args) if err != nil { return nil, err diff --git a/agent/health_endpoint.go b/agent/health_endpoint.go index 379919de00..49027ffffe 100644 --- a/agent/health_endpoint.go +++ b/agent/health_endpoint.go @@ -198,7 +198,7 @@ func (s *HTTPServer) healthServiceNodes(resp http.ResponseWriter, req *http.Requ var out structs.IndexedCheckServiceNodes defer setMeta(resp, &out.QueryMeta) - if args.QueryOptions.UseCache { + if s.agent.config.HTTPUseCache && args.QueryOptions.UseCache { raw, m, err := s.agent.cache.Get(cachetype.HealthServicesName, &args) if err != nil { return nil, err diff --git a/agent/prepared_query_endpoint.go b/agent/prepared_query_endpoint.go index 5e1afbd5b0..9c238ea44c 100644 --- a/agent/prepared_query_endpoint.go +++ b/agent/prepared_query_endpoint.go @@ -121,7 +121,7 @@ func (s *HTTPServer) preparedQueryExecute(id string, resp http.ResponseWriter, r var reply structs.PreparedQueryExecuteResponse defer setMeta(resp, &reply.QueryMeta) - if args.QueryOptions.UseCache { + if s.agent.config.HTTPUseCache && args.QueryOptions.UseCache { raw, m, err := s.agent.cache.Get(cachetype.PreparedQueryName, &args) if err != nil { // Don't return error if StaleIfError is set and we are within it and had diff --git a/website/pages/docs/agent/options.mdx b/website/pages/docs/agent/options.mdx index 0c37dad83a..53f79b73aa 100644 --- a/website/pages/docs/agent/options.mdx +++ b/website/pages/docs/agent/options.mdx @@ -1415,6 +1415,8 @@ Valid time units are 'ns', 'us' (or 'µs'), 'ms', 's', 'm', 'h'." - `allow_write_http_from` This object is a list of networks in CIDR notation (eg "127.0.0.0/8") that are allowed to call the agent write endpoints. It defaults to an empty list, which means all networks are allowed. This is used to make the agent read-only, except for select ip ranges. - To block write calls from anywhere, use `[ "255.255.255.255/32" ]`. - To only allow write calls from localhost, use `[ "127.0.0.0/8" ]` - To only allow specific IPs, use `[ "10.0.0.1/32", "10.0.0.2/32" ]` + - `use_cache` Defaults to true. If disabled, the agent won't be using [agent caching](/api/features/caching) to answer the request. Even when the url parameter is provided. + - `leave_on_terminate` If enabled, when the agent receives a TERM signal, it will send a `Leave` message to the rest of the cluster and gracefully leave. The default behavior for this feature varies based on whether or not the agent is running as a client or a server (prior to Consul 0.7 the default value was unconditionally set to `false`). On agents in client-mode, this defaults to `true` and for agents in server-mode, this defaults to `false`. - `limits` Available in Consul 0.9.3 and later, this is a nested