Skip to content

Commit

Permalink
[WIP]feature/issue-495-Cache_DNS (TykTechnologies#2040)
Browse files Browse the repository at this point in the history
* Init implementation

* add cache hook for api's defaultTransport().
add unit test(unfinished)

* more tests move dns cache init to tyk/dns_cache.go

* issue/495: finish unit tests for storage;config,reverse_proxy tests&linting

* remove log

* add lint tests

* refactor mock dns server config push/pull in unit tests

* add ws/wss unit tests

* fixes

* issue/495: make config.dns optional; allow non-expired dns cache configurations(dns.check_interval == -1)

* fix failed tests

* review fixes

* review fixes

* test branch build

* fix build

* Update main.go

* REVERTME: check timeouts on CI

* fix
">go run -race " call

* fix ">go vet" & ">gofmt"

* fix ">goimports ..."

* Fixed race conditions for CI:
 - redis legacy driver(SingleRedisMode rw access);
 - dns server (DefaultResolver rw access)

* fix go vet errors

* fix mock dns race condition

* gofmt fix

* REVERTME: ci timeout

* REVERTME: ci timeout

* fix timeout

* Revert "REVERTME: ci timeout"

This reverts commit d1991eb

* REVERTME: ci timeout; revert 300ms timeout

* fix timeouts

* revert travis config

* goimports fix

* goimports revert fix; fix -race within host_checker.go

* fix gofmt; fix race

* fix race redisStorage; fix api_id rate limit collision in TestRateLimitForAPIAndRateLimitAndQuotaCheck

* revert ci-test.sh

* review fix https://github.com/TykTechnologies/tyk/pull/2040/files#r251038351

* review: rename dns_cache to dnscache

* gofmt fix

* review docs

* review docs

* review docs

* review: rename *.conf dns { enable_caching... } -> dns_cache { enabled... };
add defaults 1h/1m ( for ttl/check_interval ) to *example.conf.

* use seconds for config values in order to be unix-time compatible;
add unit tests for default config;

* review docs

* review fix

* fix for integration with updated TykTechnologies/redigocluster.
Requires this PR to be merged - TykTechnologies/redigocluster#4

* Update redis_cluster.go

* update redigocluster in ./vendor

* review: fix {interface{},*struct) covariance

* fix host & port parsing.
add DialContext error handling

* unit-tests fix

* go fmt fix

* goimports fix

* fix net.DefaultResolver r/w race

* fix gofmt/goimports

* goimports fix/gofmt fix

* fix log

* review fix

* format docs

* review fixes

* review fix: REMOVE dns_cache.check_interval from configs

* add dns_cache.multiple_ips_handle_strategy: config update+unit tests

* add init strategies implementation to dnsCachingManager. RandomStrategy should be implemented

* fix random caching

* remove item.IsEqualsTo->test.IsDnsRecordsAddrsEqualsTo

* fix manager/storage tests

* fmt/import fix

* review fix
  • Loading branch information
evityuk authored Feb 8, 2019
1 parent 65c0582 commit 3830ba8
Show file tree
Hide file tree
Showing 39 changed files with 1,455 additions and 221 deletions.
10 changes: 5 additions & 5 deletions api_loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type ChainObject struct {
Subrouter *mux.Router
}

func prepareStorage() (storage.RedisCluster, storage.RedisCluster, storage.RedisCluster, *RPCStorageHandler, *RPCStorageHandler) {
func prepareStorage() (storage.RedisCluster, storage.RedisCluster, storage.RedisCluster, RPCStorageHandler, RPCStorageHandler) {
redisStore := storage.RedisCluster{KeyPrefix: "apikey-", HashKeys: config.Global().HashKeys}
redisOrgStore := storage.RedisCluster{KeyPrefix: "orgkey."}
healthStore := storage.RedisCluster{KeyPrefix: "apihealth."}
Expand All @@ -44,7 +44,7 @@ func prepareStorage() (storage.RedisCluster, storage.RedisCluster, storage.Redis

FallbackKeySesionManager.Init(&redisStore)

return redisStore, redisOrgStore, healthStore, &rpcAuthStore, &rpcOrgStore
return redisStore, redisOrgStore, healthStore, rpcAuthStore, rpcOrgStore
}

func skipSpecBecauseInvalid(spec *APISpec, logger *logrus.Entry) bool {
Expand Down Expand Up @@ -298,7 +298,7 @@ func processSpec(spec *APISpec, apisByListen map[string]int,
mwAppendEnabled(&chainArray, &TransformMiddleware{baseMid})
mwAppendEnabled(&chainArray, &TransformJQMiddleware{baseMid})
mwAppendEnabled(&chainArray, &TransformHeaders{BaseMiddleware: baseMid})
mwAppendEnabled(&chainArray, &RedisCacheMiddleware{BaseMiddleware: baseMid, CacheStore: cacheStore})
mwAppendEnabled(&chainArray, &RedisCacheMiddleware{BaseMiddleware: baseMid, CacheStore: &cacheStore})
mwAppendEnabled(&chainArray, &VirtualEndpoint{BaseMiddleware: baseMid})
mwAppendEnabled(&chainArray, &URLRewriteMiddleware{BaseMiddleware: baseMid})
mwAppendEnabled(&chainArray, &TransformMethod{BaseMiddleware: baseMid})
Expand Down Expand Up @@ -406,7 +406,7 @@ func processSpec(spec *APISpec, apisByListen map[string]int,
mwAppendEnabled(&chainArray, &TransformJQMiddleware{baseMid})
mwAppendEnabled(&chainArray, &TransformHeaders{BaseMiddleware: baseMid})
mwAppendEnabled(&chainArray, &URLRewriteMiddleware{BaseMiddleware: baseMid})
mwAppendEnabled(&chainArray, &RedisCacheMiddleware{BaseMiddleware: baseMid, CacheStore: cacheStore})
mwAppendEnabled(&chainArray, &RedisCacheMiddleware{BaseMiddleware: baseMid, CacheStore: &cacheStore})
mwAppendEnabled(&chainArray, &TransformMethod{BaseMiddleware: baseMid})
mwAppendEnabled(&chainArray, &VirtualEndpoint{BaseMiddleware: baseMid})

Expand Down Expand Up @@ -591,7 +591,7 @@ func loadApps(specs []*APISpec, muxer *mux.Router) {
subrouter = muxer
}

chainObj := processSpec(spec, apisByListen, redisStore, redisOrgStore, healthStore, rpcAuthStore, rpcOrgStore, subrouter, logrus.NewEntry(log))
chainObj := processSpec(spec, apisByListen, &redisStore, &redisOrgStore, &healthStore, &rpcAuthStore, &rpcOrgStore, subrouter, logrus.NewEntry(log))
apisMu.Lock()
spec.middlewareChain = chainObj
apisMu.Unlock()
Expand Down
3 changes: 2 additions & 1 deletion apidef/importer/swagger.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import (
"io"
"strings"

"github.com/TykTechnologies/tyk/apidef"
uuid "github.com/satori/go.uuid"

"github.com/TykTechnologies/tyk/apidef"
)

const SwaggerSource APIImporterSource = "swagger"
Expand Down
3 changes: 2 additions & 1 deletion auth_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import (
"sync/atomic"
"time"

uuid "github.com/satori/go.uuid"

"github.com/TykTechnologies/tyk/config"
"github.com/TykTechnologies/tyk/storage"
"github.com/TykTechnologies/tyk/user"
uuid "github.com/satori/go.uuid"

"github.com/Sirupsen/logrus"
)
Expand Down
16 changes: 16 additions & 0 deletions cli/lint/lint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,22 @@ var tests = []struct {
"BadPolicySource", `{"policies": {"policy_source": "internet"}}`,
`policies.policy_source: policies.policy_source must be one of the following: "", "service", "rpc"`,
},
{
"MalformedDnsCacheEntry", `{"dns_cache": { "enabled": true, "tttl": 10} }`,
`tttl: Additional property tttl is not allowed`,
},
{
"BadDnsCacheTTL", `{"dns_cache": { "enabled": false, "ttl": -2 } }`,
`dns_cache.ttl: Must be greater than or equal to -1`,
},
{
"ExtraDnsCacheCheckInterval", `{"dns_cache": { "enabled": true, "ttl": -1, "check_interval": 2500 } }`,
`check_interval: Additional property check_interval is not allowed`,
},
{
"InvalidDnsCacheMultipleIPsHandleStrategy", `{"dns_cache": { "enabled": true, "ttl": 1, "multiple_ips_handle_strategy": "true" } }`,
`dns_cache.multiple_ips_handle_strategy: dns_cache.multiple_ips_handle_strategy must be one of the following: "pick_first", "random", "no_cache"`,
},
}

func allContains(got, want []string) bool {
Expand Down
17 changes: 17 additions & 0 deletions cli/lint/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,23 @@ const confSchema = `{
}
}
},
"dns_cache": {
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean"
},
"ttl": {
"type": "integer",
"minimum": -1
},
"multiple_ips_handle_strategy": {
"type": "string",
"enum": ["pick_first", "random", "no_cache"]
}
}
},
"hide_generator_header": {
"type": "boolean"
},
Expand Down
25 changes: 25 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@ import (
"github.com/TykTechnologies/tyk/regexp"
)

type IPsHandleStrategy string

const (
dnsCacheDefaultTtl = 3600
dnsCacheDefaultCheckInterval = 60

PickFirstStrategy IPsHandleStrategy = "pick_first"
RandomStrategy IPsHandleStrategy = "random"
NoCacheStrategy IPsHandleStrategy = "no_cache"
)

var log = logger.Get()

var global atomic.Value
Expand Down Expand Up @@ -81,6 +92,13 @@ type HealthCheckConfig struct {
HealthCheckValueTimeout int64 `json:"health_check_value_timeouts"`
}

type DnsCacheConfig struct {
Enabled bool `json:"enabled"`
TTL int64 `json:"ttl"`
CheckInterval int64 `json:"-" ignored:"true"` //controls cache cleanup interval. By convention shouldn't be exposed to config or env_variable_setup
MultipleIPsHandleStrategy IPsHandleStrategy `json:"multiple_ips_handle_strategy"`
}

type MonitorConfig struct {
EnableTriggerMonitors bool `json:"enable_trigger_monitors"`
Config WebHookHandlerConf `json:"configuration"`
Expand Down Expand Up @@ -211,6 +229,7 @@ type Config struct {
EnableAnalytics bool `json:"enable_analytics"`
AnalyticsConfig AnalyticsConfigConfig `json:"analytics_config"`
HealthCheck HealthCheckConfig `json:"health_check"`
DnsCache DnsCacheConfig `json:"dns_cache"`
UseAsyncSessionWrite bool `json:"optimisations_use_async_session_write"`
SessionUpdatePoolSize int `json:"session_update_pool_size"`
SessionUpdateBufferSize int `json:"session_update_buffer_size"`
Expand Down Expand Up @@ -334,6 +353,12 @@ var Default = Config{
AnalyticsConfig: AnalyticsConfigConfig{
IgnoredIPs: make([]string, 0),
},
DnsCache: DnsCacheConfig{
Enabled: false,
TTL: dnsCacheDefaultTtl,
CheckInterval: dnsCacheDefaultCheckInterval,
MultipleIPsHandleStrategy: NoCacheStrategy,
},
}

func init() {
Expand Down
75 changes: 59 additions & 16 deletions config/config_test.go
Original file line number Diff line number Diff line change
@@ -1,30 +1,73 @@
package config

import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
)

func TestWriteDefaultConf(t *testing.T) {
conf := &Config{}
os.Unsetenv("TYK_GW_LISTENPORT")
defer os.Unsetenv("TYK_GW_LISTENPORT")
if err := WriteDefault("", conf); err != nil {
t.Fatal(err)
}
if conf.ListenPort != 8080 {
t.Fatalf("Expected ListenPort to be set to its default")
}
*conf = Config{}
os.Setenv("TYK_GW_LISTENPORT", "9090")
if err := WriteDefault("", conf); err != nil {
t.Fatal(err)
func TestDefaultValueAndWriteDefaultConf(t *testing.T) {
cases := []struct {
FieldName string
EnvVarName string
FieldGetter func(*Config) interface{}

defaultValue interface{}
expectedValue interface{}
}{
{
"ListenPort", "TYK_GW_LISTENPORT",
func(c *Config) interface{} { return c.ListenPort },
8080, 9090,
},
{
"DnsCacheEnabled", "TYK_GW_DNSCACHE_ENABLED",
func(c *Config) interface{} { return c.DnsCache.Enabled },
false, true,
},
{
"DnsCacheTTL", "TYK_GW_DNSCACHE_TTL",
func(c *Config) interface{} { return c.DnsCache.TTL },
int64(3600), int64(300),
},
{
"CheckInterval", "TYK_GW_DNSCACHE_CHECKINTERVAL",
func(c *Config) interface{} { return c.DnsCache.CheckInterval },
int64(60),
int64(60), //CheckInterval shouldn't be configured from *.conf and env var
},
{
"CheckMultipleIPsHandleStrategy", "TYK_GW_DNSCACHE_MULTIPLEIPSHANDLESTRATEGY",
func(c *Config) interface{} { return c.DnsCache.MultipleIPsHandleStrategy },
NoCacheStrategy,
RandomStrategy,
},
}
if conf.ListenPort != 9090 {
t.Fatalf("Expected ListenPort to be set to 9090")

for _, tc := range cases {
t.Run(tc.FieldName, func(t *testing.T) {
conf := &Config{}
os.Unsetenv(tc.EnvVarName)
defer os.Unsetenv(tc.EnvVarName)
if err := WriteDefault("", conf); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(tc.FieldGetter(conf), tc.defaultValue) {
t.Fatalf("Expected %v to be set to its default %v, but got %v", tc.FieldName, tc.defaultValue, tc.FieldGetter(conf))
}
expectedValue := fmt.Sprint(tc.expectedValue)
os.Setenv(tc.EnvVarName, expectedValue)
if err := WriteDefault("", conf); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(tc.FieldGetter(conf), tc.expectedValue) {
t.Fatalf("Expected %s to be set to %v, but got %v", tc.FieldName, tc.expectedValue, tc.FieldGetter(conf))
}
})
}
}

Expand Down
6 changes: 3 additions & 3 deletions coprocess_id_extractor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ func createSpecTestFrom(t testing.TB, def *apidef.APIDefinition) *APISpec {
loader := APIDefinitionLoader{}
spec := loader.MakeSpec(def, nil)
tname := t.Name()
redisStore := storage.RedisCluster{KeyPrefix: tname + "-apikey."}
healthStore := storage.RedisCluster{KeyPrefix: tname + "-apihealth."}
orgStore := storage.RedisCluster{KeyPrefix: tname + "-orgKey."}
redisStore := &storage.RedisCluster{KeyPrefix: tname + "-apikey."}
healthStore := &storage.RedisCluster{KeyPrefix: tname + "-apihealth."}
orgStore := &storage.RedisCluster{KeyPrefix: tname + "-orgKey."}
spec.Init(redisStore, redisStore, healthStore, orgStore)
return spec
}
Expand Down
Loading

0 comments on commit 3830ba8

Please sign in to comment.