blob: 67b8e17cbf0c3022978fa13ac056e96c8a0033c1 [file] [log] [blame]
// Copyright 2023 Red Hat, Inc. and/or its affiliates
//
// 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 controllers
import (
"context"
"fmt"
"reflect"
"time"
"k8s.io/klog/v2"
buildv1 "github.com/openshift/api/build/v1"
imgv1 "github.com/openshift/api/image/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/kiegroup/kogito-serverless-operator/utils"
operatorapi "github.com/kiegroup/kogito-serverless-operator/api/v1alpha08"
"github.com/kiegroup/kogito-serverless-operator/controllers/builder"
"github.com/kiegroup/kogito-serverless-operator/log"
)
// SonataFlowBuildReconciler reconciles a SonataFlowBuild object
type SonataFlowBuildReconciler struct {
client.Client
Scheme *runtime.Scheme
Recorder record.EventRecorder
Config *rest.Config
}
const (
requeueAfterForNewBuild = 10 * time.Second
requeueAfterForBuildRunning = 30 * time.Second
)
// +kubebuilder:rbac:groups=sonataflow.org,resources=sonataflowbuilds,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=sonataflow.org,resources=sonataflowbuilds/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=sonataflow.org,resources=sonataflowbuilds/finalizers,verbs=update
// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// the SonataFlowBuild object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.12.1/pkg/reconcile
func (r *SonataFlowBuildReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
build := &operatorapi.SonataFlowBuild{}
err := r.Client.Get(ctx, req.NamespacedName, build)
if err != nil {
if errors.IsNotFound(err) {
return ctrl.Result{}, nil
}
klog.V(log.E).ErrorS(err, "Failed to get the SonataFlowBuild")
return ctrl.Result{}, err
}
phase := build.Status.BuildPhase
buildManager, err := builder.NewBuildManager(ctx, r.Client, r.Config, build.Name, build.Namespace)
if err != nil {
klog.V(log.E).ErrorS(err, "Failed to get create a build manager to handle the workflow build")
return ctrl.Result{}, err
}
if phase == operatorapi.BuildPhaseNone {
if err = buildManager.Schedule(build); err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{RequeueAfter: requeueAfterForNewBuild}, r.manageStatusUpdate(ctx, build)
// TODO: this smells, why not just else? review in the future: https://issues.redhat.com/browse/KOGITO-8785
} else if phase != operatorapi.BuildPhaseSucceeded && phase != operatorapi.BuildPhaseError && phase != operatorapi.BuildPhaseFailed {
beforeReconcileStatus := build.Status.DeepCopy()
if err = buildManager.Reconcile(build); err != nil {
return ctrl.Result{}, err
}
if !reflect.DeepEqual(build.Status, beforeReconcileStatus) {
if err = r.manageStatusUpdate(ctx, build); err != nil {
return ctrl.Result{}, err
}
}
return ctrl.Result{RequeueAfter: requeueAfterForBuildRunning}, nil
}
return ctrl.Result{}, nil
}
func (r *SonataFlowBuildReconciler) manageStatusUpdate(ctx context.Context, instance *operatorapi.SonataFlowBuild) error {
err := r.Status().Update(ctx, instance)
if err == nil {
r.Recorder.Event(instance, corev1.EventTypeNormal, "Updated", fmt.Sprintf("Updated buildphase to %s", instance.Status.BuildPhase))
}
return err
}
// SetupWithManager sets up the controller with the Manager.
func (r *SonataFlowBuildReconciler) SetupWithManager(mgr ctrl.Manager) error {
if utils.IsOpenShift() {
return ctrl.NewControllerManagedBy(mgr).
For(&operatorapi.SonataFlowBuild{}).
Owns(&buildv1.BuildConfig{}).
Owns(&imgv1.ImageStream{}).
Complete(r)
}
return ctrl.NewControllerManagedBy(mgr).
For(&operatorapi.SonataFlowBuild{}).
Complete(r)
}