Cache kubernetes API resource discovery (resolves #170) (#176)

This commit is contained in:
Jesse Suen 2018-05-08 12:56:15 -07:00 committed by GitHub
parent b5c20e9b46
commit 5bbb4fe1a1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 66 additions and 17 deletions

View file

@ -1 +1 @@
0.3.3
0.4.0

View file

@ -2,8 +2,10 @@ package main
import (
"context"
"flag"
"fmt"
"os"
"strconv"
"time"
argocd "github.com/argoproj/argo-cd"
@ -39,6 +41,7 @@ func newCommand() *cobra.Command {
repoServerAddress string
workers int
logLevel string
glogLevel int
)
var command = cobra.Command{
Use: cliName,
@ -48,6 +51,11 @@ func newCommand() *cobra.Command {
errors.CheckError(err)
log.SetLevel(level)
// Set the glog level for the k8s go-client
flag.CommandLine.Parse([]string{})
flag.Lookup("logtostderr").Value.Set("true")
flag.Lookup("v").Value.Set(strconv.Itoa(glogLevel))
config, err := clientConfig.ClientConfig()
errors.CheckError(err)
@ -94,6 +102,7 @@ func newCommand() *cobra.Command {
command.Flags().StringVar(&repoServerAddress, "repo-server", "localhost:8081", "Repo server address.")
command.Flags().IntVar(&workers, "workers", 1, "Number of application workers")
command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
command.Flags().IntVar(&glogLevel, "gloglevel", 0, "Set the glog logging level")
return &command
}

View file

@ -10,7 +10,9 @@ import (
"os"
"os/exec"
"sync"
"time"
"github.com/argoproj/argo-cd/util/cache"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/api/equality"
@ -41,9 +43,15 @@ const (
DeploymentKind = "Deployment"
)
const (
apiResourceCacheDuration = 10 * time.Minute
)
var (
// location to use for generating temporary files, such as the ca.crt needed by kubectl
// location to use for generating temporary files, such as the kubeconfig needed by kubectl
kubectlTempDir string
// apiResourceCache is a in-memory cache of api resources supported by a k8s server
apiResourceCache = cache.NewInMemoryCache(apiResourceCacheDuration)
)
func init() {
@ -84,19 +92,33 @@ func MustToUnstructured(obj interface{}) *unstructured.Unstructured {
return uObj
}
// ListAPIResources discovers all API resources supported by the Kube API sererver
func ListAPIResources(disco discovery.DiscoveryInterface) ([]metav1.APIResource, error) {
apiResources := make([]metav1.APIResource, 0)
resList, err := disco.ServerResources()
// GetCachedServerResources discovers API resources supported by a Kube API server.
// Caches the results for apiResourceCacheDuration (per host)
func GetCachedServerResources(host string, disco discovery.DiscoveryInterface) ([]*metav1.APIResourceList, error) {
var resList []*metav1.APIResourceList
cacheKey := fmt.Sprintf("apires|%s", host)
err := apiResourceCache.Get(cacheKey, &resList)
if err == nil {
log.Debugf("cache hit: %s", cacheKey)
return resList, nil
}
if err == cache.ErrCacheMiss {
log.Infof("cache miss: %s", cacheKey)
} else {
log.Warnf("cache error %s: %v", cacheKey, err)
}
resList, err = disco.ServerResources()
if err != nil {
return nil, errors.WithStack(err)
}
for _, resGroup := range resList {
for _, apiRes := range resGroup.APIResources {
apiResources = append(apiResources, apiRes)
}
err = apiResourceCache.Set(&cache.Item{
Key: cacheKey,
Object: resList,
})
if err != nil {
log.Warnf("Failed to cache %s: %v", cacheKey, err)
}
return apiResources, nil
return resList, nil
}
// GetLiveResource returns the corresponding live resource from a unstructured object
@ -124,7 +146,7 @@ func WatchResourcesWithLabel(ctx context.Context, config *rest.Config, namespace
if err != nil {
return nil, err
}
serverResources, err := disco.ServerResources()
serverResources, err := GetCachedServerResources(config.Host, disco)
if err != nil {
return nil, err
}
@ -185,7 +207,7 @@ func GetResourcesWithLabel(config *rest.Config, namespace string, labelName stri
if err != nil {
return nil, err
}
resources, err := disco.ServerResources()
resources, err := GetCachedServerResources(config.Host, disco)
if err != nil {
return nil, err
}
@ -251,7 +273,7 @@ func DeleteResourceWithLabel(config *rest.Config, namespace string, labelName st
if err != nil {
return err
}
resources, err := disco.ServerResources()
resources, err := GetCachedServerResources(config.Host, disco)
if err != nil {
return err
}

View file

@ -62,14 +62,32 @@ func resourceList() []*metav1.APIResourceList {
}
}
func TestListAPIResources(t *testing.T) {
func TestGetCachedServerResources(t *testing.T) {
kubeclientset := fake.NewSimpleClientset(test.DemoService(), test.DemoDeployment())
fakeDiscovery, ok := kubeclientset.Discovery().(*fakediscovery.FakeDiscovery)
assert.True(t, ok)
fakeDiscovery.Fake.Resources = resourceList()
apiRes, err := ListAPIResources(fakeDiscovery)
resList, err := GetCachedServerResources("host", fakeDiscovery)
count := 0
for _, resGroup := range resList {
for _ = range resGroup.APIResources {
count++
}
}
assert.Nil(t, err)
assert.Equal(t, 11, len(apiRes))
assert.Equal(t, 11, count)
// set resources to empty list and make sure we get the cached result
fakeDiscovery.Fake.Resources = []*metav1.APIResourceList{}
resList, err = GetCachedServerResources("host", fakeDiscovery)
count = 0
for _, resGroup := range resList {
for _ = range resGroup.APIResources {
count++
}
}
assert.Nil(t, err)
assert.Equal(t, 11, count)
}
func TestGetLiveResource(t *testing.T) {