Newer
Older
/*
Licensed under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.gnu.org/licenses/agpl-3.0.html
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 reconciler
import (
"context"
"sort"
"github.com/hashicorp/go-version"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.libre.sh/controller-utils/application"
interfaces "k8s.libre.sh/controller-utils/interfaces"
"k8s.libre.sh/controller-utils/status"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
type InstanceReconciler struct {
*ReconcilerBase
Instance application.Instance
SyncOrder map[int]string
}
func NewInstanceReconciler(r *ReconcilerBase, i application.Instance, order map[int]string) *InstanceReconciler {
return &InstanceReconciler{
ReconcilerBase: r,
Instance: i,
SyncOrder: order,
}
}
func (r *InstanceReconciler) ManageError(owner status.ObjectWithStatus) (reconcile.Result, error) {
return reconcile.Result{}, nil
}
func RemoverOwnerRef(c client.Client, obj interfaces.Object, owner metav1.Object) error {
objectKey, err := client.ObjectKeyFromObject(obj)
if err != nil {
return err
}
if err := c.Get(context.Background(), objectKey, obj); err != nil {
return err
}
ownerRefs := []metav1.OwnerReference{}
for _, ref := range obj.GetOwnerReferences() {
if ref.UID != owner.GetUID() {
ownerRefs = append(ownerRefs, ref)
}
}
obj.SetOwnerReferences(ownerRefs)
if err := c.Update(context.Background(), obj); err != nil {
return err
}
return nil
}
func (r *InstanceReconciler) Reconcile(request reconcile.Request) (reconcile.Result, error) {
appStatus := r.Instance.GetApplicationStatus()
oldStatus := appStatus.DeepCopy()
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
///////////////////////////////////
/////////// Finalizers ////////////
//////////////////////////////////
// name of our custom finalizer
myFinalizerName := "apps.bound.finalizers.k8s.libre.sh"
// examine DeletionTimestamp to determine if object is under deletion
bar := r.Instance.GetOwner()
if bar.GetDeletionTimestamp().IsZero() {
// The object is not being deleted, so if it does not have our finalizer,
// then lets add the finalizer and update the object. This is equivalent
// registering our finalizer.
if !controllerutil.ContainsFinalizer(r.Instance.GetOwner(), myFinalizerName) {
controllerutil.AddFinalizer(r.Instance.GetOwner(), myFinalizerName)
}
if err := r.GetClient().Update(context.Background(), r.Instance.GetOwner()); err != nil {
return ctrl.Result{}, err
}
} else {
// The object is being deleted
if controllerutil.ContainsFinalizer(r.Instance.GetOwner(), myFinalizerName) {
foo := r.Instance.GetOwner().GetApplicationStatus()
if err := CleanUpOwnerRef(foo.Watched.Objects, r.Instance.GetOwner(), r.GetClient()); err != nil {
return ctrl.Result{}, err
}
}
// remove our finalizer from the list and update it.
controllerutil.RemoveFinalizer(r.Instance.GetOwner(), myFinalizerName)
if err := r.GetClient().Update(context.Background(), r.Instance.GetOwner()); err != nil {
return ctrl.Result{}, err
}
// Stop reconciliation as the item is being deleted
return ctrl.Result{}, nil
}
err := application.Init(r.Instance, r.ReconcilerBase)
if err != nil {
return reconcile.Result{}, err
}
/////////////////////////////////////
/////////////////////////////////////
for name, s := range r.Instance.GetSettings() {
res, err := SyncComponent(context.TODO(), r.ReconcilerBase, s, r.Instance.GetOwner())
cptStatus := status.ComponentStatus{
Resources: res,
}
if len(appStatus.Settings) == 0 {
appStatus.Settings = make(map[string]status.ComponentStatus)
}
appStatus.Settings[name] = cptStatus
appStatus.Settings[name] = cptStatus
cpts := make(map[string]application.ComponentMutate)
current, err := version.NewVersion(r.Instance.GetApplicationStatus().Version)
// get nextcloud desired version in spec
desired, err := version.NewVersion(r.Instance.GetVersion())
if jobs != nil && len(jobs) > 0 {
for name, j := range jobs {
cpts[name] = j
if len(r.Instance.GetApplicationStatus().Version) == 0 {
// Sync INSTALL
res, err := SyncComponent(context.TODO(), r.ReconcilerBase, jobs["install"], r.Instance.GetOwner())
cptStatus := status.ComponentStatus{
Resources: res,
}
if len(appStatus.Components) == 0 {
appStatus.Components = make(map[string]status.ComponentStatus)
}
if err != nil {
cptStatus.ComponentsReady = "NotReady"
appStatus.Components["jobs"] = cptStatus
return ctrl.Result{}, err
}
cptStatus.ComponentsReady = "Ready"
appStatus.Components["jobs"] = cptStatus
// ADD manage success
// return ctrl.Result{}, nil
} else {
switch current.Compare(desired) {
case 1:
// current version is greater than desired, no downgrade
// TODO Format error
return ctrl.Result{}, err
case -1:
// current version lower than desired, upgrade job
// SYNC UPGRADE
delete(jobs, "install")
case 0:
// SYNC CPTS
// current version and desired are the same, normal start
delete(jobs, "install")
delete(jobs, "upgrade")
}
for name, c := range r.Instance.GetComponents() {
cpts[name] = c
}
sts, err := SyncComponents(context.TODO(), r.ReconcilerBase, cpts, r.Instance.GetOwner(), r.SyncOrder)
appStatus.Components = sts.Components
appStatus.SetConditions(sts.Conditions...)
appStatus.Version = desired.String()
owner := r.Instance.GetOwner()
owner.SetApplicationStatus(*appStatus)
if err := r.ReconcilerBase.GetClient().Status().Update(context.TODO(), owner); err != nil {
return ctrl.Result{}, err
}
if err := r.ReconcilerBase.GetClient().Status().Update(context.TODO(), owner); err != nil {
// Clean up resources and update status
err = CleanUpResources(oldStatus.GetResources(), appStatus.GetResources(), r.ReconcilerBase.GetClient(), r.Instance.GetNamespace())
if err != nil {
diff := Difference(&oldStatus.Watched, &appStatus.Watched)
if len(diff.Objects) > 0 {
if err := CleanUpOwnerRef(diff.Objects, r.Instance.GetOwner(), r.GetClient()); err != nil {
return ctrl.Result{}, err
}
}
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
func CleanUpOwnerRef(osts []status.ObjectStatus, owner metav1.Object, c client.Client) error {
for _, ost := range osts {
ro := RuntimeObjFromObjStatus(ost)
ro.SetNamespace(owner.GetNamespace())
if err := RemoverOwnerRef(c, ro, owner); err != nil {
return err
}
}
return nil
}
func RuntimeObjFromObjStatus(ost status.ObjectStatus) interfaces.Object {
switch ost.Kind {
case "ConfigMap":
obj := &corev1.ConfigMap{}
obj.SetName(ost.Name)
return obj
case "Secret":
obj := &corev1.Secret{}
obj.SetName(ost.Name)
return obj
}
return nil
}
func Difference(original, desired *status.Resources) *status.Resources {
diff := &status.Resources{}
diffByKey := make(map[string]status.ObjectStatus, len(original.Objects))
desiredByKey := make(map[string]status.ObjectStatus, len(desired.Objects))
for _, obj := range original.Objects {
diffByKey[obj.Name] = obj
}
for _, obj := range desired.Objects {
desiredByKey[obj.Name] = obj
}
for item := range desiredByKey {
delete(diffByKey, item)
}
for _, obj := range diffByKey {
diff.Objects = append(diff.Objects, obj)
}
return diff
}
func SyncComponents(ctx context.Context, r interfaces.Reconcile, cpts map[string]application.ComponentMutate, owner interfaces.Object, order map[int]string) (appStatus status.ApplicationStatus, err error) {
keys := []int{}
appStatus.Components = make(map[string]status.ComponentStatus)
for k := range order {
keys = append(keys, k)
}
sort.Ints(keys)
for _, k := range keys {
if cpts[order[k]] != nil {
resources, err := SyncComponent(ctx, r, cpts[order[k]], owner)
cptStatus := status.ComponentStatus{
cptStatus.ComponentsReady = "NotReady"
appStatus.Components[order[k]] = cptStatus
appStatus.SetConditions(status.ReconcileError(err))
cptStatus.ComponentsReady = "Ready"
appStatus.Components[order[k]] = cptStatus
appStatus.SetConditions(status.ReconcileSuccess())