Newer
Older
/*
Copyright 2018 Pressinfra SRL.
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 syncer
import (
"context"
"fmt"
"github.com/ankitrgadiya/operatorlib/pkg/interfaces"
"github.com/go-test/deep"
"github.com/iancoleman/strcase"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
logf "sigs.k8s.io/controller-runtime/pkg/log"
)
var log = logf.Log.WithName("syncer")
const (
eventNormal = "Normal"
eventWarning = "Warning"
)
func getKey(obj interfaces.Object) (types.NamespacedName, error) {
key := types.NamespacedName{}
objMeta, ok := obj.(metav1.Object)
if !ok {
return key, fmt.Errorf("%T is not a metav1.Object", obj)
}
key.Name = objMeta.GetName()
key.Namespace = objMeta.GetNamespace()
return key, nil
}
func basicEventReason(objKindName string, err error) string {
if err != nil {
return fmt.Sprintf("%sSyncFailed", strcase.ToCamel(objKindName))
}
return fmt.Sprintf("%sSyncSuccessfull", strcase.ToCamel(objKindName))
}
// Sync does the actual syncing and implements the syncer.Inteface Sync method
func Sync(ctx context.Context, s Interface) (SyncResult, error) {
result := SyncResult{}
recorder := s.GetRecorder()
key, err := getKey(s.Object())
if err != nil {
return result, err
}
result.Operation, err = controllerutil.CreateOrUpdate(ctx, s.GetClient(), s.Object(), Mutate(s))
previousObject := s.PreviousWithoutSecretData()
diff := deep.Equal(&previousObject, s.ObjectWithoutSecretData())
// don't pass to user error for owner deletion, just don't create the object
// nolint: gocritic
if err == errOwnerDeleted {
log.Info(string(result.Operation), "key", key, "kind", s.ObjectType(), "error", err)
err = nil
} else if err == ErrIgnore {
log.V(1).Info("syncer skipped", "key", key, "kind", s.ObjectType())
err = nil
} else if err != nil {
result.SetEventData(eventWarning, basicEventReason(s.ObjectName(), err),
fmt.Sprintf("%s %s failed syncing: %s", s.ObjectType(), key, err))
log.Error(err, string(result.Operation), "key", key, "kind", s.ObjectType(), "diff", diff)
} else {
result.SetEventData(eventNormal, basicEventReason(s.ObjectName(), err),
fmt.Sprintf("%s %s %s successfully", s.ObjectType(), key, result.Operation))
log.V(1).Info(string(result.Operation), "key", key, "kind", s.ObjectType(), "diff", diff)
}
if recorder != nil && s.ObjectOwner() != nil && result.EventType != "" && result.EventReason != "" && result.EventMessage != "" {
if err != nil || result.Operation != controllerutil.OperationResultNone {
recorder.Eventf(s.ObjectOwner(), result.EventType, result.EventReason, result.EventMessage)
}
}
return result, err
}
// WithoutOwner partially implements implements the syncer interface for the case the subject has no owner
type WithoutOwner struct{}
// GetOwner implementation of syncer interface for the case the subject has no owner
func (*WithoutOwner) GetOwner() interfaces.Object {
return nil
}
// Given an ObjectSyncer, returns a controllerutil.MutateFn which also sets the
// owner reference if the subject has one
func Mutate(s Interface) controllerutil.MutateFn {
return func() error {
obj := s.Object()
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
mutate := s.Mutate()
err := mutate()
if err != nil {
return err
}
if s.ObjectOwner() != nil {
existingMeta, ok := obj.(metav1.Object)
if !ok {
return fmt.Errorf("%s is not a metav1.Object", s.ObjectType())
}
ownerMeta, ok := s.ObjectOwner().(metav1.Object)
if !ok {
return fmt.Errorf("%s is not a metav1.Object", s.ObjectOwnerType())
}
// set owner reference only if owner resource is not being deleted, otherwise the owner
// reference will be reset in case of deleting with cascade=false.
if ownerMeta.GetDeletionTimestamp().IsZero() {
err := controllerutil.SetControllerReference(ownerMeta, existingMeta, s.GetScheme())
if err != nil {
return err
}
} else if ctime := existingMeta.GetCreationTimestamp(); ctime.IsZero() {
// the owner is deleted, don't recreate the resource if does not exist, because gc
// will not delete it again because has no owner reference set
return errOwnerDeleted
}
}
return nil
}
}