diff --git a/VERSION b/VERSION index 1c09c74e22..1d0ba9ea18 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.3.3 +0.4.0 diff --git a/cmd/argocd-application-controller/main.go b/cmd/argocd-application-controller/main.go index d32dde2e3a..0b7ceb5558 100644 --- a/cmd/argocd-application-controller/main.go +++ b/cmd/argocd-application-controller/main.go @@ -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 } diff --git a/util/kube/kube.go b/util/kube/kube.go index a757f8262b..8e87092a33 100644 --- a/util/kube/kube.go +++ b/util/kube/kube.go @@ -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 } diff --git a/util/kube/kube_test.go b/util/kube/kube_test.go index 09479b9868..6fb7b82de0 100644 --- a/util/kube/kube_test.go +++ b/util/kube/kube_test.go @@ -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) {