2017-12-05 16:21:12 +00:00
|
|
|
package lifecycle
|
|
|
|
|
|
|
|
import (
|
2018-01-12 10:13:16 +00:00
|
|
|
"reflect"
|
|
|
|
|
2017-12-05 16:21:12 +00:00
|
|
|
"github.com/rancher/norman/clientbase"
|
|
|
|
"github.com/rancher/norman/types/slice"
|
|
|
|
"k8s.io/apimachinery/pkg/api/meta"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2018-01-15 20:38:10 +00:00
|
|
|
created = "lifecycle.cattle.io/create"
|
|
|
|
finalizerKey = "controller.cattle.io/"
|
|
|
|
ScopedFinalizerKey = "clusterscoped.controller.cattle.io/"
|
2017-12-05 16:21:12 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type ObjectLifecycle interface {
|
2017-12-14 00:20:18 +00:00
|
|
|
Create(obj runtime.Object) (runtime.Object, error)
|
|
|
|
Finalize(obj runtime.Object) (runtime.Object, error)
|
|
|
|
Updated(obj runtime.Object) (runtime.Object, error)
|
2017-12-05 16:21:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type objectLifecycleAdapter struct {
|
2018-01-15 20:38:10 +00:00
|
|
|
name string
|
|
|
|
clusterScoped bool
|
|
|
|
lifecycle ObjectLifecycle
|
|
|
|
objectClient *clientbase.ObjectClient
|
2017-12-05 16:21:12 +00:00
|
|
|
}
|
|
|
|
|
2018-01-15 20:38:10 +00:00
|
|
|
func NewObjectLifecycleAdapter(name string, clusterScoped bool, lifecycle ObjectLifecycle, objectClient *clientbase.ObjectClient) func(key string, obj runtime.Object) error {
|
2017-12-05 16:21:12 +00:00
|
|
|
o := objectLifecycleAdapter{
|
2018-01-15 20:38:10 +00:00
|
|
|
name: name,
|
|
|
|
clusterScoped: clusterScoped,
|
|
|
|
lifecycle: lifecycle,
|
|
|
|
objectClient: objectClient,
|
2017-12-05 16:21:12 +00:00
|
|
|
}
|
|
|
|
return o.sync
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *objectLifecycleAdapter) sync(key string, obj runtime.Object) error {
|
|
|
|
if obj == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
metadata, err := meta.Accessor(obj)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if cont, err := o.finalize(metadata, obj); err != nil || !cont {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-12-05 21:15:52 +00:00
|
|
|
if cont, err := o.create(metadata, obj); err != nil || !cont {
|
2017-12-05 16:21:12 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-12-16 08:22:48 +00:00
|
|
|
obj = obj.DeepCopyObject()
|
2018-01-12 10:13:16 +00:00
|
|
|
newObj, err := o.lifecycle.Updated(obj)
|
|
|
|
o.update(metadata.GetName(), obj, newObj)
|
|
|
|
return err
|
|
|
|
}
|
2017-12-14 00:20:18 +00:00
|
|
|
|
2018-01-12 10:13:16 +00:00
|
|
|
func (o *objectLifecycleAdapter) update(name string, orig, obj runtime.Object) (runtime.Object, error) {
|
|
|
|
if obj != nil && !reflect.DeepEqual(orig, obj) {
|
|
|
|
return o.objectClient.Update(name, obj)
|
|
|
|
}
|
|
|
|
return obj, nil
|
2017-12-05 16:21:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (o *objectLifecycleAdapter) finalize(metadata metav1.Object, obj runtime.Object) (bool, error) {
|
|
|
|
// Check finalize
|
|
|
|
if metadata.GetDeletionTimestamp() == nil {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
2018-01-03 21:50:43 +00:00
|
|
|
if !slice.ContainsString(metadata.GetFinalizers(), o.constructFinalizerKey()) {
|
2017-12-05 16:21:12 +00:00
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
obj = obj.DeepCopyObject()
|
2017-12-16 08:22:48 +00:00
|
|
|
if newObj, err := o.lifecycle.Finalize(obj); err != nil {
|
2018-01-12 10:13:16 +00:00
|
|
|
o.update(metadata.GetName(), obj, newObj)
|
2017-12-16 08:22:48 +00:00
|
|
|
return false, err
|
|
|
|
} else if newObj != nil {
|
|
|
|
obj = newObj
|
|
|
|
}
|
|
|
|
|
2018-01-03 21:50:43 +00:00
|
|
|
if err := removeFinalizer(o.constructFinalizerKey(), obj); err != nil {
|
2017-12-16 08:22:48 +00:00
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := o.objectClient.Update(metadata.GetName(), obj)
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func removeFinalizer(name string, obj runtime.Object) error {
|
2017-12-05 16:21:12 +00:00
|
|
|
metadata, err := meta.Accessor(obj)
|
|
|
|
if err != nil {
|
2017-12-16 08:22:48 +00:00
|
|
|
return err
|
2017-12-05 16:21:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var finalizers []string
|
|
|
|
for _, finalizer := range metadata.GetFinalizers() {
|
2017-12-16 08:22:48 +00:00
|
|
|
if finalizer == name {
|
2017-12-05 16:21:12 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
finalizers = append(finalizers, finalizer)
|
|
|
|
}
|
|
|
|
metadata.SetFinalizers(finalizers)
|
|
|
|
|
2017-12-16 08:22:48 +00:00
|
|
|
return nil
|
2017-12-05 16:21:12 +00:00
|
|
|
}
|
|
|
|
|
2017-12-05 21:15:52 +00:00
|
|
|
func (o *objectLifecycleAdapter) createKey() string {
|
|
|
|
return created + "." + o.name
|
2017-12-05 16:21:12 +00:00
|
|
|
}
|
|
|
|
|
2018-01-03 21:50:43 +00:00
|
|
|
func (o *objectLifecycleAdapter) constructFinalizerKey() string {
|
2018-01-15 20:38:10 +00:00
|
|
|
if o.clusterScoped {
|
|
|
|
return ScopedFinalizerKey + o.name
|
|
|
|
}
|
2018-01-03 21:50:43 +00:00
|
|
|
return finalizerKey + o.name
|
|
|
|
}
|
|
|
|
|
2017-12-05 21:15:52 +00:00
|
|
|
func (o *objectLifecycleAdapter) create(metadata metav1.Object, obj runtime.Object) (bool, error) {
|
2018-01-12 10:13:16 +00:00
|
|
|
if o.isInitialized(metadata) {
|
2017-12-05 16:21:12 +00:00
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
2018-01-12 10:13:16 +00:00
|
|
|
// addFinalizer will always return a DeepCopy
|
|
|
|
obj, err := o.addFinalizer(obj)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
orig := obj.DeepCopyObject()
|
2017-12-16 08:22:48 +00:00
|
|
|
if newObj, err := o.lifecycle.Create(obj); err != nil {
|
2018-01-12 10:13:16 +00:00
|
|
|
o.update(metadata.GetName(), orig, newObj)
|
2017-12-16 08:22:48 +00:00
|
|
|
return false, err
|
|
|
|
} else if newObj != nil {
|
|
|
|
obj = newObj
|
|
|
|
}
|
|
|
|
|
2018-01-12 10:13:16 +00:00
|
|
|
return false, o.setInitialized(obj)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *objectLifecycleAdapter) isInitialized(metadata metav1.Object) bool {
|
|
|
|
initialized := o.createKey()
|
|
|
|
return metadata.GetAnnotations()[initialized] == "true"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *objectLifecycleAdapter) setInitialized(obj runtime.Object) error {
|
2017-12-05 16:21:12 +00:00
|
|
|
metadata, err := meta.Accessor(obj)
|
|
|
|
if err != nil {
|
2018-01-12 10:13:16 +00:00
|
|
|
return err
|
2017-12-05 16:21:12 +00:00
|
|
|
}
|
|
|
|
|
2018-01-12 10:13:16 +00:00
|
|
|
initialized := o.createKey()
|
|
|
|
|
2017-12-16 08:22:48 +00:00
|
|
|
if metadata.GetAnnotations() == nil {
|
|
|
|
metadata.SetAnnotations(map[string]string{})
|
2017-12-05 16:21:12 +00:00
|
|
|
}
|
2017-12-16 08:22:48 +00:00
|
|
|
metadata.GetAnnotations()[initialized] = "true"
|
2017-12-05 16:21:12 +00:00
|
|
|
|
2017-12-14 00:20:31 +00:00
|
|
|
_, err = o.objectClient.Update(metadata.GetName(), obj)
|
2018-01-12 10:13:16 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *objectLifecycleAdapter) addFinalizer(obj runtime.Object) (runtime.Object, error) {
|
|
|
|
obj = obj.DeepCopyObject()
|
|
|
|
|
|
|
|
metadata, err := meta.Accessor(obj)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if slice.ContainsString(metadata.GetFinalizers(), o.constructFinalizerKey()) {
|
|
|
|
return obj, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
metadata.SetFinalizers(append(metadata.GetFinalizers(), o.constructFinalizerKey()))
|
|
|
|
return o.objectClient.Update(metadata.GetName(), obj)
|
2017-12-05 16:21:12 +00:00
|
|
|
}
|