pkg/controller/shared_library/controller.go (184 lines of code) (raw):
package sharedLibrary
import (
"context"
"errors"
"fmt"
"path"
"reflect"
"time"
"github.com/go-logr/logr"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
jenkinsApi "github.com/epam/edp-jenkins-operator/v2/pkg/apis/v2/v1"
"github.com/epam/edp-jenkins-operator/v2/pkg/controller/helper"
"github.com/epam/edp-jenkins-operator/v2/pkg/controller/jenkins"
jenkinsService "github.com/epam/edp-jenkins-operator/v2/pkg/service/jenkins"
"github.com/epam/edp-jenkins-operator/v2/pkg/service/platform"
platformHelper "github.com/epam/edp-jenkins-operator/v2/pkg/service/platform/helper"
"github.com/epam/edp-jenkins-operator/v2/pkg/util/consts"
plutil "github.com/epam/edp-jenkins-operator/v2/pkg/util/platform"
)
const (
scriptConfigMapName = "jenkins-shared-libraries-controller"
finalizer = "shared_library.jenkins.finalizer.name"
)
type Reconcile struct {
client client.Client
log logr.Logger
platformService platform.PlatformService
templatesPath string
}
func NewReconcile(k8sCl client.Client, logf logr.Logger, ps platform.PlatformService, templatesPath string) *Reconcile {
return &Reconcile{
client: k8sCl,
log: logf.WithName("controller_shared_library"),
platformService: ps,
templatesPath: templatesPath,
}
}
func (r *Reconcile) SetupWithManager(mgr ctrl.Manager) error {
p := predicate.Funcs{
UpdateFunc: specUpdated,
}
err := ctrl.NewControllerManagedBy(mgr).
For(&jenkinsApi.JenkinsSharedLibrary{}, builder.WithPredicates(p)).
Complete(r)
if err != nil {
return fmt.Errorf("failed to create new managed controller: %w", err)
}
return nil
}
func specUpdated(e event.UpdateEvent) bool {
oldObject, ok := e.ObjectOld.(*jenkinsApi.JenkinsSharedLibrary)
if !ok {
return false
}
newObject, ok := e.ObjectNew.(*jenkinsApi.JenkinsSharedLibrary)
if !ok {
return false
}
return !reflect.DeepEqual(oldObject.Spec, newObject.Spec) ||
(oldObject.GetDeletionTimestamp().IsZero() && !newObject.GetDeletionTimestamp().IsZero())
}
func (r *Reconcile) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) {
reqLogger := r.log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
reqLogger.V(2).Info("Reconciling JenkinsSharedLibrary has been started")
var (
instance jenkinsApi.JenkinsSharedLibrary
result reconcile.Result
)
if err := r.client.Get(ctx, request.NamespacedName, &instance); err != nil {
if k8serrors.IsNotFound(err) {
reqLogger.Info("instance not found")
return result, nil
}
return result, fmt.Errorf("failed to get JenkinsSharedLibrary instance: %w", err)
}
if err := r.tryToReconcile(ctx, &instance); err != nil {
r.log.Error(err, "error during reconciliation", "instance", instance)
instance.Status.Value = err.Error()
result = reconcile.Result{RequeueAfter: helper.DefaultRequeueTime * time.Second}
} else {
instance.Status.Value = helper.StatusSuccess
}
if err := r.client.Status().Update(ctx, &instance); err != nil {
return result, fmt.Errorf("failed to update status: %w", err)
}
reqLogger.V(2).Info("Reconciling JenkinsSharedLibrary has been finished")
return result, nil
}
func (r *Reconcile) tryToReconcile(ctx context.Context, instance *jenkinsApi.JenkinsSharedLibrary) error {
if instance.Status.Value == helper.StatusSuccess && instance.ObjectMeta.DeletionTimestamp.IsZero() {
return nil
}
rootJenkins, err := plutil.GetJenkinsInstanceOwner(r.client, instance.Name, instance.Namespace,
instance.Spec.OwnerName, instance.GetOwnerReferences())
if err != nil {
return fmt.Errorf("failed to get owner for jenkins folder %s: %w", instance.Name, err)
}
if rootJenkins.Status.Status != jenkins.StatusReady {
return errors.New("root jenkins is not ready")
}
sharedLibraries, err := r.prepareSharedLibraries(ctx, instance, rootJenkins.Spec.SharedLibraries)
if err != nil {
return fmt.Errorf("failed to prepare shared libraries: %w", err)
}
if err = r.createLibrariesScript(rootJenkins, sharedLibraries); err != nil {
return fmt.Errorf("failed to create libraries script: %w", err)
}
needToUpdate, err := helper.TryToDelete(instance, finalizer, func() error {
return nil
})
if err != nil {
return fmt.Errorf("failed to delete resource: %w", err)
}
if !needToUpdate {
return nil
}
if err := r.client.Update(ctx, instance); err != nil {
return fmt.Errorf("failed to update instance: %w", err)
}
return nil
}
func (r *Reconcile) prepareSharedLibraries(ctx context.Context,
instance *jenkinsApi.JenkinsSharedLibrary,
rootLibraries []jenkinsApi.JenkinsSharedLibraries,
) ([]jenkinsApi.JenkinsSharedLibraries, error) {
var libList jenkinsApi.JenkinsSharedLibraryList
if err := r.client.List(ctx, &libList, &client.ListOptions{Namespace: instance.Namespace}); err != nil {
return nil, fmt.Errorf("failed to list jenkins shared libraries: %w", err)
}
for i := 0; i < len(libList.Items); i++ {
libOwnerName, instanceOwnerName := "", ""
if libList.Items[i].Spec.OwnerName != nil {
libOwnerName = *libList.Items[i].Spec.OwnerName
}
if instance.Spec.OwnerName != nil {
instanceOwnerName = *instance.Spec.OwnerName
}
if libOwnerName == instanceOwnerName && libList.Items[i].Name != instance.Name {
credentialID := libList.Items[i].Spec.CredentialID
rootLibraries = append(rootLibraries, jenkinsApi.JenkinsSharedLibraries{
Name: libList.Items[i].Spec.Name,
CredentialID: &credentialID,
Tag: libList.Items[i].Spec.Tag,
URL: libList.Items[i].Spec.URL,
})
}
}
if instance.ObjectMeta.DeletionTimestamp.IsZero() {
rootLibraries = append(rootLibraries, jenkinsApi.JenkinsSharedLibraries{
Name: instance.Spec.Name,
CredentialID: &instance.Spec.CredentialID,
Tag: instance.Spec.Tag,
URL: instance.Spec.URL,
})
}
return rootLibraries, nil
}
func (r *Reconcile) createLibrariesScript(
rootJenkins *jenkinsApi.Jenkins,
sharedLibraries []jenkinsApi.JenkinsSharedLibraries,
) error {
buffer, err := platformHelper.ParseTemplate(
&platformHelper.JenkinsScriptData{JenkinsSharedLibraries: sharedLibraries},
path.Join(r.templatesPath, jenkinsService.SharedLibrariesTemplateName),
jenkinsService.SharedLibrariesTemplateName)
if err != nil {
return fmt.Errorf("failed to parse template: %w", err)
}
isUpdated, err := r.platformService.CreateConfigMapWithUpdate(rootJenkins, scriptConfigMapName,
map[string]string{consts.JenkinsDefaultScriptConfigMapKey: buffer.String()})
if err != nil {
return fmt.Errorf("failed to create config map: %w", err)
}
if _, err = r.platformService.CreateJenkinsScript(rootJenkins.Namespace, scriptConfigMapName, isUpdated); err != nil {
return fmt.Errorf("failed to create jenkins script: %w", err)
}
return nil
}