Skip to content

Commit

Permalink
Issue #696 - Support apps with static namespaces in resources (#842)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexmt authored Nov 29, 2018
1 parent 800c4b1 commit 6dede28
Show file tree
Hide file tree
Showing 14 changed files with 559 additions and 321 deletions.
38 changes: 38 additions & 0 deletions controller/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ import (

log "github.com/sirupsen/logrus"

"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/discovery"
"k8s.io/client-go/tools/cache"

appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
Expand All @@ -23,6 +26,7 @@ const (
)

type LiveStateCache interface {
IsNamespaced(server string, gvk schema.GroupVersionKind) (bool, error)
// Returns child nodes for a given k8s resource
GetChildren(server string, obj *unstructured.Unstructured) ([]appv1.ResourceNode, error)
// Returns state of live nodes which correspond for target nodes of specified application.
Expand All @@ -31,6 +35,14 @@ type LiveStateCache interface {
Run(ctx context.Context)
}

func GetTargetObjKey(a *appv1.Application, un *unstructured.Unstructured, isNamespaced bool) kube.ResourceKey {
key := kube.GetResourceKey(un)
if isNamespaced && key.Namespace == "" {
key.Namespace = a.Spec.Destination.Namespace
}
return key
}

func NewLiveStateCache(db db.ArgoDB, appInformer cache.SharedIndexInformer, kubectl kube.Kubectl, onAppUpdated func(appName string)) LiveStateCache {
return &liveStateCache{
appInformer: appInformer,
Expand Down Expand Up @@ -76,6 +88,7 @@ func (c *liveStateCache) getCluster(server string) (*clusterInfo, error) {
}

info = &clusterInfo{
apis: make(map[schema.GroupVersionKind]v1.APIResource),
lock: &sync.Mutex{},
nodes: make(map[kube.ResourceKey]*node),
onAppUpdated: c.onAppUpdated,
Expand All @@ -86,6 +99,23 @@ func (c *liveStateCache) getCluster(server string) (*clusterInfo, error) {
}

c.clusters[cluster.Server] = info
disco, err := discovery.NewDiscoveryClientForConfig(cluster.RESTConfig())
if err != nil {
return nil, err
}
resources, err := disco.ServerResources()
if err != nil {
return nil, err
}
for _, r := range resources {
gv, err := schema.ParseGroupVersion(r.GroupVersion)
if err != nil {
gv = schema.GroupVersion{}
}
for i := range r.APIResources {
info.apis[gv.WithKind(r.APIResources[i].Kind)] = r.APIResources[i]
}
}
}
c.lock.Unlock()
err := info.ensureSynced()
Expand All @@ -95,6 +125,14 @@ func (c *liveStateCache) getCluster(server string) (*clusterInfo, error) {
return info, nil
}

func (c *liveStateCache) IsNamespaced(server string, gvk schema.GroupVersionKind) (bool, error) {
clusterInfo, err := c.getCluster(server)
if err != nil {
return false, err
}
return clusterInfo.isNamespaced(gvk), nil
}

func (c *liveStateCache) GetChildren(server string, obj *unstructured.Unstructured) ([]appv1.ResourceNode, error) {
clusterInfo, err := c.getCluster(server)
if err != nil {
Expand Down
10 changes: 9 additions & 1 deletion controller/cache/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const (
)

type clusterInfo struct {
apis map[schema.GroupVersionKind]metav1.APIResource
nodes map[kube.ResourceKey]*node
lock *sync.Mutex
onAppUpdated func(appName string)
Expand Down Expand Up @@ -115,6 +116,13 @@ func (c *clusterInfo) getChildren(obj *unstructured.Unstructured) []appv1.Resour
return children
}

func (c *clusterInfo) isNamespaced(gvk schema.GroupVersionKind) bool {
if api, ok := c.apis[gvk]; ok && !api.Namespaced {
return false
}
return true
}

func (c *clusterInfo) getControlledLiveObjs(a *appv1.Application, targetObjs []*unstructured.Unstructured) (map[kube.ResourceKey]*unstructured.Unstructured, error) {
c.lock.Lock()
defer c.lock.Unlock()
Expand All @@ -128,7 +136,7 @@ func (c *clusterInfo) getControlledLiveObjs(a *appv1.Application, targetObjs []*
lock := &sync.Mutex{}
err := util.RunAllAsync(len(targetObjs), func(i int) error {
targetObj := targetObjs[i]
key := kube.GetResourceKeyNS(targetObj, util.FirstNonEmpty(targetObj.GetNamespace(), a.Spec.Destination.Namespace))
key := GetTargetObjKey(a, targetObj, c.isNamespaced(targetObj.GroupVersionKind()))
lock.Lock()
controlledObj := controlledObjs[key]
lock.Unlock()
Expand Down
51 changes: 36 additions & 15 deletions controller/cache/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,15 @@ import (
"sync"
"testing"

"github.com/argoproj/argo-cd/common"

corev1 "k8s.io/api/core/v1"

"github.com/argoproj/argo-cd/util/kube"

"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/watch"

"github.com/argoproj/argo-cd/common"
"github.com/argoproj/argo-cd/errors"
"github.com/argoproj/argo-cd/util/kube"
"github.com/ghodss/yaml"
"github.com/stretchr/testify/assert"

Expand Down Expand Up @@ -83,6 +81,7 @@ func newCluster(resources ...*unstructured.Unstructured) *clusterInfo {
cluster: &appv1.Cluster{},
syncTime: nil,
syncLock: &sync.Mutex{},
apis: make(map[schema.GroupVersionKind]v1.APIResource),
}
}

Expand All @@ -93,18 +92,26 @@ func TestGetChildren(t *testing.T) {

rsChildren := cluster.getChildren(testRS)
assert.Equal(t, []appv1.ResourceNode{{
Kind: "Pod", Namespace: "default", Name: "helm-guestbook-pod", Group: "", Version: "v1", Tags: []string{"0/0"}, Children: make([]appv1.ResourceNode, 0),
Kind: "Pod",
Namespace: "default",
Name: "helm-guestbook-pod",
Group: "",
Version: "v1",
Tags: []string{"0/0"},
Children: make([]appv1.ResourceNode, 0),
ResourceVersion: "123",
}}, rsChildren)
deployChildren := cluster.getChildren(testDeploy)

assert.Equal(t, []appv1.ResourceNode{{
Kind: "ReplicaSet",
Namespace: "default",
Name: "helm-guestbook-rs",
Group: "extensions",
Version: "v1beta1",
Children: rsChildren,
Tags: []string{},
Kind: "ReplicaSet",
Namespace: "default",
Name: "helm-guestbook-rs",
Group: "extensions",
Version: "v1beta1",
ResourceVersion: "123",
Children: rsChildren,
Tags: []string{},
}}, deployChildren)
}

Expand Down Expand Up @@ -169,9 +176,23 @@ func TestProcessNewChildEvent(t *testing.T) {

rsChildren := cluster.getChildren(testRS)
assert.Equal(t, []appv1.ResourceNode{{
Kind: "Pod", Namespace: "default", Name: "helm-guestbook-pod", Group: "", Version: "v1", Tags: []string{"0/0"}, Children: make([]appv1.ResourceNode, 0),
Kind: "Pod",
Namespace: "default",
Name: "helm-guestbook-pod",
Group: "",
Version: "v1",
Tags: []string{"0/0"},
Children: make([]appv1.ResourceNode, 0),
ResourceVersion: "123",
}, {
Kind: "Pod", Namespace: "default", Name: "helm-guestbook-pod2", Group: "", Version: "v1", Tags: []string{"0/0"}, Children: make([]appv1.ResourceNode, 0),
Kind: "Pod",
Namespace: "default",
Name: "helm-guestbook-pod2",
Group: "",
Version: "v1",
Tags: []string{"0/0"},
Children: make([]appv1.ResourceNode, 0),
ResourceVersion: "123",
}}, rsChildren)
}

Expand Down
8 changes: 7 additions & 1 deletion controller/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,14 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision st

controlledLiveObj := make([]*unstructured.Unstructured, len(targetObjs))
for i, obj := range targetObjs {

gvk := obj.GroupVersionKind()
key := kubeutil.NewResourceKey(gvk.Group, gvk.Kind, util.FirstNonEmpty(obj.GetNamespace(), app.Spec.Destination.Namespace), obj.GetName())

ns := util.FirstNonEmpty(obj.GetNamespace(), app.Spec.Destination.Namespace)
if namespaced, err := m.liveStateCache.IsNamespaced(app.Spec.Destination.Server, obj.GroupVersionKind()); err == nil && !namespaced {
ns = ""
}
key := kubeutil.NewResourceKey(gvk.Group, gvk.Kind, ns, obj.GetName())
if liveObj, ok := liveObjByKey[key]; ok {
controlledLiveObj[i] = liveObj
delete(liveObjByKey, key)
Expand Down
Loading

0 comments on commit 6dede28

Please sign in to comment.