mirror of
https://github.com/kubevirt/containerized-data-importer.git
synced 2025-06-03 06:30:22 +00:00
179 lines
5.5 KiB
Go
179 lines
5.5 KiB
Go
/*
|
|
Copyright 2018 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package controllerutil
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"reflect"
|
|
|
|
"k8s.io/apimachinery/pkg/api/errors"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
|
)
|
|
|
|
// AlreadyOwnedError is an error returned if the object you are trying to assign
|
|
// a controller reference is already owned by another controller Object is the
|
|
// subject and Owner is the reference for the current owner
|
|
type AlreadyOwnedError struct {
|
|
Object v1.Object
|
|
Owner v1.OwnerReference
|
|
}
|
|
|
|
func (e *AlreadyOwnedError) Error() string {
|
|
return fmt.Sprintf("Object %s/%s is already owned by another %s controller %s", e.Object.GetNamespace(), e.Object.GetName(), e.Owner.Kind, e.Owner.Name)
|
|
}
|
|
|
|
func newAlreadyOwnedError(Object v1.Object, Owner v1.OwnerReference) *AlreadyOwnedError {
|
|
return &AlreadyOwnedError{
|
|
Object: Object,
|
|
Owner: Owner,
|
|
}
|
|
}
|
|
|
|
// SetControllerReference sets owner as a Controller OwnerReference on owned.
|
|
// This is used for garbage collection of the owned object and for
|
|
// reconciling the owner object on changes to owned (with a Watch + EnqueueRequestForOwner).
|
|
// Since only one OwnerReference can be a controller, it returns an error if
|
|
// there is another OwnerReference with Controller flag set.
|
|
func SetControllerReference(owner, object v1.Object, scheme *runtime.Scheme) error {
|
|
ro, ok := owner.(runtime.Object)
|
|
if !ok {
|
|
return fmt.Errorf("is not a %T a runtime.Object, cannot call SetControllerReference", owner)
|
|
}
|
|
|
|
gvk, err := apiutil.GVKForObject(ro, scheme)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create a new ref
|
|
ref := *v1.NewControllerRef(owner, schema.GroupVersionKind{Group: gvk.Group, Version: gvk.Version, Kind: gvk.Kind})
|
|
|
|
existingRefs := object.GetOwnerReferences()
|
|
fi := -1
|
|
for i, r := range existingRefs {
|
|
if referSameObject(ref, r) {
|
|
fi = i
|
|
} else if r.Controller != nil && *r.Controller {
|
|
return newAlreadyOwnedError(object, r)
|
|
}
|
|
}
|
|
if fi == -1 {
|
|
existingRefs = append(existingRefs, ref)
|
|
} else {
|
|
existingRefs[fi] = ref
|
|
}
|
|
|
|
// Update owner references
|
|
object.SetOwnerReferences(existingRefs)
|
|
return nil
|
|
}
|
|
|
|
// Returns true if a and b point to the same object
|
|
func referSameObject(a, b v1.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
|
|
}
|
|
|
|
// OperationResult is the action result of a CreateOrUpdate call
|
|
type OperationResult string
|
|
|
|
const ( // They should complete the sentence "Deployment default/foo has been ..."
|
|
// OperationResultNone means that the resource has not been changed
|
|
OperationResultNone OperationResult = "unchanged"
|
|
// OperationResultCreated means that a new resource is created
|
|
OperationResultCreated OperationResult = "created"
|
|
// OperationResultUpdated means that an existing resource is updated
|
|
OperationResultUpdated OperationResult = "updated"
|
|
)
|
|
|
|
// CreateOrUpdate creates or updates the given object obj in the Kubernetes
|
|
// cluster. The object's desired state should be reconciled with the existing
|
|
// state using the passed in ReconcileFn. obj must be a struct pointer so that
|
|
// obj can be updated with the content returned by the Server.
|
|
//
|
|
// It returns the executed operation and an error.
|
|
func CreateOrUpdate(ctx context.Context, c client.Client, obj runtime.Object, f MutateFn) (OperationResult, error) {
|
|
// op is the operation we are going to attempt
|
|
op := OperationResultNone
|
|
|
|
// get the existing object meta
|
|
metaObj, ok := obj.(v1.Object)
|
|
if !ok {
|
|
return OperationResultNone, fmt.Errorf("%T does not implement metav1.Object interface", obj)
|
|
}
|
|
|
|
// retrieve the existing object
|
|
key := client.ObjectKey{
|
|
Name: metaObj.GetName(),
|
|
Namespace: metaObj.GetNamespace(),
|
|
}
|
|
err := c.Get(ctx, key, obj)
|
|
|
|
// reconcile the existing object
|
|
existing := obj.DeepCopyObject()
|
|
existingObjMeta := existing.(v1.Object)
|
|
existingObjMeta.SetName(metaObj.GetName())
|
|
existingObjMeta.SetNamespace(metaObj.GetNamespace())
|
|
|
|
if e := f(obj); e != nil {
|
|
return OperationResultNone, e
|
|
}
|
|
|
|
if metaObj.GetName() != existingObjMeta.GetName() {
|
|
return OperationResultNone, fmt.Errorf("ReconcileFn cannot mutate objects name")
|
|
}
|
|
|
|
if metaObj.GetNamespace() != existingObjMeta.GetNamespace() {
|
|
return OperationResultNone, fmt.Errorf("ReconcileFn cannot mutate objects namespace")
|
|
}
|
|
|
|
if errors.IsNotFound(err) {
|
|
err = c.Create(ctx, obj)
|
|
op = OperationResultCreated
|
|
} else if err == nil {
|
|
if reflect.DeepEqual(existing, obj) {
|
|
return OperationResultNone, nil
|
|
}
|
|
err = c.Update(ctx, obj)
|
|
op = OperationResultUpdated
|
|
} else {
|
|
return OperationResultNone, err
|
|
}
|
|
|
|
if err != nil {
|
|
op = OperationResultNone
|
|
}
|
|
return op, err
|
|
}
|
|
|
|
// MutateFn is a function which mutates the existing object into it's desired state.
|
|
type MutateFn func(existing runtime.Object) error
|