Skip to content

Commit

Permalink
Merge pull request #87 from barney-s/adopt
Browse files Browse the repository at this point in the history
Add 'addOwnerRef' flag to the spec.
  • Loading branch information
k8s-ci-robot authored Feb 20, 2019
2 parents 39cace6 + d1629e8 commit 37ccdcb
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 15 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ aggregation and display of all the components in the Application.
used as the selector for an Application named "my-cool-app", and each component should contain a label that
matches.</td>
</tr>
<tr>
<td>spec.addOwnerRef</td>
<td>bool</td>
<td>Flag controlling if the matched resources need to be adopted by the Application object. When adopting, an <a href=https://kubernetes.io/docs/concepts/workloads/controllers/garbage-collection/#owners-and-dependents>OwnerRef</a> to the Application object is inserted into the matched objects <i>.metadata.[]OwnerRefs</i>.
The injected OwnerRef has <i>blockOwnerDeletion</i> set to True and <i>controller</i> set to False.
</td>
</tr>
<tr>
<td>spec.descriptor.version</td>
<td>string</a></td>
Expand Down
27 changes: 19 additions & 8 deletions pkg/apis/app/v1beta1/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@ import (

// Mutate - mutate expected
func (a *Application) Mutate(rsrc interface{}, labels map[string]string, status interface{}, expected, dependent, observed *resource.ObjectBag) (*resource.ObjectBag, error) {
return observed, nil
exp := resource.ObjectBag{}
for _, o := range observed.Items() {
o.Lifecycle = resource.LifecycleManaged
exp.Add(o)
}
return &exp, nil
}

// Finalize - execute finalizers
Expand Down Expand Up @@ -140,14 +145,20 @@ func (a *Application) Components() []component.Component {
}

// OwnerRef returns owner ref object with the component's resource as owner
func (a *Application) OwnerRef() []metav1.OwnerReference {
return []metav1.OwnerReference{
*metav1.NewControllerRef(a, schema.GroupVersionKind{
Group: SchemeGroupVersion.Group,
Version: SchemeGroupVersion.Version,
Kind: "Application",
}),
func (a *Application) OwnerRef() *metav1.OwnerReference {
if !a.Spec.AddOwnerRef {
return nil
}

isController := false
gvk := schema.GroupVersionKind{
Group: SchemeGroupVersion.Group,
Version: SchemeGroupVersion.Version,
Kind: "Application",
}
ref := metav1.NewControllerRef(a, gvk)
ref.Controller = &isController
return ref
}

// NewRsrc - return a new resource object
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/app/v1beta1/application_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ type ApplicationSpec struct {
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors
Selector *metav1.LabelSelector `json:"selector,omitempty"`

// AddOwnerRef objects - flag to indicate if we need to add OwnerRefs to matching objects
// Matching is done by using Selector to query all ComponentGroupKinds
AddOwnerRef bool `json:"addOwnerRef,omitempty"`

// Info contains human readable key,value pairs for the Application.
Info []InfoItem `json:"info,omitempty"`

Expand Down
7 changes: 3 additions & 4 deletions pkg/component/component_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,9 @@ var FilerObject = Filer{

var _ = Describe("Resource", func() {
var c = component.Component{
Handle: nil,
Name: "base",
CR: &FilerObject,
OwnerRef: nil,
Handle: nil,
Name: "base",
CR: &FilerObject,
}

BeforeEach(func() {
Expand Down
2 changes: 1 addition & 1 deletion pkg/component/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type Component struct {
Handle
Name string
CR metav1.Object
OwnerRef []metav1.OwnerReference
OwnerRef *metav1.OwnerReference
}

// KVMap is a map[string]string
Expand Down
37 changes: 35 additions & 2 deletions pkg/genericreconciler/genericreconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
urt "k8s.io/apimachinery/pkg/util/runtime"
Expand Down Expand Up @@ -96,6 +97,7 @@ func specDiffers(o1, o2 metav1.Object) bool {
e = reflect.Indirect(reflect.ValueOf(o1)).FieldByName("Data")
o = reflect.Indirect(reflect.ValueOf(o2)).FieldByName("Data")
}

if e.IsValid() && o.IsValid() {
if reflect.DeepEqual(e.Interface(), o.Interface()) {
return false
Expand All @@ -104,6 +106,36 @@ func specDiffers(o1, o2 metav1.Object) bool {
return true
}

// If both ownerRefs have the same group/kind/name but different uid, that means at least one of them doesn't exist anymore.
// If we compare `uid` in this function, we'd set both as owners which is not what we want
// Because in the case that the original owner is already gone, we want its dependent to be garbage collected with it.
func isReferringSameObject(a, b metav1.OwnerReference) bool {
aGV, err := schema.ParseGroupVersion(a.APIVersion)
if err != nil {
return false
}
bGV, err := schema.ParseGroupVersion(b.APIVersion)
if err != nil {
return false
}
return aGV == bGV && a.Kind == b.Kind && a.Name == b.Name
}

func injectOwnerRefs(o metav1.Object, ref *metav1.OwnerReference) bool {
if ref == nil {
return false
}
objRefs := o.GetOwnerReferences()
for _, r := range objRefs {
if isReferringSameObject(*ref, r) {
return false
}
}
objRefs = append(objRefs, *ref)
o.SetOwnerReferences(objRefs)
return true
}

// ReconcileCR is a generic function that reconciles expected and observed resources
func (gr *Reconciler) ReconcileCR(namespacedname types.NamespacedName, handle cr.Handle) error {
var status interface{}
Expand Down Expand Up @@ -239,7 +271,6 @@ func (gr *Reconciler) ReconcileComponent(crname string, c component.Component, s
aggregated.Add(expected.Items()...)
log.Printf("%s Expected Resources:\n", cname)
for _, e := range expected.Items() {
e.Obj.SetOwnerReferences(c.OwnerRef)
log.Printf("%s exp: %s/%s/%s\n", cname, e.Obj.GetNamespace(), reflect.TypeOf(e.Obj).String(), e.Obj.GetName())
}
log.Printf("%s Observed Resources:\n", cname)
Expand All @@ -262,7 +293,8 @@ func (gr *Reconciler) ReconcileComponent(crname string, c component.Component, s
}
// rsrc is seen in both expected and observed, update it if needed
e.Obj.SetResourceVersion(o.Obj.GetResourceVersion())
if e.Lifecycle == resource.LifecycleManaged && specDiffers(e.Obj, o.Obj) && c.Differs(e.Obj, o.Obj) {
e.Obj.SetOwnerReferences(o.Obj.GetOwnerReferences())
if e.Lifecycle == resource.LifecycleManaged && (specDiffers(e.Obj, o.Obj) && c.Differs(e.Obj, o.Obj) || injectOwnerRefs(e.Obj, c.OwnerRef)) {
if err := gr.Update(context.TODO(), e.Obj.(runtime.Object).DeepCopyObject()); err != nil {
errs = handleErrorArr("update", eRsrcInfo, err, errs)
} else {
Expand All @@ -278,6 +310,7 @@ func (gr *Reconciler) ReconcileComponent(crname string, c component.Component, s
// rsrc is in expected but not in observed - create
if !seen {
if e.Lifecycle == resource.LifecycleManaged {
injectOwnerRefs(e.Obj, c.OwnerRef)
if err := gr.Create(context.TODO(), e.Obj.(runtime.Object)); err != nil {
errs = handleErrorArr("Create", cname, err, errs)
} else {
Expand Down

0 comments on commit 37ccdcb

Please sign in to comment.