Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #696 - Support apps with static namespaces in resources #842

Merged
merged 1 commit into from
Nov 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any problem here that if we don't see the GVK in the cluster, that we unconditionally consider it as a namespaced? e.g. will deploying an app for the first time that contains a cluster-scoped CRD + cluster-scoped CRD object work okay?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point. working improving it in next PR

}

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