controllers/gerritproject/controller.go (148 lines of code) (raw):
package gerritproject
import (
"context"
"fmt"
"os"
"reflect"
"time"
"github.com/go-logr/logr"
"github.com/pkg/errors"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
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"
gerritApi "github.com/epam/edp-gerrit-operator/v2/api/v1"
"github.com/epam/edp-gerrit-operator/v2/controllers/helper"
gerritClient "github.com/epam/edp-gerrit-operator/v2/pkg/client/gerrit"
"github.com/epam/edp-gerrit-operator/v2/pkg/service/gerrit"
"github.com/epam/edp-gerrit-operator/v2/pkg/service/platform"
)
const (
finalizerName = "gerritproject.gerrit.finalizer.name"
syncIntervalEnv = "GERRIT_PROJECT_SYNC_INTERVAL"
defaultSyncInterval = 300 * time.Second // 5 minutes
requeueTime = 10 * time.Second
)
type Reconcile struct {
client client.Client
service gerrit.Interface
log logr.Logger
}
func NewReconcile(k8sClient client.Client, scheme *runtime.Scheme, log logr.Logger) (*Reconcile, error) {
ps, err := platform.NewService(helper.GetPlatformTypeEnv(), scheme)
if err != nil {
return nil, errors.Wrap(err, "unable to create platform service")
}
return &Reconcile{
client: k8sClient,
service: gerrit.NewComponentService(ps, k8sClient, scheme),
log: log.WithName("gerrit-project"),
}, nil
}
func (r *Reconcile) SetupWithManager(mgr ctrl.Manager, syncInterval time.Duration) error {
pred := predicate.Funcs{
UpdateFunc: isSpecUpdated,
}
go r.syncBackendProjects(syncInterval)
err := ctrl.NewControllerManagedBy(mgr).
For(&gerritApi.GerritProject{}, builder.WithPredicates(pred)).
Complete(r)
if err != nil {
return fmt.Errorf("failed to setup GerritProject controller: %w", err)
}
return nil
}
func isSpecUpdated(e event.UpdateEvent) bool {
oo, ok := e.ObjectOld.(*gerritApi.GerritProject)
if !ok {
return false
}
no, ok := e.ObjectNew.(*gerritApi.GerritProject)
if !ok {
return false
}
return !reflect.DeepEqual(oo.Spec, no.Spec) ||
(oo.GetDeletionTimestamp().IsZero() && !no.GetDeletionTimestamp().IsZero())
}
//+kubebuilder:rbac:groups=v2.edp.epam.com,namespace=placeholder,resources=gerritprojects,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=v2.edp.epam.com,namespace=placeholder,resources=gerritprojects/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=v2.edp.epam.com,namespace=placeholder,resources=gerritprojects/finalizers,verbs=update
func (r *Reconcile) Reconcile(ctx context.Context, request reconcile.Request) (result reconcile.Result, resError error) {
reqLogger := r.log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
reqLogger.V(2).Info("Reconciling GerritProject has been started")
var instance gerritApi.GerritProject
if err := r.client.Get(context.TODO(), request.NamespacedName, &instance); err != nil {
if k8sErrors.IsNotFound(err) {
reqLogger.Info("instance not found")
return
}
return reconcile.Result{}, errors.Wrap(err, "unable to get GerritProject instance")
}
defer func() {
if err := r.client.Status().Update(context.Background(), &instance); err != nil {
reqLogger.Error(err, "unable to update instance status")
}
}()
if err := r.tryToReconcile(ctx, &instance); err != nil {
reqLogger.Error(err, "unable to reconcile GerritProject")
instance.Status.Value = err.Error()
return reconcile.Result{RequeueAfter: requeueTime}, nil
}
instance.Status.Value = helper.StatusOK
return
}
func (r *Reconcile) tryToReconcile(ctx context.Context, instance *gerritApi.GerritProject) error {
cl, err := helper.GetGerritClient(ctx, r.client, instance, instance.Spec.OwnerName, r.service)
if err != nil {
return errors.Wrap(err, "unable to init gerrit client")
}
_, err = cl.GetProject(instance.Spec.Name)
if err != nil && !gerritClient.IsErrDoesNotExist(err) {
return errors.Wrap(err, "unable to get project")
}
prj := gerritClient.Project{
Name: instance.Spec.Name,
Description: instance.Spec.Description,
Parent: instance.Spec.Parent,
Branches: instance.Spec.Branches,
CreateEmptyCommit: instance.Spec.CreateEmptyCommit,
Owners: instance.Spec.Owners,
PermissionsOnly: instance.Spec.PermissionsOnly,
RejectEmptyCommit: instance.Spec.RejectEmptyCommit,
SubmitType: instance.Spec.SubmitType,
}
if gerritClient.IsErrDoesNotExist(err) {
if err := cl.CreateProject(&prj); err != nil {
return errors.Wrap(err, "unable to create gerrit project")
}
} else {
if err := cl.UpdateProject(&prj); err != nil {
return errors.Wrap(err, "unable to update project")
}
}
if err := helper.TryToDelete(ctx, r.client, instance, finalizerName,
r.makeDeletionFunc(cl, instance.Spec.Name)); err != nil {
return errors.Wrap(err, "error during TryToDelete")
}
return nil
}
func (*Reconcile) makeDeletionFunc(gc gerritClient.ClientInterface, projectName string) func() error {
return func() error {
if err := gc.DeleteProject(projectName); err != nil {
return errors.Wrap(err, "unable to delete project")
}
return nil
}
}
func SyncInterval() time.Duration {
value, ok := os.LookupEnv(syncIntervalEnv)
if !ok {
return defaultSyncInterval
}
interval, err := time.ParseDuration(value)
if err != nil {
return defaultSyncInterval
}
return interval
}