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).
This commit is contained in:
Antoine Pelisse 2017-06-09 10:30:47 -07:00
parent a1d0384e82
commit d7bba25d4a
8 changed files with 43 additions and 0 deletions

View File

@ -71,6 +71,10 @@ type Config struct {
// TODO: demonstrate an OAuth2 compatible client. // TODO: demonstrate an OAuth2 compatible client.
BearerToken string 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 is the configuration that RESTClient will use for impersonation.
Impersonate ImpersonationConfig Impersonate ImpersonationConfig

View File

@ -249,6 +249,7 @@ func TestAnonymousConfig(t *testing.T) {
expected.BearerToken = "" expected.BearerToken = ""
expected.Username = "" expected.Username = ""
expected.Password = "" expected.Password = ""
expected.CacheDir = ""
expected.AuthProvider = nil expected.AuthProvider = nil
expected.AuthConfigPersister = nil expected.AuthConfigPersister = nil
expected.TLSClientConfig.CertData = nil expected.TLSClientConfig.CertData = nil

View File

@ -89,6 +89,7 @@ func (c *Config) TransportConfig() (*transport.Config, error) {
}, },
Username: c.Username, Username: c.Username,
Password: c.Password, Password: c.Password,
CacheDir: c.CacheDir,
BearerToken: c.BearerToken, BearerToken: c.BearerToken,
Impersonate: transport.ImpersonationConfig{ Impersonate: transport.ImpersonationConfig{
UserName: c.Impersonate.UserName, UserName: c.Impersonate.UserName,

View File

@ -22,6 +22,7 @@ import (
"io/ioutil" "io/ioutil"
"net/url" "net/url"
"os" "os"
"path/filepath"
"strings" "strings"
"github.com/golang/glog" "github.com/golang/glog"
@ -31,16 +32,19 @@ import (
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
clientauth "k8s.io/client-go/tools/auth" clientauth "k8s.io/client-go/tools/auth"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api" clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/client-go/util/homedir"
) )
var ( var (
// ClusterDefaults has the same behavior as the old EnvVar and DefaultCluster fields // ClusterDefaults has the same behavior as the old EnvVar and DefaultCluster fields
// DEPRECATED will be replaced // DEPRECATED will be replaced
ClusterDefaults = clientcmdapi.Cluster{Server: getDefaultServer()} ClusterDefaults = clientcmdapi.Cluster{Server: getDefaultServer()}
cacheDirDefault = filepath.Join(homedir.HomeDir(), ".kube", "http-cache")
// DefaultClientConfig represents the legacy behavior of this package for defaulting // DefaultClientConfig represents the legacy behavior of this package for defaulting
// DEPRECATED will be replace // DEPRECATED will be replace
DefaultClientConfig = DirectClientConfig{*clientcmdapi.NewConfig(), "", &ConfigOverrides{ DefaultClientConfig = DirectClientConfig{*clientcmdapi.NewConfig(), "", &ConfigOverrides{
ClusterDefaults: ClusterDefaults, ClusterDefaults: ClusterDefaults,
CacheDir: cacheDirDefault,
}, nil, NewDefaultClientConfigLoadingRules(), promptedCredentials{}} }, nil, NewDefaultClientConfigLoadingRules(), promptedCredentials{}}
) )
@ -131,6 +135,7 @@ func (config *DirectClientConfig) ClientConfig() (*restclient.Config, error) {
clientConfig := &restclient.Config{} clientConfig := &restclient.Config{}
clientConfig.Host = configClusterInfo.Server clientConfig.Host = configClusterInfo.Server
clientConfig.CacheDir = config.overrides.CacheDir
if len(config.overrides.Timeout) > 0 { if len(config.overrides.Timeout) > 0 {
timeout, err := ParseTimeout(config.overrides.Timeout) timeout, err := ParseTimeout(config.overrides.Timeout)

View File

@ -17,11 +17,13 @@ limitations under the License.
package clientcmd package clientcmd
import ( import (
"path/filepath"
"strconv" "strconv"
"github.com/spf13/pflag" "github.com/spf13/pflag"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api" 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 // 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 Context clientcmdapi.Context
CurrentContext string CurrentContext string
Timeout string Timeout string
CacheDir string
} }
// ConfigOverrideFlags holds the flag names to be used for binding command line flags. Notice that this structure tightly // 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 ContextOverrideFlags ContextOverrideFlags
CurrentContext FlagInfo CurrentContext FlagInfo
Timeout FlagInfo Timeout FlagInfo
CacheDir FlagInfo
} }
// AuthOverrideFlags holds the flag names to be used for binding command line flags for AuthInfo objects // AuthOverrideFlags holds the flag names to be used for binding command line flags for AuthInfo objects
@ -146,10 +150,12 @@ const (
FlagUsername = "username" FlagUsername = "username"
FlagPassword = "password" FlagPassword = "password"
FlagTimeout = "request-timeout" FlagTimeout = "request-timeout"
FlagCacheDir = "cachedir"
) )
// RecommendedConfigOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing // RecommendedConfigOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing
func RecommendedConfigOverrideFlags(prefix string) ConfigOverrideFlags { func RecommendedConfigOverrideFlags(prefix string) ConfigOverrideFlags {
defaultCacheDir := filepath.Join(homedir.HomeDir(), ".kube", "http-cache")
return ConfigOverrideFlags{ return ConfigOverrideFlags{
AuthOverrideFlags: RecommendedAuthOverrideFlags(prefix), AuthOverrideFlags: RecommendedAuthOverrideFlags(prefix),
ClusterOverrideFlags: RecommendedClusterOverrideFlags(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"}, 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."}, 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) BindContextFlags(&overrides.Context, flags, flagNames.ContextOverrideFlags)
flagNames.CurrentContext.BindStringFlag(flags, &overrides.CurrentContext) flagNames.CurrentContext.BindStringFlag(flags, &overrides.CurrentContext)
flagNames.Timeout.BindStringFlag(flags, &overrides.Timeout) 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 // BindAuthInfoFlags is a convenience method to bind the specified flags to their associated variables

View File

@ -30,6 +30,9 @@ go_library(
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//vendor/github.com/golang/glog:go_default_library", "//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", "//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
], ],
) )

View File

@ -34,6 +34,10 @@ type Config struct {
// Bearer token for authentication // Bearer token for authentication
BearerToken string 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 is the config that this Config will impersonate using
Impersonate ImpersonationConfig Impersonate ImpersonationConfig

View File

@ -23,6 +23,9 @@ import (
"time" "time"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/gregjones/httpcache"
"github.com/gregjones/httpcache/diskcache"
"github.com/peterbourgon/diskv"
utilnet "k8s.io/apimachinery/pkg/util/net" 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 { len(config.Impersonate.Extra) > 0 {
rt = NewImpersonatingRoundTripper(config.Impersonate, rt) rt = NewImpersonatingRoundTripper(config.Impersonate, rt)
} }
if len(config.CacheDir) > 0 {
rt = NewCacheRoundTripper(config.CacheDir, rt)
}
return rt, nil return rt, nil
} }
@ -87,6 +93,17 @@ type authProxyRoundTripper struct {
rt http.RoundTripper 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 // NewAuthProxyRoundTripper provides a roundtripper which will add auth proxy fields to requests for
// authentication terminating proxy cases // authentication terminating proxy cases
// assuming you pull the user from the context: // assuming you pull the user from the context: