From d7bba25d4a42f346f1963c86fc0dab43aa4f242e Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Fri, 9 Jun 2017 10:30:47 -0700 Subject: [PATCH] c-go: Use http Etag cache Add a new command-line cachedir flag to specify where to store the http cache responses. This cache will only be used for OpenAPI Swagger spec for now (as this is the only end-point that returns an ETag). --- staging/src/k8s.io/client-go/rest/config.go | 4 ++++ .../src/k8s.io/client-go/rest/config_test.go | 1 + staging/src/k8s.io/client-go/rest/transport.go | 1 + .../client-go/tools/clientcmd/client_config.go | 5 +++++ .../client-go/tools/clientcmd/overrides.go | 8 ++++++++ staging/src/k8s.io/client-go/transport/BUILD | 3 +++ .../src/k8s.io/client-go/transport/config.go | 4 ++++ .../client-go/transport/round_trippers.go | 17 +++++++++++++++++ 8 files changed, 43 insertions(+) diff --git a/staging/src/k8s.io/client-go/rest/config.go b/staging/src/k8s.io/client-go/rest/config.go index dca7333dd00..627a9cc9672 100644 --- a/staging/src/k8s.io/client-go/rest/config.go +++ b/staging/src/k8s.io/client-go/rest/config.go @@ -71,6 +71,10 @@ type Config struct { // TODO: demonstrate an OAuth2 compatible client. BearerToken string + // CacheDir is the directory where we'll store HTTP cached responses. + // If set to empty string, no caching mechanism will be used. + CacheDir string + // Impersonate is the configuration that RESTClient will use for impersonation. Impersonate ImpersonationConfig diff --git a/staging/src/k8s.io/client-go/rest/config_test.go b/staging/src/k8s.io/client-go/rest/config_test.go index f04135a4288..f20ed722dd6 100644 --- a/staging/src/k8s.io/client-go/rest/config_test.go +++ b/staging/src/k8s.io/client-go/rest/config_test.go @@ -249,6 +249,7 @@ func TestAnonymousConfig(t *testing.T) { expected.BearerToken = "" expected.Username = "" expected.Password = "" + expected.CacheDir = "" expected.AuthProvider = nil expected.AuthConfigPersister = nil expected.TLSClientConfig.CertData = nil diff --git a/staging/src/k8s.io/client-go/rest/transport.go b/staging/src/k8s.io/client-go/rest/transport.go index ba43752bc93..4c5b1648e96 100644 --- a/staging/src/k8s.io/client-go/rest/transport.go +++ b/staging/src/k8s.io/client-go/rest/transport.go @@ -89,6 +89,7 @@ func (c *Config) TransportConfig() (*transport.Config, error) { }, Username: c.Username, Password: c.Password, + CacheDir: c.CacheDir, BearerToken: c.BearerToken, Impersonate: transport.ImpersonationConfig{ UserName: c.Impersonate.UserName, diff --git a/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go b/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go index a8698af2432..9646c6b7c24 100644 --- a/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go +++ b/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go @@ -22,6 +22,7 @@ import ( "io/ioutil" "net/url" "os" + "path/filepath" "strings" "github.com/golang/glog" @@ -31,16 +32,19 @@ import ( restclient "k8s.io/client-go/rest" clientauth "k8s.io/client-go/tools/auth" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + "k8s.io/client-go/util/homedir" ) var ( // ClusterDefaults has the same behavior as the old EnvVar and DefaultCluster fields // DEPRECATED will be replaced ClusterDefaults = clientcmdapi.Cluster{Server: getDefaultServer()} + cacheDirDefault = filepath.Join(homedir.HomeDir(), ".kube", "http-cache") // DefaultClientConfig represents the legacy behavior of this package for defaulting // DEPRECATED will be replace DefaultClientConfig = DirectClientConfig{*clientcmdapi.NewConfig(), "", &ConfigOverrides{ ClusterDefaults: ClusterDefaults, + CacheDir: cacheDirDefault, }, nil, NewDefaultClientConfigLoadingRules(), promptedCredentials{}} ) @@ -131,6 +135,7 @@ func (config *DirectClientConfig) ClientConfig() (*restclient.Config, error) { clientConfig := &restclient.Config{} clientConfig.Host = configClusterInfo.Server + clientConfig.CacheDir = config.overrides.CacheDir if len(config.overrides.Timeout) > 0 { timeout, err := ParseTimeout(config.overrides.Timeout) diff --git a/staging/src/k8s.io/client-go/tools/clientcmd/overrides.go b/staging/src/k8s.io/client-go/tools/clientcmd/overrides.go index 963ac8fae9b..25ab1ea1aa6 100644 --- a/staging/src/k8s.io/client-go/tools/clientcmd/overrides.go +++ b/staging/src/k8s.io/client-go/tools/clientcmd/overrides.go @@ -17,11 +17,13 @@ limitations under the License. package clientcmd import ( + "path/filepath" "strconv" "github.com/spf13/pflag" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + "k8s.io/client-go/util/homedir" ) // ConfigOverrides holds values that should override whatever information is pulled from the actual Config object. You can't @@ -34,6 +36,7 @@ type ConfigOverrides struct { Context clientcmdapi.Context CurrentContext string Timeout string + CacheDir string } // ConfigOverrideFlags holds the flag names to be used for binding command line flags. Notice that this structure tightly @@ -44,6 +47,7 @@ type ConfigOverrideFlags struct { ContextOverrideFlags ContextOverrideFlags CurrentContext FlagInfo Timeout FlagInfo + CacheDir FlagInfo } // AuthOverrideFlags holds the flag names to be used for binding command line flags for AuthInfo objects @@ -146,10 +150,12 @@ const ( FlagUsername = "username" FlagPassword = "password" FlagTimeout = "request-timeout" + FlagCacheDir = "cachedir" ) // RecommendedConfigOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing func RecommendedConfigOverrideFlags(prefix string) ConfigOverrideFlags { + defaultCacheDir := filepath.Join(homedir.HomeDir(), ".kube", "http-cache") return ConfigOverrideFlags{ AuthOverrideFlags: RecommendedAuthOverrideFlags(prefix), ClusterOverrideFlags: RecommendedClusterOverrideFlags(prefix), @@ -157,6 +163,7 @@ func RecommendedConfigOverrideFlags(prefix string) ConfigOverrideFlags { CurrentContext: FlagInfo{prefix + FlagContext, "", "", "The name of the kubeconfig context to use"}, Timeout: FlagInfo{prefix + FlagTimeout, "", "0", "The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests."}, + CacheDir: FlagInfo{prefix + FlagCacheDir, "", defaultCacheDir, "Path to http-cache directory"}, } } @@ -198,6 +205,7 @@ func BindOverrideFlags(overrides *ConfigOverrides, flags *pflag.FlagSet, flagNam BindContextFlags(&overrides.Context, flags, flagNames.ContextOverrideFlags) flagNames.CurrentContext.BindStringFlag(flags, &overrides.CurrentContext) flagNames.Timeout.BindStringFlag(flags, &overrides.Timeout) + flagNames.CacheDir.BindStringFlag(flags, &overrides.CacheDir) } // BindAuthInfoFlags is a convenience method to bind the specified flags to their associated variables diff --git a/staging/src/k8s.io/client-go/transport/BUILD b/staging/src/k8s.io/client-go/transport/BUILD index 1a54f4835e9..c17299d5355 100644 --- a/staging/src/k8s.io/client-go/transport/BUILD +++ b/staging/src/k8s.io/client-go/transport/BUILD @@ -30,6 +30,9 @@ go_library( tags = ["automanaged"], deps = [ "//vendor/github.com/golang/glog:go_default_library", + "//vendor/github.com/gregjones/httpcache:go_default_library", + "//vendor/github.com/gregjones/httpcache/diskcache:go_default_library", + "//vendor/github.com/peterbourgon/diskv:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library", ], ) diff --git a/staging/src/k8s.io/client-go/transport/config.go b/staging/src/k8s.io/client-go/transport/config.go index 820594ba354..e34d6e8c774 100644 --- a/staging/src/k8s.io/client-go/transport/config.go +++ b/staging/src/k8s.io/client-go/transport/config.go @@ -34,6 +34,10 @@ type Config struct { // Bearer token for authentication BearerToken string + // CacheDir is the directory where we'll store HTTP cached responses. + // If set to empty string, no caching mechanism will be used. + CacheDir string + // Impersonate is the config that this Config will impersonate using Impersonate ImpersonationConfig diff --git a/staging/src/k8s.io/client-go/transport/round_trippers.go b/staging/src/k8s.io/client-go/transport/round_trippers.go index c728b18775f..2394c42c9b0 100644 --- a/staging/src/k8s.io/client-go/transport/round_trippers.go +++ b/staging/src/k8s.io/client-go/transport/round_trippers.go @@ -23,6 +23,9 @@ import ( "time" "github.com/golang/glog" + "github.com/gregjones/httpcache" + "github.com/gregjones/httpcache/diskcache" + "github.com/peterbourgon/diskv" utilnet "k8s.io/apimachinery/pkg/util/net" ) @@ -56,6 +59,9 @@ func HTTPWrappersForConfig(config *Config, rt http.RoundTripper) (http.RoundTrip len(config.Impersonate.Extra) > 0 { rt = NewImpersonatingRoundTripper(config.Impersonate, rt) } + if len(config.CacheDir) > 0 { + rt = NewCacheRoundTripper(config.CacheDir, rt) + } return rt, nil } @@ -87,6 +93,17 @@ type authProxyRoundTripper struct { rt http.RoundTripper } +// NewCacheRoundTripper creates a roundtripper that reads the ETag on +// response headers and send the If-None-Match header on subsequent +// corresponding requests. +func NewCacheRoundTripper(cacheDir string, rt http.RoundTripper) http.RoundTripper { + d := diskv.New(diskv.Options{BasePath: cacheDir}) + t := httpcache.NewTransport(diskcache.NewWithDiskv(d)) + t.Transport = rt + + return t +} + // NewAuthProxyRoundTripper provides a roundtripper which will add auth proxy fields to requests for // authentication terminating proxy cases // assuming you pull the user from the context: